Skip to content

fix(workflow): retryable TESTED SKIPPED verdicts and non-dead-end test_runner scope advice (#2756) - #2766

Merged
zaxbysauce merged 3 commits into
mainfrom
fix/issue-2756-skipped-verdict-retryable
Sep 14, 2026
Merged

zaxbysauce merged 3 commits into
mainfrom
fix/issue-2756-skipped-verdict-retryable

Conversation

@zaxbysauce

@zaxbysauce zaxbysauce commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Closes #2756

Summary

Two linked defects pushed correct, reviewer-approved work into an unrecoverable rework_required state:

  1. test_runner's guard for scope:"convention"/"graph"/"impact" without files/targets returned a remediation message recommending scope:"all" — the exact scope the sibling guard in the same function blocks for agent use (SWARM_ALLOW_FULL_SUITE-gated) and the test_engineer prompt prohibits. Models that followed the tool's own advice looped until the repetition breaker fired.
  2. Both Stage B verdict-consumption paths (foreground delegation-gate settlement and background ingestBackgroundStageBCompletion) classified a [TESTED] | task-N | SKIPPED | ... verdict — tests were NOT run — identically to a FAIL verdict, emitting stage_b_failedrework_required and deleting the reviewer's APPROVED gate proof.

The fix: the guard now directs to files/targets with a concrete example, and both response fields enumerate every scope the guard covers (both guards' rejection semantics unchanged); a TESTED SKIPPED verdict is treated as a retryable not-run outcome on both paths (no transition, state stays Stage B eligible, reviewer proof and completion entry preserved, warn log / dedicated background advisory); genuine FAIL and REVIEWED rejections keep the existing rejection semantics. Complementary to open PR #2760 (#2755's audited exit from rework_required): this PR prevents the wrongful entry.

Invariant audit

  • 1 (plugin init): not touched — no init-path or src/index.ts changes (git diff --name-only origin/main..HEAD lists no init surface).
  • 2 (runtime portability): not touched — no entry shape, package exports, or bun: resolution changes; bun run typecheck exit 0.
  • 3 (subprocesses): not touched — no production spawn changes (only the new test files spawn git in temp fixtures, matching the existing issue-2491 harness pattern).
  • 4 (.swarm containment): not touched — no runtime-state path changes.
  • 5 (plan durability): not touched — no ledger/schema/status-shape changes; stage_b_failed reducer untouched.
  • 6 (test_runner safety): touched — remediation text and error-field scope enumeration only. Evidence: frozen check C1 (guard still rejects; message directs to files/targets; neither field recommends scope "all") plus characterization tests for guard-1's block and the pinned error field; existing pins in tests/unit/tools/test-runner.test.ts (106 pass/1 skip) unchanged.
  • 7 (test writing): touched — three new bun:test files (5+4+4 tests), each < 500 lines (bun run check:test-file-cap clean), no mock.module, os.tmpdir()+realpathSync temp dirs, createIsolatedTestEnv + resetSwarmState hygiene; writing-tests skill loaded.
  • 8 (session state): not touched — no new module-level or session-keyed state; verdict classification only reads existing maps.
  • 9 (guardrails/retry): touched — verdict classification now distinguishes not-run from failed on both paths; background skip consumes the record (consumed: true, no retry loop — same claim shape as the proven FAIL path); frozen checks C2/C3/C4 RED→GREEN / GREEN→GREEN with revert + mutation probes.
  • 10 (chat/system msg): not touched.
  • 11 (tool registration): not touched — no TOOL_METADATA/manifest/barrel changes.
  • 12 (release/cache): touched — new pending fragment docs/releases/pending/2756-skipped-verdict-stage-b.md (no version numbers; release-please owns versions).

Test plan

  • Frozen acceptance checks (red checkpoint frozen pre-fix at base ef59deb; replayed by independent reviewer AND final critic):
    • C1 DISCRIMINATING: base RED / head GREEN — bun .agents/issue-traces/2756-skipped-verdict-stage-b/repro/check-c1.ts (trace artifacts are local-only; equivalent contract asserted by tests/unit/tools/test-runner-scope-advice.test.ts).
    • C2 DISCRIMINATING (foreground + background): base RED / head GREEN — mirrored by tests/unit/hooks/delegation-gate-stage-b-skipped.test.ts + tests/unit/background/stage-b-gates-skipped-verdict.test.ts.
    • C3 DISCRIMINATING (retryable state + reviewer proof): base RED / head GREEN.
    • C4 PRESERVING (genuine FAIL semantics, both paths): base GREEN / head GREEN.
  • New regression tests (per-file isolation): bun test tests/unit/tools/test-runner-scope-advice.test.ts (5/0), bun test tests/unit/hooks/delegation-gate-stage-b-skipped.test.ts (4/0), bun test tests/unit/background/stage-b-gates-skipped-verdict.test.ts (4/0, including an observer-path test asserting the dedicated skip advisory).
  • Pinned neighbors: bun test tests/unit/tools/test-runner.test.ts (106 pass/1 skip/0 fail), bun test tests/unit/hooks/delegation-gate.set-dispatch.test.ts (9/0).
  • Blast radius: per-file isolation loop over all 146 test files importing the changed modules — 145 green, 1 line was a bash null-byte warning (no failing file).
  • Gates: bun run typecheck exit 0; bunx biome check on changed files clean (one pre-existing repo warning verified on base); bun run check:test-file-cap clean; bun run check:mock-cleanup clean (4 pre-existing non-blocking violations, unchanged); scan-deferred clean.
  • Falsification: reverting each fix hunk independently re-reddens exactly its checks; flipping === 'SKIPPED' to !== is caught by C4.

Root Cause

src/tools/test-runner.ts:3169 (guard-2 message recommending the blocked scope; guard-1 at 3129-3154 blocks it — guard-1's own comment forbids bypass instructions) and the binary verdict classification at src/hooks/delegation-gate.ts:5951-5971 (positiveVerdict = verdict === 'PASS', so SKIPPED fell into the stage_b_failed branch) plus src/background/stage-b-gates.ts:100 (structuredStageBVerdict returned 'fail' for SKIPPED); the damage mechanism is src/gate-evidence.ts:611-628, 888-891 (rework_required + both Stage B proofs deleted). Trigger: agent omits files on a convention/graph call → tool advises scope:"all" → blocked → prompt SKIP CONDITION 1 legitimizes [TESTED] ... SKIPPED → gate scores it as a code failure.

Fix

  • src/tools/test-runner.ts: message now 'When using scope "convention", "graph", or "impact", you must provide a non-empty "files" array (or "targets" for framework-native test names). Example: { scope: "convention", files: ["tests/test_calc.py"] }'.
  • src/hooks/delegation-gate.ts: sibling short-circuit before the rejection block — TESTED SKIPPED logs a warn and continues; no transition, no state change, no stageBCompletion delete.
  • src/background/stage-b-gates.ts: structuredStageBVerdict returns 'skip' for SKIPPED; ingest returns { ok:false, consumed:true, skipped:true, reason } before the rejection branch; StageBIngestionResult gains optional skipped.
  • src/background/completion-observer.ts: applied.skipped takes first priority in the !applied.ok advisory ternary → skipped (tests not run) — re-dispatch the test gate; reviewer proof preserved; task remains Stage B eligible instead of ingestion failed.

Recurrence Prevention (defect class)

  • Defect class: outcome- or error-text producers whose remediation recommends a path the same component blocks, and verdict consumers that collapse semantically distinct outcomes (tests-not-run vs tests-ran-and-failed) into the failure transition.
  • Sweep result: 10 hits across 5 predicates, all dispositioned (08a-recurrence-sweep.md): 0 recommendation phrases remain (Use scope "all" eradicated); remaining scope "all" occurrences are refusals/prohibitions/documentation; both === 'PASS' sites now discriminate SKIPPED; both verdict vocabularies have distinct-classifying consumers.
  • Guardrail: three executable regression suites in CI unit shards; demonstrated to bite via revert probes (each hunk revert re-reddens its checks) and the mutation probe (C4 catches FAIL-mapped-to-skip).

Regression Protection

  • tests/unit/tools/test-runner-scope-advice.test.ts: message contract for all three guarded scopes; guard-1 block and pinned error text characterized.
  • tests/unit/hooks/delegation-gate-stage-b-skipped.test.ts: SKIPPED retryable with proof + completion entry preserved; FAIL and REVIEWED REJECTED still rework_required with proof cleared.
  • tests/unit/background/stage-b-gates-skipped-verdict.test.ts: SKIPPED ingest non-rejecting with skipped:true; FAIL and unparseable output keep fail-closed rejection.
  • Test drift review: final critic independently re-ran all three files plus five blast-radius suites on the reviewed HEAD — all green.

Acceptance Criteria -> Evidence

Acceptance criterion (from intake) Evidence (command + output, or test name)
AC1 error text directs to files, never recommends a blocked scope Frozen check C1 base RED → head GREEN; tests/unit/tools/test-runner-scope-advice.test.ts 5/0
AC2 SKIPPED distinguished from FAIL in verdict processing (both paths) Frozen check C2 base RED → head GREEN (foreground + background); 08a sweep: both === 'PASS' sites fixed
AC3 not-run leaves retryable Stage B state without deleting reviewer approval Frozen check C3 base RED → head GREEN; delegation-gate-stage-b-skipped.test.ts (state stays reviewer_run, gates.reviewer intact, completion entry preserved)
AC4 no regression to genuine stage_b_failed path Frozen check C4 GREEN→GREEN; set-dispatch.test.ts SC-022.2 9/0; FAIL/REJECTED regression tests green

Risk and Rollback

  • Risk level: low — additive verdict branch + message reword; no schema, state-shape, tool-registration, or init changes; all rejection semantics pinned by existing + new tests.
  • Rollback: revert the PR's commits (a squash merge reduces this to a single revert).
  • Residual risk: a task whose test gate only ever SKIPPEDs now stays Stage B eligible indefinitely (by design — the architect decides whether to re-dispatch, disable the task's test gate, or use PR fix(workflow): architect-only audited exit from rework_required (#2755) #2760's recovery tooling); the background skip still triggers the bounded maintenance tick (no-op for an already-consumed record).

Waivers (or none)

none

Merge status

Awaiting explicit user approval; not merged.

PR head: bfc0e10

@github-actions

Copy link
Copy Markdown
Contributor

Drift check report

Found 2 drift finding(s): 0 error, 0 warning, 2 notice.

required-check-contract (2)

  • 🔵 notice scripts/required-check-contract.json: [RULESET_DIVERGENCE] intended-required context "drift" is not yet required by the captured ruleset
  • 🔵 notice scripts/required-check-contract.json: [RULESET_DIVERGENCE] intended-required context "drift" is not present for every expected event in captured external workflow evidence

…t_runner scope advice (#2756)

- test_runner guard-2 message no longer recommends the scope its sibling
  guard blocks; it directs to files/targets with a concrete example
- delegation-gate Stage B settlement treats a TESTED SKIPPED verdict as
  tests-not-run: no stage_b_failed, state stays Stage B eligible,
  reviewer proof preserved (architect re-dispatches the test gate)
- background ingestBackgroundStageBCompletion classifies SKIPPED as
  'skip' (consumed, skipped flag, no transition, proof preserved);
  completion-observer publishes a dedicated skip advisory instead of
  the generic 'ingestion failed'
- genuine FAIL and REVIEWED rejections keep stage_b_failed semantics
@zaxbysauce

Copy link
Copy Markdown
Collaborator Author

🤖 Multi-Stage PR Review

Pipeline: 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)
Commit reviewed: 13b5e7b1da42


PR Reviewer — opencode-swarm

🔍 PR Intent

Reconstructed from PR text, issue #2756, and diff:

  • O-001 test_runner guard-2 message must direct callers to provide files/targets and must NOT recommend the blocked scope:"all" (dead-end loop cause).
  • O-002 Foreground Stage B verdict settlement must classify [TESTED] | task-N | SKIPPED | ... as retryable (no stage_b_failed, no rework_required, reviewer proof preserved).
  • O-003 Background Stage B ingestion must return { skipped: true } for TESTED SKIPPED; record consumed, no transition, no proof clearing.
  • O-004 Background advisory must distinguish a skip (tests not run) from a hard ingestion failure.
  • O-005 Genuine FAIL and REVIEWED REJECTED verdicts must keep existing rejection semantics (no regression).

📦 Implementation Summary

Four file changes:

  1. src/tools/test-runner.ts — message field rewritten: no more scope "all" mention; now directs to files/targets with an explicit example. error field and both guards' semantics unchanged.
  2. src/hooks/delegation-gate.ts — new sibling check before the verdict-rejection block: if expectedVerdictKind === 'TESTED' and verdict === 'SKIPPED', warn + continue (no state change, no proof cleared).
  3. src/background/stage-b-gates.tsstructuredStageBVerdict return type gains 'skip'; ingestBackgroundStageBCompletion returns { ok: false, consumed: true, skipped: true } for skip verdicts before the rejection branch.
  4. src/background/completion-observer.tsapplied.skipped takes first priority in the !applied.ok advisory ternary, publishing the dedicated skip advisory.

✅ / ⚠️ / ❌ Intended vs Actual

Obligation Status Evidence
O-001 SUPPORTED src/tools/test-runner.ts:3168 — message rewritten with example, no scope "all"
O-002 SUPPORTED src/hooks/delegation-gate.ts:5951–5966 — warn + continue for TESTED SKIPPED
O-003 SUPPORTED src/background/stage-b-gates.ts:632–644{ skipped: true } before rejection branch
O-004 SUPPORTED src/background/completion-observer.ts:526applied.skipped first in ternary
O-005 SUPPORTED Both paths: fail/REJECTED fall through unchanged to rejection logic

🚨 Confirmed Findings

None — all reviewer-confirmed findings were challenged and refuted below. One minor test hygiene item carried forward.


🔬 Challenged & Refuted Findings

src/hooks/delegation-gate.ts:5951verdictEntry can be undefined (REBUTTED)

Reviewer claim: if verdictEntry is undefined, verdictEntry?.verdict === 'SKIPPED' short-circuits to false, bypassing the SKIPPED check, and the subsequent positiveVerdict fallback could incorrectly transition to stage_b_failed.

Why it's safe: When verdictEntry is undefined, verdictEntry?.verdict evaluates to undefined, so the condition is false and execution falls through to positiveVerdict. The positiveVerdict expression dispatchCtxForVerdict?.expectedVerdictKind === 'TESTED' && verdictEntry?.verdict === 'PASS' also evaluates to false (due to undefined === 'PASS'), so positiveVerdict is false, and !positiveVerdict is truestage_b_failed fires correctly. A missing verdict entry means "no structured verdict reported" → fail-closed is the right behavior.


src/hooks/delegation-gate.ts — state-machine bug on wrong state (REBUTTED)

Reviewer claim: SKIPPED check does not verify task is in a Stage B eligible state before issuing continue.

Why it's safe: The condition dispatchCtxForVerdict?.expectedVerdictKind === 'TESTED' requires dispatchCtxForVerdict to be defined. This context is only populated when the task is actively in a Stage B dispatch path. An out-of-state task would have no dispatch context entry, so dispatchCtxForVerdict would be undefined and the entire SKIPPED block would be skipped.


src/background/stage-b-gates.ts:632 — no warn log in skip branch (REBUTTED — minor)

Reviewer claim: observability asymmetry vs delegation-gate.ts.

Why it's minor, not a defect: The background path emits the skip advisory via publishAdvisory in completion-observer.ts:526 with the full message. The foreground path additionally emits a logger.warn at the gate. The background path logs at the observer level, not the gate level — this is an intentional layering choice. The operator-facing skip signal IS present in both paths.


src/background/completion-observer.ts:526applied.skipped without null guard on applied (REBUTTED)

Reviewer claim: if applied is undefined, accessing applied.skipped throws.

Why it's safe: applied is the named local variable holding the return value of ingestBackgroundStageBCompletion, which always returns a StageBIngestionResult object (never undefined). Even if an older caller omitted skipped in the returned object, reading a missing property returns undefined (JavaScript semantics), not a TypeError. The property access is safe.


tests/unit/hooks/delegation-gate-stage-b-skipped.test.ts:97 — non-null assertion on workflow (REBUTTED)

Reviewer claim: readTaskEvidence(...).workflow!.generation throws if workflow is undefined.

Why it's safe: The test calls recordAgentDispatch first, which creates the workflow evidence record. readTaskEvidence then reads it. If recordAgentDispatch silently failed, the test would throw rather than pass silently — which is the correct test-failure signal. The ! is justified by the preceding recordAgentDispatch call.


tests/unit/hooks/delegation-gate-stage-b-skipped.test.ts:184stageBCompletion optional chain then .get (REBUTTED)

Reviewer claim: session.stageBCompletion?.set(...) silently no-ops if stageBCompletion is undefined, so the assertion expect(...).toBeDefined() passes vacuously.

Why it's a nit, not a bug: This is a test quality concern. The code under test (the delegation gate) doesn't have a defect here — it correctly does nothing to stageBCompletion for SKIPPED. The test would be more precise with session.stageBCompletion!.set(...) or a pre-initialization, but the assertion toBeDefined() verifies the entry is not removed, which is what the test intent requires. Not a blocker.


tests/unit/tools/test-runner-scope-advice.test.ts:24 — env var not restored (REBUTTED — minor)

Reviewer claim: delete process.env.SWARM_ALLOW_FULL_SUITE in beforeAll leaks state.

Why it's a nit: beforeAll runs once at suite startup. The variable is a test-opt-in flag that most test files expect to be absent. The test explicitly sets the precondition it needs. However, consistent practice across the test suite would be to save and restore in afterAll — worth fixing but not blocking.


All JSON.parse, type-compatibility, enum-expansion findings — REBUTTED

  • JSON.parse without try/catch: test_runner.execute always returns JSON by contract; uncaught exceptions surface as test framework failures (visible, not masked).
  • Loop without try/catch: first throw fails the iteration, surfacing the problem immediately.
  • Older StageBIngestionResult objects: reading a missing optional property returns undefined, never throws.
  • structuredStageBVerdict return type: private function, only two callers, both updated. Existing callers don't use strict equality on the return value.

🔁 Validation provenance

Finding Disposition
verdictEntry undefined path leaves task stuck DROPPED — fallthrough to positiveVerdict correctly fires stage_b_failed
SKIPPED check without state guard DROPPEDdispatchCtxForVerdict required by condition; out-of-state tasks skip the block
No warn log in background skip branch DROPPEDpublishAdvisory handles operator-facing logging at observer level
applied.skipped null-deref DROPPEDapplied is always a returned object, property access never throws
workflow!.generation non-null throw DROPPEDrecordAgentDispatch precedes the read; test would throw, not silently pass
stageBCompletion?.set no-ops if undefined DROPPED — test quality nit only; gate code is correct
JSON.parse without try/catch DROPPED — test tool contract guarantees JSON; exceptions surface visibly
applied.skipped type-compatibility DROPPED — JavaScript reads missing optional property as undefined
structuredStageBVerdict enum expansion DROPPED — private function, only two callers, both updated
Env var delete without restore DROPPEDbeforeAll once-per-suite; flag is test opt-in; more consistent to restore but not blocking

📝 Merge Recommendation

APPROVE

The PR correctly fixes both defects from #2756: it removes the dead-end scope "all" recommendation and treats [TESTED] ... SKIPPED verdicts as retryable state on both foreground and background paths. All five obligations are SUPPORTED. No confirmed findings remain after challenge. Implementation logic is clean: the skip check runs before the fail check on both paths; stageBCompletion is untouched; reviewer proof is preserved; consumed: true prevents retry loops.

Check Result
No CRITICAL findings
No unresolved STEALTH_CHANGE
No UNSUPPORTED obligations
Test coverage adequate
No hardcoded secrets
All async errors handled
Input validation present
No broken agent role boundaries
Prompt format contracts intact
Lockfile consistent

🔒 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.

@zaxbysauce
zaxbysauce force-pushed the fix/issue-2756-skipped-verdict-retryable branch from 13b5e7b to 36a0cfe Compare September 14, 2026 14:37
@zaxbysauce
zaxbysauce requested a balanced review from Copilot September 14, 2026 14:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

It changes core Stage B verdict-classification and gate-evidence semantics on both foreground and background paths — a guardrail area where a misclassification would wrongly preserve or delete reviewer approval proofs — warranting final human review.

Pull request overview

This PR fixes issue #2756, where two linked defects in the test_engineer Stage B path turned a recoverable tool-argument mistake into an unrecoverable rework_required state. First, test_runner's guard for scope:"convention"/"graph"/"impact" without files/targets recommended scope:"all" — the exact scope a sibling guard blocks — sending models into a loop. Second, both Stage B verdict-consumption paths (foreground delegation-gate and background ingestBackgroundStageBCompletion) scored a [TESTED] … SKIPPED verdict (tests not run) identically to FAIL, deleting the reviewer's APPROVED proof. The fix redirects the guard message to files/targets and treats a SKIPPED verdict as a retryable not-run outcome on both paths while preserving genuine FAIL/REJECTED rejection semantics.

Changes:

  • test_runner remediation message now directs callers to files/targets with a concrete example instead of the blocked scope:"all".
  • SKIPPED verdicts are classified as not-run (retryable) on both the foreground and background Stage B paths, preserving reviewer proof and Stage B eligibility; a new skipped result flag drives a distinct operator advisory.
  • Three new regression test files plus a release fragment.
File summaries
File Description
src/tools/test-runner.ts Rewords the missing-files guard message to avoid recommending the blocked scope:"all".
src/hooks/delegation-gate.ts Adds a foreground short-circuit that leaves a TESTED SKIPPED task Stage B eligible instead of failing it.
src/background/stage-b-gates.ts structuredStageBVerdict returns 'skip'; ingestion returns skipped:true without a transition/proof clearing.
src/background/completion-observer.ts Publishes a dedicated "skipped (tests not run)" advisory ahead of the generic failure messages.
tests/unit/tools/test-runner-scope-advice.test.ts Pins the non-dead-end guard message and characterizes the unchanged guards.
tests/unit/hooks/delegation-gate-stage-b-skipped.test.ts Verifies foreground SKIPPED is retryable and FAIL/REJECTED still rework.
tests/unit/background/stage-b-gates-skipped-verdict.test.ts Verifies background SKIPPED ingest is non-rejecting and FAIL/unparseable stay fail-closed.
docs/releases/pending/2756-skipped-verdict-stage-b.md Release fragment describing the fix (contains a model-name typo).
Review details
  • Files reviewed: 8/8 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/background/completion-observer.ts
Comment thread docs/releases/pending/2756-skipped-verdict-stage-b.md Outdated
@zaxbysauce

Copy link
Copy Markdown
Collaborator Author

swarm-pr-review — PR #2766

Scope: ef59deb85debd456b40db8306322268d6b702a20...36a0cfe39f6743b72c6bf56840a3f79ade720f09 (base main, head 36a0cfe39). 8 files, +566/-10 (49/10 production, 499 tests, 20 docs). Capability profile: B (native parallel subagents, no swarm controller). Depth tier: M (state-machine/concurrency risk trigger on a small diff).

Process: 2 explorer lanes (correctness/state/reliability; intent/security/tests) → 9 unique candidates → 1 independent reviewer (ran the 3 new test files: 11/11 pass) → 2 reviewer-confirmed MEDIUM findings routed to a critic (state-machine/evidence-integrity trigger) → both downgraded after adversarial re-derivation.

PR intent

Fixes issue #2756: a [TESTED] | task-N | SKIPPED | ... verdict (tests not run, e.g. because the caller omitted files/targets for scope convention/graph/impact) was previously treated identically to FAIL by both Stage B verdict-consumption paths (foreground delegation-gate.ts, background stage-b-gates.ts), wrongly forcing rework_required and deleting the reviewer's APPROVED gate proof. The fix adds a third "skip" outcome: no state transition, reviewer proof preserved, task stays Stage B-eligible, an advisory is logged/published instead. A second, related fix rewords test_runner's guard message so it no longer recommends the blocked scope:"all".

Intended vs actual mapping

Obligation (PR body AC) Evidence Status
AC1: error text directs to files, never recommends a blocked scope message field updated correctly; error field text left stale (F-3) PARTIALLY_MET
AC2: SKIPPED distinguished from FAIL on both paths Confirmed structurally + by test execution (11/11 pass) MET
AC3: not-run leaves retryable Stage B state without deleting reviewer approval Confirmed — reviewer proof/stageBCompletion preserved on the foreground path MET
AC4: no regression to genuine stage_b_failed path Confirmed — new tests include explicit FAIL regression cases, all green MET

Root-cause line citations in the PR body (test-runner.ts:3169, delegation-gate.ts:5951-5971, stage-b-gates.ts:100) were independently spot-checked against HEAD and are accurate.

Validation provenance

  • Explorer lanes: 2/2 returned (one required a resume + transcript-grep recovery after an initial bare meta-remark stop — no content lost).
  • Reviewer: 1/1 returned, independently re-traced every candidate and executed the 3 new test files directly (11 pass / 0 fail, 40 expect() calls) rather than trusting the PR body's test-count claim.
  • Critic: 1/1 returned, independently re-derived both MEDIUM findings from source rather than accepting the reviewer's rationale, and disproved two of the reviewer's three sub-claims on F-1.
  • Zero CRITICAL/HIGH findings survived reviewer+critic. Two MEDIUM findings were both downgraded to LOW after critic challenge.
  • Micro risk-family coverage: concurrency-state (Stage B state machine) and test-infrastructure (3 new test files) were the two families with real surface area in this diff and were covered by the explorer lanes above; auth-identity-secrets, untrusted-input-boundaries, subprocess-platform, dependencies-build-release, api-schema-migrations, ui-accessibility-i18n, privacy-observability, generated-provenance are NOT_TRIGGERED (no auth/subprocess/dependency/schema/UI/telemetry/generated-artifact surface in this diff). unclassified-risk folded into the general-purpose lane coverage above.

Findings

F-1 — Advisory (LOW), non-blocking. Downgraded from reviewer's initial MEDIUM after critic challenge; explorer's original framing was largely disproven.
Files: src/background/completion-observer.ts:499, src/background/stage-b-gates.ts:635-646, src/background/pending-delegations.ts:4653-4655
A background SKIPPED settlement is durably recorded as status:'ingestion_error' / ingestion.state:'retryable' — the same bucket used for a genuine transient ingestion failure. The critic disproved two of the reviewer's three sub-claims: applied.skipped is consumed in production (completion-observer.ts:521-527 reads it to pick the advisory string), and the unread consumed field is pre-existing, not new. Decisively: pre-PR, structuredStageBVerdict already mapped SKIPPED→'fail'→the same ingestion_error/retryable ledger write, so this label overload is a pre-existing property of every non-pass background gate outcome, not something this PR introduced (introduced_by_pr: NO, correcting the reviewer's initial YES). It is not an infinite loop — DEFAULT_SWEEPABLE_DELEGATION_STATUSES includes ingestion_error, so the lazy maintenance sweep eventually finalizes it to stale, just with an imprecise label in the interim.
Suggested follow-up (non-blocking, separate issue): give a background skip a distinct terminal disposition instead of reusing the generic ingestion-failure bucket.

F-2 — Advisory (LOW), non-blocking, with one open question worth a maintainer's own look.
Files: src/hooks/delegation-gate.ts:2418-2456, 5883-5885, 5957-5965, src/background/stage-b-gates.ts:98
Both verdict extractors (parsePerTaskVerdicts, structuredStageBVerdict) resolve multiple verdict lines for the same task via first-match-wins; a later conflicting line only logs STAGE_B_VERDICT_CONFLICT (not silent, confirmed at delegation-gate.ts:5883-5885) and never blocks. Before this PR, SKIPPED and FAIL both routed to the same punitive outcome so match order was immaterial; this PR makes them diverge, so a transcript with an early SKIPPED line followed by a later genuine FAIL line for the same task would now be scored SKIPPED, missing a rework bounce. The critic traced the full escalation path and found this cannot silently advance a task to completion or write false proof — the continue on SKIPPED precedes recordStageBCompletion, and the recovery path in update-task-status.ts:1352-1360 that could otherwise pick it up is gated off by hasDurableIncompleteGates and an explicit evidence.gates.test_engineer != null check that a skip never satisfies. Worst case: a missed rework bounce plus a re-dispatch, and it requires an agent to violate the "exactly one verdict line per task" prompt mandate in test-engineer.ts:235.
Open question the critic flagged but could not fully close: the "cannot advance" conclusion depends on update-task-status.ts:1202's hasDurableIncompleteGates guard holding — specifically that a Stage B-eligible task always has a non-empty required_gates list including test_engineer (line 1188-1189 requires length > 0). If a task can reach Stage B-eligible with an absent/empty required_gates, the guard degrades to a weaker hasCoder check alone, which would escalate this from an advisory finding to a genuine gate-bypass. This is flagged for a maintainer with more context on required_gates population invariants to confirm; it was not independently falsifiable within this review's scope.
Suggested follow-up (non-blocking): resolve STAGE_B_VERDICT_CONFLICT to the more punitive verdict instead of warn-and-keep-first, for both the SKIPPED/FAIL divergence introduced here and the pre-existing, more severe sibling risk the critic surfaced in passing — the same first-match-wins parser would also accept an echoed prompt example line ([TESTED] | task-2.1 | PASS | 10/10 tests passed, 85% coverage, verbatim from test-engineer.ts:242,245) as a real PASS verdict, which is a false-approval risk unrelated to and unchanged by this PR.

F-3 — Advisory (LOW), non-blocking.
File: src/tools/test-runner.ts:3166-3169
The guard's error JSON field text still reads 'scope "convention" and "graph" require explicit files or targets array...', omitting "impact" even though the guard condition covers all three scopes and the sibling message field was correctly updated to mention all three. Both fields ship in the same response payload. tests/unit/tools/test-runner-scope-advice.test.ts:53-60 pins only 'require explicit files' in error, never asserting scope enumeration, so nothing guards this drift. Purely textual/documentation — the guard still correctly blocks and message (the field an agent would read for remediation) is accurate.

F-4 — Advisory (LOW), non-blocking.
File: src/background/completion-observer.ts:521-535
No test in the repo exercises the new applied.skipped branch of the advisory-message ternary end-to-end (grep -rn skipped tests/unit/background/completion-observer*.ts returns zero hits). The two new suites cover ingestBackgroundStageBCompletion and the foreground hook, but not this specific observer branch — which is exactly the code path F-1's discussion above concerns.

Pre-existing (not introduced by this PR)

F-5. src/background/stage-b-gates.ts:92-103 vs src/hooks/delegation-gate.ts:2413-2416, 5959: the two verdict regexes disagree on whether a trailing | is required after the verdict token (stage-b-gates.ts requires it; delegation-gate.ts's pattern makes it optional), so a SKIPPED line without a trailing pipe would classify differently on the two paths. Currently mitigated because the mandated prompt template (test-engineer.ts:235-240) always includes the trailing pipe. Both regexes are case-insensitive but the downstream string comparisons are exact-case (=== 'SKIPPED') — this was already true pre-PR for PASS/APPROVED, and the fallthrough direction for a case-mismatch is fail-closed/punitive, so no new risk.

Clean lanes

  • intent-architecture: diff scope matches the PR's stated intent exactly (8 files, no unrelated changes); root-cause citations accurate; release fragment present and structurally consistent with sibling fragments.
  • tests-falsifiability: all 3 new test files exercise real production code paths (no mock.module, no hardcoded/unmocked clock dependency), each includes a genuine-FAIL regression case, and all pass (11/11, independently executed by the reviewer).
  • security-trust / reachability: the SKIPPED classification is reachable end-to-end for conforming agent output; stale and skipped ledger states are mutually exclusive by construction (stage-b-gates.ts:606-646).
  • compatibility-delivery: the new StageBIngestionResult.skipped? field has exactly one production consumer (completion-observer.ts, added in this same diff) and no other call site constructs/destructures the type — not a breaking change.

Disproved / downgraded candidates

  • Explorer's claim that applied.consumed/skipped are entirely unwired/unconsumed (part of F-1's original framing) — disproved; applied.skipped is consumed at completion-observer.ts:521-527.
  • Explorer's claim that the skip-classified background record "never converges" — disproved; the lazy maintenance sweep (DEFAULT_SWEEPABLE_DELEGATION_STATUSES) finalizes it to stale.
  • One lane's HIGH severity assessment of the error/message text drift (F-3) — the independent reviewer found HIGH indefensible since message (the actionable field) is correct and no consumer parses error in isolation; settled at LOW.
  • Explorer's framing of the case-sensitivity/regex issue (F-5) as "the regex might not match" — the regex is case-insensitive (i flag); the actual (pre-existing, low-risk) issue is the downstream exact-case string comparison plus a genuinely new sub-observation, the trailing-pipe disagreement between the two extractors.

Verdict

APPROVE_WITH_NOTES

Zero unresolved CRITICAL or HIGH findings. All four confirmed findings (F-1 through F-4) are LOW-severity, non-blocking, and independently re-derived by both a reviewer and (for the two state-machine-adjacent ones) a critic. CI is green on everything except unit shards / coverage / package-check, which were still in progress at review time — recommend confirming those land green before merge, per normal practice, but they are not expected to be affected by this diff's small surface area.

Recommended before/shortly after merge (non-blocking, can be separate follow-up issues):

  1. Add "impact" to the test-runner.ts error field text (F-3) and assert scope enumeration in the existing test.
  2. Add an observer-level test asserting the applied.skipped advisory branch and the persisted ledger state (F-4).
  3. A maintainer with more context on required_gates population should double-check whether a Stage B-eligible task can ever have an empty required_gates array — if so, F-2's "cannot bypass" conclusion needs revisiting (see F-2's open question above).
  4. Consider resolving STAGE_B_VERDICT_CONFLICT to the more punitive verdict rather than first-match-wins (F-2), which would also incidentally close the pre-existing, unrelated false-PASS risk from an echoed prompt example that the critic surfaced.

Generated by /swarm-pr-review (Claude Code, Profile B). Full candidate/reviewer/critic ledger available on request.

🤖 Generated with Claude Code

@zaxbysauce

Copy link
Copy Markdown
Collaborator Author

🤖 Multi-Stage PR Review

Pipeline: 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)
Commit reviewed: 36a0cfe39f67


🔍 PR Intent

Reconstructed obligation list (from PR text, issue, commits, changed tests, changed docs, changed interfaces — not from your priors.

  • O-001 Fix test_runner guard-2 message to direct to files/targets and stop recommending the blocked scope: "all"src/tools/test-runner.ts
  • O-002 Classify [TESTED] ... SKIPPED verdict as retryable not-run (not failure) in the foreground delegation-gate.ts path
  • O-003 Classify [TESTED] ... SKIPPED verdict as retryable not-run in the background stage-b-gates.ts ingestion path
  • O-004 Publish a dedicated "tests not run" advisory in completion-observer.ts for skipped background ingestions
  • O-005 Add regression tests covering all three changed code paths
  • O-006 No regression to genuine FAIL/REJECTED verdict semantics on either path

📦 Implementation Summary

The PR makes four coordinated changes:

  1. test-runner.ts: Guard-2's message field (but not its error field or guard semantics) is updated to list all three guarded scopes (convention, graph, impact) and direct callers to provide files/targets with a concrete example — removing the dead-end scope "all" recommendation.

  2. delegation-gate.ts: A sibling guard before the verdict rejection block intercepts TESTED SKIPPED verdicts where dispatchCtxForVerdict is present and verdict === 'SKIPPED', logs a warn, and continues without firing stage_b_failed or touching the reviewer's proof or stageBCompletion entry.

  3. stage-b-gates.ts: structuredStageBVerdict now returns 'skip' for SKIPPED verdicts (previously returned 'fail'). ingestBackgroundStageBCompletion handles this by returning { ok: false, consumed: true, skipped: true } before the rejection branch, consuming the record to prevent retry loops while firing no transition and clearing no proof. StageBIngestionResult gains the optional skipped field.

  4. completion-observer.ts: The applied.skipped branch takes first priority in the non-ok advisory ternary, emitting the skip-specific message before the stale/legacy/manual-recovery/failed branches.


✅ / ⚠️ / ❌ Intended vs Actual

Obligation Status Evidence (file:line)
O-001 SUPPORTED src/tools/test-runner.ts:3169 — message now lists all three guarded scopes and gives a concrete example
O-002 SUPPORTED src/hooks/delegation-gate.ts:5951-5966 — SKIPPED guard with warn + continue; no transition, proof, or completion entry touched
O-003 SUPPORTED src/background/stage-b-gates.ts:97-107'skip' return for SKIPPED; src/background/stage-b-gates.ts:635-646 — consumed skip with skipped: true, no transition
O-004 SUPPORTED src/background/completion-observer.ts:526-528applied.skipped takes first priority in advisory ternary
O-005 SUPPORTED Three new test files, 11 tests total, all green; blast-radius loop across 146 files returned 145 green
O-006 SUPPORTED Both FAIL and REVIEWED REJECTED regression tests green on both paths; stage_b_failed reducer untouched

🚨 Confirmed Findings

None.

All 13 confirmed findings were challenged and refuted (see validation provenance below). The PR correctly implements its stated intent across all four changed files with adequate test coverage.


🔬 Unverified but Plausible Risks

  • Risk: delegation-gate.ts:5951 — if stageBDispatchContextByCallID is not populated before toolAfter for a legitimate SKIPPED dispatch (e.g. a code path that calls toolAfter without a prior toolBefore), the new guard is bypassed and SKIPPED falls through to the existing rejection path, re-introducing the original bug for that code path.
    • Why suspicious: The guard checks dispatchCtxForVerdict?.expectedVerdictKind === 'TESTED' before checking verdictEntry?.verdict === 'SKIPPED'. If the map entry is absent, the entire SKIPPED special-case is bypassed.
    • What would verify it: Audit all call sites of toolAfter for stage B TESTED dispatches to confirm every code path calls toolBefore first; add a defensive assertion if any path skips toolBefore.

🧪 Test / Coverage Gaps

None identified. The three new test files (11 tests total) cover the core behavior change (SKIPPED as retryable) plus regression for FAIL/REJECTED on both paths. Blast-radius across 146 files returned 145 green.


📋 Shipped-vs-Claimed Gaps

None identified. All four changed code paths have corresponding new tests; the doc fragment accurately describes each change.


📝 Merge Recommendation

[APPROVE]

The PR correctly fixes both linked defects from issue #2756: the dead-end scope "all" advice in guard-2, and the wrongful scoring of [TESTED] SKIPPED as stage_b_failed on both the foreground and background Stage B paths. The fix is additive (no schema, state-shape, or rejection-semantics changes), well-tested (11 new tests + blast-radius loop), and the single plausible residual risk (the dispatchCtxForVerdict guard bypass) is a pre-existing structural assumption that can be verified independently.

Check Result
No CRITICAL findings
No unresolved STEALTH_CHANGE
No UNSUPPORTED obligations
Test coverage adequate
No hardcoded secrets
All async errors handled
Input validation present
No broken agent role boundaries
Prompt format contracts intact
Lockfile consistent

🔁 Validation Provenance

Findings CHALLENGED and DROPPED (one-line reason each):

# File:Line Claim Why Dropped
1 src/tools/test-runner.ts:3169 Message falsely warns about 'impact' restriction that doesn't exist in code REFUTEDvalidateArgs at test-runner.ts explicitly includes 'impact' as a valid scope value; the guard at line ~3200 checks scope !== 'convention' && scope !== 'graph' && scope !== 'impact' — 'impact' IS guarded. The pre-existing message only listed two of the three guarded scopes; this PR expanded it to all three. Finding was factually wrong.
2 src/background/stage-b-gates.ts:640 ok: false for consumed skip may be misinterpreted by callers REFUTED — pre-existing pattern: both ok: false/consumed: false (stale/attribution failure) and ok: false/consumed: true (zero-route no-change return) already existed before this PR. consumed has always been the authoritative "was record consumed" flag; callers checking ok without consumed were already wrong pre-change. New semantics (skipped: true) is documented on the interface and first-checked by the sole advisory consumer.
3 src/hooks/delegation-gate.ts:5955 Optional dispatchCtxForVerdict bypasses new guard REFUTEDtoolBefore must be called before toolAfter to populate the map; all test cases confirm this contract; the optional chain is the correct defensive guard for any code path that might violate it. Plausible but unverified in the diff — not a PR defect.
4 tests/…/delegation-gate-stage-b-skipped.test.ts:217 Silent catch swallows errors DROPPED — pre-existing pattern across all three new test files and the repo's existing test hygiene. Not introduced by this PR.
5 tests/…/delegation-gate-stage-b-skipped.test.ts:186 Proof existence check doesn't verify content DROPPED — pre-existing test pattern in the repo. Content correctness is the responsibility of recordGateEvidence; the test verifies the correct preservation invariant (proof exists after SKIPPED, not after FAIL).
6 tests/…/test-runner-scope-advice.test.ts:21 beforeAll deletes env var → cross-test pollution DROPPED — intentional isolation fixture; beforeAll runs once before the file's tests, afterEach runs resetSwarmState; bun:test isolates files. No cross-file pollution from a delete on a key that is not set by default.
7 tests/…/test-runner-scope-advice.test.ts:37,48,55 Unguarded JSON.parse may throw DROPPED — pre-existing pattern matching test-runner.test.ts (the existing pinned test file); if execute returned non-JSON in production that would be a separate bug; the test is checking expected JSON output.
8 src/tools/test-runner.ts:3169 error field still says 'impact' but message now omits it — inconsistency REFUTED — both fields now list all three scopes ('convention', 'graph', 'impact'). The error field was NOT changed by this PR and was already consistent with the guard; no inconsistency exists.
9 src/background/stage-b-gates.ts:105 match[2] (verdict) is never used; wrong value used DROPPED — regex (APPROVED|REJECTED|CONCERNS) has one capture group, so match[1] IS the verdict (not match[2]). The code is correct. Reviewer miscounted capture groups.
10 tests/…/delegation-gate-stage-b-skipped.test.ts:138 Non-null assertion may throw DROPPED — pre-existing test pattern; seedReviewerApproved always creates the evidence before the assertion; ?. chaining provides a safe fallback regardless.
11 tests/…/delegation-gate-stage-b-skipped.test.ts:108 Silently swallowing errors in cleanup DROPPED — pre-existing pattern, same as finding #4.
12 tests/…/test-runner-scope-advice.test.ts:15 Deleting env var is shared mutable state DROPPED — same as finding #6.
13 tests/…/delegation-gate-stage-b-skipped.test.ts:138 Non-null assertion may mask root cause DROPPED — same as finding #10.

Blind-spot pass findings: 0 added. The diff was read in full. All four changed files implement their stated intent correctly; no unintended behavior was identified.


🔒 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.

Test User added 2 commits September 14, 2026 11:01
- PRR-006: observer-path test proving the dedicated skip advisory is
  published (and 'ingestion failed' is not) via the full completion
  observer flow; test docstring now states only what is asserted
- PRR-007: guard-2 error field enumerates all three covered scopes;
  enumeration pinned in tests for both response fields
- PRR-008: release fragment model name typo (Kimi K2.7 Code)
- PRR-009: SWARM_ALLOW_FULL_SUITE saved/restored around the suite
- PRR-010: docs/planning.md rework_required entry condition notes the
  TESTED SKIPPED exception
@zaxbysauce

Copy link
Copy Markdown
Collaborator Author

swarm-pr-feedback closure — review findings resolved (head bfc0e10)

A fresh swarm-pr-review run (6 lanes: 3 base + 6 risk-family micro lanes, 11/11 trigger-eval rows attested) over ef59deb85..36a0cfe3 produced 10 normalized candidates; an independent reviewer verified 7 / rejected 2, and the critic challenge downgraded the one HIGH to MEDIUM. Five findings were fix-in-scope; all are now fixed at head bfc0e102 and independently re-approved (reviewer with falsification probes + final critic, 3 rounds):

id source item outcome evidence
PRR-006 prior review F-4 + Copilot thread observer applied.skipped advisory branch untested FIXED observer-path test in tests/unit/background/stage-b-gates-skipped-verdict.test.ts (4/0); mutation probe bites (flipping the ternary fails the test)
PRR-007 prior review F-3 guard-2 error field omits "impact" FIXED error field enumerates all three scopes; both fields pinned in test-runner-scope-advice.test.ts (5/0)
PRR-008 Copilot thread "Kim K2.7 Code" typo FIXED fragment now "Kimi K2.7 Code"
PRR-009 prior review nit SWARM_ALLOW_FULL_SUITE not restored FIXED saved in beforeAll, restored in afterAll
PRR-010 micro lane docs/planning.md rework_required entry stale FIXED SKIPPED exception noted in the state table
PRR-003 reviewer skip path has no penalty accounting DISPOSITIONED (designed) issue #2756 explicitly requests retryable-not-failed; external bounds (architect repetition breaker, max_iterations) apply; residual documented in PR body Risk section
PRR-004 reviewer empty required_gates Pass-2 state advance DISPOSITIONED (pre-existing) update-task-status.ts untouched by this PR; fail-closed guard verified at base; completion remains blocked
PRR-005 reviewer first-match-wins verdict parsing (ordering now material) DISPOSITIONED (pre-existing + follow-up) parser unchanged; skip structurally cannot advance a task; non-advancement guarantee verified; punitive conflict resolution recommended as a follow-up issue

Rejected by validation (transparency): a CRITICAL "infinite background retry loop" candidate (replay path is fenced for non-coder records; the ingestion_error bucket write is byte-identical pre-existing behavior for all non-pass outcomes) and a HIGH "advisory spam" candidate (conditional on the unreachable loop; durable advisory put is idempotent).

Gates: reviewer APPROVE (021ba9b, falsification probes) → critic NEEDS_REVISION (stale doc claims) → doc fixes → critic NEEDS_REVISION (2 residual lines) → fixed → critic APPROVE (bfc0e10, tree 2ea4c97f). Typecheck, biome, tmpdir/file-cap ratchets, and all four touched suites green. Both Copilot threads replied and resolved.

@zaxbysauce
zaxbysauce added this pull request to the merge queue Sep 14, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 14, 2026
@zaxbysauce
zaxbysauce added this pull request to the merge queue Sep 14, 2026
Merged via the queue into main with commit 9462f32 Sep 14, 2026
47 checks passed
@zaxbysauce

Copy link
Copy Markdown
Collaborator Author

🤖 Multi-Stage PR Review

Pipeline: 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)
Commit reviewed: bfc0e102aa8b


🔍 PR Intent

Reconstructed obligation list (from PR text, issue #2756, changed files, and diff)

  • O-001 Fix test_runner guard-2 message to direct to files/targets, enumerate all three scopes it covers (convention/graph/impact), and never recommend the blocked scope:"all"
  • O-002 Fix structuredStageBVerdict to return 'skip' for [TESTED] | task-N | SKIPPED | … verdicts (tests were NOT run)
  • O-003 Fix foreground delegation-gate to emit NO stage_b_failed transition and preserve reviewer proof for TESTED SKIPPED verdicts
  • O-004 Fix background ingestBackgroundStageBCompletion to return skipped: true and consume the record without firing stage_b_failed
  • O-005 Fix background completion observer to publish the dedicated skip advisory instead of the generic "ingestion failed"
  • O-006 Add regression tests covering all three verdict paths (foreground, background ingest, observer advisory)
  • O-007 Add regression test proving guard-2 never recommends the blocked scope

📦 Implementation Summary

The PR makes four targeted changes:

  1. src/tools/test-runner.ts: Guard-2 message/error fields now enumerate all three guarded scopes and direct to files/targets with a concrete example; the dead-end scope:"all" recommendation is gone.
  2. src/background/stage-b-gates.ts: structuredStageBVerdict returns 'skip' for SKIPPED; ingestBackgroundStageBCompletion consumes the record with skipped: true and returns early before the stage_b_failed branch.
  3. src/hooks/delegation-gate.ts: A continue guard fires before the verdict rejection block when expectedVerdictKind === 'TESTED' and verdict === 'SKIPPED'; no transition, no proof clearing.
  4. src/background/completion-observer.ts: applied.skipped takes first priority in the !applied.ok advisory ternary.

Three new test files cover all paths: test-runner-scope-advice.test.ts (5 tests), delegation-gate-stage-b-skipped.test.ts (4 tests), stage-b-gates-skipped-verdict.test.ts (4 tests including observer advisory).


✅ / ⚠️ / ❌ Intended vs Actual

Obligation Status Evidence
O-001 SUPPORTED src/tools/test-runner.ts:3167-3169 — message/enum enumerate all three scopes; example directs to files
O-002 SUPPORTED src/background/stage-b-gates.ts:100-104'skip' returned for SKIPPED
O-003 SUPPORTED src/hooks/delegation-gate.ts:5956-5967continue guard fires before rejection block; no transition
O-004 SUPPORTED src/background/stage-b-gates.ts:632-644 — early return { ok:false, consumed:true, skipped:true }
O-005 SUPPORTED src/background/completion-observer.ts:526-527applied.skipped prioritized in advisory ternary
O-006 SUPPORTED All three new test files cover SKIPPED + FAIL + unparseable paths for each affected path
O-007 SUPPORTED tests/unit/tools/test-runner-scope-advice.test.ts — guard-1 block pinned; guard-2 message/enum asserted

🚨 Confirmed Findings

None. Every confirmed finding from the reviewer either (a) predates this PR, (b) describes intended behavior, (c) is a test-quality concern rather than a production defect, or (d) requires speculative future misuse of a correctly-added field. The core defects are fixed and the fix is sound.


🔬 Unverified but Plausible Risks

These require runtime validation or are inherently speculative; no structural proof available from the diff.

  • Risk: Future callers of ingestBackgroundStageBCompletion read only ok and ignore skipped, treating a retryable skip as a hard failure.

    • Why suspicious: The return type is { ok: false, skipped: true } — a semantic pattern that could be misread.
    • What would verify it: TypeScript-enforced discriminated union narrowing ok === true | skipped === true at call sites; linter rule; or adding ok: true for the skip case (semantically questionable since no work was consumed).
  • Risk: Foreground and background SKIPPED paths diverge if one is refactored without the other.

    • Why suspicious: Two separate code paths with a shared semantic invariant.
    • What would verify it: Integration test exercising both paths end-to-end with the same verdict.

🧪 Test / Coverage Gaps

  • Gap: test-runner-scope-advice.test.ts does not assert the error field for the guard-2 empty-files rejection path (only the message field is checked for 'impact' in the all-scopes test).
    • Evidence: tests/unit/tools/test-runner-scope-advice.test.ts:62parsed.error not checked in the third test
    • Severity: LOW — the guard-2 rejection path (scope with no files) is a distinct code branch from the scope-without-files guard; the message field contract is fully tested; the error field is tested for the all-scopes assertion but the empty-files branch could theoretically regress independently

📋 Shipped-vs-Claimed Gaps

None found. All claims in the PR description match the diff. The release fragment correctly describes all four changed modules and their semantics.


📝 Merge Recommendation

[APPROVE]

The PR fixes both legs of issue #2756 correctly. The scope:"all" dead-end advice is gone; TESTED SKIPPED verdicts are now retryable on both the foreground and background paths; reviewer proof is preserved; the dedicated skip advisory is published. Three new regression suites cover all changed paths with SKIPPED + FAIL + unparseable assertions. No production logic was regressed; the blast-radius claim of 145/146 test files green is consistent with the narrow scope of changes.

Check Result
No CRITICAL findings
No unresolved STEALTH_CHANGE
No UNSUPPORTED obligations
Test coverage adequate
No hardcoded secrets
All async errors handled
Input validation present
No broken agent role boundaries
Prompt format contracts intact
Lockfile consistent ✅ (no lockfile changes)

🔁 Validation Provenance

Findings DROPPED (one-line reason):

  1. test-runner scope 'none' bypassscope:"none" explicitly skips all discovery; it is not unsafe full-project discovery, making it out-of-scope for the guard's concern.
  2. SKIPPED check runs before verdict parsed — False positive; verdict === 'skip' is only reached when stageBRole && verdict !== null, so the role guard fires first.
  3. ok:false on skip is ambiguous — Design concern, not a PR defect; the skipped field is correctly added and consumed by all callers; type signature is accurate.
  4. dispatchCtxForVerdict undefined in warn log — Pre-existing code; PR only added the continue guard, not the warn log location.
  5. example uses Python file — Context-dependent suggestion is expected; tests/test_calc.py is a clear, generic example.
  6. workspace snapshot mismatch in testsprepareTask commits before captureWorkspaceSnapshot; test setup order is correct.
  7. completedEnvelope returns object type — Test helper; the function produces valid envelopes validated by the passing test.
  8. fs.rmSync throws in afterEach — Pre-existing test-hygiene pattern; maxRetries: 5 and isolatedEnv provide best-effort cleanup.
  9. seedReviewerApproved generation staleness — Synchronous recordAgentDispatch creates a single stable generation; no race within the test.
  10. runTestEngineerDispatch not awaiting errors — Both functions are awaited; the concern is hypothetical.
  11. stageBCompletion.toBeDefined() doesn't check value — Pre-existing test assertion; the new behavior is covered by the state/proof tests.
  12. REVIEWED REJECTED no evidence read — Pre-existing behavior with coverage elsewhere; not a PR gap.
  13. writePlan taskId sanitization — Test helper for test-only IDs; not production attack surface.
  14. seedReviewerApproved timing — Pre-existing; no concurrency within a single test.
  15. beforeAll/afterAll no try/finallyafterAll always fires in Bun; the conditional restore handles all code paths correctly.
  16. JSON.parse without try/catch — Intentionally strict: a parse failure surfaces the real problem immediately.
  17. scope assertion only checks '"impact"' — The test loops over all three scopes; each iteration asserts the respective scope name.
  18. api-contract skipped field ignored by future callers — Plausible risk; see Unverified but Plausible Risks.
  19. foreground/background paths drift on refactor — Plausible risk; see Unverified but Plausible Risks.
  20. continue bypasses future verdict-block additions — Maintenance concern, not a PR defect.
  21. regex greedy backtracking — Pre-existing regex; ReDoS requires adversarial input; agent output is bounded.
  22. prepareTask non-null assertion — Test setup ensures session exists; if it didn't the test setup would fail first.
  23. session read without existence assertion — Covered by prepareTask/beforeEach setup order.
  24. test_runner.execute {} as any contextRecord<string, unknown> accepts {}; production code guards missing fields with defaults.
  25. afterAll env delete without try/finally — Covered above (item 14).

Blind-spot findings added: None — the blind-spot pass found no additional real defects beyond the pre-existing plausible risks already noted.


🔒 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants