Skip to content

fix: the coordinator is not a persona, and sync says what it left behind - #31

Merged
d6veteran merged 2 commits into
mainfrom
fix/coordinator-and-sync-leftovers
Jul 31, 2026
Merged

fix: the coordinator is not a persona, and sync says what it left behind#31
d6veteran merged 2 commits into
mainfrom
fix/coordinator-and-sync-leftovers

Conversation

@d6veteran

Copy link
Copy Markdown
Collaborator

Two defects found while reviewing the repo, both in the same area: the tool's own idea of what counts as a persona.

1. claude-team use coordinator activated a behavior layer as a team member

cmd_list and persona_roster both skip coordinator*. resolve_name, the choke point for show, use, and launch, did not. So the coordinator resolved as a persona and was installed into the CLAUDE-TEAM block.

Reproduced before the fix, against a throwaway HOME:

$ claude-team use coordinator
✓ Claude Team CLI is now active.

$ claude-team coordinator on
$ claude-team status
Active team member: Claude Team CLI
Coordinator:        on (casual) — branch enforcement off

$ grep -c "Session Greeting" ~/.claude/CLAUDE.md
2

Two copies of the same instructions in one file, one in each marker pair, and a "team member" that is not one. launch coordinator had the same hole: it opened a session with a behavior layer as its system prompt.

The guard is not in resolve_name, and that is the point. It is a separate assert_is_persona, called by use and launch, deliberately not by show:

  • Printing the coordinator profile is a legitimate thing to want, and show only cats a file.
  • coordinator-prod is the fixture the existing "hyphenated name still resolves" test uses (tests/run.sh:211). A guard in resolve_name breaks that test for a reason unrelated to what it tests.

It runs after resolve_name, so use coordinatr still gets "no profile found" rather than a misleading coordinator message.

2. sync leaves removed personas installed forever, silently

sync only ever copies. Delete or rename a persona in the clone and its three installed files stay. cmd_list and persona_roster read PROFILES_DIR, not the repo, so the removed persona keeps appearing in claude-team list and its /<name> command keeps resolving.

sync now names the leftovers and prints the rm for each:

! Installed but no longer in the repo: piper
  sync only copies, so a removed or renamed persona leaves its files behind.
  These still appear in 'claude-team list'. Nothing was deleted. To remove:
  rm -f ~/.claude/team/piper.md ~/.claude/agents/piper.md ~/.claude/commands/piper.md

It reports and does not delete, deliberately. ~/.claude/commands and ~/.claude/agents also hold files this tool never wrote, from other tools and from the user, and sync has no marker separating its own from theirs. A prune would be an rm driven by a guess about ownership, inside the user's global config. That is the one place this tool is careful everywhere else: CLAUDE.md is protected by markers, settings.json by a merge, a foreign pre-commit hook by an outright refusal.

Three tests assert each of the three files still exists after the report, so moving to a prune has to be a deliberate change that turns those tests red.

Verification

293/293 tests pass (up from 276) and shellcheck is clean on both files.

Per CONTRIBUTING.md: "A test that cannot fail is not a test. Before trusting a new one, break the thing it guards and watch it go red." Four mutations, each against a scratch copy:

Mutation Tests that went red
Remove the assert_is_persona calls the 5 coordinator rejection tests
Remove the warn_orphaned_personas call the 3 sync report tests
Make sync prune instead of report the 3 "deletes nothing" tests, plus the roster symptom test
Move the guard into resolve_name (over-block) show still reads the coordinator profile and the pre-existing hyphenated name still resolves

The last row is the one worth reading. It reproduces the regression this PR was one decision away from shipping, and it is now defended by a test that explains itself.

New tests run from a throwaway copy of the repo, following the pattern documented at tests/run.sh:1466: sync resolves its repo directory from the CLI's own path and regenerates agents/ and commands/ there, so running it against the real clone would rewrite tracked files and silently repair a drift the suite exists to catch.

Open question

Should sync eventually prune rather than report? Doing it safely needs an explicit ownership marker on generated slash commands. agents/<name>.md already carries <!-- GENERATED from profiles/... -->; persona commands carry none, which is why this PR does not delete. Happy to follow up if that is the direction.

🤖 Generated with Claude Code

d6veteran and others added 2 commits July 31, 2026 15:50
'claude-team use coordinator' resolved and installed the coordinator profile
into the CLAUDE-TEAM block. 'status' then reported "Active team member: Claude
Team CLI", and with the coordinator also enabled, ~/.claude/CLAUDE.md held two
copies of the same instructions, one in each marker pair. 'launch coordinator'
opened a session with a behavior layer as its persona.

cmd_list and persona_roster both skip 'coordinator*'. resolve_name did not, and
it is the choke point for show, use, and launch.

The guard is a separate assert_is_persona rather than a case in resolve_name,
called by use and launch and deliberately not by show. Two reasons. Printing the
coordinator profile is a legitimate thing to want, and coordinator-prod is the
fixture the existing hyphenated-name test uses, so blocking it in resolve_name
would fail that test for a reason unrelated to what it tests. It is called after
resolve_name, so a name that merely starts with "coordinator" and has no profile
still gets "no profile found" rather than this message.

Tests cover both directions: use and launch reject both coordinator profiles and
write nothing, and show still reads them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
sync only ever copies. Deleting or renaming a persona in the clone leaves its
three installed files behind, and nothing looks at them again. cmd_list and
persona_roster read PROFILES_DIR rather than the repo, so a removed persona
keeps appearing in 'claude-team list' and its /<name> command keeps resolving,
forever.

sync now names the leftovers and prints the rm that clears each one.

It does not delete them, and that is the decision, not an omission.
~/.claude/commands and ~/.claude/agents also hold files this tool never wrote,
and sync has no marker separating its own from a user's, so a prune would be an
rm driven by a guess about ownership in the user's global config. That is the
one place this tool is careful everywhere else: CLAUDE.md is protected by
markers, settings.json by a merge, a foreign pre-commit hook by an outright
refusal. Naming the paths costs one command and cannot destroy anything.

Three tests assert each of the three files still exists after the report, so a
later change to pruning has to be a deliberate one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@d6veteran
d6veteran merged commit 7f8344f into main Jul 31, 2026
4 checks passed
@d6veteran
d6veteran deleted the fix/coordinator-and-sync-leftovers branch July 31, 2026 22:57
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