Skip to content

Finish the legacy Claude TeamCreate retirement in the binary and skills - #736

Merged
clkao merged 2 commits into
mainfrom
spacedock-ensign/finish-legacy-claude-team-retirement
Aug 20, 2026
Merged

Finish the legacy Claude TeamCreate retirement in the binary and skills#736
clkao merged 2 commits into
mainfrom
spacedock-ensign/finish-legacy-claude-team-retirement

Conversation

@clkao

@clkao clkao commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

A First Officer told only "team mode" reached for the advertised --team-name flag and invented a team name. The flag selects a dispatch path no supported host can consume. This removes it.

What changed

  • Remove --team-name, TeamName, teamNamePattern, LegacyTeamNameAdvisory, and MemberExists.
  • Remove spawn-standing (singular) and spawn-standing-all --team; both now refuse.
  • Rewrite commission Step 3 from a TeamCreate probe to the shipped SendMessage probe.
  • Bind the break-glass template's subagent_type to a literal, not a placeholder.
  • Add a contractlint invariant that fails on any shipped skill instructing TeamCreate(.

Evidence

  • go test ./... and -race: green, one pre-existing environment-local failure reproduced identically on main.
  • Keep-list verified by blob hash: 12 paths unchanged, zero historical-tree edits.
  • Every AC carries a falsifying change that was run. The contractlint invariant flags 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

clkao and others added 2 commits August 18, 2026 21:22
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>
@clkao

clkao commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator Author

Corroborating validation notes

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

main carried TestBuildEmitsStandingFetchLineUnderMods, which actively asserted the injection: two fetch commands, driven by a team_name stdin key. Cycle 1 inverted it into TestBuildOmitsStandingFetchLineEvenUnderMods, requiring exactly one.

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 go test ./... was run three ways — with CLAUDE_CODE_SESSION_ID unset, with it exported, and under -race. All three: exactly one failure, the known environment-local TestCodexResolveManifestAgainstInstalledHost, and zero data races. The landed report ran -race on dispatch and claudeteam only.


Neither finding changes the verdict. Both raise confidence in it.

@clkao
clkao merged commit c01e427 into main Aug 20, 2026
6 of 8 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