Problem
When a machine-generated artifact is synced to Notion via @overeng/notion-md, there is currently no clean, Notion-native way to mark the resulting page as generated (or carry any provenance). The only honest options today are:
- An HTML comment / callout block in the body — visible clutter, not filterable, and notion-md has no typed handle to keep it canonical (a human edit or re-pull can move/duplicate it).
- A real DB property (
generated checkbox/select) — fully queryable, but only works when the page is a data_source/database row.
The blocker is upstream and parent-type-specific: for a page-parented page, the Notion API only allows updating the title property; arbitrary properties (select/checkbox/rich_text/…) require a data_source parent. See https://developers.notion.com/reference/patch-page ("properties … can only be used if the page's parent is a data source, aside from updating the title").
So a generated/provenance marker that should work across a page-parented hierarchy (e.g. a generated docs tree) has no first-class home in the current NmdFrontmatterV2 envelope.
Relevant current surfaces
- Envelope:
NmdFrontmatterV2 — packages/@overeng/notion-effect-client/src/nmd.ts (notion_md = { …, parent, page, properties }).
- Two write paths in
packages/@overeng/notion-md/src/live.ts:
updatePageProperties → NotionPages.update({ properties }) (rejected for non-title props on page-parented pages).
updatePageMetadata → NotionPages.update({ title?/icon?/cover?/… }) — title/icon/cover are valid on any parent.
- The only parent-agnostic, round-tripped non-property page metadata is
icon / cover (NmdPageState, nmd.ts). There is no page-level description/arbitrary-metadata field for standalone pages.
Proposed general mechanism
Add one optional, schema-validated provenance field to the notion_md envelope, and let notion-md choose the Notion surface per parent type at push time. It is general (a small structured record, not a boolean), and the projection is the package's responsibility, not the caller's:
notion_md:
# …
provenance: # optional; absent ⇒ no marker (back-compat)
generated: true
generator: "<source that produced this page>"
source_hash: "sha256:…" # free, schema-validated k/v
Projection (owned by notion-md, derived from parent):
- data_source / database parent → write a real DB property (configurable
generated checkbox/select + optional rich_text for generator/source). Fully filterable; reuses the existing property encoder + write path.
- page / block / workspace parent → set the page icon to a stable provenance icon (parent-agnostic, round-tripped via
updatePageMetadata) and keep the record in frontmatter as source of truth. Optionally a single notion-md-managed marker block kept canonical (not an unmanaged body block). Searchable, not filterable — the genuine upstream ceiling for standalone pages.
This degrades honestly: standalone pages get search/visual provenance, DB rows get native filtering, and the caller never has to know the difference. Because notion-md owns both the write and the read-back of whichever surface it picked, it round-trips cleanly.
Use cases beyond the originating one
- Generated docs tree (the originating case): a page-parented hierarchy regenerated wholesale from a catalog; each page should self-declare it's generated + which generator/source-hash produced it.
- Synced records in a tracker DB: rows imported/maintained by automation that humans should be able to filter by "machine-managed vs hand-edited" — needs the native DB-property projection.
- Report/digest pages posted by a bot under a parent page: want a visible/searchable "auto-generated, do not hand-edit" provenance without a body banner.
Constraint reference
The page-parented property limitation is exactly the class of inherent Notion-API edge catalogued in #602 — cross-linking for context. This issue is a notion-md feature (envelope field + parent-aware projection), not a datasource-sync child workstream (#698).
Filed from downstream investigation against effect-utils@main.
Posted on behalf of @schickling
| field |
value |
agent_name |
💪 cl2-grit |
agent_session_id |
20af0604-b67f-4c87-a5f5-631198740bab |
agent_tool |
Claude Code |
agent_tool_version |
2.1.160 |
agent_runtime |
Claude Code 2.1.160 |
agent_model |
claude-opus-4-8 |
worktree |
dotfiles/schickling/2026-06-03-scg-v2 |
machine |
dev3 |
tooling_profile |
dotfiles@unknown-dirty |
Problem
When a machine-generated artifact is synced to Notion via
@overeng/notion-md, there is currently no clean, Notion-native way to mark the resulting page as generated (or carry any provenance). The only honest options today are:generatedcheckbox/select) — fully queryable, but only works when the page is a data_source/database row.The blocker is upstream and parent-type-specific: for a page-parented page, the Notion API only allows updating the title property; arbitrary properties (select/checkbox/rich_text/…) require a
data_sourceparent. See https://developers.notion.com/reference/patch-page ("properties … can only be used if the page's parent is a data source, aside from updating the title").So a generated/provenance marker that should work across a page-parented hierarchy (e.g. a generated docs tree) has no first-class home in the current
NmdFrontmatterV2envelope.Relevant current surfaces
NmdFrontmatterV2—packages/@overeng/notion-effect-client/src/nmd.ts(notion_md = { …, parent, page, properties }).packages/@overeng/notion-md/src/live.ts:updatePageProperties→NotionPages.update({ properties })(rejected for non-title props on page-parented pages).updatePageMetadata→NotionPages.update({ title?/icon?/cover?/… })—title/icon/coverare valid on any parent.icon/cover(NmdPageState,nmd.ts). There is no page-level description/arbitrary-metadata field for standalone pages.Proposed general mechanism
Add one optional, schema-validated
provenancefield to thenotion_mdenvelope, and let notion-md choose the Notion surface per parent type at push time. It is general (a small structured record, not a boolean), and the projection is the package's responsibility, not the caller's:Projection (owned by notion-md, derived from
parent):generatedcheckbox/select + optional rich_text for generator/source). Fully filterable; reuses the existing property encoder + write path.updatePageMetadata) and keep the record in frontmatter as source of truth. Optionally a single notion-md-managed marker block kept canonical (not an unmanaged body block). Searchable, not filterable — the genuine upstream ceiling for standalone pages.This degrades honestly: standalone pages get search/visual provenance, DB rows get native filtering, and the caller never has to know the difference. Because notion-md owns both the write and the read-back of whichever surface it picked, it round-trips cleanly.
Use cases beyond the originating one
Constraint reference
The page-parented property limitation is exactly the class of inherent Notion-API edge catalogued in #602 — cross-linking for context. This issue is a notion-md feature (envelope field + parent-aware projection), not a datasource-sync child workstream (#698).
Filed from downstream investigation against
effect-utils@main.Posted on behalf of @schickling
agent_nameagent_session_idagent_toolagent_tool_versionagent_runtimeagent_modelworktreemachinetooling_profile