fix: the coordinator is not a persona, and sync says what it left behind - #31
Merged
Conversation
'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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 coordinatoractivated a behavior layer as a team membercmd_listandpersona_rosterboth skipcoordinator*.resolve_name, the choke point forshow,use, andlaunch, did not. So the coordinator resolved as a persona and was installed into theCLAUDE-TEAMblock.Reproduced before the fix, against a throwaway
HOME:Two copies of the same instructions in one file, one in each marker pair, and a "team member" that is not one.
launch coordinatorhad 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 separateassert_is_persona, called byuseandlaunch, deliberately not byshow:showonly cats a file.coordinator-prodis the fixture the existing "hyphenated name still resolves" test uses (tests/run.sh:211). A guard inresolve_namebreaks that test for a reason unrelated to what it tests.It runs after
resolve_name, souse coordinatrstill gets "no profile found" rather than a misleading coordinator message.2.
syncleaves removed personas installed forever, silentlysynconly ever copies. Delete or rename a persona in the clone and its three installed files stay.cmd_listandpersona_rosterreadPROFILES_DIR, not the repo, so the removed persona keeps appearing inclaude-team listand its/<name>command keeps resolving.syncnow names the leftovers and prints thermfor each:It reports and does not delete, deliberately.
~/.claude/commandsand~/.claude/agentsalso hold files this tool never wrote, from other tools and from the user, andsynchas no marker separating its own from theirs. A prune would be anrmdriven by a guess about ownership, inside the user's global config. That is the one place this tool is careful everywhere else:CLAUDE.mdis protected by markers,settings.jsonby 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/293tests 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:assert_is_personacallswarn_orphaned_personascallsyncprune instead of reportresolve_name(over-block)show still reads the coordinator profileand the pre-existinghyphenated name still resolvesThe 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:syncresolves its repo directory from the CLI's own path and regeneratesagents/andcommands/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
synceventually prune rather than report? Doing it safely needs an explicit ownership marker on generated slash commands.agents/<name>.mdalready 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