From 89b85aea84c23b6c7e23042f640215c9f5cc9402 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 29 Jul 2026 14:29:38 +0000 Subject: [PATCH] docs: retire the customization roadmap, and say what a new session activates Two changes, both about not promising more than the product delivers. Local profile overrides were the top unbuilt item through v2. They are now retired rather than deferred. A per-user override layer creates a second source of persona truth competing with the repo, and the confusion between core version and local customization costs more than the convenience buys. Team-scoped profiles, which put the same layer at project scope via claude-team init and .claude-team/, are retired for the same reason. Session handoff briefing stays: it is coordinator behavior, not a second source of truth. Customizing a persona now has two documented paths, both of which keep one source of truth: open a pull request, or fork. A new CONTRIBUTING.md covers the contribution path and persona authoring, including the requirement most likely to bite a first contributor, which is that generate-agents.sh aborts on a profile with no ## Greeting section. README gains a Customizing the Team section stating the two paths and why there is no third. The dated ROADMAP row that promoted overrides to top priority stays verbatim; a new row records the retirement. The installer overstated what it had activated. The earlier claim that it names subagents as ready to delegate to was wrong: that message was deleted by cbbb7a4 when install.sh began delegating to claude-team sync. The real defect was silence. Claude Code registers subagents and hooks when a session starts and reads slash commands on demand, so a sync activates one of the three immediately. Nothing said so, and three green checkmarks in a row read as three live capabilities. install.sh then closed with "Your Claude dev team is ready." A SessionStart hook had the starkest version of it, since it cannot by definition fire in the session that registers it. A say_session_scope helper keeps the wording in one place, called from cmd_sync. cmd_install_hook and the installer's closing block get their own line. install.sh needs no sync message of its own because it already delegates to the CLI. Every message that was already accurate is untouched. Seven tests added, five of which fail on the previous code. The other two are regression guards on messages that were already correct: install-commands still promising immediacy, and use still asking for a restart. Nothing asserted any user-facing messaging before, so a checkmark could claim a capability that was not live and CI stayed green. Also corrected: the add-a-persona steps never mentioned ## Greeting, so following them literally hit a hard generator abort with no forewarning from the section being read. Stale test counts in README and ROADMAP said 135 and 136. 170 tests. shellcheck clean. Verified by running the installer end to end and reading the full transcript. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_019j5DHEZsoeCGRueTbNLuTb --- CONTRIBUTING.md | 78 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 25 ++++++++++++---- ROADMAP.md | 15 ++++------ bin/claude-team | 14 +++++++++ install.sh | 7 ++++- tests/run.sh | 20 +++++++++++++ 6 files changed, 142 insertions(+), 17 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..abbeac8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,78 @@ +# Contributing + +Thanks for helping. This project is a set of persona profiles plus a Bash CLI, so contributing is mostly writing Markdown carefully and running two commands before you push. + +## How to customize a persona + +Two supported paths, and no third: + +**Open a pull request** when the change makes the persona better for everyone. Sharper domain expertise, a communication rule that improves every answer, a fixed factual error. + +**Fork the repo** when the change is specific to how you or your team work. Your fork is yours. Pull from upstream when you want the improvements. + +There is deliberately no per-user override layer. It was on the roadmap and was retired. An override directory means two competing definitions of a persona and no reliable way to tell which one produced a given answer. One source of truth is worth more than the convenience of a local tweak. + +Never hand-edit anything under `~/.claude/`. Those are installed copies, and the next `claude-team sync` overwrites them. + +## The one rule that matters + +`profiles/.md` is the only source of truth for a persona. + +Two files are generated from it and must never be edited by hand: + +| Generated file | Serves | Contains | +|---|---|---| +| `agents/.md` | delegation, "have robin review this diff" | the profile minus `## Greeting` | +| `commands/.md` | the `/` slash command | the profile minus `## Greeting` and minus `## Required Interactive Behaviors`, with the greeting line as a trailer | + +`scripts/generate-agents.sh` writes both. `claude-team sync` runs the generator and then installs all three copies. The test suite fails if either generated file drifts from its profile, so a profile edit without a regeneration will not pass CI. + +## Adding or changing a persona + +1. Edit `profiles/.md`, or copy an existing profile as a starting point. +2. Keep the title in the form `# Name — Role`. Both the CLI and the generator split it at the first em dash, so the role may contain one but the separator must be there. +3. **Write a `## Greeting` section.** One line, the sentence the persona says when someone runs `/`. The generator aborts without it rather than shipping a slash command that ends in a bare separator. This is the most common thing to forget. +4. Optionally add a model tier in `profiles/tiers.conf`. Without one the persona still works and uses the default. +5. Regenerate and test: + +```bash +bash scripts/generate-agents.sh +bash tests/run.sh +``` + +6. Commit the regenerated `agents/` and `commands/` files alongside the profile. They are committed artifacts, not build output. + +Coordinator profiles (`profiles/coordinator.md`, `profiles/coordinator-prod.md`) are exempt. The generator skips them, and they need no `## Greeting`. + +## Before you push + +Both of these run in CI, so running them locally saves a round trip: + +```bash +bash tests/run.sh +shellcheck bin/claude-team bin/team-session-start install.sh scripts/generate-agents.sh tests/run.sh +``` + +The suite must be fully green. Shellcheck must be silent; it reads `.shellcheckrc` from the repo root, which documents the three disables and why each exists. + +The suite runs on Linux and macOS. macOS ships Bash 3.2 and every entry point enforces a Bash 4+ floor, so use current Bash locally if you are on a Mac. + +## House style + +**No emdashes in prose.** Restructure with commas, colons, semicolons, parentheses, or separate sentences. Emdashes are fine as delimiters in structured lists, such as the `# Name — Role` title or a glossary entry. + +**The six coding personas follow [WRITING.md](WRITING.md).** Akira, Sasha, Robin, Alex, Morgan, and Jordan carry a plain technical English standard adapted from the plain-language principles of ASD-STE100. If you are editing one of those profiles, read that file first. The other eleven personas are deliberately exempt, because a word-count rule would strip the craft out of narrative and marketing work. + +## Changing the CLI + +`bin/claude-team` is a single Bash file with `set -euo pipefail`. A few conventions worth knowing before you edit it: + +- **Writes to shared files take the lock.** `~/.claude/branches/INDEX.md` and `~/.claude/CLAUDE.md` are shared across every session on the machine. Both appends and rewrites must serialize: an append that lands between a rewriter's read and its rename is erased by that rename. +- **Temp files are created beside their destination**, never in `TMPDIR`. A cross-device rename degrades to copy plus unlink, during which a reader can observe a half-written file. +- **`awk > tmp && mv` hides failures.** `set -e` exempts a failing command in an `&&` list, so check the result explicitly. +- **Quote `"done"`** when passing it as an argument, or shellcheck reads it as a loop terminator. +- **A test that cannot fail is not a test.** Before trusting a new one, break the thing it guards and watch it go red. + +## Reporting a problem + +Open an issue with the command you ran, what you expected, what happened, and your OS and Bash version (`bash --version`). diff --git a/README.md b/README.md index 270b3d2..ff9a7bc 100644 --- a/README.md +++ b/README.md @@ -766,6 +766,19 @@ Your existing `~/.claude/CLAUDE.md` content is preserved. The team member profil --- +## Customizing the Team + +There are two supported ways to change how a persona behaves, and both keep one source of truth: + +- **Open a pull request.** If the change makes the persona better for everyone, send it upstream. See [CONTRIBUTING.md](CONTRIBUTING.md). +- **Fork the repo.** If the change is specific to how you or your team work, fork and own your copy. `git pull` from upstream when you want the improvements. + +There is deliberately no third option. A per-user override layer was on the roadmap and was retired: it would mean two competing definitions of a persona, and no reliable way to tell whether Akira is behaving like upstream Akira or like your local edit. One source of truth is worth more than the convenience. + +Practically: edit `profiles/.md` in your clone, run `claude-team sync`, and never hand-edit anything under `~/.claude/`. + +--- + ## Adding Your Own Team Members 1. Create a new profile file in `profiles/`: @@ -776,15 +789,17 @@ cp profiles/robin.md profiles/yourname.md 2. Edit it to define the persona, expertise, security focus, and communication style. -3. Optionally add a model tier in `profiles/tiers.conf`. +3. Keep the `## Greeting` section, and write one line for your persona. It is the sentence they say when someone runs `/yourname`. This is required: `scripts/generate-agents.sh` refuses to run without it, so a missing greeting stops the install rather than shipping a broken slash command. + +4. Optionally add a model tier in `profiles/tiers.conf`. -4. Install it everywhere: +5. Install it everywhere: ```bash claude-team sync ``` -5. Activate it: +6. Activate it: ```bash claude-team use yourname @@ -898,7 +913,7 @@ The `0.4` through `0.7` numbers used during development are retired. v1 is the f - `claude-team sync` propagates a profile edit to all three installed copies - `claude-team install-hook` registers session context on the clone install path - Plain technical English standard for the six coding specialists ([WRITING.md](WRITING.md)) -- 135-test suite covering the CLI commands, both coordinator modes, and the install path +- 170-test suite covering the CLI commands, both coordinator modes, and the install path ### v1.0 @@ -915,6 +930,4 @@ The `0.4` through `0.7` numbers used during development are retired. v1 is the f If any of these would change how you use the tool, [open an issue](https://github.com/code-katz/claude-team-cli/issues). -- **Local profile overrides:** `~/.claude/team/local/` for per-user customizations without forking the repo. This is the next thing we want to build. `claude-team sync` made persona edits stick across all three copies, but the edit still lives in tracked files that the next `git pull` overwrites. Overrides are what turn "customize a persona" into something you only have to do once. -- **Team-scoped profiles:** `claude-team init` creates a `.claude-team/` config in a project repo, shared across the dev team - **Session handoff briefing:** when switching team members mid-task, the coordinator generates a structured briefing so the incoming specialist doesn't start cold diff --git a/ROADMAP.md b/ROADMAP.md index 666c126..2bba785 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -22,7 +22,7 @@ v1 delivered the roster and the coordinator. v2 closes the gaps that made the te - [x] `claude-team install-hook` — the SessionStart hook now registers on the `install.sh` path, not only the plugin path - [x] Plain technical English standard for the six coding specialists, documented in [WRITING.md](WRITING.md) - [x] Slash commands generated from profiles, so a new persona means writing one file instead of three -- [x] 136-test suite covering the CLI commands, both coordinator modes, and the install path +- [x] 170-test suite covering the CLI commands, both coordinator modes, and the install path ### Shipped — v1.0 @@ -40,7 +40,9 @@ v1 delivered the roster and the coordinator. v2 closes the gaps that made the te ### Near-Term -**Local profile overrides** is the highest-value unbuilt item on this list. Details in the section below. +**Local profile overrides are retired, not deferred.** They were the top item here through v2. Building them would create a second source of persona truth competing with the repo, and the confusion between core version and local customization costs more than the convenience buys. Team-scoped profiles (`claude-team init` writing `.claude-team/`) are retired for the same reason at project scope. + +Customizing a persona has two supported paths, both of which keep one source of truth: open a pull request so everyone gets the improvement, or fork and own your copy. See [CONTRIBUTING.md](CONTRIBUTING.md). Slash command generation shipped in v2.0 and is no longer queued. `scripts/generate-agents.sh` now writes both the delegation subagent and the `/name` slash command from the profile, so adding a persona means writing one profile. @@ -50,14 +52,6 @@ Slash command generation shipped in v2.0 and is no longer queued. `scripts/gener These are directions we're exploring. If any of them would change how you use the tool, [open an issue](https://github.com/code-katz/claude-team-cli/issues). Real usage feedback shapes what gets built next. -**Local profile overrides (top priority)** -A `~/.claude/team/local/` directory for per-user customizations without forking the repo. Override any persona's behavior for your specific workflow without touching the upstream source. - -This moved from "exploring" to "next thing we want to build" during v2. `claude-team sync` fixed the symptom, which was an edit landing in one installed copy out of three. It did not fix the cause: the only place to customize a persona is a tracked file that the next `git pull` overwrites. Until overrides exist, "make Akira work the way my team works" is something you have to redo after every update. Customizing the team is the whole point of the product, so this is the gap worth closing first. - -**Team-scoped profiles** -`claude-team init` creates a `.claude-team/` config at the project root, so team conventions are shared across developers on the same repo. Consistent team member behaviors without everyone managing their own `~/.claude/team/` independently. - **Session handoff briefing** When switching team members mid-task, the coordinator generates a structured briefing so the incoming specialist doesn't start cold: decisions made this session, open questions, and a direct question addressed to the new team member by name. @@ -69,3 +63,4 @@ When switching team members mid-task, the coordinator generates a structured bri |---|---|---| | 2026-04-09 | snapshot | Initial ROADMAP.md — v0.6 shipping, aspirational backlog identified | | 2026-07-29 | snapshot | Renumbered to v2.0 current, v1.0 shipped; pre-release `0.x` numbers retired. Corrected test count to 135. Local profile overrides promoted to top priority. | +| 2026-07-29 | decision | Local profile overrides and team-scoped profiles retired. Both create a second source of persona truth competing with the repo. Supported paths are a pull request or a fork, documented in CONTRIBUTING.md. | diff --git a/bin/claude-team b/bin/claude-team index 59a8cd3..f7ce362 100755 --- a/bin/claude-team +++ b/bin/claude-team @@ -31,6 +31,16 @@ yellow() { printf '\033[33m%s\033[0m' "$*"; } die() { echo "$(red "error:") $*" >&2; exit 1; } +# Claude Code registers subagents and hooks when a session starts, and reads +# slash commands on demand. So a sync activates the slash commands immediately +# and leaves the other two waiting for the next session. Saying so is the whole +# point: a green checkmark on all three reads as "all three are live now". +say_session_scope() { + echo "" + echo "$(dim "Live now: slash commands (/akira, /robin, ...). No restart needed.")" + echo "$(dim "Next session: subagents and the SessionStart hook. Claude Code registers those at startup.")" +} + lowercase() { printf '%s\n' "${1,,}"; } capitalize() { printf '%s\n' "${1^}"; } @@ -1158,6 +1168,7 @@ Make sure you are running this from the claude-team-cli repo." mkdir -p "$commands_dst" cp "$repo_dir/commands"/*.md "$commands_dst/" echo "$(green "✓") Slash commands synced to $(dim "$commands_dst")" + say_session_scope echo "" echo "$(dim "Edit profiles in $repo_dir/profiles, then rerun: claude-team sync")" echo "" @@ -1230,6 +1241,9 @@ os.replace(tmp, settings_path) PY echo "$(green "✓") SessionStart hook registered: $(dim "$settings")" + # A SessionStart hook cannot fire in the session that registers it, so a bare + # checkmark here reads as "it ran" when nothing has run yet. + echo "$(dim " Runs from your next Claude Code session onward, not this one.")" } cmd_help() { diff --git a/install.sh b/install.sh index 2f1be6d..066a933 100755 --- a/install.sh +++ b/install.sh @@ -118,7 +118,12 @@ case "$(echo "$coord_answer" | tr '[:upper:]' '[:lower:]')" in esac echo "" -echo "$(bold "Done!") Your Claude dev team is ready." +echo "$(bold "Done!") Your Claude dev team is installed." +echo "" +# "ready" overstated it: the subagents, the SessionStart hook, and the +# coordinator all wait for a new session. Only the slash commands are live. +echo "$(yellow "→") $(bold "Start a new Claude Code session") to activate the subagents, the" +echo " SessionStart hook, and the coordinator. Slash commands work right now." echo "" echo "Quick start:" echo " claude-team list $(dim "# see your team")" diff --git a/tests/run.sh b/tests/run.sh index 0eb717c..15ded81 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -691,10 +691,30 @@ assert_file_has "sync regenerates the subagent from the edited profile" \ assert_file_has "sync regenerates the slash command from the edited profile" \ "$SYNC_HOME/.claude/commands/robin.md" "SYNC-PROFILE-MARKER" assert_file_has "sync installs tiers.conf" "$SYNC_HOME/.claude/team/tiers.conf" "akira" +# Three green checkmarks read as "all three are live now". Only the slash +# commands are: Claude Code registers subagents and hooks at session start. +out=$(HOME="$SYNC_HOME" bash "$SYNC_REPO/bin/claude-team" sync 2>&1) +assert_contains "sync says slash commands are live now" "No restart needed" "$out" +assert_contains "sync says subagents wait for a new session" "Next session" "$out" rm -rf "$SYNC_REPO" "$SYNC_HOME" echo "" +# Messaging accuracy. Nothing asserted any of these strings before, so a +# checkmark could claim a capability that is not live yet and CI stayed green. +echo "session-scope messaging" + +out=$(run_cmd install-hook 2>&1) +assert_contains "install-hook says the hook starts next session" "next Claude Code session" "$out" +out=$(run_cmd install-commands 2>&1) +assert_contains "install-commands still promises immediacy" "No new session required" "$out" +out=$(run_cmd use robin 2>&1) +assert_contains "use still asks for a new session" "Start a new Claude Code session" "$out" +run_cmd reset >/dev/null 2>&1 +assert_file_has "installer names the restart requirement" "$REPO_DIR/install.sh" "Start a new Claude Code session" +assert_file_lacks "installer no longer claims the team is ready" "$REPO_DIR/install.sh" "dev team is ready" +echo "" + # install.sh end to end into an isolated HOME: files land where the CLI # expects them, and the coordinator prompt delegates to the CLI code path echo "install.sh"