Skip to content

feat(sync): one-way insert-only push to a foreign team board#274

Merged
tieubao merged 2 commits into
masterfrom
feat/notion-taskboard-push
Jul 18, 2026
Merged

feat(sync): one-way insert-only push to a foreign team board#274
tieubao merged 2 commits into
masterfrom
feat/notion-taskboard-push

Conversation

@tieubao

@tieubao tieubao commented Jul 18, 2026

Copy link
Copy Markdown
Member

What

Adds a notion-taskboard app to the board-sync engine (lib/sync): a one-way,
insert-only push of a repo's BACKLOG.md rows out to a foreign, team-OWNED
Notion board. Implements ops-toolkit board row ID-138 (design locked
2026-06-16) as dwarves-kit SPEC-003.

Consumer wiring (dfoundation DF- board -> the Dwarves Notion Task Board) is a
separate PR in tieubao/dfoundation; tenant IDs (the Task Board UUID, Han's
Notion person id) live there, never in this repo.

Contract (why it is safe against a live team board)

  • One-way: board is the source of truth; the sink is never read for merge
    and the board file is never written.
  • Insert-only: fields set only on page-create; a row already in the local
    sync-state map is never re-pushed, so team edits are never overwritten.
  • Never mutates the team schema: status/priority/weight map to the board's
    OWN option names via config; preflight reads the schema (never PATCHes) and
    rejects an unknown option instead of silently auto-creating a select option.
  • No duplicates on failure: state is checkpointed after each create, so a
    mid-batch ntn error never re-pushes a page next run.

Isolation

The two-way mesh (Reminders/Notion/Hermes/Multica) is untouched: create-only is
a separate planner (plan_create_only) + apply path, dispatched on a source
attribute. parse_board stays ID--only for the two-way path (its id-minting
siblings stay consistent); only the one-way READ view accepts any [A-Z]+-NNN
prefix, and it never mints or writes ids.

Notion access

Through the ntn CLI (keychain auth), per Han's standing Notion rule. No raw
API tokens; no live team-board writes in this PR.

Tests / proof

  • bash tests/test-sync.sh -> 89 passed (76 pre-change + 13 new; two-way
    suites unchanged).
  • Negative controls: dropped-not-pushed, already-pushed-frozen + idempotent
    re-run (board byte-identical), partial-batch-failure-never-duplicates,
    unknown-option-rejected, dry-run-validates, missing-config-fails-closed.
  • Meta suite 715/715, docs-wiring 22/22, config-registry 19/19, sync-dispatch
    5/5 green.
  • Fixtures/dry-run only, no live Notion writes. Live activation is an operator
    step (dfoundation .kit.toml + bin/board sync --dry-run then real).
  • Reviewed by kit:code-reviewer (architecture) + kit:advisor (critique, fable);
    6 confirmed findings applied (see the second commit).

Proof: lib/sync/docs/proof-of-done.md; spec: lib/sync/docs/specs/SPEC-003-oneway-create-push.md.

tieubao added 2 commits July 18, 2026 20:28
Add a `notion-taskboard` app: a one-way, insert-only push of a repo's
board rows out to a team-OWNED Notion board (implements ops-toolkit
ID-138, design locked 2026-06-16). The board is the source of truth; the
sink is never read for merge and the board file is never written. Fields
are set only on page-create, so a team member's later edits on the card
are never overwritten. The local sync-state map is the identity index, a
row is pushed exactly once.

Status/Priority/Weight map to the team board's own option names via
`.kit.toml [sync]` config, so the sink never mutates the team schema (it
does one benign read to resolve the data source, never a PATCH). Dropped
rows are skipped; a board state absent from the map uses status_default
or fails closed with guidance rather than guessing a column.

The two-way mesh and its four live adapters (Reminders/Notion/Hermes/
Multica) are untouched: create-only is a separate planner + apply path,
dispatched on a source attribute. The engine's board parser is
generalized from ID- to any [A-Z]+-\d+ prefix so it reads every adopted
repo's board (e.g. dfoundation DF-NN).

Fixtures only, no live team-board writes. 12 new cases incl. negative
controls (dropped not pushed, already-pushed row frozen, unmapped status
fails closed, skip-tag filter, missing config fails closed).

See lib/sync/docs/specs/SPEC-003-oneway-create-push.md.
From kit:code-reviewer (architecture) + kit:advisor (critique):

- No partial-batch duplicates: apply() checkpoints state after each
  create via an on_created callback, and a preflight validates the whole
  batch (maps resolve, options exist) before any POST. A mid-batch ntn
  failure never leaves a created page unrecorded, so the next run never
  re-pushes it. Preflight also runs before the dry-run return, so
  --dry-run surfaces config errors with zero writes.
- Never auto-create a team option: preflight reads the board schema
  (never PATCHes it) and rejects any mapped option name not already on
  the board; select-type props no longer silently gain options on a
  typo. Prop types are auto-detected from the schema.
- Two-way mesh stays ID-only: reverted TITLE_RE and gave parse_board a
  strict_id flag (default True). The one-way path reads with
  strict_id=False (any [A-Z]+-NNN) but never mints ids;
  warn_duplicate_ids takes the same flag so a DF- board still warns.
- ensure_binding discards a cached binding for a different db_id (a
  repointed target never keeps pushing to the old board).
- Dropped the unused skip_statuses param and the dead intake config key.
@tieubao
tieubao merged commit 7f29112 into master Jul 18, 2026
2 checks passed
@tieubao
tieubao deleted the feat/notion-taskboard-push branch July 18, 2026 13:47
tieubao added a commit that referenced this pull request Jul 18, 2026
Reconciles the board against PRs #264-#277: strip-personal-paths,
rustup-rustfmt, proof-ledger per-repo override scoping, learn-propose
precision, five-legs rename, plain-words cheap cluster + glossary,
behavioral-test tiering, sync cron + cockpit-mirror re-land, mega
folder, and rung-4 redteam ledger. ID-020/ID-036 drop their stale
2026-06-27 open-status notes now that the absence-check and
hook-fallback gaps they tracked are closed. New rows (ID-305..308)
capture the deferred remainders: learn-propose precision follow-up,
the redteam ledger row itself, the plain-words semantic-rename
migration cluster, and the ADR-0034 orphan-folder question
(HAN-GATED).

ID-138 (notion-taskboard sync, PR #274) is an ops-toolkit board row;
no matching row exists on this board, so it is left for the
ops-toolkit board-scribe pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant