Skip to content

Kill the sticky salvage hard-block's false positives - #579

Merged
TheGreatAxios merged 3 commits into
mainfrom
cl-6964-delete-the-unreachable-thrash-matcher-hint-and-salvage-class
Aug 23, 2026
Merged

Kill the sticky salvage hard-block's false positives#579
TheGreatAxios merged 3 commits into
mainfrom
cl-6964-delete-the-unreachable-thrash-matcher-hint-and-salvage-class

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Collaborator

Closes CL-6964. Closes CL-6704. Closes CL-6710.

Three related fixes to the sub-agent stop/salvage machinery in src/subagent/stop-policy.ts and src/subagent/brief-dispatch.ts.

CL-6964 — delete the unreachable thrash salvage machinery

PR #558 removed the re-read thrash stop, so no report can ever carry a "progressive thrash" Summary anymore. Deleted the now-dead isThrashSubAgentReport, appendThrashParentHint/THRASH_PARENT_HINT, the classifyBriefSalvage thrash branch, and the "thrash" entries in HARD_BLOCK_SALVAGES/BriefSalvageKind. Every other salvage kind (no-progress, no-ship, repetition, never-acted, never-edited, turn-budget, deadline, stalled, cancelled, incomplete-report) is untouched.

CL-6704 — stop hard-blocking on Summary substrings

The classifiers matched free-text Summary substrings like "no progress" or "cancelled", so a SUCCESSFUL report whose Summary happened to contain those words was recorded as a forced stop — hard-blocking identical re-dispatch and arming primary salvage nudges for work that actually succeeded.

Fixed by making forcedStopReport's summary strings a single FORCED_STOP_SUMMARIES lookup, shared with the classifiers via isForcedStopSubAgentReport, which now compares with exact equality instead of .includes/.toLowerCase().includes. A typed marker would need a report schema change (that's CL-6786, human-gated, left alone) — exact string matching is the fix that fits here.

False-positive paths now gone: a successful report whose Summary happens to mention "no progress," "cancelled," "long silence," etc. no longer classifies as any forced-stop salvage.

CL-6710 — clear the sticky block after a parallel sibling succeeds

Two concurrent identical-brief task calls can both admit. If one salvaged and the other succeeded, recordOutcome kept the hard-block lastSalvage sticky through the success, so the failing sibling's block later refused a brief that had already produced a good report in the same wave.

Fixed: a successful complete now always clears lastSalvage — the success itself proves the brief is re-dispatchable.

Regressions added

  • src/subagent/index.test.ts: successful Summaries containing forced-stop phrases classify as null (no salvage); true forced-stop strings still classify correctly; parallel admit → salvage + success → next admit allowed.
  • Existing thrash-labeled ledger tests renamed to use no-progress (the class they were actually generically exercising) since "thrash" is gone from BriefSalvageKind.

Gate

bun run check (lint, typecheck, build, full test suite) green: 5332 pass, 0 fail.

CL-6936/PR #558 removed the re-read thrash stop, so no report can ever
carry a 'progressive thrash' Summary. Remove the now-dead
isThrashSubAgentReport/appendThrashParentHint/THRASH_PARENT_HINT in
stop-policy.ts, the classifyBriefSalvage thrash branch in
brief-dispatch.ts, and the 'thrash' entries in HARD_BLOCK_SALVAGES and
BriefSalvageKind. Other salvage kinds are untouched.
Salvage classifiers matched free-text Summary substrings ('no
progress', 'cancelled', etc), so a SUCCESSFUL report whose Summary
happened to contain those words was recorded as a forced stop,
hard-blocking identical re-dispatch. forcedStopReport's summaries are
now a single FORCED_STOP_SUMMARIES lookup shared by both the producer
and the isXxxSubAgentReport classifiers, which compare against it with
exact equality instead of .includes.

A typed marker on the report would need a schema change (CL-6786,
human-gated, out of scope) so this tightens to exact string matching
instead.
Two concurrent identical-brief task calls can both admit. If one
salvages and the other succeeds, recordOutcome kept the hard-block
lastSalvage sticky through the success, so the failing sibling's
sticky block later refused a brief that already produced a good
report in the same wave. A successful complete now always clears
lastSalvage, since the success itself proves the brief is
re-dispatchable.
@linear-code

linear-code Bot commented Aug 23, 2026

Copy link
Copy Markdown

CL-6964

CL-6704

CL-6710

@TheGreatAxios
TheGreatAxios merged commit 5c95132 into main Aug 23, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant