fix(background): read legacy deadline lane-failure rows without wedging the delegation store - #2792
Conversation
…ng the delegation store
Drift check reportFound 2 drift finding(s): 0 error, 0 warning, 2 notice. required-check-contract (2)
|
There was a problem hiding this comment.
🔵 Needs a closer look
It modifies the authoritative delegation-store schema and fail-closed uncertainty contract that gates every PR workflow, so the high-stakes coordination surface warrants final human review despite no defects being found.
Pull request overview
This PR fixes a critical wedging bug (issue #2791) in the background delegation store. Durable delegation records written by pre-#2615 plugin builds carry the retired workflowLaneFailureClass: 'deadline' value. Because both strict readers (coordinationRowsToDelegations for the SQLite coordination namespace and foldLedgerTail for the legacy JSONL tail) validate every row through the same RecordSchema, a single legacy row caused the whole namespace read to return typed uncertainty — fail-closing prepare_pr_workflow_checkout, abort_pr_workflow, complete_pr_workflow, dispatch_lanes_async batch uniqueness, and pr_workflow_status across the affected workspace with no operator escape. The fix widens only the read vocabulary to admit 'deadline', mirroring the disclosure-side precedent established by #2615, while keeping the live-producer union closed and preserving fail-closed behavior for genuinely unknown values (#2511).
Changes:
- Adds
BackgroundDelegationLegacyWorkflowLaneFailureClass/BackgroundDelegationPersistedWorkflowLaneFailureClasstypes and widens theResultSchemaenum + record field to accept the retired'deadline'member (read-only); a new parity anchor forbids any'deadline'producer insrc/. - Splits
coordinationRowsToDelegations' conflated failure message into distinct schema-validation vs authority-binding (correlation/generation/status) diagnostics, and widensPrReviewLatestTypedFailure.failureClassto the disclosure vocabulary. - Adds a regression suite, re-pins retention-registry citations for the shifted line numbers, and ships a release fragment.
File summaries
| File | Description |
|---|---|
src/background/pending-delegations.ts |
Adds persisted-vocabulary types, widens ResultSchema enum to include 'deadline', and splits row-validation diagnostics into distinct schema/authority messages. |
src/pr-review/completion.ts |
Widens PrReviewLatestTypedFailure.failureClass to PrReviewDisclosureFailureClass so legacy 'deadline' records flow through completion evidence. |
tests/unit/background/pending-delegations-legacy-deadline-read.test.ts |
New suite proving both readers accept a legacy 'deadline' row verbatim, unknown values still fail closed, and the shadow projection converges without rewriting SQLite. |
tests/unit/pr-review/lane-failure-class-parity.test.ts |
Adds a source-scan anchor asserting no 'deadline' producer exists anywhere in src/. |
scripts/retention-registry.data.ts |
Re-pins delegation-store reader/writer citation line numbers (+43) to track the source shift. |
docs/releases/pending/delegation-store-legacy-deadline-read.md |
Release fragment describing the read-compatibility fix and recovery path. |
I verified the change end-to-end: the widened enum keeps the compile-time key-based parity guard satisfied; the PR_REVIEW_FAILURE_CLASS_SAFE_DETAILS map already keys 'deadline' (completion.ts:504) so no runtime undefined access is introduced; no downstream consumer performs an exhaustive switch on the field; foldLedgerTail shares the same RecordSchema, so both the strict and lenient paths converge correctly; the retention citations resolve to their intended definitions; and the new parity scanner produces no false positives against src/ (no source line contains both the key and 'deadline'). I found no blocking or notable defects.
Review details
- Files reviewed: 6/6 changed files
- Comments generated: 0
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Lane R01 — correctness / logic review (bound head
|
| Step | Base evidence |
|---|---|
| Enum rejects the durable value | src/background/pending-delegations.ts:612 — .enum(['contract', 'resource', 'liveness']) inside the .strict() ResultSchema consumed by RecordSchema |
| Whole namespace read fails | coordinationRowsToDelegations validates each row with RecordSchema and threw the conflated delegation coordination row failed schema or authority binding validation for <key> (base:1120); the catch calls recordLedgerUncertainty(..., 'coordination-read') and returns null |
null becomes typed uncertainty |
readDelegationLoadAttempt → {status:'uncertain', source:'coordination-read'}; readDelegationsDetailed retries once then returns it; scanDelegationsForRecovery does the same |
| All-or-nothing, not one-row-degraded | the row map() throws on the first bad row, so a single legacy row hides every healthy row |
Proven empirically at head, not just by reading: the new suite's bogus case keeps a healthy row in the store and still gets uncertain from both strict readers.
Consumers that refuse on that uncertainty (all verified): prepare-pr-workflow-checkout.ts:664-669, pr-workflow-gate.ts:3966-3971 (abort_pr_workflow, agent + human force), pr-workflow-gate.ts:12422-12426 (complete_pr_workflow), dispatch-lanes.ts:1290-1301 (dispatch_lanes_async batch uniqueness), pr-workflow-status.ts:334-341.
2. The fix is at the right chokepoint — no second schema still rejects 'deadline'
Widening ResultSchema's enum (head:640) + the record field (head:484, BackgroundDelegationPersistedWorkflowLaneFailureClass) covers every durable validator, because they all route through ResultSchema/RecordSchema:
- SQLite coordination rows
:1138· legacy JSONL fold:1912· checkpoint records:1430-1431· fallback artifact:1041· compaction:2488· closed-summary projection:2233.
Strict mode keeps its documented invalid record at line N uncertainty for anything outside the persisted vocabulary, and lenient mode keeps skipping (the run below still logs the lenient CRITICAL-WARN for the unknown value only).
3. Premise check — durable 'deadline' rows are real, and it is the only retired member
Era audit of writers: 4e948c0f2 introduced contract|resource|deadline with a live producer; 6e58044b5 (#2381, “make PR-review collection a non-destructive observer”) deleted the wait-deadline terminalizer and states in its own message that this removed “the only producer of workflowLaneFailureClass: 'deadline'”; 283b01977 (#2615) then replaced the member with 'liveness' in both the union and ResultSchema. The possible durable value set is therefore exactly contract|resource|deadline|liveness — the vocabulary this PR declares. Every literal producer in src today is contract/resource/liveness (grep + the unchanged parity anchor). No other member was ever retired, so nothing else can be hiding in existing stores.
4. Every reader of .workflowLaneFailureClass — audited (7 sites)
circuit.ts:246 (=== 'liveness') · pr-workflow-gate.ts:1808-1809 (=== 'liveness') · pending-delegations.ts:3685-3687 (=== 'liveness') · :3847 (verbatim equality in sameRetainedResult) · completion.ts:809 (=== 'contract') · completion.ts:909 (leaf propagation) · dispatch-lanes.ts:4919-4922 (re-emission into the tool-output field).
No record/map index, exhaustive switch, or assertNever is keyed by the value (the repo's only assertNever is in tools/secretscan.ts, unrelated). The two map indexings that do take a failure class are total over the 4-member disclosure union: PR_REVIEW_FAILURE_CLASS_SAFE_DETAILS (completion.ts:497-507), used at :634 and :1192-1194. PrReviewLatestTypedFailure.failureClass (:865) is PrReviewDisclosureFailureClass, extensionally identical to the widened record vocabulary (:369-371), so no value outside the consumer's assumed set can flow; its consumers are the disclosure enum (:515, accepts deadline), the disclosure write, and the safe-detail map (write-pr-review-artifact.ts:820 is an unvalidated JSON field). The tool-output field (dispatch-lanes.ts:811) widens consistently with the record it is derived from (:4921), and no docs/skills/tool metadata enumerate a closed 3-member list (workflow_lane_failure_class in docs/ + skills/: 0 hits).
5. New split throw sites — diagnostics only, no behavior change
The four new throws preserve the old short-circuit order (schema → correlationId → generation → status), all sit inside the same try/catch, so the all-or-nothing return and the uncertain classification are identical; parsed.data is narrowed after the !success guard (no undefined access); no partial result escapes; no error is swallowed. The new messages never reach the published reason — that path substitutes the fixed string background delegation coordination state is unreadable or over-bound — so only the health-artifact reason changes. Stale-matcher sweep: the retired text now exists only in two different namespaces untouched here (:5838 reservation rows, pr-subscriptions.ts:618), zero hits in tests/, scripts/, docs/.
6. Reads gained no writes; the newly-unblocked maintenance path is convergent
synchronizeCoordinationAfterCompaction (:2452-2509) re-serializes JSON.stringify(record) from the retention projection only on a genuine isDeepStrictEqual mismatch (:2494-2504) — same compare-and-swap shape as before, now reachable for a legacy row instead of failing closed on it. The checkpoint/compaction projection preserves the class: dropTerminalResultBody (:2268-2273) → dropResultBody (:2256-2266) strips only text/error/outputPreviewChars. The only write near the read path is the designed JSONL shadow reconciliation (:1198-1208 + projection marker).
7. Empirical run at the bound head
bun test tests/unit/background/pending-delegations-legacy-deadline-read.test.ts
→ 5 pass / 0 fail (22 expect() calls)
Proves: a legacy deadline row reads ok through readDelegationsDetailed and scanDelegationsForRecovery with all rows returned; the value is read verbatim and the authoritative SQLite payload bytes are unchanged; an unknown value still fails both strict reads closed (#2511 preserved) while the lenient reader skips it; the JSONL deadline tail is accepted by the strict scan; the shadow projection converges once and stays byte-stable.
8. Finding — LOW, test-coverage (tests/unit/pr-review/lane-failure-class-parity.test.ts:146)
The new negative anchor that forbids any producer of the retired value is line-based (isDeadlineProducerLine applied per split('\n') line), while the sibling live-producer anchor at :116 uses a newline-tolerant /workflowLaneFailureClass\s*[:=]\s*'x'/ regex over whole-file text — so the comment's “same [:=] form” claim does not hold. Replaying both matchers on five producer shapes:
| producer shape | line guard | live-anchor regex |
|---|---|---|
workflowLaneFailureClass: 'deadline', |
true | true |
x.workflowLaneFailureClass = 'deadline'; |
true | true |
workflowLaneFailureClass: ⏎ 'deadline', |
false | true |
x.workflowLaneFailureClass = ⏎ 'deadline'; |
false | true |
| nested object, value on next line | false | true |
A producer committed with the value on the following line would slip past the regression guard. Suggested fix: test each file's whole text with the same \s*-tolerant regex (or join first and scan once) instead of scanning line by line.
Not verified (honest limits)
- No RED execution against the base build — read-only tree, no base worktree. The base failure is established by code path (
base:612→RecordSchema.safeParse→base:1120throw →null→ typeduncertain) plus the head suite'sboguscase, which exercises exactly the branch'deadline'hit at base. - No observability data proving a real workspace hit the wedge; only that the durable shape is representable by historical writers and that every reader path fails the whole namespace closed on it.
- Acceptance criterion 6 (existing delegation / PR-review suites unchanged) was not exercised, per lane instruction not to run the full suite.
🤖 Multi-Stage PR ReviewPipeline: MiniMax-M2.7-highspeed (orientation) (context pack) → MiniMax-M2.7-highspeed (explorer) + MiniMax-M2.7-highspeed (explorer B) (parallel explore, distinct lenses) → GLM-5-turbo (critique) ↔ GLM-5-turbo (critique) (cross-critique) → MiniMax-M2.7-highspeed (fallback arbiter) (arbiter: blind-spot + synthesize) 🔍 PR Intent
📦 Implementation SummaryThe PR widens the read vocabulary of ✅ /
|
| Obligation | Status | Evidence (file:line) |
|---|---|---|
| O-001 | SUPPORTED |
pending-delegations.ts:635 — enum widened to ['contract','resource','liveness','deadline'] |
| O-002 | SUPPORTED |
Same RecordSchema at pending-delegations.ts:635; same schema validates both readers |
| O-003 | SUPPORTED |
pending-delegations-legacy-deadline-read.test.ts:140–150 — 'bogus' still yields uncertain |
| O-004 | SUPPORTED |
lane-failure-class-parity.test.ts:122–170 — string-scanner filter finds zero producer files |
| O-005 | SUPPORTED |
completion.ts:865 — failureClass: PrReviewDisclosureFailureClass with doc comment |
| O-006 | SUPPORTED |
pending-delegations-legacy-deadline-read.test.ts:117–121 — SHA confirms payload unchanged |
| O-007 | SUPPORTED |
pending-delegations-legacy-deadline-read.test.ts:153–180 — first read projects; second read is byte-identical |
| O-008 | SUPPORTED |
pending-delegations.ts:1141–1163 — four distinct throw messages, one per predicate |
| O-009 | SUPPORTED |
pending-delegations-legacy-deadline-read.test.ts — 5 tests, SQLite + JSONL, shadow + unknown-value cases |
🚨 Confirmed Findings
[MEDIUM] Dead code: Math.max(…, 1) guard is provably redundant after the ?? 1 default on the same expression
- Location:
src/background/pending-delegations.ts:1151 - Why it matters: The outer
Math.max(x, 1)wherex = parsed.data.generation ?? 1is always ≥ 1 — the?? 1already guarantees the floor.Math.max(x, 1)simplifies toxunconditionally. This is not a functional bug, but it signals either a misunderstanding of the guard's intent or a residual from a prior refactor. - Evidence:
src/background/pending-delegations.ts:1151—Math.max(parsed.data.generation ?? 1, 1) !== row.generationparsed.data.generation ?? 1evaluates to a number ≥ 1 (orundefinedreplaced by1).Math.max(n, 1)wheren ≥ 1always returnsn.- Therefore the outer
Math.maxis a pure no-op.
- Fix direction: Replace with
parsed.data.generation ?? 1 !== row.generation, or if a floor-1 guard is genuinely needed for values< 1(not justundefined), move it to the nullish coalescing side:(parsed.data.generation ?? 1) !== row.generationthen separately assertparsed.data.generation >= 1— or document the intent if the double guard was intentional. Simpler: just drop theMath.max.
🔬 Unverified but Plausible Risks
- Risk:
PrReviewLatestTypedFailureflows to downstream consumers (e.g., reporting, audit) that may switch onfailureClassusing the narrowerBackgroundDelegationWorkflowLaneFailureClasstype at the call site.- Why suspicious: The type at
completion.ts:859is nowPrReviewDisclosureFailureClass, which includes'deadline'; a TypeScript switch with nodeadlinearm would be a compile error, but a JavaScript consumer or a type-assertion (as never) could silently misbehave. - What would verify it: Search all callers of
PrReviewLatestTypedFailurefor runtime switch logic onfailureClass.
- Why suspicious: The type at
🧪 Test / Coverage Gaps
- Gap: The new distinct error messages (
"failed schema validation","authority binding mismatch: correlationId","authority binding mismatch: generation","authority binding mismatch: status") have no test asserting the specific message text.- Evidence:
coordinationRowsToDelegationsatpending-delegations.ts:1141–1163; the test suite exercisesstatus === 'uncertain'but never asserts the thrownErrormessage.
- Evidence:
📋 Shipped-vs-Claimed Gaps
None — the PR's core claim (legacy 'deadline' rows read ok, unknown values stay uncertain, producers stay closed) is fully backed by the code and test suite.
📝 Merge Recommendation
[APPROVE_WITH_FIXES]
The PR correctly fixes the core wedged-delegation-store bug (O-001–O-003) and its supporting machinery (O-004–O-009). The dead-code Math.max redundancy is the only non-nit finding; it is fixable in one line and does not block merge.
| Check | Result |
|---|---|
| No CRITICAL findings | ✅ |
| No unresolved STEALTH_CHANGE | ✅ |
| No UNSUPPORTED obligations | ✅ |
| Test coverage adequate | ✅ (one minor gap: error-message specificity not asserted) |
| No hardcoded secrets | ✅ |
| All async errors handled | ✅ |
| Input validation present | ✅ |
| No broken agent role boundaries | ✅ |
| Prompt format contracts intact | ✅ |
| Lockfile consistent | ✅ |
🔁 Validation provenance
Confirmed findings carried to review:
[MEDIUM] Math.max redundancy— KEPT; evidence atpending-delegations.ts:1151is structurally proven (see proof above). Not a runtime bug but real dead code.
Confirmed findings DROPPED (with reason):
[HIGH] Error message contract break— DROPPED: the original code threw on every failure with one conflated message; callers pattern-matching that string already failed regardless of which predicate failed. The new distinct messages are strictly more informative; no caller was relying on the conflated form.[MEDIUM] Type widening for PrReviewLatestTypedFailure— DROPPED: explicitly intentional and documented (completion.ts:859doc comment), matching the establishedPrReviewDisclosureFailureClassprecedent the PR describes. TypeScript would catch any structural consumer issue at compile time.[LOW] Missing try-catch for new throws— DROPPED: the function threw before and after; nothing was relying on a non-throwing codepath.[LOW] Entity key error disclosure— DROPPED:entityKeyis a correlationId/sessionId (opaque technical identifiers), not user data. Pre-existing pattern in the file (the original conflated throw already included it).[MEDIUM] Blocking I/O in test— DROPPED:fs.readFileSyncin tests is a pre-existing convention in this file and the codebase. The same concern applies to every other test inlane-failure-class-parity.test.ts(e.g.,readSourceat line 52), making it a pre-existing pattern rather than a PR-introduced defect.
Blind-spot findings:
- Error message text is not asserted in tests (moved to Minor).
- No downstream runtime switch on
PrReviewLatestTypedFailure.failureClassfound in the diff context; plausible risk noted above but unverifiable without the full caller graph.
🔒 Reviewed by a 3-model cross-family adversarial debate (architect → dual-lens parallel explorers → cross-critique → arbiter) for high recall with low false-positive noise. Findings are advisory — verify before acting.
Review Synthesis — PR #2792 (run pr2792-review-20260915-051012)Head reviewed: 8147c13 · base 4e45e15 Coverage attestation8 lanes (6 base + 2 consolidated micro covering 7 matched families), all 11 Verified findings (post-critic)
Rejected candidates (transparency)
Obligation checkAll 9 obligations (O-001..O-009 per the multi-stage bot's reconstruction) Verdict: REQUEST_CHANGESLoad-bearing: PRR-001 (CI-blocking, deterministic). All findings are |
…hor scanner, pin split diagnostics, re-pin citations
…legacy-deadline-read
Closure Ledger — PR #2792 feedback round (run pr2792-review-20260915-051012)Fix commit: 7480b00 · sync-merge: 47ff6a0 (origin/main 2b51abf)
Post-fix validation (orchestrator, at 47ff6a0)
Gates
|
🤖 Multi-Stage PR ReviewPipeline: MiniMax-M2.7-highspeed (orientation) (context pack) → MiniMax-M2.7-highspeed (explorer) + MiniMax-M2.7-highspeed (explorer B) (parallel explore, distinct lenses) → GLM-5-turbo (critique) ↔ GLM-5-turbo (critique) (cross-critique) → MiniMax-M2.7-highspeed (fallback arbiter) (arbiter: blind-spot + synthesize) PR Reviewer — opencode-swarm🔍 PR IntentReconstructed obligation list:
📦 Implementation SummaryThe PR makes four targeted changes: (1) adds two new type aliases for the persisted failure-class vocabulary and exports them; (2) widens ✅ /
|
| Obligation | Status | Evidence (file:line) |
|---|---|---|
| O-001 | SUPPORTED |
src/background/pending-delegations.ts:639 — enum widened to include 'deadline' |
| O-002 | SUPPORTED |
src/background/pending-delegations.ts:479 — field type uses BackgroundDelegationPersistedWorkflowLaneFailureClass |
| O-003 | SUPPORTED |
src/pr-review/completion.ts:864 — field type widened to PrReviewDisclosureFailureClass |
| O-004 | SUPPORTED |
src/background/pending-delegations.ts:1138–1166 — four distinct throw branches |
| O-005 | SUPPORTED |
tests/unit/pr-review/lane-failure-class-parity.test.ts:128–170 — isDeadlineProducerText string scan with expect(producers).toEqual([]) |
| O-006 | SUPPORTED |
tests/unit/background/pending-delegations-legacy-deadline-read.test.ts — 5 tests covering all stated scenarios |
| O-007 | SUPPORTED |
tests/unit/background/pending-delegations-legacy-deadline-read.test.ts:180–207 — 'bogus' yields uncertain |
| O-008 | SUPPORTED |
tests/unit/background/pending-delegations-legacy-deadline-read.test.ts:157–158 — SHA comparison proves bytes unchanged |
🚨 Confirmed Findings
None. Every confirmed finding failed challenge; no new blind-spot defects were found.
🔬 Unverified but Plausible Risks
None at actionable confidence. All speculative risks are bounded by runtime guards (schema strictness, parity test, source-tree scope) already in place.
🧪 Test / Coverage Gaps
- Gap: None — 5 legacy-read tests + 9 parity tests cover all changed behaviour paths.
- Evidence:
tests/unit/background/pending-delegations-legacy-deadline-read.test.ts(5test()blocks);tests/unit/pr-review/lane-failure-class-parity.test.ts(9it()blocks at head).
- Evidence:
📋 Shipped-vs-Claimed Gaps
- Gap: None — every claimed change maps to a concrete diff line.
📝 Merge Recommendation
[APPROVE]
All eight obligations are supported by diff evidence. The four confirmed findings were each examined and dropped: one was intentional by-design behavior (type widening is the fix), two were speculative about future maintainers bypassing existing guards (the guards are in place and tested), and the remaining ones conflated test-environment constraints with production defects. No concrete, PR-introduced, unmitigated defect was found.
| Check | Result |
|---|---|
| No CRITICAL findings | ✅ |
| No unresolved STEALTH_CHANGE | ✅ — type changes documented in release fragment |
| No UNSUPPORTED obligations | ✅ |
| Test coverage adequate | ✅ — 14 targeted tests for 4 changed behaviour surfaces |
| No hardcoded secrets | ✅ |
| All async errors handled | ✅ — no new async paths introduced |
| Input validation present | ✅ — ResultSchema strictness preserved, unknown values fail closed |
| No broken agent role boundaries | ✅ — no agent interface changes |
| Prompt format contracts intact | ✅ — no output-format token changes |
| Lockfile consistent | ✅ — package.json unchanged |
🔁 Validation provenance
Findings challenged and dropped (1-line rationale each):
- [MEDIUM] Math.max false-positive claim —
Math.max(-999, 1) = 1 ≠ row.generation ≥ 1so the guard throws correctly; not a defect. - [HIGH] Exhaustive-switch silent fallback (completion.ts) —
PrReviewLatestTypedFailureis module-internal; no external API surface changed. - [MEDIUM] Cast drops mismatched fields —
z.strict()rejects extra fields;ascast is safe aftersafeParsesuccess. - [LOW] db.query.get() undefined guard removable — guard is present; finding is speculative about future maintainers.
- [HIGH] Infinite recursion / symlink cycle — test file, controlled environment;
bun testwould catch any cycle fast. - [MEDIUM] Missing I/O try/catch — test file; permission errors propagate as test failures, which is correct.
- [LOW] Comment-vs-implementation mismatch — comment is accurate;
isSpaceloops over newlines between op and quote. - [MEDIUM] Exhaustive-switch silent fallback (BackgroundDelegationResult) — intentional by-design; parity test prevents regression; PR acknowledges it.
- [MEDIUM] Schema validates writes accepting 'deadline' — intentional; no live producer can write it (parity test blocks it).
- [MEDIUM] Exhaustive-switch silent fallback (PrReviewLatestTypedFailure) — same as Swarm config is completely ignored #2; module-internal, no external API.
- [MEDIUM] Symlink traversal outside src/ —
path.jointoREPO_ROOTbounds traversal scope regardless of symlinks. - [HIGH] Computed-property producer bypass — TypeScript requires a string literal for object property values;
workflowLaneFailureClasscan't be a runtime-computed key in a producer context. - [HIGH] Unicode/escape bypass — TypeScript string literals in source code are what the scan targets; an escape
\u0064in source ≠'deadline'in source. - [MEDIUM] Template literal bypass — Intentionally out-of-scope per the test's own documented scope ("single- and double-quoted strings").
Blind-spot pass findings added: None.
🔒 Reviewed by a 3-model cross-family adversarial debate (architect → dual-lens parallel explorers → cross-critique → arbiter) for high recall with low false-positive noise. Findings are advisory — verify before acting.
Closes #2791
Summary
Durable delegation records written by pre-#2615 plugin builds carry the retired
workflowLaneFailureClass: 'deadline'value. Both strict readers of thedelegation store (
coordinationRowsToDelegationsfor the SQLite coordinationnamespace and
foldLedgerTailfor the legacy JSONL tail — both validatethrough the same
RecordSchema) treated that value as corruption: ONE suchrow made the whole namespace read return typed uncertainty
(
background delegation coordination state is uncertain), which blockedprepare_pr_workflow_checkout,abort_pr_workflow(agent and human force),complete_pr_workflow,dispatch_lanes_asyncbatch uniqueness, andpr_workflow_statusin every affected project workspace. Two such rows(written 2026-08-25, imported verbatim into SQLite 2026-09-06) wedged a live
PR_REVIEW workflow; runtime reproduction on a forensic copy of the affected
store is EXECUTION_PROVEN (fixture with only those two values rewritten reads
all 623 rows ok).
The fix widens only the READ vocabulary — a new
BackgroundDelegationPersistedWorkflowLaneFailureClass(live union +'deadline') used byResultSchema's enum and the record interface field —mirroring the disclosure-side
PrReviewDisclosureFailureClassprecedent that#2615 itself established. The live-producer union and its parity test are
untouched; a new negative source anchor forbids any producer of the retired
value; unknown values (anything outside the persisted vocabulary) still fail
the namespace read closed (#2511 preserved).
PrReviewLatestTypedFailure(PR-review completion evidence) widens to the disclosure vocabulary — the one
required downstream type change.
coordinationRowsToDelegationsnow reportswhich predicate failed (schema validation vs correlation/generation/status
authority binding) instead of one conflated message. No authoritative SQLite
data is mutated: affected workspaces recover the moment the fixed build loads
(the one-time JSONL shadow reconciliation is the store's designed convergence
path), then
/swarm abort-pr-workflowclears a wedged gate.Invariant audit
bun run buildpassed.bun run build,node --input-type=module -e "await import('./dist/index.js')"OK, no new bun:/ usage, plugin shape unchanged.bun run check:retentionpassed, 123 rows).bun test.tests/unit/background/pending-delegations-legacy-deadline-read.test.ts(bun:test, canonicalMkdtemp, closeAllProjectDbs in afterEach, no mock.module; 5 tests, under the 500-line cap) and one new anchor test in the parity suite (string-scanner, no regex transport hazards);check-test-file-cap,check-test-tmpdir,check-test-clock,check-mock-cleanupall report 0 new violations.docs/releases/pending/delegation-store-legacy-deadline-read.mdshipped; no version files hand-edited (release-please owns them); user-facing recovery requires the fixed release to reach the three documented plugin cache layouts, after which/swarm abort-pr-workflowclears wedged gates without any data repair.Test plan
Frozen acceptance checks (disposable-worktree replay, base 4e45e15; manifest at the issue-tracer trace):
deadlinerow reads ok through both strict readers — REDLEGACY_READ_FAILED→ GREENLEGACY_READ_OKPASSVALUE_NOT_PRESERVED→ GREENVALUE_PRESERVEDPASSdeadlineproducer literal insrc/PASS'bogus'still fails the namespace closed (UNKNOWN_UNCERTAIN) PASSMISSING:→ GREENREGRESSION_SUITE_GREENPASSrepro-check.sh verify-checkpoint: all 5 frozen blobs OK.Local commands (exit 0 unless noted):
bun test tests/unit/background/pending-delegations-legacy-deadline-read.test.ts(5 pass),bun test tests/unit/pr-review/lane-failure-class-parity.test.ts(9 pass),bun test tests/unit/background/pending-delegations-sqlite-authority.test.ts,bun test tests/unit/scripts/retention-registry-rows.test.ts(17 pass), per-file sweep of 10 sibling suites (completion/circuit/authority/uncertainty/exactly-once/generation — all pass),bun run typecheck,bun run build, dist ESM import,bunx @biomejs/biome ci src tests scripts,bun run check:invariants,bun run check:registry-citations,bun run check:retention,DRIFT_CHECK_ENFORCE=1 bun run drift:check(after restoring this checkout's pr-standards.yml line endings to blob form; the initial error was a pre-existing CRLF-on-disk artifact, proven absent at base in a fresh worktree),scripts/check-test-file-cap.ts,check-test-tmpdir.sh,check-test-clock.sh,check-mock-cleanup.sh,check:gate-portability,check-bash-portability.sh,check-cross-contamination.sh.Mutation/falsifiability: seeded a genuine
workflowLaneFailureClass: 'deadline'producer intosrc/pr-review/circuit.ts→ the parity negative anchor fails at exactlyexpect(producers).toEqual([]); restored → 9/9 pass. The frozen RED base legs at 4e45e15 are the enum-removal mutation proof.Independent gates: fresh-context implementation reviewer (Phase 4.5) and final critic (Phase 4.6) verdicts recorded in the issue-tracer trace; this PR is published at merge state AWAITING_USER_APPROVAL (merge only on separately recorded user approval).