overengineeringstudio / overengineeringstudio/effect-utils
notion-md: first-class generated/provenance marker for synced pages
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 82
- Forks
- 2
- Avg merge
- 1d 8h
- Merged PRs (30d)
- 121
Description
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 (
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_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/coverare 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
generatedcheckbox/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 |
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with NmdFrontmatterV2 in packages/@overeng/notion-effect-client/src/nmd.ts, then trace updatePageProperties and updatePageMetadata in packages/@overeng/notion-md/src/live.ts. Compare the existing page metadata and property round trips against the parent-type constraints; done means the optional provenance record is validated and consistently written and read back for the supported parent types.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend-api-design
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100