126 lines
5.2 KiB
Markdown
126 lines
5.2 KiB
Markdown
# M-13 — Notion Structured Export Decision Memo
|
|
|
|
**Status:** BLOCKED on Marko's design decisions — DO NOT IMPLEMENT until
|
|
the four questions below are answered.
|
|
|
|
**Context:** M-13 is the last sub-item in the M-11..14 Wiki v2 block. The
|
|
audit (`docs/plans/WIKI-V2-AUDIT-2026-04-20.md`) noted it's the only item
|
|
with no prior art and needs external-API integration choices. M-11/M-12/
|
|
M-14 shipped in this session; M-13 is queued.
|
|
|
|
## The four questions
|
|
|
|
### Q1 — Auth surface
|
|
|
|
**Option A — Reuse the existing agent's Notion OAuth flow**
|
|
`packages/agent/src/connectors/notion-connector.ts` has a Notion OAuth
|
|
client already wired up. It's scoped to READ for memory ingestion. For
|
|
writes we'd need to broaden the scope (new consent screen text, new
|
|
redirect URI if different, new token column, migration for existing
|
|
connected users).
|
|
|
|
- **Pro:** Single identity per user; the same Notion connection powers
|
|
both "read my Notion into memory" and "write my wiki into Notion."
|
|
- **Con:** Scope creep on the existing consent screen; users who
|
|
consented to read-only feel different when they see "allow writes";
|
|
existing tokens need re-consent.
|
|
|
|
**Option B — Separate Notion write token in Vault**
|
|
Add a new Vault entry `notion-wiki-token` that the user pastes from
|
|
Notion's internal-integration settings (https://www.notion.so/my-integrations).
|
|
|
|
- **Pro:** Zero coupling with the ingest flow; users who don't use
|
|
Notion-as-source can still export; simpler consent story.
|
|
- **Con:** Two Notion creds to manage; power users ask "why does my
|
|
connector not work for export?"
|
|
|
|
**Recommendation:** B for v1. Separate write token. Ships clean without
|
|
OAuth scope migrations; upgrade to unified OAuth if M-29 MS Graph pattern
|
|
(which is v2-deferred anyway) ever lands.
|
|
|
|
### Q2 — Parent-page UX
|
|
|
|
Notion requires a parent page/database when creating child pages. Options:
|
|
|
|
**Option A — Auto-create** a top-level "Waggle Wiki" page on first
|
|
export, remember its page_id in `wiki_pages.notion_root_page_id` (new
|
|
column) or a settings row.
|
|
|
|
**Option B — User selects** at export time. UI either shows a list of
|
|
the user's top-level pages (requires search API call) OR asks for the
|
|
page URL paste.
|
|
|
|
**Recommendation:** B with paste-URL fallback. "Paste the URL of the
|
|
Notion page that should be your wiki's root." Obsidian M-12 already
|
|
established the pattern: prompt for path. Notion prompt for URL.
|
|
|
|
### Q3 — Update vs. insert on re-run
|
|
|
|
Re-running export against the same Notion root: should we UPDATE
|
|
existing Notion pages or create new ones?
|
|
|
|
**Option A — Always update** (keep a `wiki_pages.notion_page_id` column
|
|
keyed by slug). Requires a migration to add the column + handling
|
|
partial-fail cleanup.
|
|
|
|
**Option B — Delete + recreate** on every export. Simpler but loses
|
|
Notion's comment threads, per-block history.
|
|
|
|
**Option C — Delta** only changed pages (detected via `content_hash`).
|
|
Best UX but requires A's column + delta comparison.
|
|
|
|
**Recommendation:** C. Uses the existing `content_hash` from
|
|
`CompilationState.upsertPage` for change detection. Migration adds
|
|
one INTEGER column (`notion_page_id`). Worst case falls back to A
|
|
behavior (update even unchanged pages).
|
|
|
|
### Q4 — Markdown → Notion blocks converter
|
|
|
|
Current wiki markdown is simple (H1/H2/H3, paragraphs, bullet lists,
|
|
occasional blockquotes, no tables per compiler.ts inspection).
|
|
|
|
**Option A — Roll our own** ~150 LOC converter that handles the 5-6
|
|
block types we actually emit.
|
|
|
|
**Option B — Use a library** (`marked` + custom renderer, or
|
|
`@notionhq/client`'s built-in support? Notion's API takes blocks
|
|
directly; no library I know of converts markdown to Notion blocks
|
|
canonically).
|
|
|
|
**Recommendation:** A. The markdown shape is predictable and narrow.
|
|
Writing the 150 LOC means we don't inherit a library's edge-case
|
|
behavior for something we control end-to-end. Tests cover H1-H3,
|
|
paragraphs, bullets, links (become Notion rich_text with link
|
|
property), and YAML frontmatter extraction (drops from the body,
|
|
maps to Notion page metadata).
|
|
|
|
## Proposed v1 scope if all four land as recommended
|
|
|
|
1. Add `notion-wiki-token` Vault entry type.
|
|
2. UI: WikiTab "Export to Notion" button → prompt for (a) vault token
|
|
if not set, (b) root page URL.
|
|
3. Adapter `packages/wiki-compiler/src/adapters/notion.ts`:
|
|
- `writeToNotionWorkspace(pages, { token, rootPageId }): Promise<NotionExportResult>`
|
|
- Markdown-to-blocks converter (H1/H2/H3, paragraphs, bullets, links, blockquotes).
|
|
- Per-page: if `notion_page_id` in cache + content_hash matches: skip; if mismatch: `pages.update` + replace block children; else `pages.create` under root.
|
|
4. Migration: `ALTER TABLE wiki_pages ADD COLUMN notion_page_id TEXT`.
|
|
5. Route: `POST /api/wiki/export/notion { rootPageUrl }` — reads token from Vault.
|
|
6. Tests: unit for the block converter, integration mock for the API client.
|
|
|
|
**Scope estimate with recommendations accepted:** ~1 d (matches audit).
|
|
|
|
**Scope estimate if rewriting auth (Option A Q1):** ~1.5 d + OAuth scope
|
|
migration.
|
|
|
|
## What to do next session
|
|
|
|
1. Marko reviews and picks answers (or proposes alternatives) for Q1-Q4.
|
|
2. Add an M-13 task with the chosen scope.
|
|
3. Execute. First commit: the migration. Second: the adapter + converter
|
|
+ tests. Third: route + UI.
|
|
|
|
---
|
|
|
|
**Author:** Claude (memo queued per S4 session M-13 deferral)
|
|
**Date:** 2026-04-20
|