Skip to content

notion-md: first-class generated/provenance marker for synced pages #751

@schickling-assistant

Description

@schickling-assistant

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: NmdFrontmatterV2packages/@overeng/notion-effect-client/src/nmd.ts (notion_md = { …, parent, page, properties }).
  • Two write paths in packages/@overeng/notion-md/src/live.ts:
    • updatePagePropertiesNotionPages.update({ properties }) (rejected for non-title props on page-parented pages).
    • updatePageMetadataNotionPages.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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:notionNotion API client / react / schema packages · Set: manualorigin:agentFiled or primarily produced by an AI agent · Set: AI agent or manualtype:featureNew user-visible or system capability · Set: manual

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions