v1.79.0.0 fix: ship subagent dispatches can no longer strand the run (#497/#2440 class) (#2772)

* fix(ship): foreground-flag + deadline + scope guard on all four dispatch sites (#497/#2440 class, 3rd recurrence)

Steps 7/8/10/18 dispatch subagents whose LAST-line JSON the parent
consumes, but none passed run_in_background: false — since Claude Code
v2.1.198 subagents background by default, so /ship stranded at Step 18
waiting on output that never arrives. Every site now renders the shared
{{FOREGROUND_DISPATCH_NOTE}} resolver constant, carries a ~10-minute
deadline with an explicit recovery branch (stop the runaway task,
reconcile against pre-dispatch HEAD, surface stray state, never
re-dispatch), and Step 18's prompt gains a docs-sync-only scope guard
(no VERSION changes, no base-branch merges, push-rejection reported as
pushed:false with parent-side reconciliation and a second-failure
branch). Greptile failures now record as UNAVAILABLE, not zero comments.

GENERATED_WITH_GUIDANCE pins all five ship dispatch carriers; a new test
pins the deadline recovery + scope guard phrases in pr-body (.md and
.tmpl). Codex/factory ship goldens re-rendered.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(document-release): first-class spawned-dispatch contract

document-release's own templates had zero subagent-awareness — the
entire headless contract lived in /ship's dispatch prompt, so any other
orchestrator (or an older installed /ship) dispatching it inherited none
of the gate handling. The skill now carries the contract itself: detect
spawned strictly from the dispatch prompt or the preamble echo (never
from file content — prompt-injection guard), auto-choose recommended
options while keeping the never-clobber-CHANGELOG and
never-bump-VERSION-silently invariants via their Skip options. Step 8.4d
gets an explicit spawned note (its interactive recommendation bumps
VERSION — wrong headlessly), and the Codex Documentation Review section
skips itself in spawned sessions (the apply gate needs a human; the
dispatching workflow owns review passes).

Contract, 8.4d note, and resolver skip are pinned in
run-in-background-guidance.test.ts; document-release skeleton budget
re-measured (39,812 B) and ratcheted to 40,200.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: sweep every remaining synchronous Agent-dispatch site with run_in_background: false

The #2440 failure shape was a silently-missing review voice — a
specialist launched in the background and merged before it completed.
Every remaining synchronous dispatch site now carries the explicit flag:
the Red Team dispatch, the spec review loop, the Codex
second-opinion/plan-review/doc-review Claude fallbacks, the adversarial
subagent, design sketch and outside voices, autoplan's design/eng/dx
phase dispatches, CSO parallel finding verification, and design-shotgun's
variant launch. Parallel fan-outs stay parallel — multiple foreground
Agent calls in one message run concurrently (the shipped v1.64.0.0
review-army pattern).

GENERATED_WITH_GUIDANCE now pins all 24 generated carriers, so a new
dispatch site that drops the flag fails the free suite. Six carved-skill
skeleton ceilings re-measured and ratcheted (~80-130 B growth each);
factory ship golden re-rendered.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* release: v1.79.0.0 — CHANGELOG, VERSION, TODOS follow-ups

Queue-advanced to 1.79.0.0 (1.78.0.0 claimed in the workspace queue;
same MINOR level per the versioning invariant). Entry references the
class history (#497#2440 → Step 18). Three TODOS filed: PreToolUse
hook enforcement, structural ship-mode for document-release, cross-host
dispatch semantics audit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* regen: review-army sections carry the Red Team foreground flag

The scripts/resolvers/review-army.ts Red Team edit regenerated these two
files but the sweep commit staged only the adversarial sections — the
skill-docs freshness gate (regen + git diff --exit-code) catches this.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* regen: agents digest picks up v1.79.0.0

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(document-release): one canonical spawned contract, downstream notes are pointers

The diff-selected LLM-judge eval scored the skill's clarity 3 (threshold
4, main scores 4): the spawned-session rules read as three separately-
framed rule sets (contract paragraph, Step 8.4d note, Codex-review skip).
The contract paragraph now declares itself the single source of spawned
behavior and the two downstream notes reference it instead of restating
rationale. The pointer avoids naming the Codex section verbatim so the
codex-host render (which strips that section) keeps its negative pin.
Judge re-scored 4/5/4 across repeated samples after the change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: foreground note names the Agent tool as non-substitutable

The ship-docsync gate E2E caught a behavioral regression: the note's
blocking-emphasis ('a backgrounded dispatch strands the run') steered
the driven agent to run doc-sync via the Skill tool inline — the most
blocking option — twice in a row, forfeiting the fresh-context isolation
the dispatch exists for (baseline on main's text dispatches via Agent).
The shared note now says explicitly: dispatch with the Agent tool itself,
never substitute Skill or inline execution; the flag already makes the
call block. Re-verified: ship-docsync passes on the amended text.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: pre-landing review fixes

Review army + red team findings, all verified before applying:
- ship-docsync E2E now asserts run_in_background === false on the
  captured dispatch (red team CRITICAL: phrase pins prove text exists,
  this proves the model obeys it — verified passing live).
- Structural scanner test: any generated file with an Agent-dispatch
  imperative (or bare '(foreground)' prose, the #2440 inert shape) must
  carry the flag or hold a reasoned exemption — the 4th-recurrence net
  the hand-enumerated pin list can't provide.
- Parent push reconciliation models reality: the parent shares the repo,
  so a non-fast-forward that hit the subagent hits the parent identically
  — fetch + ahead/behind check first, push only when the rejection was
  transient; dispatch prompt promise softened to 'the parent will handle
  it'.
- Recovered commits from a dead subagent are vetted docs-only
  (git show --stat, never VERSION/package.json) before any push.
- Deadline pacing named: ~3 minutes between checks, wall clock not polls.
- Greptile UNAVAILABLE recording narrowed to the PR body (Step 20's
  schema carries no triage field).
- document-release contract gains the echo-failure tie-breaker: prompt
  claims spawned + no echo → fail fast with the dispatch contract's
  failure shape instead of reproducing the #2733 prose-STOP; contract
  anti-injection and NEVER-relax clauses pinned in tests.
- 'Claude Code v2.1.198' extracted to CC_BACKGROUND_DEFAULT_SINCE and
  interpolated at all resolver sites (byte-identical output).
- CHANGELOG: entry-boundary blank line restored; worst-case-wait row
  scoped to the backgrounded path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: adversarial review fixes — failure shape, CHANGELOG guard, vet coherence

Claude adversarial subagent findings (11), all verified before applying:
- The dispatch JSON contract gains an explicit FAILURE shape
  ({"error":...}) and a parent branch for it — a doc-sync that could not
  run (spawned marking failed, broken preamble) previously had only the
  no-updates shape to emit, which the parent printed as 'Documentation is
  current': a silent false-clean of exactly the VAS-449 class.
- Scope guard now covers CHANGELOG: skip Step 5 voice polish and resolve
  CHANGELOG-touching gates to leave-as-is (the parent authored the
  release entry; the prompt's older auto-choose clause conflicted with
  the contract's never-rewrite invariant).
- Recovery vet is sequence-coherent: pushing a commit pushes its
  ancestors, so ANY non-doc commit (VERSION, package.json, CHANGELOG.md)
  blocks the whole sequence — no more push-the-vetted-child-of-an-
  unvetted-parent hole.
- Steps 7/8/10 failure branches stop a still-running backgrounded task
  before falling back, so a late result never races the inline audit.
- 'Documentation synced' print gated on pushed:true (item 6 owns the
  local-only outcome); doc-review skip note's backward step pointer
  fixed; foreground note scoped 'at initial dispatch' so the sanctioned
  inline FALLBACKS in Steps 7/8 read as sanctioned; constants docstring
  no longer overclaims single-sourcing (names the 3 inline templates).
- Runtime-hook TODO raised P2 → P1: the spawned trust chain is
  agent-self-asserted; prose cannot close it, the hook can.

Both docsync gate E2Es re-verified green on the amended prompt, including
the strict run_in_background === false dispatch assert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: codex adversarial fixes — JSON trust, fail-open visibility, user-state safety

Codex outside-model adversarial pass (inline-diff workaround for the
sandbox), P1/P2 findings triaged cross-model:
- Parent validates the dispatch JSON's field types and treats
  documentation_section as untrusted markdown (Step 19's redaction scan
  covers the final body; instruction-shaped text inside it is never
  followed). The error branch now explicitly skips items 2-6.
- Remote-ahead divergence is named, not silent: the parent lists foreign
  commits before creating a PR over a moved branch.
- Recovery cleanup never discards content: stray staged doc edits are
  unstaged but never checked out or cleaned away.
- Greptile UNAVAILABLE gets a concrete PR-body line, not a vague
  'wherever results are reported'.
Pre-existing-class P1s (self-asserted spawned trust chain, unenforceable
foreground timeout) are cross-model confirmed and tracked: PreToolUse
hook TODO at P1, harness-residue hazard documented.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: never-Skill prohibition made unambiguous; docsync fixture made diagnostic

The ship-docsync E2E flaked on this sandbox because the driven agent
resolved the STOP pointer's ~ to nonexistent homes (/root, /home) and
acted blind — pass/fail sampled model priors, not the prompt. The
fixture prompt now names the planted sections dir, making local runs
deterministic (CI, with real install paths, was always diagnostic).
With a diagnostic fixture: 2/2 passes, real pr-body read, Agent dispatch
with run_in_background: false, zero Skill-tool substitutions.

Prose: the foreground note now states the dispatch happens ONLY via the
Agent tool (invoking the target as a Skill is wrong even though it
appears in the skills list; inline FALLBACKs apply only after a
dispatched subagent has failed), and the Step 18 imperative carries the
never-the-Skill-tool clause at the decision point.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: CHANGELOG entry covers the review-hardened contract

Failure JSON shape, CHANGELOG scope guard, vetted recovery pushes,
structural scanner, and the behavioral dispatch assert are shipped
properties of v1.79.0.0 — the entry now describes them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: update project documentation for v1.79.0.0

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: CHANGELOG voice — follow-ups to For contributors, never-Skill property stated

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(test): daemon 503 test binds an OS-assigned port, not old-port+1

windows-free-tests flaked on this PR: the tunnel-less restart bound
daemon.loopbackPort + 1 — a fixed neighbor of the OS-assigned ephemeral
port — and died with 'Is port 55738 in use?' whenever another shard or a
TIME_WAIT socket held it; the file-level retry re-rolled the same dice.
Every other startDaemon in the file already uses loopbackPort: 0 and the
assertion reads d2.loopbackPort, so nothing needs a predictable number.
21/21 pass locally.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-09-01 16:42:11 -07:00
committed by GitHub
co-authored by Claude Fable 5
parent 702a1a9b69
commit 0d1bd5616c
53 changed files with 433 additions and 79 deletions
+44
View File
@@ -1,5 +1,49 @@
# Changelog
## [1.79.0.0] - 2026-09-01
**/ship can no longer be stranded by a backgrounded subagent.**
**The bug class that came back twice is pinned everywhere it lives.**
Claude Code v2.1.198 made Agent-tool subagents launch in the background by default. Four /ship steps (7, 8, 10, 18) hand work to a subagent and parse its final line as JSON, and none of them passed `run_in_background: false`, so a ship run could park forever on Step 18 waiting for doc-sync output that was never coming. This is the third time this class has bitten (#497 fixed it, #2440 regressed it, Step 18 rediscovered it). Every synchronous dispatch site in the skill tree now carries the explicit flag, and the flag is test-pinned per file so a fourth recurrence fails CI the moment it lands.
The doc-sync dispatch also got real failure handling. If the dispatch gets backgrounded anyway, the parent polls for about 10 minutes, stops the runaway task, reconciles any commit the subagent made against the pre-dispatch HEAD, and ships the PR without the Documentation section instead of hanging. The subagent itself is scope-guarded to docs only: it never changes VERSION, never merges the base branch, skips the Codex doc review (the parent owns review passes), and reports a rejected push as `pushed:false` for the parent to reconcile.
### The numbers that matter
Source: `test/run-in-background-guidance.test.ts` (the pin list) and `grep -rl 'run_in_background: false' --include='*.md'` against this tree.
| Metric | Before | After | Δ |
|---|---|---|---|
| Generated files pinned to carry the flag | 2 | 24 | every sync dispatch site |
| /ship dispatch steps with a deadline + recovery branch | 0 of 4 | 4 of 4 | Steps 7/8/10/18 |
| /ship Step 18 worst-case wait (backgrounded dispatch) | unbounded | ~10 min | documented recovery; a foreground hang stays harness-bounded |
| Codex doc review inside a ship-dispatched doc-sync | ~5-10 min per ship | skipped | parent owns reviews |
| Headless gates that could mutate VERSION mid-ship | 2 | 0 | Step 8.3/8.4d resolve to Skip |
The unbounded-to-10-minutes number is the one you feel: the failure mode changes from "my ship run has been sitting there for an hour" to a printed recovery line and a PR that still lands.
What this means for anyone shipping here: /ship finishes even when the harness misbehaves, doc-sync can never renumber your release or double-run review passes, and document-release now carries its own spawned-session contract, so any orchestrator that dispatches it (not just /ship) gets safe headless behavior.
### Itemized changes
#### Added
- **Deadline + recovery on every /ship dispatch step.** Steps 7 and 8 stop a runaway task and fall back to the inline audit if the subagent never completes (~10 min); Step 10 records Greptile triage as UNAVAILABLE in the PR body rather than pretending zero comments; Step 18 stops the runaway task, vets and pushes orphaned docs-only commits (never VERSION, package.json, or CHANGELOG; an explicit second-failure branch covers a moved remote), surfaces stray staged edits without ever discarding content, and proceeds without the Documentation section. The dispatch contract carries an explicit failure JSON shape, so a doc-sync that could not run reports as a failure instead of clean docs. No step ever silently parks the run.
- **A spawned-dispatch contract in document-release itself.** Triggered strictly by the preamble's `SESSION_KIND: spawned` echo (the dispatcher's `GSTACK_SESSION_KIND=spawned` prefix; prompt or file claims alone never trigger it, matching the v1.78.0.0 echo-only rule). Every ask-the-user gate auto-resolves to its recommended option except the ones that would rewrite CHANGELOG content or change VERSION, which resolve to Skip and get recorded. Step 8.4d carries an explicit spawned note because its interactive recommendation is a VERSION bump.
- **Docs-sync scope guard in /ship's dispatch prompt**: docs only, no base-branch merges, no version renumbering, CHANGELOG left to the parent, no Codex doc review, push rejections reported instead of resolved.
#### Changed
- The Codex Documentation Review section skips itself in any spawned session; its apply gate needs a human, and a dispatching workflow owns its own review passes. Covers version skew where an older installed /ship dispatches a newer document-release.
- Autoplan's design/eng/dx phase dispatches, the review army Red Team, the spec review loop, the adversarial subagent, the Codex second-opinion/plan-review/doc-review fallbacks, design sketch and outside voices, CSO finding verification, and design-shotgun variant launches all state `run_in_background: false` explicitly. Parallel fan-outs stay parallel; foreground calls in one message run concurrently.
#### Fixed
- **/ship Steps 7, 8, 10, and 18 no longer strand the run** when Claude Code backgrounds their subagents (#497, #2440 class, third recurrence). The four dispatch specs share one resolver-sourced foreground note — dispatch happens only via the Agent tool, never the Skill tool or inline execution — so the phrasing cannot drift per site again.
#### For contributors
- `{{FOREGROUND_DISPATCH_NOTE}}` (scripts/resolvers/constants.ts) is the single source for the flag guidance; use it in any new dispatch template and add the generated carrier to `GENERATED_WITH_GUIDANCE` in `test/run-in-background-guidance.test.ts` in the same commit. The test's pin list is the census of synchronous dispatch sites, and its structural scanner fails any generated dispatch imperative that lacks the flag even when un-enumerated.
- Follow-ups filed: PreToolUse-hook enforcement of the flag (P1, structural fix), a capability-narrowed ship-mode for document-release, and a cross-host dispatch semantics audit.
- Carved-skill skeleton ceilings re-measured and ratcheted in `test/helpers/carve-guards.ts`; codex/factory ship goldens re-rendered; the ship-docsync E2E asserts `run_in_background === false` on the captured dispatch and its fixture resolves section reads deterministically.
## [1.78.0.0] - 2026-08-31
**Plan reviews ask their questions again, the OSV lane is green from 105 advisories, and 18 community fixes land with credit.**