fix(delegation-gate): require explicit critic task attribution - #2772
Conversation
Drift check reportFound 2 drift finding(s): 0 error, 0 warning, 2 notice. required-check-contract (2)
|
🤖 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 IntentReconstructed from PR text, issue #2757, and test intent:
📦 Implementation SummaryThe PR adds ✅ /
|
| Obligation | Status | Evidence |
|---|---|---|
| O-001 | SUPPORTED |
delegation-gate.ts — isExplicitTaskEvidenceAgent gate prevents prose-only attribution for critic/critic_sounding_board |
| O-002 | SUPPORTED |
delegation-gate.ts — policy: 'attribution' passed to resolveEvidenceTaskId at all three binding points |
| O-003 | SUPPORTED |
delegation-gate.ts:4290 — !isExplicitTaskEvidenceAgent(targetAgent) preserves known-ID check for reviewer/test_engineer; tests confirm TASK-line routing |
| O-004 | SUPPORTED |
Three new test files cover the fix, over-limit plans, and public boundaries |
🚨 Confirmed Findings
None — all reviewer findings were challenged and refuted, or reduced to non-actionable nits.
🔬 Unverified but Plausible Risks
None — all plausible risks were verified against the diff and test assertions.
🧪 Test / Coverage Gaps
None.
📋 Shipped-vs-Claimed Gaps
None.
🔁 Validation Provenance
| Finding | Disposition | Reason |
|---|---|---|
Reviewer #1: allowSessionFallback: undefined → backward-compat concern |
DROPPED | evidenceTaskResolutionOptions returning undefined for non-explicit agents is intentional; reviewer/test_engineer unchanged, covered by existing test 'preserves TASK-line routing for reviewer and test_engineer' |
Reviewer #2: targetAgentForEvidence dead code before block |
DROPPED | Variable is declared before the if block but consumed inside it — valid hoisting, not dead code |
Reviewer #3: normalizedSubagentType redundant |
DROPPED | LOW/NIT: refactor clarity improvement, not a defect; already collapsed to minor |
| Reviewer #4/9: Over-limit plan foreign-ID slip-through | DROPPED | Verified: resolveTaskId returns null for unresolved IDs before the full-plan check; delegation-gate-critic-task-attribution-large-plan.test.ts explicitly asserts foreign ID rejection |
| Reviewer #5: Reviewer text-only plan attribution not tested | DROPPED | PR intent explicitly preserves pre-fix behavior for reviewer; 'preserves TASK-line routing' test covers it; not a PR-introduced defect |
Reviewer #6: critic_sounding_board + TASK: marker not tested |
DROPPED | Test 'requires explicit sounding-board attribution' uses mega_critic_sounding_board + TASK: ${TASK_TWO} — swarm-prefix stripping IS covered |
Reviewer #7: workflowGeneration: 0 vacuous assertion |
DROPPED | PR author wrote and validated this assertion; test passes; stageBDispatchGenerationsByCallID is populated before toolAfter |
| Reviewer #8: Negative background critic path not tested via observer | DROPPED | Test 'does not create task evidence from text-only plan critic' and 'keeps an unbound background plan critic out of pending gate ingestion' cover the negative path for both settlement and pending capture |
Blind spot: targetAgentForEvidence early declaration style |
DROPPED | Style preference only; no behavior change |
Blind spot: TaskIdPolicy import added |
DROPPED | Required for the new EvidenceTaskResolutionOptions type; correct |
📝 Merge Recommendation
[APPROVE]
The fix correctly closes #2757. The EXPLICIT_TASK_EVIDENCE_AGENTS gate, threaded via evidenceTaskResolutionOptions() into all three binding points, ensures critic/critic_sounding_board can no longer acquire durable per-task gates from incidental plan prose. All other roles retain plan-aware routing. Three new test files provide targeted regression, large-plan, and public-hook coverage. No confirmed defects remain.
| 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 | ✅ (not touched) |
🔒 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.
There was a problem hiding this comment.
🔵 Needs a closer look
The change alters gate-evidence attribution in the security-critical delegation-gate guardrail, a fail-closed correctness boundary where subtle mistakes warrant final human review despite the code appearing correct and well-tested.
Pull request overview
This PR fixes issue #2757, where a plan-level critic / critic_sounding_board dispatch that merely mentioned a plan task ID in its review prose (the default critic_pre_plan: true case) was recorded as a per-task critic gate. The next accepted coder mutation cleared that gate's proof but kept the requirement, and nothing could re-satisfy it — wedging an otherwise-complete task at completion.
The fix introduces an attribution-only evidence policy for exactly those two critic roles: their task-ID resolution now requires a structured task_id/taskId field or an exact TASK:/task_id: marker (via resolveTaskId policy 'attribution'), disables the session-state fallback, and skips incidental plan-text extraction — applied consistently at launch, background pending capture, and foreground settlement. Reviewer/test-engineer plan-aware routing and large-plan explicit attribution are preserved. I traced all resolveEvidenceTaskId call sites and confirmed the two untouched ones (reviewer routing at 4077, coder preflight at 4564) are correctly out of scope, and that the completion observer consumes the stored record task ID rather than re-resolving from prose.
Changes:
- Add
EXPLICIT_TASK_EVIDENCE_AGENTS+evidenceTaskResolutionOptionshelpers and thread anEvidenceTaskResolutionOptions { policy, allowSessionFallback }throughresolveEvidenceTaskId, including a bounded over-limit-plan path that defers numeric membership to the existing full-plan check. - Skip incidental plan-text task extraction at launch for the two critic roles so only explicit attribution binds.
- Add frozen/large-plan/public-hook regression tests and a pending release note with recovery/migration guidance.
File summaries
| File | Description |
|---|---|
src/hooks/delegation-gate.ts |
Core fix: attribution-only policy for critic/critic_sounding_board across launch, background capture, and settlement; over-limit plan handling. |
tests/unit/hooks/delegation-gate-critic-task-attribution-2757.test.ts |
Regression coverage for the dead-end, explicit-attribution recording, and preserved reviewer/test_engineer TASK-line routing. |
tests/unit/hooks/delegation-gate-critic-task-attribution-public.test.ts |
Public-hook boundaries: sounding-board attribution, background ingestion, over-limit launch/settlement. |
tests/unit/hooks/delegation-gate-critic-task-attribution-large-plan.test.ts |
Unit coverage of resolveEvidenceTaskId attribution policy for over-limit plans. |
docs/releases/pending/fix-plan-critic-task-attribution-2757.md |
User-visible release note with recovery and migration guidance. |
Review details
- Files reviewed: 5/5 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.
swarm-pr-review — PR #2772Profile: B (Claude Code native subagents, no controller) · Depth tier: M (guardrail/evidence state-machine risk trigger) · Scope: PR intentCloses #2757: a Intended vs actual mapping
Validation provenance
Confirmed findingsF2 — HIGH — CI genuinely red; PR body's "checks passed" claim is false — blocking The Suggested fix: replace lines 45-47 with F5 — MEDIUM — the #2757 wedge remains reachable via the now-required explicit path This PR fixes the trigger (ambient prose) but not the underlying mechanism (issue #2757's root cause: Suggested action: either fix the clear/re-record asymmetry itself (out of scope for a task-attribution fix, may warrant its own issue), or add a regression test proving explicit per-task critic gates survive an accepted mutation, and note the residual risk in the release note. Downgraded / re-characterized findings (critic-revised — kept for transparency, not blocking)F1 — downgraded HIGH-candidate → LOW (robustness/hygiene, not security) F4 — downgraded MEDIUM-candidate → LOW (docs gap only, not a regression) Advisory / low-severity (not blocking, reviewer-confirmed)
Suppressed candidatesNone suppressed — all candidates were routed to reviewer per protocol regardless of severity. Disproved / materially revised claims
Verdict: APPROVE_WITH_NOTES, conditioned on fixing F2 before mergeF2 is a genuine, currently-failing, merge-blocking CI check (one-line fix). Everything else is advisory. The core fix for #2757 is sound and well-targeted; the critic's independent pass found the two most severe-looking candidate findings were overstated on closer tracing, which is disclosed above rather than smoothed into the headline severity. Before merge:
Worth a follow-up issue (non-blocking): 🤖 Generated via |
Feedback closure ledger (head
|
| ID | Disposition | Evidence |
|---|---|---|
| F1 | RESOLVED | Strict numeric attribution is required before durable critic-family evidence. |
| F2 | RESOLVED | Non-strict explicit IDs retry marker-only resolution and fail closed otherwise. |
| F3 | RESOLVED | Named IDs cannot shadow a valid numeric TASK: marker; regression passes. |
| F4 | RESOLVED | Unsupported named IDs cannot settle durable critic evidence. |
| F5 | RESOLVED | Mixed named-ID/marker coverage passes. |
| F6 | RESOLVED | Changed fixtures use canonicalMkdtemp and safeRmRecursive. |
| F7 | RESOLVED | Existing marker semantics remain anchored and covered. |
| F8 | DISPROVED / NO REGRESSION | Security-bypass paths are unchanged; attribution is stricter. |
| F9 | PRE-EXISTING / NO REGRESSION | Actor/scope binding is outside the diff. |
| F10 | RESOLVED | Text-only critic dispatches remain unbound; explicit paths pass. |
| F11 | PRE-EXISTING / NO REGRESSION | Recovery-document behavior is unchanged. |
| F12 | PRE-EXISTING / NO REGRESSION | Bounded full-plan membership handling is unchanged and covered. |
| F13 | RESOLVED | Foreground/background attribution boundaries pass. |
| F14 | RESOLVED | Accepted coder mutation clears critic proof while retaining the requirement; a fresh explicit critic re-satisfies it. |
| F15 | RESOLVED | Drift, hallucination, and architecture critic roles require explicit attribution. |
| F16 | RESOLVED | Architect prompt now requires consistent numeric TASK:/task_id attribution. |
| F17 | RESOLVED | Reviewer/test-engineer plan-list routing is tested with both session fallbacks cleared. |
| F18 | PRE-EXISTING / INTENTIONAL | Explicit critic roles intentionally disable session fallback. |
| CI-001 | RESOLVED | check:test-tmpdir passes with 0 violations on the committed head; GitHub quality is green. |
| CI-002 | RESOLVED | The prior unit skip was downstream of old quality; the new head has fresh unit jobs running. |
| CONFLICT-001 | CLEAR | PR remains mergeable with no conflict markers. |
| STALE-001 | CLEAR | Local, remote branch, and PR head are bound to the published commit. |
| BODY-001 | RESOLVED | PR body refreshed with current summary, invariant audit, and test plan. |
| COMMENT-001 | NO ACTIONABLE FINDING | Copilot comment requested a closer look but supplied no concrete change. |
| COMMENT-002 | ADVISORY ONLY | Drift report contains only baseline RULESET_DIVERGENCE notices. |
Local validation includes typecheck, lint, build, Node bundle import, focused 15-test regression coverage, and all publication ratchets. package:smoke passed with an isolated npm cache outside the restricted sandbox; the default Windows profile’s swarm-model failure reproduces only as an existing %USERPROFILE%\\.config\\opencode permission issue.
Close the feedback loop by enforcing strict task identity for critic-family evidence, preserving reviewer/test-engineer plan routing, and documenting the delegation contract. Add regression coverage for mixed IDs, mutation re-gating, Windows-safe fixtures, and prompt guidance.
bb411e6 to
b18b417
Compare
🤖 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) Phase 0 — Reconstruct IntentIssue #2757: A Obligations from PR description, release note, and diff:
Phase 1 — Summarise Actual BehaviourThe PR:
Phase 2 — Compare Intended vs Actual
Phase 3 — Challenge Confirmed FindingsChallenge:
|
| Obligation | Status | Evidence (file:line) |
|---|---|---|
| O-001 | SUPPORTED |
delegation-gate.ts:2668–2677 — EXPLICIT_TASK_EVIDENCE_AGENTS; evidenceTaskResolutionOptions returns 'attribution' policy |
| O-002 | SUPPORTED |
delegation-gate.ts:3095–3116 — marker-only recheck after stripping explicit fields; fails closed if no strict numeric marker |
| O-003 | SUPPORTED |
delegation-gate.ts:5414–5426 — evidenceTaskResolutionOptions passed at background pending launch |
| O-004 | SUPPORTED |
delegation-gate.ts:6259–6269 — evidenceTaskResolutionOptions passed at foreground settlement |
| O-005 | SUPPORTED |
delegation-gate.ts:4368–4378 — !isExplicitTaskEvidenceAgent(targetAgent) guard; reviewer/test_engineer continue with 'plan' policy |
| O-006 | SUPPORTED |
delegation-gate.ts:3100–3108 — empty context for over-limit plans; full-plan membership at 3117–3122 |
| O-007 | SUPPORTED |
architect.ts:741–748 — new TASK ATTRIBUTION block in the delegation contract |
🚨 Confirmed Findings
None. All 19 confirmed findings were refuted as false positives.
🔬 Unverified but Plausible Risks
None — all structural paths were verified against the diff and the full file content provided.
🧪 Test / Coverage Gaps
- Gap: No test covers the exact regression from the issue (critic with ambient
1.1in prompt, then accepted coder mutation, then evidence still absent).- Evidence: The regression test
'does not leave an unsatisfiable critic requirement after a coder mutation'attests/unit/hooks/delegation-gate-critic-task-attribution-2757.test.ts:258exercises exactly this sequence: unbound critic prompt → accepted mutation → stage A/B completion → evidencerequired_gatesdoes not containcritic. This is the correct coverage.
- Evidence: The regression test
- Gap: No test covers a critic dispatch with
task_idargument AND an invalidTASK: 9.9marker that should resolve tonull.- Evidence: The large-plan and public tests cover explicit foreign IDs and marked foreign IDs separately. A mixed case (valid explicit + invalid marker) is not tested. However, the recheck strips the explicit field and evaluates the marker in isolation — the test for "named ID shadowing numeric marker" (
delegation-gate-critic-task-attribution-2757.test.ts:175) implicitly covers the inverse: when a named explicit + valid marker are both present, the numeric marker wins.
- Evidence: The large-plan and public tests cover explicit foreign IDs and marked foreign IDs separately. A mixed case (valid explicit + invalid marker) is not tested. However, the recheck strips the explicit field and evaluates the marker in isolation — the test for "named ID shadowing numeric marker" (
📋 Shipped-vs-Claimed Gaps
- Gap: None. The PR's claims are matched by code: critic gating, marker precedence, background/foreground parity, reviewer/test_engineer preservation, large-plan fallback, and architect guidance are all present.
📝 Merge Recommendation
[APPROVE]
The PR correctly fixes #2757: critic-family dispatches no longer acquire per-task gates from ambient plan prose. All six obligations are satisfied, the architecture is sound (marker-only recheck with strict numeric validation, full-plan membership guard for over-limit plans), and no confirmed defects remain.
| 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
Confirmed findings reviewed: 19 items from the reviewer-confirmed list — all DROPPED as false positives.
Dropped findings and reasons:
- Hardcoded offset / limit drift (2): both the test constant and the plan array use the same imported
maxKnownIds— cannot drift independently. - Boundary-adjacent invalid IDs: the full-plan membership check is the authoritative guard;
9.9exercises it identically. - Cleanup ordering:
isolatedEnvandtmpDirare independentcanonicalMkdtemppaths with no ordering constraint. - Null generation:
seedStageAonly accessesgetTaskWorkflowSnapshotafter a successfulawait transitionTaskWorkflowEvidence—nullis not returned through this path. - Prefix stripping:
stripKnownSwarmPrefixis the documented contract; the test exercises it correctly. - Hardcoded
workflowGeneration: 0: the test documents its expectation; the assertion is correct for null-ID pending records. - Partial record consumption: explicit assertions verify fully-populated vs null-ID paths.
- Zombie pending record: foreign ID returns
nullfromresolveEvidenceTaskId; no pending record is created. - API contract back-compat: type widens by adding an optional field; all callers use named args.
- Over-limit empty context: full-plan membership check at 3117–3122 guards against arbitrary numeric IDs.
- Prose guidance enforcement: a model ignoring guidance is rejected at the hook level — this is by design.
- Shallow copy mutation: local variable, no async gaps, no alias — standard slice-and-retry.
EXPLICIT_FIELDSrename: the constant was never exported before; no consumer could have imported it.- Observer resource leaks:
resetSwarmState()inafterEachclears the session-scoped observer registry. - 1025 task allocation: performance nit; mocking plan loading would increase complexity without improving correctness.
Blind-spot pass: 4 items examined — all resolved as intentional design (variable scope lift, null-task-ID fallback, marker regex semantics, last-token requirement). No new defects found.
🔒 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.
Close the feedback loop by enforcing strict task identity for critic-family evidence, preserving reviewer/test-engineer plan routing, and documenting the delegation contract. Add regression coverage for mixed IDs, mutation re-gating, Windows-safe fixtures, and prompt guidance.
…-task-attribution-2757
🤖 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 from issue #2757, PR description, and diff:
📦 Implementation SummaryThe PR introduces an ✅ /
|
| Obligation | Status | Evidence |
|---|---|---|
| O-001 | SUPPORTED |
delegation-gate.ts:2668–2682 — EXPLICIT_TASK_EVIDENCE_AGENTS set + evidenceTaskResolutionOptions() route critics to attribution policy |
| O-002 | SUPPORTED |
delegation-gate.ts:3097–3110 — marker-only re-resolution with strict-numeric check |
| O-003 | SUPPORTED |
delegation-gate.ts:2668–2679 — reviewer/test_engineer not in EXPLICIT_TASK_EVIDENCE_AGENTS; reviewer/test coverage in all three new test files confirms routing |
| O-004 | SUPPORTED |
architect.ts:740–747 — new TASK ATTRIBUTION block added to delegation guidance |
🚨 Confirmed Findings
[LOW] safeRmRecursive is not awaited in afterEach cleanup
- Location:
tests/unit/hooks/delegation-gate-critic-task-attribution-public.test.ts:55,delegation-gate-critic-task-attribution-2757.test.ts:55 - Why it matters:
safeRmRecursivewrapsfs.promises.rmand is async. Withoutawait, the test body proceeds while cleanup is still pending. If the rm call throws, the error surfaces asynchronously and may not be visible to the test runner as a test failure. - Evidence:
afterEach(() => { ...; safeRmRecursive(tmpDir); ... })—safeRmRecursiveis not prefixed withawait. Same pattern in both new test files. - Fix direction: Change to
await safeRmRecursive(tmpDir)inside theafterEach. SinceafterEachis async-capable inbun:test,awaitis legal here.
All other confirmed findings from the reviewer are refuted or reduced — see §🔁 Validation provenance.
🔬 Unverified but Plausible Risks
None at threshold.
🧪 Test / Coverage Gaps
- Gap: No test exercises
resolveEvidenceTaskIdfor a non-critic agent with an active PR review binding and no plan context (theallowSessionFallback: false+!planTaskIdContextearly-return path).- Why this matters: The refactor moved the early-return from a pre-
getEvidenceTaskIdguard into the newif (!resolvedTaskId)block. While the logic is correct, this specific combination is not exercised. - What would verify it: A test similar to the
stage-b-task-linetest but withactivePrReviewBinding: trueand sessioncurrentTaskIdpopulated, asserting thatnullis returned (fail-closed for PR binding without plan). - Confidence: LOW — the pre-existing
allowSessionFallback: falsebehavior was already tested for other paths; the new block is structurally equivalent.
- Why this matters: The refactor moved the early-return from a pre-
📋 Shipped-vs-Claimed Gaps
None.
🔁 Validation provenance
Findings CHALLENGED and DROPPED
| Finding | Reason for drop |
|---|---|
Lines 132/162: structuredGeneration/markerGeneration unassigned before use |
Both seedStageA() calls are awaited; structuredGeneration and markerGeneration are assigned before use in toMatchObject. This is standard async/await — no bug. |
Lines 149/208: mega_critic_* subagent type not normalizing |
isExplicitTaskEvidenceAgent calls stripKnownSwarmPrefix() internally; both the agent check and the gate recording use the stripped name. Test correctly expects gates.critic_sounding_board and gates.critic. |
Line 3069: options.policy throws on undefined |
EvidenceTaskResolutionOptions has policy?: TaskIdPolicy (optional). resolveEvidenceTaskId defaults via const policy = options.policy ?? 'plan'. Default parameters handle undefined. |
Line 3053: plan?.phases null dereference |
loadPlanJsonOnly returns `Plan |
| Line 3088: over-limit bypass of bounded-ID resolver | Intentional: the bounded resolver cannot hold >1024 IDs; the full-plan membership check below (plan?.phases.some(...)) performs numeric validation against the complete loaded plan instead. |
| Line 4416: dispatch-plan ID binding skipped for explicit evidence agents | candidateTaskIds is pre-populated from resolveEvidenceTaskId which already performed plan membership checks (including full-plan re-check for over-limit). The skipped block would redundantly check the same membership. |
| Lines 3098/3100: shallow spread mutation leak | The test passes fresh args objects from hook.toolBefore; callers do not hold references to the inner args fields. Mutation does not leak. |
Line 5414: normalizedSubagentType unused after refactor |
Variable was extracted to avoid calling stripKnownSwarmPrefix twice (once for evidenceTaskResolutionOptions, once for TASK_GATE_AGENTS.has). Used at line 5459. Not dead code. |
| Line 22 architect-test regex: single-digit only | Pattern \d+\.\d+(?:\.\d+)* matches multi-component IDs (e.g., 10.1, 1.2.3). + is one-or-more, not single-digit. |
| Line 28 large-plan allocation | Single synchronous allocation per test run, not per assertion. Acceptable for regression coverage. |
Lines 89/172: seedStageA return/observer concurrency |
seedStageA awaits both transitions and propagates errors. Background completion observer in tests uses synchronous in-process dispatch, not deferred queuing. |
| Lines 57/55/247: resource leaks in test hygiene | beforeEach throwing before isolatedEnv assignment: afterEach runs, isolatedEnv?.cleanup() safely no-ops. closeProjectDb before safeRmRecursive is correct order. TOCTOU/symlink: test uses isolated temp dir, not shared paths. |
| Lines 137 test-scope gap | Test explicitly sets both session fields to null to force plan-text parsing. Comment acknowledges this is intentional. |
Line 48 EXPLICIT_TASK_ID_FIELDS export misuse |
Misuse (iterating and deleting non-existent fields) silently no-ops — not a bug in the exported constant itself. |
Blind-spot pass
Scanned resolveEvidenceTaskId control flow for all branches. The if (!resolvedTaskId) early-return at the new block (lines ~3131–3135) is reachable for non-critic agents when plan context is absent and allowSessionFallback is false. The original code also returned null in this case (the allowSessionFallback: false guard was pre-existing). The additional resolution.status checks ('invalid', 'ambiguous') are stricter than before for those statuses — a behavioral improvement, not a regression. No structural defect found.
Merge Recommendation
[APPROVE_WITH_FIXES]
The core fix is correct, well-tested, and fully addresses #2757. One LOW cleanup issue (un-awaited safeRmRecursive in two test files) should be fixed before merge to ensure temp directory errors surface correctly in CI.
| Check | Result |
|---|---|
| No CRITICAL findings | ✅ |
| No unresolved STEALTH_CHANGE | ✅ |
| No UNSUPPORTED obligations | ✅ |
| Test coverage adequate | ✅ |
| No hardcoded secrets | ✅ |
| All async errors handled | safeRmRecursive not awaited in afterEach |
| Input validation present | ✅ |
| No broken agent role boundaries | ✅ |
| Prompt format contracts intact | ✅ |
| Lockfile consistent | ✅ (not changed) |
🔒 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.
…x-plan-critic-task-attribution-2757 # Conflicts: # src/hooks/delegation-gate.ts # tests/unit/hooks/delegation-gate-critic-task-attribution-2757.test.ts
Feedback closure ledger — PR #2772, head
|
| Finding | Status | Closure |
|---|---|---|
| F1 | RESOLVED | Critic-family durable evidence requires strict task attribution; session fallback is disabled. |
| F2 | RESOLVED | Invalid or unbound critic task IDs fail closed. |
| F3 | RESOLVED | Public-hook regression covers a named ID alongside a valid standalone TASK: marker. |
| F4 | RESOLVED | Launch, pending-evidence capture, and settlement use strict critic attribution. |
| F5 | RESOLVED | Mixed named-ID/marker behavior is tested through the public Task hook. |
| F6 | RESOLVED | Added fixtures use synchronous, bounded safeRmRecursive; unchanged older raw cleanup is pre-existing. |
| F7 | RESOLVED | Fenced, quoted, blockquoted, prose-suffixed, and free-text markers are rejected; bare standalone markers remain supported. |
| F8 | DISPROVED | The alleged security bypass is not present; the change narrows attribution. |
| F9 | PRE-EXISTING | Actor/scope concern is outside the changed code. |
| F10 | DISPROVED | Non-critic plan-aware routing remains the default and is covered by tests. |
| F11 | PRE-EXISTING | Recovery documentation is unchanged. |
| F12 | PRE-EXISTING | Full-plan membership validation remains in the over-limit path. |
| F13 | RESOLVED | New fixtures use canonical temp paths and safe cleanup; ratchets pass. |
| F14 | RESOLVED | Accepted mutation followed by refreshed explicit critic evidence is tested. |
| F15 | RESOLVED | All five critic-family roles use strict attribution. |
| F16 | RESOLVED | Architect prompt guidance and its regression test specify numeric task attribution. |
| F17 | RESOLVED | Reviewer/test-engineer plan-list routing remains covered. |
| F18 | RESOLVED / INTENTIONAL | Critic roles explicitly disable session fallback. |
| FB-001 | DISPROVED | safeRmRecursive is synchronous; awaiting it is unnecessary. |
| FB-002 | RESOLVED | New plan-free integration regression sets stale currentTaskId, verifies unresolved receipt + consumed authorization, and rejects replay. |
| CI-001 | RESOLVED LOCALLY | Exact-head temp-dir gate and all local ratchets pass; fresh GitHub checks are now running. |
| CI-002 | SUPERSEDED / PRE-EXISTING | The old unit-passed status followed skipped unit work after the stale-head quality failure; it says nothing about a83. |
| CONFLICT-001 | RESOLVED | Base-sync history is clean; GitHub reports the published head mergeable. |
| STALE-001 | RESOLVED | GitHub now reports head a83ebe73a81a28e755fad81ffc67e106d95e596f. |
| BODY-001 | RESOLVED | PR description refreshed with exact-head validation and the full invariant audit. |
| COMMENT-001 | NO SOURCE FINDING | Copilot left a COMMENTED advisory; human APPROVED review is still required by the merge gate. |
| COMMENT-002 / drift | ADVISORY ONLY | Drift report had two baseline RULESET_DIVERGENCE notices and zero errors/warnings. |
The prior red CI run was for b18b4174…; its release-owner-guard failure was caused by the stale-base comparison, and downstream unit jobs were skipped. Do not treat that run as a result for a83. No GitHub review thread was programmatically resolved. Merge remains gated on green required checks and a human APPROVED review; no self-approval or bypass was used.
Closes #2757
Summary
TASK:marker while preserving reviewer and test-engineer plan-list routing.Invariant audit
bun run buildpassed.test_runnerscope was used.check:test-file-cappassed.currentTaskIdis ignored, the one-shot authorization is consumed, and replay is rejected.dist/files changed.Test plan
tests/integration/pr-workflow-taskless-reentry-real-host.test.ts.bun run build,bun run typecheck,bun run lint:ci, andbun run check:invariantspassed. Lint reported 4 existing warnings; invariants reported 236 advisory subprocess-timeout warnings.check:test-tmpdir,check:test-file-cap,check:test-clock, andcheck:mock-cleanuppassed; their reported warnings are pre-existing.bun run package:smokepassed:opencode-swarm-7.181.2.tgz(1337 files).git diff --check origin/main...HEADpassed. The nine-file diff contains nopackage.json,CHANGELOG.md,.release-please-manifest.json, or generateddist/changes.gpt-5.6-sol, high): PASS, no severity-tagged findings.a83ebe73a81a28e755fad81ffc67e106d95e596f: APPROVED.a83ebe73a81a28e755fad81ffc67e106d95e596fis green, including all six unit shards and theunit-passedaggregator. The prior red run was against the stale pushed head; external human approval is the only remaining merge gate.Feedback status
The exact-head feedback closeout records F1–F18 and FB-001/FB-002 as resolved, disproved, or pre-existing/no-regression. The new taskless-reentry test closes FB-002. No GitHub review thread was programmatically resolved. The Copilot review remains COMMENTED, not APPROVED; required human approval and green current-head CI remain merge gates.