Skip to content

fix(heal-board): the board self-heal could not see the drift that mattered - #2014

Merged
4444J99 merged 1 commit into
mainfrom
fix/heal-board-cannot-see-canonical-drift
Aug 7, 2026
Merged

fix(heal-board): the board self-heal could not see the drift that mattered#2014
4444J99 merged 1 commit into
mainfrom
fix/heal-board-cannot-see-canonical-drift

Conversation

@4444J99

@4444J99 4444J99 commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Layer 4 of #1995 — and the reason #2001 is correctly red.

The blind spot

Every repair in this organ reads tasks.yaml — the local projection, which mirrors the keeper
only after a board-publication PR merges to main. When that merge stalls, canonical state drifts
somewhere no self-heal rung can see, and each one reports a healthy board while looking at a stale
copy of it.

Measured 2026-08-07. Twelve needs-human-labelled ASK-quicken-* tasks — login, credential,
d2l, delete, send
, and seven escalations — sat at open on the keeper's published projection while
main had them correctly at needs_human:

ASK-quicken-* status
origin/main:tasks.yaml needs_human (12/12)
origin/tabularius/board-projection:tasks.yaml open (12/12)

So this organ's own needs-human reconcile — which exists precisely to stop the fleet re-picking a
human-gated lever — read the local board, found nothing wrong, and did nothing, while the canonical
board was the one offering delete/send atoms to agents.

The loop closes on itself

That same drift held main red on validate-task-board.py (needs-human-in-open), which is what
blocked the publication merge that would have refreshed the local mirror. The drift was protecting
itself from the repair.
Opening that loop is what this PR does.

(The current reclassify-needs-human.py classifier is already correct — 94 KEEP / 0 FLIP / 15 REVIEW
— so this is historical damage from an earlier version, not a live misclassification.)

The fix

--canonical runs the same repairs against the keeper's published projection and submits them
through the authenticated relay with that published board as the explicit compare-and-swap before,
so each ticket's precondition is computed from canonical state instead of the local mirror. Nothing
is written locally; the keeper stays the only board writer.

Read from the publication ref rather than over HTTP because that ref is the projection contract
(the keeper publishes only through it) and a several-MB fetch has no place on a beat rung. An
unreadable ref — shallow clone, unfetched remote — makes the rung a no-op rather than a guess, and a
keeper that has published past this head simply fails the CAS so the next pass re-derives.
Fail-open like its siblings.

Blast radius, measured against the live estate

heal-board: WOULD reconcile canonical drift at origin/tabularius/board-projection:
  0 reopened-done, 12 needs-human, 0 log-mismatch — needs-human: ASK-quicken-login,
  ASK-quicken-credential, ASK-quicken-d2l, ASK-quicken-delete, ASK-quicken-send, ...

Exactly the 12 that block #2001. Nothing else.

Wiring

Beside the TABVLARIVS relay in heartbeat-loop.sh (the other network rung), gated on
LIMEN_BOARD_CANONICAL_HEAL. Both new parameters are declared in the panel — check-params caught
their absence first, which is the registry discipline working as designed.

⚠️ This is a loop-body edit, so the running daemon needs a kickstart to load it.

I did not run the live --canonical apply from this session: the auto-mode classifier blocked the
canonical board write, correctly — a board mutation belongs to the beat's own authority, not an
interactive session. The rung is the durable form regardless.

Tests

2 added: the unreadable-ref no-op, and the structural case — a healthy local board beside a
canonical board carrying the regression, asserting the default pass reports healthy while
--canonical reports the drift, and that the local projection is never written.
cli/tests/test_heal_board.py: 6 passed.

Local pytest-cli has one unrelated environmental failure in this sandbox
(test_workstream_command.py:735 — "configured remote origin is unavailable"); CI is the arbiter.

Non-deploy paths.

Refs #1995

Copilot AI lite review requested due to automatic review settings August 7, 2026 16:45
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@4444J99, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 3 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 20efe551-a4c6-498a-8ecc-6bae0e111cd9

📥 Commits

Reviewing files that changed from the base of the PR and between d1257c3 and 2d792e9.

📒 Files selected for processing (4)
  • cli/tests/test_heal_board.py
  • institutio/governance/parameters.yaml
  • scripts/heal-board.py
  • scripts/heartbeat-loop.sh

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Multi-agent review roll call
@codex review

(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.)

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

4444J99 added a commit that referenced this pull request Aug 7, 2026
…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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a “canonical” mode to the board self-heal so it can detect and reconcile lifecycle drift that exists on the keeper’s published projection (tabularius publication ref) even when the local tasks.yaml mirror is stale, and wires that reconcile into the heartbeat loop behind new parameter-panel flags.

Changes:

  • Add scripts/heal-board.py --canonical to read origin/tabularius/board-projection:tasks.yaml, run the existing lifecycle repairs against that canonical snapshot, and submit deltas via the authenticated relay using the canonical snapshot as the CAS base.
  • Wire the canonical reconcile into scripts/heartbeat-loop.sh gated by LIMEN_BOARD_CANONICAL_HEAL.
  • Add parameter-panel entries and CLI tests covering unreadable-ref no-op and the “local healthy vs canonical drift” structural case.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
scripts/heartbeat-loop.sh Runs heal-board.py --canonical each beat (gated) to reconcile keeper-published drift.
scripts/heal-board.py Implements canonical projection loading from a git ref and a --canonical repair path that relays against the keeper.
institutio/governance/parameters.yaml Declares LIMEN_BOARD_CANONICAL_HEAL and LIMEN_BOARD_CANONICAL_REF in the parameter panel.
cli/tests/test_heal_board.py Adds tests for unreadable publication ref (fail-open) and canonical-only drift detection without local writes.

Comment thread scripts/heal-board.py
Comment on lines +235 to +242
try:
raw = subprocess.run(
["git", "-C", str(ROOT), "show", f"{CANONICAL_REF}:tasks.yaml"],
capture_output=True,
text=True,
timeout=120,
check=False,
)
Comment thread scripts/heal-board.py
Comment on lines +247 to +250
try:
return LimenFile.model_validate(yaml.safe_load(raw.stdout))
except Exception:
return None
…tered

Every repair in this organ reads `tasks.yaml` — the LOCAL projection, which mirrors the keeper
only after a board-publication PR merges to `main`. When that merge stalls, canonical state
drifts somewhere no self-heal rung can see, and each one reports a healthy board while looking
at a stale copy of it.

Measured 2026-08-07. Twelve `needs-human`-labelled `ASK-quicken-*` tasks — login, credential,
d2l, delete, send, and seven escalations — sat at `open` on the keeper's published projection
while `main` had them correctly at `needs_human`. So this organ's own needs-human reconcile,
which exists precisely to stop the fleet re-picking a human-gated lever, read the local board,
found nothing wrong, and did nothing — while the canonical board was the one offering
delete/send atoms to agents.

The loop closes on itself: that same drift held `main` red on `validate-task-board.py`
(`needs-human-in-open`), which is what blocked the publication merge that would have refreshed
the local mirror. The drift was protecting itself from the repair.

`--canonical` runs the SAME repairs against the keeper's published projection and submits them
through the authenticated relay with that published board as the explicit compare-and-swap base,
so each ticket's precondition is computed from canonical state instead of the local mirror.
Nothing is written locally; the keeper stays the only board writer.

Read from the publication ref rather than over HTTP because that ref IS the projection contract
(the keeper publishes only through it) and a several-MB fetch has no place on a beat rung. An
unreadable ref — shallow clone, unfetched remote — makes the rung a no-op rather than a guess,
and a keeper that has published past this head simply fails the compare-and-swap so the next
pass re-derives. Fail-open like its siblings.

Dry-run against the live estate reports exactly the 12, and nothing else:

    heal-board: WOULD reconcile canonical drift at origin/tabularius/board-projection:
      0 reopened-done, 12 needs-human, 0 log-mismatch — needs-human: ASK-quicken-login,
      ASK-quicken-credential, ASK-quicken-d2l, ASK-quicken-delete, ASK-quicken-send, ...

Wired into heartbeat-loop.sh beside the TABVLARIVS relay (the other network rung), gated on
LIMEN_BOARD_CANONICAL_HEAL. Both new parameters are declared in the panel — check-params caught
their absence, which is the registry discipline working.

NOTE: this is a loop-body edit, so the running daemon needs a kickstart to load it.

2 tests: the unreadable-ref no-op, and the structural case — a HEALTHY local board beside a
canonical board carrying the regression, where the default pass must report healthy and
`--canonical` must report the drift. cli/tests/test_heal_board.py: 6 passed.

Refs #1995
@4444J99
4444J99 force-pushed the fix/heal-board-cannot-see-canonical-drift branch from db5a54a to 2d792e9 Compare August 7, 2026 17:00
@4444J99

4444J99 commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

Rebased onto main after #2016 merged — the conflict was structural, not incidental: both branches append one rung to the same region of heartbeat-loop.sh and one block to the same parameters.yaml section, so git cannot know both additions are wanted.

Resolution keeps both, and the order is deliberate:

:434  heal-board.py --canonical     # repairs the 12 regressed needs-human atoms
:445  publish-board-pr.sh           # opens the PR carrying the keeper's board to main

Canonical heal runs first, because the drift it repairs is exactly what holds validate-task-board red on the publication PR. Heal-then-publish converges in one beat; publish-then-heal opens a red PR and waits for the next one. That ordering constraint is now recorded in a comment on the rung itself.

Verified post-rebase: bash -n + YAML parse clean, all 5 parameters present, both rungs present, check-params: OK — 950 declared, cli/tests/test_heal_board.py 6 passed. git diff origin/main | grep '^-[^-]' shows exactly one removed line — the limen.models import this branch deliberately replaces to add LimenFile — so nothing from #2016 or any other lane was lost in the resolution.

4444J99 added a commit that referenced this pull request Aug 7, 2026
…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
@4444J99
4444J99 merged commit c4e0ae9 into main Aug 7, 2026
12 checks passed
4444J99 added a commit that referenced this pull request Aug 7, 2026
…— `tail -1` was eating the reason (#2050)

The measured defect: #2014's canonical-heal rung has been failing on EVERY beat with

    limen.conduct.broker.ConductError: conduct broker rejected request (500): {
      "detail": "Exceeded allowed rows written in Durable Objects free tier."
    }

61 diagnostic lines. The beat log received a bare `}`. Neither "Durable Objects" nor
"rejected request (500)" appeared in ANY log file estate-wide, so twelve regressed
needs-human board atoms stayed regressed behind a rung that looked like it was working.

`<cmd> 2>&1 | tail -1 || true` destroys three things at once:

  1. the exit status — a pipeline's `$?` is TAIL's, ~always 0, so the `|| true` is
     decorative and nothing can distinguish a hard failure from a clean run;
  2. the diagnostic — `tail -1` of a traceback is the last line of the exception's own
     repr, which for an HTTP error carrying a JSON body is the closing brace;
  3. the record — nothing is written down, so the failure has no reader at all.

That is the "signal with no effector" class #2023 exists to close, reappearing one level
up: here the SIGNAL was destroyed before any effector could exist.

All 65 heartbeat-loop.sh rungs now route through the loop's own inline `beat_run` helper.
Happy path is byte-identical (one line, the organ's own last line — log volume unchanged);
a failure prints a `── RUNG FAIL [label] exit=N ──` banner plus the real tail; either way
the outcome is appended to logs/beat-rungs.jsonl as {ts,rung,exit}. That ledger is the
reader the idiom never had — labels are stable per rung (heal-board vs heal-board-canonical)
because they are the streak buckets a later efficacy rung folds. `beat_run` stays INLINE so
the daemon takes on no new file dependency; a missing sourced helper would kill the beat.

Gate `check-beat-diagnostics` (GATES registry, pr-gate) has two rungs:
  · a shrink-only per-file ratchet — heartbeat-loop.sh at 0; the 8 sibling beat-script sites
    are a declared position in institutio/governance/beat-diagnostics-baseline.txt, and the
    ceiling moves up only by a reviewable hand edit, never by a flag;
  · helper integrity, which EXECUTES the shipped helper against a fabricated failing rung.
    A grep here would be as blind as the idiom it polices: removing the opening banner
    leaves "RUNG FAIL" intact in the closing one, so a string check passes on a helper that
    has already been gutted. Caught during development, which is why it runs the bytes.

Verified at runtime, not by tests alone: drove the real heartbeat-loop.sh in a sandbox
LIMEN_ROOT whose heal-board.py succeeds plainly and, under --canonical, fails with a
traceback whose final line is `}`. The beat log now carries the quota sentence where it
used to carry `}`, the succeeding rung above it is still exactly one line, and the ledger
records exit=1 for heal-board-canonical across consecutive beats.

Two pre-existing self-load tests broke and were fixed rather than worked around: the rung
now calls beat_run, so their extraction carries the helper too — a test of the real
composition instead of one where the runner does not exist.

Gates: check-beat-diagnostics 0 · check-gates 0 (108 gates) · check-sensors 0 (88 sensors)
· check-params 0 (the 4 new LIMEN_* vars are declared in the panel) · ruff 0 ·
verify-scoped.sh 0 (5511 cli + 48 api).


Claude-Session: https://claude.ai/code/session_01E3fxQ3ez5DtSsEpzbScKLA

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
4444J99 added a commit that referenced this pull request Aug 7, 2026
…a registry owner, not a traceback (#2057)

THE LIVE BLOCKER ON 100 TASKS/DAY. Measured 2026-08-07: POST /api/conduct/sessions answers

    500 {"detail": "Exceeded allowed rows written in Durable Objects free tier."}

`_register_relay_session` is on EVERY relay write path, so this single wall blocks the
#2014 canonical-heal rung, dispatch receipts, and board publication at once. The keeper is
the only sanctioned board writer, so while its writes are refused the board cannot advance
at all: twelve needs-human `ASK-quicken-*` atoms stay regressed on
origin/tabularius/board-projection and the throughput governor stays at `dispatched: 0`,
bootstrap cap 25. The last successful keeper write was 2026-08-07T12:55:47-04:00 —
immediately after #2016's own merge, so unblocking the write path is what spent the day's
remaining allowance.

Resolving it is a recurring SPEND, which is a human-gated lever, not agent work. What the
fleet owes is that the condition be legible and HOMED rather than recited at the operator:

  · `BrokerQuotaExhausted(ConductError)` — its own type, because this is neither a bug nor a
    transient fault: no retry, no backoff, and no correct client code makes the next write
    land. A subclass, so every existing `except ConductError` keeps working unchanged.
  · `heal-board.py --canonical` catches it and reports ONE legible line naming its registry
    owner, exiting 75 (EX_TEMPFAIL). Deliberately non-zero: a tidy exit 0 here would restore
    exactly the "everything looks healthy" blindness that let this sit unseen, and the beat's
    rung ledger needs a real failure to record.
  · lever `L-CLOUDFLARE-DO-QUOTA` in his-hand-levers.json (issue #2054) — the durable home,
    with both answers spelled out: raise the plan, or decide explicitly not to and accept a
    board that advances once per daily reset. Either is legitimate; being the default nobody
    noticed is not.

ONE DELIBERATE RULE EXEMPTION, stated because it contradicts a rule this repo enforces:
`ConductError`'s own docstring says callers classify on `status`, never on rejection prose,
because three keepers word the same condition differently. A Cloudflare storage refusal
arrives as an undifferentiated 500 with no machine-readable field, so prose is the ONLY
available signal. The match is kept narrow (three explicit substrings, and a marker is
REQUIRED — status alone never suffices) precisely so real 500s stay real bugs; that negative
direction is the more load-bearing half of the tests. The durable fix is for the keeper to
answer a structured code, which is recorded on the lever as fleet-owned follow-on — it needs
a wrangler deploy, which does NOT happen on merge, so it is not smuggled in here.

Verified at runtime by A/B against a loopback keeper returning the exact 500 body, with the
baseline extracted from origin/main and asserted NOT to contain the fix's markers first:

  BEFORE  62 lines, an uncaught traceback whose final line is `}`            exit 1
  AFTER    6 lines naming the condition, the lever, all 12 blocked atoms     exit 75

Gates: ruff 0 · verify-scoped.sh 0 (5519 cli + 48 api) · no-tasks-on-me registry rung green
(80 levers, all owned + traceable, no PII shapes).


Claude-Session: https://claude.ai/code/session_01E3fxQ3ez5DtSsEpzbScKLA

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
4444J99 added a commit that referenced this pull request Aug 7, 2026
…fail on every single beat (#2059)

* feat(enactment): a third audit axis — a rung can be wired, live, and fail on every single beat

The audit had two rungs. WIRING proves a declared-ON flag is actually set in the beat's
wiring. LIVENESS proves the running daemon is not older than that wiring. Neither can see a
rung that is wired, live, and fails every time it runs — and that is not hypothetical.

Measured 2026-08-07: `heal-board.py --canonical` (#2014) failed on EVERY beat with
`conduct broker rejected request (500): Exceeded allowed rows written in Durable Objects free
tier` while THIS AUDIT printed "3 rung(s) green/skip". Twelve regressed needs-human atoms
stayed regressed for hours behind a fully-enacted, completely ineffective rung.

The first diagnosis of that was wrong and is worth recording: it looked like
`LIMEN_BOARD_CANONICAL_HEAL` had simply been left out of enactment-audit.py. It had not —
this audit is registry-derived, so a flag earns a row by declaring `fleet_runtime` in
parameters.yaml, and that flag WAS declared and WAS resolving to 1. Adding it to a list would
have proven the switch was on, which was never in doubt. What was missing was an AXIS: is the
rung LANDING, not merely enabled.

EFFICACY reads `logs/beat-rungs.jsonl` — the per-rung `{ts,rung,exit}` ledger that
heartbeat-loop.sh's `beat_run` helper writes — and goes RED when a rung has failed
LIMEN_RUNG_FAIL_STREAK_RED (3) consecutive beats. This is the reader the old
`2>&1 | tail -1` idiom made impossible: it reported tail's exit status, so no record of a
rung's real outcome existed for anything to consume.

Three deliberate calls, each of which could have gone the other way:

  · Streaks are TRAILING, not total. A rung that failed twice last week and has succeeded
    since is healthy. Counting history would make it permanently red, and a permanently-red
    rung is an ignored rung.
  · A single failing beat is INFO, not RED. One timeout is noise; at the 120s tempo floor a
    real defect still trips within ~6 minutes.
  · Exit 75 (EX_TEMPFAIL) is reported but NEVER red. It is how a rung says "blocked on a
    condition already filed with a human owner" — heal-board --canonical raises it for the
    keeper's spent storage plan, citing lever L-CLOUDFLARE-DO-QUOTA. Making a correctly homed
    human-gated blocker fail this gate would hold it red until the operator acts, training
    everyone to ignore it, and would violate the charter's "never re-surface a filed gate".
    So it is named and visible without failing the audit; a failure for any other reason does.

Also `--efficacy-only`, so the rung is testable against a fixture ledger without the
host-dependent liveness rung flapping between CI and the live host.

Verified by driving the real script across six ledger shapes — healthy (GREEN), a 3-beat
streak (RED, naming the rung), a single blip (INFO), a 5-beat EX_TEMPFAIL run (INFO, not
RED), failed-then-recovered (GREEN, proving the streak is trailing), and a ledger with torn
and malformed lines (skipped, never fatal). All nine new cases live in the registered gate's
own suite (`enactment-test`), and its case count is now an ASSERTION rather than a label — a
case that stops running used to leave a cheerful summary behind covering nothing, which is
the same silence this rung exists to catch one layer down.

Gates: enactment-audit.test.sh 12/12 · check-params 0 (963 declared) · check-gates 0 (108
gates) · verify-scoped.sh 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3fxQ3ez5DtSsEpzbScKLA

* fix(enactment): resolve the rung ledger against the DAEMON's checkout, not this script's

Adopts #2053's fix rather than reintroducing the bug it removed. The efficacy rung read
LIVE_ROOT, which defaults to the checkout the script lives in — per-worktree — while the
daemon it reports on is host-global. Run from a session worktree (which is where the charter
puts sessions, and where .claude/skills/verify tells them to run this organ), that worktree's
logs/ holds no ledger, so the rung would SKIP while the real evidence sat in the live
checkout. A silent SKIP where the evidence exists is the same 'I found nothing' versus 'I read
nothing' confusion this lineage is about.

live_checkout() already resolves it correctly and honours an explicit LIMEN_ROOT untouched;
LIMEN_BEAT_RUNG_LOG still overrides everything so the fixture-driven tests are unaffected.

Verified from this worktree: the rung now names /Users/4jp/Workspace/limen/logs/beat-rungs.jsonl
rather than the worktree's own logs/. Suite 12/12, check-params 0, check-gates 0, ruff 0.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
4444J99 added a commit that referenced this pull request Aug 9, 2026
check-operator-gates read BOARD = ROOT/"tasks.yaml" — the LOCAL projection, which
refreshes only when a board-publication PR merges to main. heal-board.py carried the
identical hole and it hid twelve regressed needs-human atoms (#2014).

Measured 2026-08-09 against origin/tabularius/board-projection:

  tasks           3299 keeper  vs  3111 mirror
  needs_human      126 keeper  vs   109 mirror
  HEAL open/blk    533 keeper  vs   492 mirror   (162 stale / 369 live)

The verdicts happened to agree — zero violations on both boards, and still
ZERO of 126 needs_human tasks set by a human — but the census this gate
publishes as a receipt was understated by 17. A wrong number in the artifact
whose entire job is to be the number.

- --canonical reads the keeper offline from its publication ref, the same
  idiom heal-board.py uses (LIMEN_BOARD_CANONICAL_REF).
- board_tasks() returns (tasks, source) and the census carries board_source,
  so an unreachable keeper can never print like a clean one: it yields
  CANONICAL-UNREADABLE and exit 2, never [] with a green verdict. That is the
  same fail-open shape as the .agent-runtime bug caught pre-merge in #2143.
- The default --check stays on the local mirror: hermetic, fast, no fetch
  dependency in CI. --canonical is the deliberate audit mode, as in heal-board.
- Receipt regenerated from the keeper board.
- Two regression tests: the keeper is read instead of the mirror, and an
  unreadable keeper surfaces as UNREADABLE rather than an empty clean board.

Tests 22/22. verify-scoped.sh exit 0. ruff exit 0.


Claude-Session: https://claude.ai/code/session_01E6YU33yZyHVwQp6w5TSgvv

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@4444J99
4444J99 deleted the fix/heal-board-cannot-see-canonical-drift branch August 17, 2026 18:26
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.

2 participants