Skip to content

feat(sync): re-land cockpit mirror extract+diff as a sync channel#272

Merged
tieubao merged 4 commits into
masterfrom
feat/cockpit-mirror-channel
Jul 18, 2026
Merged

feat(sync): re-land cockpit mirror extract+diff as a sync channel#272
tieubao merged 4 commits into
masterfrom
feat/cockpit-mirror-channel

Conversation

@tieubao

@tieubao tieubao commented Jul 18, 2026

Copy link
Copy Markdown
Member

What

First slice of ID-290 / SPEC-002 P2: re-land the legacy board mirror bridge as a channel inside the sync module. Ports the two DETERMINISTIC legs into lib/sync/cockpit.py:

  • Multi-source EXTRACT of a boards.txt registry (every opted-in repo's BACKLOG.md + its active _meta/megagoals/*/ROADMAP.md) into origin-keyed items (<repo>:<id>, megagoals:<repo>/<slug>), honoring the legacy [A-Z]+-[0-9]+ id pattern so prefixed multi-repo boards (BK-, DS-, DF-, ...) pool without ID collisions.
  • Keyed row_hash TRANSFORM/diff (create / unchanged / change / complete) where the git board always wins.

Carries over the two assets the port was required to keep: the row_hash git-wins conflict rule and the live-probed Hermes reachable-state map {triage, ready, blocked, done}. The extract is byte-identical to board-mirror.sh (verified with cmp on a prefixed-id fixture), so the existing bridge NDJSON snapshot is a format-compatible bearing interface for the eventual cutover.

Reachable via a new opt-in route: board mirror --engine sync --dry-run. Legacy stays the default and untouched. A bare --engine sync without --dry-run refuses (exit 64) rather than silently no-op'ing.

Deferred (documented, still on the legacy engine which stays runnable)

The live Hermes LOAD leg, two-way writeback (SPEC-149), snapshot state-shape migration, named-profile config, and retiring mirror/status/writeback to thin aliases. Security carry-over noted in the spec: the LOAD leg must wrap card text through the ported mark_untrusted_* markers.

Why a slice, not the whole port

The legacy engine is ~1000 lines of bash with a live-Hermes LOAD leg and two-way writeback that cannot be CI-tested and are risky to rewrite blind. This slice lands the deterministic, fully-testable core plus origin identity (the P2 engine change everything else builds on) without touching the working live path.

Tests / verification

  • lib/sync/tests/test_cockpit.py: 62 cases, 97% line coverage, negative controls (idempotent re-run, no re-complete of a done row, shipped/dropped + opted-out exclusion, malformed-snapshot skip, prefixed-id, wide-table, registry trailing-token folding).
  • Byte-parity vs the legacy engine confirmed with cmp (incl. prefixed ids + megas).
  • Green: test-sync (122), test-board-mirror (72, legacy untouched), test-board (45), test-board-writeback (53), test-meta (698), shellcheck clean.
  • Reviewed by architecture + security + advisor (Fable) lenses; confirmed findings applied (prefixed-id gap, git-root resolver, engine-flag validation, registry folding, untrusted markers).

Proof: lib/sync/docs/proof-of-done.md. Spec: lib/sync/docs/specs/SPEC-002-sync-mesh.md (P2).

@tieubao
tieubao force-pushed the feat/cockpit-mirror-channel branch 2 times, most recently from cab1619 to da737f8 Compare July 18, 2026 13:50
tieubao added 4 commits July 18, 2026 20:57
Port the two deterministic legs of the legacy board bridge into the sync
module as lib/sync/cockpit.py: multi-source EXTRACT (a boards.txt registry
plus each opted-in repo's active mega-goals) with origin-prefixed identity
(<repo>:ID-NNN, megagoals:<repo>/<slug>), and the keyed row_hash TRANSFORM
diff (create / unchanged / change / complete) where the git board always
wins. Carries over the two assets the port was required to keep: the
row_hash git-wins conflict rule and the live-probed Hermes reachable-state
map {triage, ready, blocked, done}.

The row_hash is byte-identical to the legacy board-mirror.sh engine, so the
existing bridge NDJSON snapshot is a format-compatible bearing interface for
a later cutover. Reachable via a new opt-in route: board mirror --engine
sync --dry-run runs the new planner; a bare --engine sync without --dry-run
refuses (exit 64) rather than silently no-op'ing, because the live Hermes
LOAD leg and two-way writeback are deferred and stay on the legacy engine,
which remains the default and keeps working.

Tests: lib/sync/tests/test_cockpit.py, 52 cases at 98% line coverage with
negative controls (idempotent second run, no re-complete of a done row,
shipped/dropped and opted-out exclusion, malformed-snapshot skip). Legacy
suite test-board-mirror.sh stays 72/72 (fold-in leaves the bridge untouched).
Apply architecture + advisor review findings on the cockpit channel:

- Prefixed-id gap (load-bearing): the extract reused sync_core.parse_board,
  which matches bare ID-NNN only, so every prefixed row (BK-, DS-, DF-, ...)
  was silently dropped and would mass-COMPLETE at a snapshot cutover. Replace
  it with parse_cockpit_board, a faithful port of the legacy pb_rows plus
  column logic: honors [A-Z]+-[0-9]+ (BACKLOG_ID_RE override), splits on the
  raw pipe like the legacy awk, and is column-agnostic (item col 3, notes
  cols 4..NF-2). Extract is now byte-identical to board-mirror.sh on a
  prefixed-id fixture (verified with cmp).
- Repo-root resolution now uses git rev-parse --show-toplevel (was a _meta
  path heuristic), so a root-level BACKLOG.md still finds _meta/megagoals.
- board mirror --engine validates its value (bogus -> exit 64) instead of
  falling through to the legacy live engine on a typo.
- Registry parser folds trailing tokens into the bridge column, matching the
  legacy read -r name path bridge (so "... on trailing" is not opted in).
- Port the MIRROR_UNTRUSTED_* content markers as constants plus helpers so the
  deferred LOAD leg wraps card text by construction and does not drop the
  prompt-injection boundary; documented in the spec's deferred list.

Tests grow to 62 cases (97% coverage) with prefixed-id, wide-table,
BACKLOG_ID_RE, git-root, registry-folding, and marker coverage.
Apply the security review's two Medium findings:

- read_snapshot crashed on a valid-JSON-but-non-object line (null, 42, [1,2]):
  json.loads succeeded, then .get("origin") raised AttributeError, killing the
  whole run and contradicting the function's own "not fatal" contract. Skip any
  non-dict parse result.
- The extract and plan --json paths print raw item/notes from contributor-
  editable board rows. Emit an out-of-band stderr banner marking that content
  as untrusted DATA (the SPEC-147 content-trust boundary), so an operator or
  agent piping the dry-run output downstream gets the warning before the LOAD
  leg's structural marking exists. The markers are deliberately NOT applied to
  the plan fields (that would corrupt the plan the LOAD leg consumes and
  double-mark card text; the LOAD leg marks at card build).

Tests: +7 cases (valid-JSON-non-object snapshot lines, banner on both output
paths); suite now 69 cockpit cases, sync suite 129 green.
@tieubao
tieubao force-pushed the feat/cockpit-mirror-channel branch from da737f8 to 704da7e Compare July 18, 2026 13:57
@tieubao
tieubao merged commit ee4bad6 into master Jul 18, 2026
2 checks passed
@tieubao
tieubao deleted the feat/cockpit-mirror-channel branch July 18, 2026 14:02
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