Skip to content

fix(dor): un-shadow the died-sidekick check in the reconcile sweep - #996

Merged
TaekeK merged 1 commit into
mainfrom
bugfixes/reconcile-liveness-shadowed
Aug 10, 2026
Merged

fix(dor): un-shadow the died-sidekick check in the reconcile sweep#996
TaekeK merged 1 commit into
mainfrom
bugfixes/reconcile-liveness-shadowed

Conversation

@TaekeK

@TaekeK TaekeK commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Closes #995

The 💀 liveness rule added in #963 has been unreachable for its entire target population since it shipped.

The bug

dor_reconcile.sh:143-150 flagged "no state:* label" as un-routed and continued — skipping the drift check, the whole case "$status" block including the "Building" liveness arm (:170), and the zombie-sidekick check (:183).

But "no state label" is not un-routed. The build side deliberately runs without one: dor_build_flow.sh drops state:awaiting-approval when it applies build-done, nothing re-adds a state label, and dor_feedback_flow.sh:32 sets board Status to Building on feedback. So an issue in exactly the state the rule describes — Status Building, sidekick dead — always arrived here label-less and could never reach the check written for it.

stalled is only populated inside that arm, so the dor-stuck label and the direct comment at :240-244 — the only signal that reaches a human — were dead for the same reason. Both pipelines are affected: dor-reconcile.yml runs the script for enhancement and again for bug.

Observed

On #370, runs 31016109943 and 31076341084 died when their sidekick was reclaimed. The sweep reported (health issue #886, 6 Aug 09:09):

🕳️ #370 is on the board (Status: Building) with no state:* label after 1151h — the agent likely never ran.

It had Status: Building in hand and still took the un-routed branch. The work sat dead ~18 hours across ~18 sweeps until @WimvandenHeijkant noticed by hand and re-posted both rounds of the requestor's feedback himself.

The fix

Ask the board whether anything owns the issue rather than trusting the label. A new build_phase() covers the four Statuses the build side owns (Building, Awaiting functional acceptance, Awaiting merge, Exceptions), and a label-less issue in one of them now falls through to the liveness and zombie checks.

  • Genuinely un-routed issues still flag exactly as before.
  • The drift check is now explicitly label-guarded (it was already a no-op without a label; made explicit so it stays that way).
  • Un-routed age comes from updated_epoch, not created_epoch. That "1151h" was measured from June, for something that had been dead 18 hours — it read as ancient backlog rather than something that had just broken.

Tests

test/ci-scripts/test-dor-reconcile-liveness.sh — a first harness for .github/scripts/*.sh. The absence of one is why this shipped shadowed and stayed that way for weeks. It puts a stub gh on PATH serving fixtures, drives the real script end to end, and asserts on the health report it publishes plus the writes it makes. No network, no tokens.

9 assertions. 7 fail against the unfixed script, including the exact #995 shape:

FAIL  a dead Building issue is flagged as a died sidekick
FAIL  …and is NOT mis-reported as un-routed
FAIL  the issue itself gets marked dor-stuck

The two that pass pre-fix are the ones that should, which is what shows the fix is narrow: a live build stays quiet, and a genuinely un-routed issue is still flagged.

Wired into the existing ci-scripts job in pr.yml, alongside the guards added in #994.

Note on how this was routed

The DoR bug agent independently traced #995 against the current scripts, confirmed it "correct in every particular", and routed it state:out-of-pipeline — correct, since CI/automation defects go through the normal dev flow rather than the autonomous builder. Its fix sketch and regression-risk notes shaped this change; the build_phase() approach is the first of the two options it laid out.

🤖 Generated with Claude Code

Closes #995

The 💀 liveness rule added in #963 has been unreachable for its entire target
population since it shipped. `dor_reconcile.sh:143-150` flagged "no `state:*`
label" as un-routed and `continue`d — skipping the drift check, the whole
`case "$status"` block including the `"Building"` liveness arm, and the zombie
sidekick check.

But "no state label" is not un-routed: the build side deliberately runs without
one. dor_build_flow.sh drops `state:awaiting-approval` when it applies
`build-done`, nothing re-adds a state label, and dor_feedback_flow.sh sets board
Status to Building on feedback. So an issue in exactly the state the rule
describes — Status Building, sidekick dead — always arrived here label-less and
could never reach the check written for it. `stalled` was only ever populated
inside that arm, so the `dor-stuck` label and the direct comment on the issue,
the only signal that reaches a human, were dead too. Both pipelines: the
workflow runs the script for enhancement and again for bug.

Observed on #370: runs 31016109943 and 31076341084 died when their sidekick was
reclaimed; the sweep reported "🕳️ #370 is on the board (Status: Building) with
no state:* label after 1151h — the agent likely never ran" (health issue #886,
6 Aug 09:09). It had Status=Building in hand and still took the un-routed
branch. The work sat dead ~18h across ~18 sweeps until a human re-posted the
requestor's feedback by hand.

Ask the board whether anything owns the issue instead of trusting the label:
a new `build_phase()` covers the four Statuses the build side owns, and a
label-less issue in one of them now falls through to the liveness and zombie
checks. Genuinely un-routed issues still flag as before; the drift check is
explicitly label-guarded. Un-routed age now comes from updated_epoch, not
created_epoch — that "1151h" was measured from June, for something that had
been dead 18 hours.

test/ci-scripts/test-dor-reconcile-liveness.sh is a first harness for
.github/scripts/*.sh — the absence of one is why this shipped shadowed. It puts
a stub `gh` on PATH and drives the real script end to end. 9 assertions; 7 fail
against the unfixed script, including the exact #995 shape (dead build reported
🕳️ instead of 💀). The two that pass pre-fix are the ones that should: a live
build stays quiet, and a genuinely un-routed issue is still flagged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@TaekeK
TaekeK merged commit 7ced4b7 into main Aug 10, 2026
30 checks passed
@TaekeK
TaekeK deleted the bugfixes/reconcile-liveness-shadowed branch August 10, 2026 12:35
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.

[BUG] Reconcile sweep can never flag a died sidekick: the un-routed check short-circuits the liveness check

1 participant