tabularius: publish board projection - #2001
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Multi-agent review roll call (CodeRabbit and Claude review automatically. Reviewers: post substantive findings only. Authors/agents: address every thread, push fixes to this branch, reply and resolve, then re-request review.) |
|
Important Review skippedReview was skipped as selected files did not have any reviewable changes. 💤 Files selected but had no reviewable changes (1)
⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
To use Codex here, create a Codex account and connect to github. |
…atch receipt
Every serial dispatch receipt has 409'd at broker registration since 2026-07-19:
ConductError: conduct broker rejected request (409):
{"detail": "session_id is already registered to another identity"}
`register()` binds agent/surface/session_id from the authenticated principal and then rejects
any re-registration whose WHOLE identity object differs. Post-binding those three are forced
equal, so the only fields that can still differ are `provider_identity` and `native_run_id` —
both client-declared. That check is therefore structurally incapable of catching an authority
mismatch (`session_principals`, two lines later, is what guards authority); it can only ever
fire on cosmetic provider drift. Since every relay call site passes a FIXED session_id literal,
a literal is claimable exactly once and the loser is refused forever.
This is the #1408 relay freeze, recurring on the fields the #1408 fix did not normalize —
`_bind_conductor_identity` carries the comment describing exactly this failure mode. Reproduced
against the real broker with ONE principal throughout:
#1 provider_identity='limen-cli' : OK
#2 same again (idempotent re-register) : OK
#3 provider_identity='limen-cli-2' : CONFLICT
#4 'limen-cli-2' again (permanent?) : CONFLICT
#5 native_run_id set, provider unchanged : CONFLICT
#6 original identity still works : OK
The freeze is per-literal, which is why the symptom looked so specific: writers using their own
literals (`harvest`, `dispatch-async/reserve`, `heal-board/lifecycle-repair`) kept landing
receipts all month — the published projection holds 177 August receipts — while the one path
that records a *dispatch* was silently dead. `logs/throughput-governor.jsonl` then read
`dispatched: 0` on every pass and pinned the jules lane in `bootstrap` at 25/day, making the
requested 100/day structurally unreachable (#1995).
Fix: a refused literal falls back once to an id keyed on exactly the identity fields the keeper
compares but does not normalize.
A FALLBACK, NOT ALWAYS-KEYED — and the first cut of this fix got that wrong. The relay session id
is not internal plumbing: the keeper stamps it into `session_id` on every projection event, so it
is part of the recorded receipt that harvest and 14 tests read. Keying it unconditionally renames
an observable identifier estate-wide to fix a condition that only arises once a literal is already
poisoned (14 tests failed on exactly that). So the healthy path keeps the stable literal and only a
refused one falls back — which also makes the fallback legible: a keyed `session_id` in a
dispatch_log entry IS the signal that its literal is frozen.
Classified on the STATUS, never the prose — ConductError's own documented contract, since three
keepers word this refusal three ways. Both 409 shapes (frozen identity, bound principal) mean one
thing here: that literal is not mine to use. Registering a different id cannot touch the session
the keeper is protecting, and every authority check still applies to whatever this client does
register. A non-conflict failure is re-raised untouched.
Verified live against the poisoned keeper: a real `limen dispatch --agent jules --limit 1 --live`
now gets PAST registration and reaches `submit()`, where it stops on a different and legitimate
409 — `task ORG-artist-organ-face-0705 exact revision moved` — because the local projection has
been frozen at `track.date: 2026-07-26` since the board-publication PR rung was retired (#2001
carries that backlog, #2014 the drift that blocks it). Stacked defects; this is one of four.
4 regression tests: the healthy path keeps the literal; a frozen literal falls back and still
relays; a non-conflict failure is never retried; and the real ConductBroker is driven through the
freeze and then through the keyed id. cli/tests/test_tabularius.py: 34 passed.
Refs #1995
|
HOLD is correct, and the blocker now has an owner.
All 12 are The repair could not happen because every self-heal organ reads the local projection, where #2014 opens that loop — So the sequence is: #2014 merges → the beat runs the canonical reconcile (it is a loop-body edit, Leaving this open rather than closing it: the backlog it carries is real (+107 tasks, +177 receipts) |
… froze for 12 days (#2016) THE MISSING HALF OF A RETIREMENT. `preserve_board_projection` was retired to `PreserveResult(skipped=True, reason="remote-keeper-owns-projection")` — correctly, since a local process must never commit or push the canonical board. But that function was ALSO the only thing that opened the publication PR, and nothing replaced it: $ grep -rn "BOARD_PUBLICATION_TITLE" --include='*.py' cli/src scripts cli/tests cli/src/limen/tabularius.py:82:BOARD_PUBLICATION_TITLE = "tabularius: publish board projection" A dead constant. The keeper kept publishing to `tabularius/board-projection` and nothing carried it to `main`. Last publication merged: 2026-07-26 (#1569) — exactly the `track.date` the board was still frozen at 12 days later. This was NOT a sync failure. The live checkout's `tasks.yaml` was byte-identical to `origin/main` (md5 25aeba82…), fully converged; the publication branch simply sat +107 tasks and +177 dispatch receipts ahead (md5 57c1bca1…). A current checkout with a dead rung. Everything downstream reads the frozen copy: - `dispatch` SELECTS from it — ORG-artist-organ-face-0705 was `open` locally and `failed_blocked` canonically, so a jules session was launched for a task the keeper considers blocked; - every receipt ticket computes its compare-and-swap precondition from it, so the keeper answers `409 task <id> exact revision moved`; - `lane_throughput_window` counts 0 dispatches in it, which pinned the jules lane in `bootstrap` at 25/day and made the requested 100/day structurally unreachable (#1995). Fix: a beat rung that opens or reports the publication PR. It NEVER merges and NEVER pushes — `merge-policy.sh` decides and the beat's merge rung owns it. Idempotent: an already-open PR is a no-op, and so is a projection branch whose commits change no board content (gated on the tasks.yaml tree, not the commit count, because the branch periodically reconciles main into itself). Fail-open on every unreadable condition; this rung must never take a beat down. WHY SHELL. Opening a PR is an outward action, and check-effectors Class C exists because a PreToolUse(Bash) hook can never see subprocess.run(["gh", ...]) inside a Python module. The `gh` call belongs on the Bash rail where that guard can reach it — the same reason session-plan.py prints its `gh` command instead of making it. Verified against the live estate: correctly detects the hand-opened #2001 and no-ops. publish-board-pr: PR #2001 already open for tabularius/board-projection -> main (owner: the beat's merge rung) check-params, check-effectors and check-runner-coverage all clean; the three new parameters are declared in the panel. NOTE: this is a loop-body edit, so the running daemon needs a kickstart to load it. Refs #1995
…atch receipt (#2005) Every serial dispatch receipt has 409'd at broker registration since 2026-07-19: ConductError: conduct broker rejected request (409): {"detail": "session_id is already registered to another identity"} `register()` binds agent/surface/session_id from the authenticated principal and then rejects any re-registration whose WHOLE identity object differs. Post-binding those three are forced equal, so the only fields that can still differ are `provider_identity` and `native_run_id` — both client-declared. That check is therefore structurally incapable of catching an authority mismatch (`session_principals`, two lines later, is what guards authority); it can only ever fire on cosmetic provider drift. Since every relay call site passes a FIXED session_id literal, a literal is claimable exactly once and the loser is refused forever. This is the #1408 relay freeze, recurring on the fields the #1408 fix did not normalize — `_bind_conductor_identity` carries the comment describing exactly this failure mode. Reproduced against the real broker with ONE principal throughout: #1 provider_identity='limen-cli' : OK #2 same again (idempotent re-register) : OK #3 provider_identity='limen-cli-2' : CONFLICT #4 'limen-cli-2' again (permanent?) : CONFLICT #5 native_run_id set, provider unchanged : CONFLICT #6 original identity still works : OK The freeze is per-literal, which is why the symptom looked so specific: writers using their own literals (`harvest`, `dispatch-async/reserve`, `heal-board/lifecycle-repair`) kept landing receipts all month — the published projection holds 177 August receipts — while the one path that records a *dispatch* was silently dead. `logs/throughput-governor.jsonl` then read `dispatched: 0` on every pass and pinned the jules lane in `bootstrap` at 25/day, making the requested 100/day structurally unreachable (#1995). Fix: a refused literal falls back once to an id keyed on exactly the identity fields the keeper compares but does not normalize. A FALLBACK, NOT ALWAYS-KEYED — and the first cut of this fix got that wrong. The relay session id is not internal plumbing: the keeper stamps it into `session_id` on every projection event, so it is part of the recorded receipt that harvest and 14 tests read. Keying it unconditionally renames an observable identifier estate-wide to fix a condition that only arises once a literal is already poisoned (14 tests failed on exactly that). So the healthy path keeps the stable literal and only a refused one falls back — which also makes the fallback legible: a keyed `session_id` in a dispatch_log entry IS the signal that its literal is frozen. Classified on the STATUS, never the prose — ConductError's own documented contract, since three keepers word this refusal three ways. Both 409 shapes (frozen identity, bound principal) mean one thing here: that literal is not mine to use. Registering a different id cannot touch the session the keeper is protecting, and every authority check still applies to whatever this client does register. A non-conflict failure is re-raised untouched. Verified live against the poisoned keeper: a real `limen dispatch --agent jules --limit 1 --live` now gets PAST registration and reaches `submit()`, where it stops on a different and legitimate 409 — `task ORG-artist-organ-face-0705 exact revision moved` — because the local projection has been frozen at `track.date: 2026-07-26` since the board-publication PR rung was retired (#2001 carries that backlog, #2014 the drift that blocks it). Stacked defects; this is one of four. 4 regression tests: the healthy path keeps the literal; a frozen literal falls back and still relays; a non-conflict failure is never retried; and the real ConductBroker is driven through the freeze and then through the keyed id. cli/tests/test_tabularius.py: 34 passed. Refs #1995
|
Blocker inventory corrected — this PR is held by two independent gates, not one. My earlier comment named only the first.
The second one compounds: the baseline pins Related, shipped in #2019: Both gates are correct to refuse. I have not tried to route around either. |
…silently (#2019) `check-board-partition.py` states its ratchet three times — module docstring ("the baseline may only shrink"), the baseline file header ("this list may only SHRINK"), and a test literally named `test_the_baseline_only_shrinks`. The code enforced it zero times: `--update` called `_write_baseline(current)`, re-pinning to whatever is on the board right now, additions included. The gap is not theoretical, it is a loaded trap in the gate's own output. When a finding clears, `--check` prints `note baseline entry no longer reproduces (run --update to drop it)`. Measured 2026-08-07 on PR #2001 (the board publication backlog): 15 entries had cleared and were advertising exactly that instruction while 8 NEW partner-lane findings were failing in the same run. Following the gate's own advice to clear the noise would have silently accepted all 8 — new named-third-party disclosures on a PUBLIC head, landed by an agent tidying up, with the diff reading as a routine re-pin. Why it went unenforced: `--update` is this predicate's only write, and its target was not redirectable. Overriding LIMEN_ROOT to point it elsewhere also relocates the partner-lane registries, so `findings()` raises PartitionRegistryError before any baseline logic runs. So the one surface that could break the invariant was the one surface no test could reach, and `test_the_baseline_only_shrinks` asserts at `--check`, which never writes. An untestable write is how a stated rule survives with no code behind it. - `--update` is shrink-only: drops cleared entries, refuses to add, exit 1, naming each would-add line. A refused run leaves the file byte-identical — a partial write is the same leak, just quieter. - Growth stays possible for a genuinely accepted disclosure, but only behind an explicit `--accept-new-disclosures`, because that is a disclosure decision and it should have to be spelled out in the command someone ran. - BASELINE is now redirectable via LIMEN_BOARD_PARTITION_BASELINE (declared in the parameter panel), independently of LIMEN_ROOT — which is what finally makes the write testable. - 5 tests covering the write surface: refuses growth, still shrinks bare, grows only when accepted out loud, a refused growth does not quietly bank the shrink, and an unset env var still resolves to the tracked file. Verified live against the real canonical board (6.1MB, extracted from origin/tabularius/board-projection): refuses all 8, exit 1, baseline untouched, and it names the 15-drop temptation instead of acting on it. The pre-fix `--update` was deliberately NOT run — it has no redirect and would have rewritten the tracked baseline, which is itself the proof of the gap. A/B is source-level: zero fix markers in `origin/main`'s copy, whose update path is a bare `_write_baseline(current)` → `return 0`. check-params OK (949 declared), ruff clean, 52 partition tests pass. Refs #1780, #2001
…lt (#2070) `check-board-partition` has held PR #2001 red since ~16:16 UTC on 8 fresh findings naming private partner lanes. The gate's docstring names "the broker scrub" as those rows' owner four times. It cannot be built as described, and the reason is structural rather than neglect: - dispatch.py:1846 hard-requires task.repo ("remote lane needs GitHub owner/repo") - dispatch.py reads tasks.yaml directly (644, 654, 701, 754) - tasks.yaml is tracked and organvm/limen is PUBLIC One file must carry `repo` (or dispatch cannot route) AND not carry partner attribution (or it is a public disclosure). No redaction placed in that file satisfies both, so a scrub that redacts partner rows silently starves the work-supply for exactly the lanes it protects — invisible, because the board still looks full. This also invalidates the obvious fix, which was this plan's own first draft: teaching generate-revenue-backlog.py and constellation/seed-tasks.py to redact before submission breaks dispatch. Recorded here so it is not re-proposed. The 425-line baseline is therefore not a backlog awaiting a scrub — it IS the estate's de-facto answer, accept-and-pin, 200 `row` entries deep. Applied to a single public board, IF-ATOM-HOMING ("counts, ids and generalizations cross into the public tree; a statement never does") has one satisfiable reading: a private board projection. That is an architecture build, not a patch. Both remaining paths are disclosure decisions, so neither was taken unilaterally. Until one is chosen PR #2001 stays red, which is the gate working, not a new failure. Issue: #2069 Claude-Session: https://claude.ai/code/session_01RzELciaZt1hwavDvA4ey4G Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
… + file L-FRICTION-FEDERATION-SOURCE (#2454) The board-partition lever claimed the three red self-repair rungs as its designed consequence; live diagnosis proved them independent defects (fixed in #2447/#2448/#2449/#2453 + worker deploy) — the lever's live consequence narrows to publication PR #2001 and the local private-custody cutover. Also files the friction-federation data-source decision surfaced by the notification-wall triage: the cloud routine's store has had no feed for 73+ days, and every fix arm is a disclosure or fleet-contract decision. Claude-Session: https://claude.ai/code/session_011nj7dCkmwjt6Jqopop5ZHx Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…e + hydrate it every beat (#2461) The partition's worker half shipped in #2299: canonical state lives in the authenticated Durable Object and the published tasks.yaml is a counts-only aggregate. The local half was missing — nothing hydrated private custody and nothing knew the aggregate is not a work board. Landing publication PR #2001 in that state would have handed every local consumer a board with ZERO tasks, which reads exactly like 'there is no work'. - private_board.py becomes the single authority on which board local code OPERATES on. operational_board_path() derives custody from the public file's SHAPE (a cheap 4KB header probe, never a 5.8MB parse): explicit LIMEN_PRIVATE_TASKS wins, an aggregate public projection resolves to $LIMEN_PRIVATE_ROOT/board/canonical.yaml, and a full board is still its own custody. Missing custody under an aggregate raises PrivateCustodyUnavailable naming the fix — loud beats empty. - cli.resolve_tasks_path() derives through it, so LIMEN_TASKS pointing at the post-cutover public file resolves by shape rather than by name. - prints what local operation resolves to (exit 3 = aggregate with no custody), so the beat and every operator share one answer. - scripts/hydrate-private-board.sh + the hydrate-private-board rung refresh custody from the keeper BEFORE any board reader runs (stale custody produces stale CAS preconditions — the 2026-08-15 'exact revision moved' wedge). Self-arming off the public shape: a pre-cutover no-op with no flag to flip. Fail-open on keeper outages with custody present, fail-loud without it. Verified live: the rung no-ops pre-cutover, and --force hydrates 3151 tasks from the keeper. test_private_board 8/8 (incl. the aggregate-without-custody must-raise property); private_board + board_partition + dispatch 340/340; check-params OK (994 declared, no new hardcodes). Claude-Session: https://claude.ai/code/session_011nj7dCkmwjt6Jqopop5ZHx Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
… is lawful (#2469) * feat(board): derive private custody from the public projection's shape + hydrate it every beat The partition's worker half shipped in #2299: canonical state lives in the authenticated Durable Object and the published tasks.yaml is a counts-only aggregate. The local half was missing — nothing hydrated private custody and nothing knew the aggregate is not a work board. Landing publication PR #2001 in that state would have handed every local consumer a board with ZERO tasks, which reads exactly like 'there is no work'. - private_board.py becomes the single authority on which board local code OPERATES on. operational_board_path() derives custody from the public file's SHAPE (a cheap 4KB header probe, never a 5.8MB parse): explicit LIMEN_PRIVATE_TASKS wins, an aggregate public projection resolves to $LIMEN_PRIVATE_ROOT/board/canonical.yaml, and a full board is still its own custody. Missing custody under an aggregate raises PrivateCustodyUnavailable naming the fix — loud beats empty. - cli.resolve_tasks_path() derives through it, so LIMEN_TASKS pointing at the post-cutover public file resolves by shape rather than by name. - prints what local operation resolves to (exit 3 = aggregate with no custody), so the beat and every operator share one answer. - scripts/hydrate-private-board.sh + the hydrate-private-board rung refresh custody from the keeper BEFORE any board reader runs (stale custody produces stale CAS preconditions — the 2026-08-15 'exact revision moved' wedge). Self-arming off the public shape: a pre-cutover no-op with no flag to flip. Fail-open on keeper outages with custody present, fail-loud without it. Verified live: the rung no-ops pre-cutover, and --force hydrates 3151 tasks from the keeper. test_private_board 8/8 (incl. the aggregate-without-custody must-raise property); private_board + board_partition + dispatch 340/340; check-params OK (994 declared, no new hardcodes). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011nj7dCkmwjt6Jqopop5ZHx * feat(board): make the board gates shape-aware so the public aggregate is lawful tasks.yaml has two lawful shapes — the pre-cutover full board and the keeper's counts-only public aggregate — and two organs misread the second in opposite, equally damaging ways: - validate.yaml carried the board contract as an INLINE heredoc that assumed the full-board shape, so the aggregate failed with 'Missing version'. That is the single gate that has held publication PR #2001 red. The contract moves into scripts/validate-task-board.py (the named predicate, runnable locally, testable) which now branches on shape. The aggregate arm is deliberately STRICTER than the full-board arm: zero task rows AND no task-material field anywhere in the document, because the partition exists precisely so partner attribution never reaches a public surface. --require-shape pins an expected shape for callers that know which side of the cutover they are on. - heal-board.py reads zero task rows as a COLLAPSE (total <= floor) and restores the board from git HEAD. Post-cutover that is an organ undoing the architecture it lives in, every beat, forever. A lawful aggregate is now recognized before the collapse arm; lifecycle repair runs against private custody (repair_lifecycle takes the board it repairs), and missing custody is loud rather than a restore. Verified against the REAL published aggregate (origin/tabularius/board-projection): the exact document that fails 'Missing version' today now validates (total=3156, zero rows, no task material). Full board still validates unchanged (3111 tasks). New suite 6/6; check-board-partition clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011nj7dCkmwjt6Jqopop5ZHx * fix(board): run status semantics before the schema contract CI caught a real regression: cli/tests/test_validate_task_board.py feeds deliberately minimal fixtures (a two-row board proving a duplicate id, no version or budget), and running the newly-absorbed contract check first answered every one of them with 'missing version' — hiding the defect each test actually exercises. Order changes which failure surfaces, never whether the contract is enforced: a real board missing its version still fails, just after its statuses are judged. test_validate_task_board + test_board_aggregate_shape 8/8; the live full board and the real published aggregate both still validate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011nj7dCkmwjt6Jqopop5ZHx --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…RTITION-DECISION) (#2480) The operator directed Option A on 2026-08-15 and the build landed in four merged PRs. B was never taken, so no partner-lane attribution was published — the fork is closed except for one mechanical step, and the lever should say so rather than keep presenting a choice that has been made. Built: private custody DERIVED from the public projection's shape plus a hydrate-private-board beat rung (#2461); validate and heal-board made shape-aware so the counts-only aggregate is lawful rather than 'Missing version' / 'collapsed' (#2469); all 24 board readers routed through the custody resolver behind a shrink-only predicate, with PrivateCustodyUnavailable inheriting BaseException so a reader's broad except cannot turn missing custody into a silent 'zero tasks' (#2478). Remaining: merge publication PR #2001, which needs one fresh keeper republish to re-run the now-fixed validate gate — blocked by the keeper's Durable Object write exhaustion (#2054), not by any decision of the operator's. The lever stays open only so that last step keeps a visible owner; close it when #2001 merges. Claude-Session: https://claude.ai/code/session_011nj7dCkmwjt6Jqopop5ZHx Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Restores the publication→
mainmerge that has been dead since 2026-07-26 (last one: #1569).What this carries
tasks.yamlonly — 5762 insertions / 760 deletions, 732 commits of keeper-publishedprojection. Concretely: +107 tasks, +177 dispatch receipts (all agents), and the budget
track advances from
2026-07-26to2026-07-27+.Why it was stuck
preserve_board_projectionwas retired toPreserveResult(skipped=True, reason="remote-keeper-owns-projection")— correctly, since a local process must never commit orpush the canonical board. But that function was also the only thing that opened this PR.
BOARD_PUBLICATION_TITLEhas been a dead constant ever since: nothing replaced thePR-opening half. The keeper kept publishing to
tabularius/board-projection(latest committoday 11:57) and nothing carried it to
main.Why it matters — this is the #1995 root cause
The live checkout's
tasks.yamlis byte-identical toorigin/main, so with the branch unmergedthe local projection froze at
track.date: 2026-07-26. Downstream:dispatchselects tasks from the frozen board — e.g.ORG-artist-organ-face-0705isopenlocally andfailed_blockedcanonically, so a jules session was launched for a taskthe keeper considers blocked;
answers
409 task <id> exact revision moved;lane_throughput_windowreads 0 jules dispatches and pins the lane inbootstrapat 25/day,making the requested 100/day structurally unreachable (Dispatch creates real jules work but records no receipt — the lane is pinned at 25/day and the daily cap does not bind #1995).
Per
ci.yml's board fast lane, a board-projection PR implicates no code, so the matrix runspost-merge for
check-main-green's exact-head evidence rather than on this PR's critical path.Refs #1995