Finish the legacy Claude TeamCreate retirement in the binary and skills - #736
Conversation
The contract already stated the post-retirement shape; the binary and skill
prose still carried the legacy TeamCreate-registry surface. Remove it and
bind the one unbound break-glass template slot that root-caused the live
selected-team red.
Binary (internal/dispatch, internal/claudeteam):
- Drop --team-name/TeamName/teamNamePattern from dispatch build; a stdin
team_name key now degrades to the ignore-unknown-keys path (byte-identical
envelope with or without it). --team-name is a refused usage error (exit 2).
- Every non-bare host=claude dispatch is now unconditionally the merged
.178+ shape (name + run_in_background, no team_name); the legacy envelope
and its stderr advisory (LegacyTeamNameAdvisory) are gone.
- Remove spawn-standing (singular, hard-required a TeamCreate name) and the
legacy --team branch of spawn-standing-all, plus the now-orphaned
MemberExists dedup probe. spawn-standing-all always emits the merged
background shape.
- Retire the dead standing-teammate auto-injection branch in dispatch build:
it only ever fired on the deleted legacy path (merged/bare dispatches
always omitted it, per existing documented behavior).
- keeps, unchanged: reconcile --team-name (auto-team identity, not the
TeamCreate envelope), leadSessionId discovery, contextbudget,
BareModeAdvisory, RenderStandingTeammatesSection.
Root-cause fix (skills/fo-dispatch-recovery): bind the break-glass manual
template's {dispatch_agent_id} slot in both arms with an inline comment at
the fill point (the stage's agent: field; spacedock:ensign default). This
was the one unbound placeholder behind the nondeterministic
TestLiveBreakGlashShimRecovery/selected-team red — the oracle already
asserted the correct post-retirement shape and needs no change.
Skills prose: rewrite commission's Step 3 from a TeamCreate probe to the
shipped SendMessage-availability boot probe; drop stale --team-name mentions
from claude-fo-dispatch.md, fo-dispatch-core.md, codex-first-officer-runtime.md,
and fo-gate-lifecycle.md. Add a contractlint invariant (AC-4) that fails on
any shipped skill instructing a TeamCreate(...) call.
Tests: delete the legacy-only vehicle files (build_team_name_advisory_test.go,
build_teamname_path_test.go, spawn_standing_all_test.go, plus the
now-impossible TestBuildLegacyModeUnchanged and
TestBuildMaxLegalDispatchFilenamesRemainExact scenarios); port the
mode-neutral spawn-standing validation cases (fable model, non-ASCII prompt
escaping, missing-model/bad-enum/trailing-heading) onto spawn-standing-all;
drop team_name from ~20 files' base stdin fixtures and regenerate the
affected goldens. Add a TestMain to internal/dispatch that clears
CLAUDE_CODE_SESSION_ID for the whole test binary: with legacy team_name gone,
every non-bare claude fixture now takes the merged session-token filename
path, which would otherwise leak a real Claude Code session id from a
developer's shell into golden fixtures.
selected-team stays hard-gating and byte-unchanged (no xfail, no registry
edit, no live-test edit) per the settled gate decision.
Net LOC vs the approved ~-500/~47-file estimate: net -832 across 76 files
(product -188/5 files, skills docs -1/6 files, tests -536/29 files, golden
fixtures -107/36 files). Product and docs landed close to estimate; the
tests+fixtures overage (net -643 across 65 files vs estimated -320/~37) is
real, not padding: team_name turned out to be pervasive base-stdin decoration
across ~24 test files (not just the crossproduct family ideation grepped),
and each one's dispatch_file_path is captured byte-for-byte in its own
golden, so removing the field's effect forced far more golden regeneration
than the cycle-1 estimate anticipated. This exceeds the declared
[-250,-750]/<=60-files tolerance; flagging for the gate rather than trimming
correct, necessary changes to fit the number.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Cycle-2 correction: the captain rejected AC-5's mechanism from cycle 1. The
observed live red was a DROPPED subagent_type parameter, not a miscomputed
one — the FO resolved {worker_key} to spacedock-ensign correctly in the same
Agent() call where it omitted subagent_type entirely. A comment next to an
unbound placeholder cannot stop a drop, and is only read by an FO that
already decided to fill the line.
skills/fo-dispatch-recovery/SKILL.md: both break-glass Agent() arms now read
subagent_type="spacedock:ensign" (literal, matching build.go rule 6's
default) with an inline override comment, instead of
subagent_type="{dispatch_agent_id}" plus an explanatory comment. Break-glass
is the only path where a placeholder ever reached a tool call unresolved;
every normal dispatch already gets this filled by dispatch build itself.
Maintenance cost, recorded per the gate: the literal now duplicates rule 6
(build.go:536, subagentType := "spacedock:ensign") and the string is also
hardcoded a third time in the shape oracle
(dispatch_recovery_assert_test.go:176, unchanged, out of scope). No sync
mechanism added — that trade was weighed at the gate and the literal won.
Two additional cheap polish items from validation's list:
- EnumerateDeclaredStandingTeammates(workflowDir, teamName) dropped the
vestigial teamName parameter: its only caller (runShowStanding) passed the
sentinel "_show_standing_" solely to defeat a teamName == "" early return
that predates the merged floor. Signature is now
EnumerateDeclaredStandingTeammates(workflowDir); the stale doc comment
describing bare mode by empty teamName is gone too.
- spawn-standing-all now refuses a legacy --team flag (exit 2), matching
dispatch build's --team-name refusal, instead of silently ignoring it
(exit 0). Added TestSpawnStandingAllTeamFlagRefused.
Not touched, per the gate's explicit scope: the oracle's hardcoded
subagent_type, the unreachable isFile branch, the codex/pi disambiguator —
all recorded findings with promote conditions, deferred on purpose.
go test ./... and -race clean (the same pre-existing, unrelated
TestCodexResolveManifestAgainstInstalledHost failure, present on main too).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Corroborating validation notesA second validator ran an independent pass on this branch after a provider disconnect, before seeing the landed report. It reached the same conclusions on every point and found no contradictions. It correctly declined to append a duplicate stage report — the entity is behind a frozen briefing digest — so its two additional findings are recorded here instead. 1. The show-standing removal is better guarded than the landed report credits.
Re-introducing the injection in a throwaway checkout reds that inverted test. So the removal is protected by a falsifying test, not only by the enumerator parity check the landed report cites. 2. Broader race coverage than reported. Full Neither finding changes the verdict. Both raise confidence in it. |
A First Officer told only "team mode" reached for the advertised
--team-nameflag and invented a team name. The flag selects a dispatch path no supported host can consume. This removes it.What changed
--team-name,TeamName,teamNamePattern,LegacyTeamNameAdvisory, andMemberExists.spawn-standing(singular) andspawn-standing-all --team; both now refuse.subagent_typeto a literal, not a placeholder.TeamCreate(.Evidence
go test ./...and-race: green, one pre-existing environment-local failure reproduced identically onmain.main's own commission text.Net −724 across 78 files, inside the declared tolerance band. The literal was confirmed by running the binary across absent, named, and empty
agent:READMEs.Two deferred risks are recorded in the entity: an oracle that hardcodes the same default, and
{worker_key}as the template's remaining unbound slot.nr
🤖 Generated with Claude Code