claude-dev-env 1.26.0 → 1.26.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/hooks/blocking/code_rules_enforcer.py +5 -1
  2. package/hooks/blocking/test_code_rules_enforcer.py +61 -0
  3. package/hooks/blocking/test_code_rules_enforcer_file_global_constants.py +4 -2
  4. package/hooks/notification/subagent_complete_notify.py +5 -5
  5. package/hooks/notification/test_subagent_complete_notify.py +7 -0
  6. package/hooks/validators/git_checks.py +4 -1
  7. package/hooks/validators/test_output_formatter.py +7 -2
  8. package/package.json +1 -1
  9. package/skills/bugteam/SKILL.md +143 -309
  10. package/skills/bugteam/SKILL_EVALS.md +46 -46
  11. package/skills/bugteam/reference/README.md +13 -0
  12. package/skills/bugteam/reference/audit-and-teammates.md +127 -0
  13. package/skills/bugteam/reference/design-rationale.md +28 -0
  14. package/skills/bugteam/reference/github-pr-reviews.md +86 -0
  15. package/skills/bugteam/reference/team-setup.md +51 -0
  16. package/skills/bugteam/reference/teardown-publish-permissions.md +70 -0
  17. package/skills/bugteam/scripts/README.md +4 -0
  18. package/skills/bugteam/{_claude_permissions_common.py → scripts/_claude_permissions_common.py} +37 -33
  19. package/skills/bugteam/scripts/bugteam_code_rules_gate.py +55 -0
  20. package/skills/bugteam/{grant_project_claude_permissions.py → scripts/grant_project_claude_permissions.py} +10 -11
  21. package/skills/bugteam/{revoke_project_claude_permissions.py → scripts/revoke_project_claude_permissions.py} +13 -14
  22. package/skills/bugteam/sources.md +93 -0
  23. /package/hooks/validators/{config.py → validator_defaults.py} +0 -0
  24. /package/skills/bugteam/{test_claude_permissions_common.py → scripts/test_claude_permissions_common.py} +0 -0
@@ -1,79 +1,77 @@
1
1
  ---
2
2
  name: bugteam
3
3
  description: >-
4
- Runs an autonomous audit-and-fix loop on the current branch's PR using a
5
- Claude Code agent team bugfind teammate (code-quality-agent, clean-room
6
- audit) and bugfix teammate (clean-coder, sonnet fix) until the audit
7
- returns zero bugs or a 10-loop safety cap is reached. One up-front
8
- confirmation authorizes the entire cycle. Each audit teammate is spawned
9
- fresh per loop to prevent anchoring bias. Wraps the cycle with project
10
- permission grant/revoke. Requires CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1.
11
- Triggers: '/bugteam', 'run the bug team',
12
- 'auto-fix the PR until clean', 'loop audit and fix'.
4
+ Claude Code agent team on the open pull request: run the CODE_RULES gate,
5
+ spawn a fresh clean-room audit (code-quality-agent, sonnet) and a fix pass
6
+ (clean-coder, sonnet), post per-loop GitHub review threads from teammates,
7
+ stop at zero findings or a 10-audit safety cap. Grants then revokes
8
+ `.claude/**` edit permission around the run. SKILL.md is the orchestration
9
+ checklist; `reference/` holds expanded prose by domain; CONSTRAINTS,
10
+ PROMPTS, EXAMPLES, and sources are companion files. Requires
11
+ CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1. Triggers: '/bugteam',
12
+ 'run the bug team', 'auto-fix the PR until clean', 'loop audit and fix'.
13
13
  ---
14
14
 
15
15
  # Bugteam
16
16
 
17
- **Core principle:** A Claude Code **agent team** runs the audit-and-fix loop until convergence. The bugfind teammate audits clean-room (own context window, no chat history); the bugfix teammate addresses each audit's findings; both spawn fresh per loop. A 10-loop hard cap prevents runaway cost. Project permissions are granted at session start and revoked at session end.
17
+ **Core principle:** Agent team runs auditfix until convergence. Bugfind: clean-room audit (fresh context each loop). Bugfix: addresses findings. Hard cap: 10 audit loops. Grant `.claude/**` permissions at start, revoke always at end.
18
18
 
19
- > **Source:** [Anthropic Orchestrate teams of Claude Code sessions](https://code.claude.com/docs/en/agent-teams). Direct quote: *"Each teammate has its own context window. When spawned, a teammate loads the same project context as a regular session: CLAUDE.md, MCP servers, and skills. It also receives the spawn prompt from the lead. The lead's conversation history does not carry over."* That isolation is the design's whole point — independent context per teammate enforces the clean-room property automatically.
20
-
21
- > **Why agent teams, not parallel subagents:** Subagents return their results into the lead's context, which accumulates across loops. Agent team teammates are independent sessions with their own context windows and do not pollute the lead. The lead can shut down + respawn each loop, guaranteeing every audit starts fresh. Per the docs: *"Use subagents when you need quick, focused workers that report back. Use agent teams when teammates need to share findings, challenge each other, and coordinate on their own."* For this skill, the independent-context property is what we need; parallel subagents fail the clean-room requirement.
19
+ Subagents fold back into the lead context; agent-team teammates do not that separation is the clean-room guarantee. Verbatim doc quotes and URLs: [`sources.md`](sources.md).
22
20
 
23
21
  ## Contents
24
22
 
25
- This file is the orchestration core. The list below is for the LLM reading this skill — partial reads (e.g., `head -100`) miss what comes later, so this section ensures the full scope is visible from the top. (Per Anthropic's [Skill authoring best practices — Structure longer reference files with table of contents](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices#structure-longer-reference-files-with-table-of-contents).)
23
+ Orchestration lives here; companion files hold prompts, invariants, examples, citations, and domain reference notes. Scan this list before a partial read.
26
24
 
27
25
  - When this skill applies — refusal cases (4) and trigger conditions
28
- - Utility scripts — pre-flight checks (`scripts/`, executed not loaded as context)
29
- - Pre-audit code rules gate — `validate_content` / hook parity before each AUDIT
30
- - The Process — Progress checklist + Steps 0–6
26
+ - Utility scripts — pre-flight (`scripts/`, executed not inlined)
27
+ - Pre-audit gate — `validate_content` before each AUDIT
28
+ - The Process — checklist + Steps 0–6
31
29
  - Step 0 — Grant project permissions
32
30
  - Step 1 — Resolve PR scope
33
31
  - Step 2 — Create the agent team
34
- - Step 2.5 — PR comment lifecycle (per-loop review with child finding comments, fix replies)
35
- - Step 3 — The cycle (AUDIT ↔ FIX, decision table, exit conditions)
36
- - Step 4 — Tear down the team and clean working tree
37
- - Step 4.5 — Finalize the PR description (via pr-description-writer)
38
- - Step 5 — Revoke project permissions
39
- - Step 6 — Print the final report
40
- - [`PROMPTS.md`](PROMPTS.md) — AUDIT spawn-prompt XML, FIX spawn-prompt XML, the 10 audit categories (A–J), and both outcome XML schemas. Load before spawning bugfind or bugfix, or when parsing teammate outcome XML.
41
- - [`EXAMPLES.md`](EXAMPLES.md) — six end-to-end scenarios (converged, cap reached, stuck, partial-fix, no-PR, dirty-tree). Load when an unfamiliar exit condition appears.
42
- - [`CONSTRAINTS.md`](CONSTRAINTS.md) — invariants plus "Why this design" rationale. Load when a constraint question arises.
32
+ - Step 2.5 — PR comment lifecycle (per-loop review + fix replies)
33
+ - Step 3 — Cycle (AUDIT ↔ FIX, exits)
34
+ - Step 4 — Teardown + clean tree
35
+ - Step 4.5 — PR body via `pr-description-writer`
36
+ - Step 5 — Revoke permissions
37
+ - Step 6 — Final report
38
+ - [`PROMPTS.md`](PROMPTS.md) — spawn XML, categories A–J, outcome schemas
39
+ - [`EXAMPLES.md`](EXAMPLES.md) — exit scenarios
40
+ - [`CONSTRAINTS.md`](CONSTRAINTS.md) — invariants and design rationale
41
+ - [`sources.md`](sources.md) — doc URLs and verbatim quotes
42
+ - [`reference/README.md`](reference/README.md) — expanded prose by topic (design, team setup, GitHub reviews, cycle, teardown)
43
43
 
44
44
  ## When this skill applies
45
45
 
46
- User wants automated convergence on a clean PR without babysitting each step. Typed `/bugteam` once = full authorization for up to 10 audit cycles and the corresponding fix commits.
46
+ `/bugteam` once authorizes the full cycle (up to 10 audits + fixes).
47
47
 
48
- Refusal cases check in order; first match short-circuits and stops:
48
+ Refusalsfirst match wins; respond with the quoted line exactly and stop:
49
49
 
50
- - **Agent teams not enabled.** Check `claude config get env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` and `~/.claude/settings.json`. If neither sets it to `"1"`, respond: `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 not set. /bugteam requires the agent teams feature. See https://code.claude.com/docs/en/agent-teams#enable-agent-teams.` and stop.
51
- - **Missing PR or upstream diff.** Respond exactly: `No PR or upstream diff. /bugteam needs a target.` and stop.
52
- - **Working tree dirty with uncommitted changes the user did not stage.** Respond: `Uncommitted changes detected. Stash, commit, or revert before /bugteam.` and stop. Reason: the fix teammate will commit the working tree, mixing user-uncommitted work into automated fixes.
53
- - **Required subagents not installed.** Before Step 0, verify `code-quality-agent` and `clean-coder` subagent types exist in the available agents list. If either is missing, respond: `Required subagent type <name> not installed. /bugteam needs both code-quality-agent and clean-coder available.` and stop.
50
+ - **Agent teams not enabled.** Check `claude config get env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` and `~/.claude/settings.json`. If neither is `"1"`: `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 not set. /bugteam requires the agent teams feature. See https://code.claude.com/docs/en/agent-teams#enable-agent-teams.`
51
+ - **No PR or upstream diff.** `No PR or upstream diff. /bugteam needs a target.`
52
+ - **Dirty tree.** `Uncommitted changes detected. Stash, commit, or revert before /bugteam.`
53
+ - **Missing subagents.** Before Step 0, confirm `code-quality-agent` and `clean-coder` exist. Else: `Required subagent type <name> not installed. /bugteam needs both code-quality-agent and clean-coder available.`
54
54
 
55
55
  ## Utility scripts
56
56
 
57
- Fragile or repeatable shell sequences belong in `scripts/` (see Anthropic [Skill authoring best practices — Progressive disclosure](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices#progressive-disclosure-patterns): utility scripts are **executed**, not loaded into context). Details: [`scripts/README.md`](scripts/README.md).
58
-
59
- ### Pre-flight (recommended before Step 0)
57
+ Shell-heavy steps live under `scripts/` (run, do not paste into context). Utility scripts are **executed**, not loaded as primary context ([`sources.md`](sources.md) § Progressive disclosure and utility scripts). [`scripts/README.md`](scripts/README.md).
60
58
 
61
- From the repository root, run:
59
+ ### Pre-flight (before Step 0)
62
60
 
63
61
  ```bash
64
62
  python "${CLAUDE_SKILL_DIR}/scripts/bugteam_preflight.py"
65
63
  ```
66
64
 
67
- If the exit code is non-zero, stop and fix failing checks before granting permissions. Optional: `BUGTEAM_PREFLIGHT_SKIP=1` skips pre-flight (emergency only). Optional: `--pre-commit` when `.pre-commit-config.yaml` exists.
65
+ Non-zero fix before grant. `BUGTEAM_PREFLIGHT_SKIP=1` emergency only. `--pre-commit` if `.pre-commit-config.yaml` exists.
68
66
 
69
67
  ## The Process
70
68
 
71
- ### Progress checklist (copy at start, tick as you go)
69
+ ### Progress checklist
72
70
 
73
71
  ```
74
72
  [ ] Step 0: project permissions granted
75
73
  [ ] Step 1: PR scope resolved
76
- [ ] Step 2: agent team created + initial loop state set
74
+ [ ] Step 2: agent team created + loop state set
77
75
  [ ] Step 3: cycle complete (converged | cap reached | stuck | error)
78
76
  [ ] Step 4: team torn down + working tree clean
79
77
  [ ] Step 4.5: PR description rewritten (or skip warning logged)
@@ -81,33 +79,27 @@ If the exit code is non-zero, stop and fix failing checks before granting permis
81
79
  [ ] Step 6: final report printed
82
80
  ```
83
81
 
84
- ### Step 0: Grant project permissions (mandatory, runs once)
85
-
86
- Before spawning any teammates, grant the team session write access to the project's `.claude/**` tree:
82
+ ### Step 0: Grant project permissions (once, first)
87
83
 
88
84
  ```bash
89
- python "${CLAUDE_SKILL_DIR}/grant_project_claude_permissions.py"
85
+ python "${CLAUDE_SKILL_DIR}/scripts/grant_project_claude_permissions.py"
90
86
  ```
91
87
 
92
- Note: `${CLAUDE_SKILL_DIR}` is a Claude Code host-managed token, pre-substituted by the runtime before any shell sees it. Unlike `${TMPDIR}` and similar shell parameter expansions, it does not depend on the shell's expansion semantics, so it works identically on Unix and Windows shells.
88
+ `${CLAUDE_SKILL_DIR}` is host-substituted before the shell runs (unlike normal env expansion). Idempotent writes to `~/.claude/settings.json` from repo root. Non-zero stop. Revoke in Step 5 on every exit path.
93
89
 
94
- The script reads `Path.cwd()` and writes idempotent allow rules into `~/.claude/settings.json`. Run from the project root. If it fails (non-zero exit), surface the error and stop — do not proceed without the grant.
90
+ ### Step 1: Resolve PR scope (once)
95
91
 
96
- This is the FIRST action of every `/bugteam` invocation, before any team creation, before any agent spawn. The corresponding revoke runs at Step 5 regardless of how the cycle exits.
92
+ Same as `/findbugs`:
97
93
 
98
- ### Step 1: Resolve PR scope (once, persisted across loops)
94
+ 1. `gh pr view --json number,baseRefName,headRefName,url`
95
+ 2. Else `git merge-base HEAD origin/<default>` then `git diff <merge-base>...HEAD`
96
+ 3. Else refuse above.
99
97
 
100
- Same resolution path as `/findbugs`:
101
-
102
- 1. `gh pr view --json number,baseRefName,headRefName,url` from the working directory.
103
- 2. Fall back to `git merge-base HEAD origin/<default>` then `git diff <merge-base>...HEAD`.
104
- 3. Neither → refuse per the refusal cases above.
105
-
106
- Capture: `<owner>/<repo>`, head branch, base branch, PR number, PR URL. This scope persists across every loop — `/bugteam` runs to completion from the single up-front confirmation.
98
+ Keep: owner/repo, branches, PR number, URL — for all loops.
107
99
 
108
100
  ### Step 2: Create the agent team
109
101
 
110
- This session is the **team lead**. Create the team by calling the `TeamCreate` tool with these exact arguments:
102
+ Lead calls `TeamCreate`:
111
103
 
112
104
  ```
113
105
  TeamCreate(
@@ -117,163 +109,114 @@ TeamCreate(
117
109
  )
118
110
  ```
119
111
 
120
- `<team_name>` is the value built below under **Team name** (sanitization + timestamp already applied). `TeamCreate` is the tool that resolves the docs' phrasing: *"tell Claude to create an agent team and describe the task and the team structure you want in natural language. Claude creates the team, spawns teammates, and coordinates work based on your prompt."*
112
+ **Team name:** `bugteam-pr-<number>-<YYYYMMDDHHMMSS>` or `bugteam-<sanitized-head>-<YYYYMMDDHHMMSS>` if no PR. Timestamp avoids collisions. `TeamCreate` implements natural-language team creation ([`sources.md`](sources.md) § Team creation in natural language).
121
113
 
122
- Team specification:
114
+ **Sanitize head branch (no-PR only):** replace characters outside `[A-Za-z0-9._-]` with `-` (e.g. `feat/foo*bar` → `feat-foo-bar`). Apply once; reuse everywhere below.
123
115
 
124
- - **Team name:** `bugteam-pr-<number>-<YYYYMMDDHHMMSS>` (or `bugteam-<sanitized-head-branch>-<YYYYMMDDHHMMSS>` if no PR). The timestamp is captured at team-creation time from the lead session and prevents two concurrent invocations on the same PR from colliding.
125
- - **Branch-name sanitization (no-PR fallback only):** Before substituting `<head-branch>` into the team_name template, replace every character outside `[A-Za-z0-9._-]` with `-`. The whitelist keeps safe portable filename characters only; OS-reserved and shell-special characters (`/ \ : * ? < > | "` plus ASCII control chars 0x00–0x1F) fall outside the whitelist and become `-`. Example: `feat/foo*bar` → `feat-foo-bar`; team_name becomes `bugteam-feat-foo-bar-<YYYYMMDDHHMMSS>`. Apply the sanitization when team_name is first assembled so every downstream use (team creation, scoped temp dir, cleanup) sees the safe form.
126
- - **Per-team temp directory (resolved once, reused everywhere):** After team_name is captured, resolve a portable absolute path with a Claude-side lookup using Python's `tempfile.gettempdir()`, which honors `TMPDIR`, `TEMP`, and `TMP` in the platform-correct order and falls back to `C:\Users\<user>\AppData\Local\Temp` on Windows or `/tmp` on Unix: `Path(tempfile.gettempdir()) / team_name` (requires `import tempfile`). The `team_name` value already carries the `bugteam-` prefix, so keep it as-is here. Let `tempfile.gettempdir()` do the lookup; use its result directly. Capture the resolved absolute path as `<team_temp_dir>` and pass that literal path to every shell command that follows. Claude performs all temp-root resolution, so every shell (bash, cmd.exe, PowerShell) receives the same literal absolute value.
127
- - **Roles defined up front (spawned per loop, not at team creation):**
128
- - `bugfind` — uses teammate role `code-quality-agent`, model sonnet
129
- - `bugfix` — uses teammate role `clean-coder`, model sonnet
130
- - **Display mode:** inherit user's default (`teammateMode` in `~/.claude.json`); do not override.
116
+ **`<team_temp_dir>`:** `Path(tempfile.gettempdir()) / team_name` (lead resolves once to an absolute path; every shell gets that literal string).
131
117
 
132
- Reference teammate role definitions by name when spawning. Per the docs: *"When spawning a teammate, you can reference a subagent type from any subagent scope: project, user, plugin, or CLI-defined. This lets you define a role once... and reuse it both as a delegated subagent and as an agent team teammate."*
118
+ **Roles (spawned per loop, not here):** bugfind `code-quality-agent` sonnet; bugfix `clean-coder` sonnet. **Display:** inherit `teammateMode` from `~/.claude.json`. Reference subagent types by name when spawning teammates ([`sources.md`](sources.md) § Referencing subagent types when spawning teammates).
133
119
 
134
- Initialize loop state. The block below mixes lead-internal variables and one shell command (the SHA capture). Read it as instructions, not a literal script:
120
+ **Loop state (lead; not a single script):**
135
121
 
136
122
  ```bash
137
123
  loop_count=0
138
124
  last_action="fresh"
139
125
  last_findings=""
140
126
  audit_log=""
141
- starting_sha="$(git rev-parse HEAD)" # captured once, used in the final report
142
- team_name="bugteam-pr-<number>-<YYYYMMDDHHMMSS>" # no-PR fallback uses sanitized branch
143
- team_temp_dir="<resolved-absolute-path>/<team_name>"
144
- loop_comment_index="" # reset at every AUDIT, see scope note below
127
+ starting_sha="$(git rev-parse HEAD)"
128
+ team_name="bugteam-pr-<number>-<YYYYMMDDHHMMSS>"
129
+ team_temp_dir="<absolute-path>/<team_name>"
130
+ loop_comment_index=""
145
131
  ```
146
132
 
147
- **`loop_comment_index` scope (per-loop, not cross-loop).** This list is reset at the start of every AUDIT action, populated as finding comments are posted during AUDIT, consumed by the matching FIX action when it posts fix replies, and discarded after FIX completes. It does not persist across loops; each loop starts with an empty index and its own fresh set of comment URLs.
148
-
149
- Each entry: `{loop, finding_id, finding_comment_id, finding_comment_url, used_fallback, fix_status}`. Populated by AUDIT, consumed by FIX.
150
-
151
- ### Step 2.5: PR comment lifecycle (one review per loop)
152
-
153
- The team narrates its work to the PR via a **GitHub pull-request review** per loop so findings render as a tree under a single parent review (like Cursor Bugbot). **Teammates own all PR comment posting** — bugfind posts the review (parent body + child finding comments in one batched POST), bugfix posts fix replies. All comment, review, and reply POSTs belong to the teammates. The lead's single PR-write action is the final description rewrite at Step 4.5 (via `pr-description-writer` agent).
154
-
155
- - **Per-loop review** — one `POST /pulls/<number>/reviews` per loop, posted by the bugfind teammate AFTER auditing. The review body is the loop header (with audit counts); the review's `comments[]` array holds one anchored finding per P0/P1/P2 finding. GitHub renders this as a single collapsible thread with each finding as a child comment — the tree shape Cursor Bugbot produces.
156
-
157
- - **Fix replies** — replies to each child finding comment. Posted by the bugfix teammate after the commit lands. Body: `Fixed in <commit_sha>` if addressed, or `Could not address this loop: <one-line reason>` if not. The `/pulls/<number>/comments/<id>/replies` endpoint works on any review comment, including those created as part of a review, so this shape is unchanged.
158
-
159
- **Ordering:** bugfind audits FIRST, buffers the findings, validates anchors against the captured diff, then posts the review ONCE at the end. The review body names the finding count authoritatively. Keep all posting bunched into that single end-of-loop review POST.
133
+ **`loop_comment_index`:** reset each AUDIT start; filled during AUDIT; FIX consumes for replies; cleared after FIX. Entries: `{loop, finding_id, finding_comment_id, finding_comment_url, used_fallback, fix_status}`.
160
134
 
161
- CLI shapes (teammate runs these). All three POSTs use the same robust pattern: build the JSON payload with `jq` (pulling file contents in with `--rawfile` or `-Rs` so markdown with backticks, newlines, and quotes survives intact), then pipe the JSON to `gh api ... --input -` on stdin. This avoids every shell-quoting edge case.
135
+ ### Step 2.5: PR comments (one review per loop)
162
136
 
163
- - **Per-loop review (one POST creates the parent review AND all child finding comments).** Build the `comments[]` array programmatically from the buffered, diff-anchored findings. The shape per finding is `{path, line, side: "RIGHT", body: <finding markdown>}` for single-line anchors; use `{path, start_line, start_side: "RIGHT", line, side: "RIGHT", body: ...}` for multi-line ranges (all four fields required).
137
+ Bugfind posts one `POST .../pulls/<n>/reviews` per loop after audit (body + `comments[]` for anchored P0/P1/P2). Bugfix posts `.../comments/<id>/replies` after push. Lead’s only PR write: Step 4.5 body edit.
164
138
 
165
- ```
166
- jq -n \
167
- --rawfile review_body <tmp_review_body.md> \
168
- --arg commit_id "$(git rev-parse HEAD)" \
169
- --rawfile finding_body_1 <tmp_finding_1.md> \
170
- --arg path_1 "<file_1>" \
171
- --argjson line_1 <line_1> \
172
- [... one finding_body_K / path_K / line_K triple per anchored finding ...] \
173
- '{
174
- commit_id: $commit_id,
175
- event: "COMMENT",
176
- body: $review_body,
177
- comments: [
178
- {path: $path_1, line: $line_1, side: "RIGHT", body: $finding_body_1}
179
- [, ... one object per anchored finding ...]
180
- ]
181
- }' \
182
- | gh api repos/<owner>/<repo>/pulls/<number>/reviews -X POST --input -
183
- ```
139
+ Order: audit → buffer → validate anchors vs diff → single review POST. Review body states counts; zero findings → still one review, `comments: []`, body `## /bugteam loop <N> audit: 0P0 / 0P1 / 0P2 → clean`.
184
140
 
185
- Response JSON carries the parent review `id` / `html_url` plus a `comments` array of child comments, each with its own `id` and `html_url`. Harvest the child entries in index order and match them to the finding list the teammate posted.
141
+ **Payloads:** build JSON with `jq --rawfile` / `-Rs`, pipe to `gh api ... --input -` (avoids shell-quoting; satisfies `gh-body-backtick-guard`). Write each markdown body to a temp file first.
186
142
 
187
- - **Fix reply** replying to a child finding comment only needs `body`:
143
+ **Review POST** (one `comments[]` object per anchored finding; single-line `{path, line, side: "RIGHT", body}`; multi-line add `start_line`, `start_side: "RIGHT"`):
188
144
 
189
- ```
190
- jq -Rs '{body: .}' < <tmp_reply.md> \
191
- | gh api repos/<owner>/<repo>/pulls/<number>/comments/<finding_comment_id>/replies -X POST --input -
192
- ```
193
-
194
- - **Review-POST failure fallback** — top-level PR comment via the issue-comments endpoint (`{issue_number}` is the PR number):
145
+ ```
146
+ jq -n \
147
+ --rawfile review_body <tmp_review_body.md> \
148
+ --arg commit_id "$(git rev-parse HEAD)" \
149
+ --rawfile finding_body_1 <tmp_finding_1.md> \
150
+ --arg path_1 "<file_1>" \
151
+ --argjson line_1 <line_1> \
152
+ [... one finding_body_K / path_K / line_K triple per finding ...] \
153
+ '{
154
+ commit_id: $commit_id,
155
+ event: "COMMENT",
156
+ body: $review_body,
157
+ comments: [
158
+ {path: $path_1, line: $line_1, side: "RIGHT", body: $finding_body_1}
159
+ [, ... ]
160
+ ]
161
+ }' \
162
+ | gh api repos/<owner>/<repo>/pulls/<number>/reviews -X POST --input -
163
+ ```
195
164
 
196
- ```
197
- jq -Rs '{body: .}' < <tmp_fallback.md> \
198
- | gh api repos/<owner>/<repo>/issues/<number>/comments -X POST --input -
199
- ```
165
+ **Fix reply:** `jq -Rs '{body: .}' <tmp_reply.md | gh api repos/<owner>/<repo>/pulls/<number>/comments/<finding_comment_id>/replies -X POST --input -`
200
166
 
201
- `<head_sha_at_post_time>` = the SHA at the moment the review is posted (run `git rev-parse HEAD` in the teammate's working dir immediately before the POST). The review anchors its finding comments to the head SHA at audit time, which is the SHA before this loop's fix lands.
167
+ **Review POST fails:** issue comment fallback: `jq -Rs '{body: .}' <tmp_fallback.md | gh api repos/<owner>/<repo>/issues/<number>/comments -X POST --input -`
202
168
 
203
- Write each body (review body and every per-finding body) to its own temp file before running the jq pipeline. The `jq --rawfile` / `jq -Rs` pattern loads file contents as a single string into the JSON payload, which preserves backticks, newlines, and quotes intact. The body stays inside the file the jq pipeline reads — it reaches GitHub as part of the JSON payload — which keeps it compatible with the `gh-body-backtick-guard` hook that scans command-line `--body` arguments.
169
+ `<head_sha_at_post_time>`: `git rev-parse HEAD` in teammate cwd immediately before POST.
204
170
 
205
- **Review body shape** (content of `<tmp_review_body.md>`):
171
+ **Review body template (`<tmp_review_body.md>`):**
206
172
 
207
173
  ```
208
174
  ## /bugteam loop <N> audit: <P0>P0 / <P1>P1 / <P2>P2
209
175
 
210
- <if any findings could not be anchored to a diff line, include this section:>
211
176
  ### Findings without a diff anchor
212
-
177
+ (only if needed)
213
178
  - **[severity] title** — <file>:<line> — <one-line description>
214
179
  ```
215
180
 
216
- If the audit returns zero findings, the teammate still posts ONE review with `event=COMMENT`, an empty `comments[]`, and body `## /bugteam loop <N> audit: 0P0 / 0P1 / 0P2 clean`. This keeps every loop's section self-contained on the PR.
217
-
218
- **Anchor-validation fallback (teammate handles).** GitHub rejects the entire review POST if any `comments[]` entry targets a line not in the diff. Before posting, the bugfind teammate validates every finding's `(file, line)` against the captured diff. Findings whose anchor is not in the diff are NOT added to `comments[]`; they are listed in the review body under `### Findings without a diff anchor`. The outcome XML records `used_fallback="true"` for each such finding, with `finding_comment_id=""` and `finding_comment_url=<review_url>` (the parent review URL, since no child comment exists for it). The teammate logs the fallback count in its outcome XML so the lead's final report can count fallbacks. Cycle continues; no anchor failure aborts the loop.
219
-
220
- **Review POST failure fallback.** If the review POST itself fails (rate limit, network, malformed payload), the teammate falls back to a single top-level issue comment containing the review body plus every finding inline (severity, file:line, description). Every finding in that run carries `used_fallback="true"` and the issue-comment URL as `finding_comment_url`. Use the Review-POST failure fallback CLI shape above (`jq -Rs | gh api .../issues/<number>/comments --input -`).
181
+ **Anchor fallback:** lines not in diff omit from `comments[]`, list under `### Findings without a diff anchor`; outcome `used_fallback="true"`, empty `finding_comment_id`, `finding_comment_url` = parent review URL.
221
182
 
222
- **GitHub REST endpoints the teammate POSTs to:**
183
+ **POST failure fallback:** one issue comment with full text; all findings `used_fallback="true"`, URLs = issue comment.
223
184
 
224
- - Per-loop batched review: `POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews` (required: `body`, `event=COMMENT`, `commit_id`; optional `comments[]` each entry needs `path`, `body`, `line`, `side`)
225
- - Fix reply: `POST /repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies` (required: `body`)
226
- - Review-POST failure fallback: `POST /repos/{owner}/{repo}/issues/{issue_number}/comments` (required: `body`; `{issue_number}` is the PR number)
185
+ **Endpoints:** `POST .../pulls/{pull}/reviews`; `POST .../pulls/{pull}/comments/{id}/replies`; fallback `POST .../issues/{issue}/comments` (`issue` = PR number).
227
186
 
228
187
  ### Step 3: The cycle
229
188
 
230
- Repeat until an exit condition fires.
189
+ Repeat until exit. **Gate:** `validate_content` / `hooks/blocking/code_rules_enforcer.py` on PR-scoped files before every AUDIT (`bugteam_code_rules_gate.py`). Lead runs gate; clean-coder clears failures; then bugfind audits.
231
190
 
232
- **Ordering principle:** Mandatory **CODE_RULES** checks (`validate_content` from `hooks/blocking/code_rules_enforcer.py`) must pass on the PR-scoped file set **before** any **AUDIT** (bugfind) teammate runs. The **clean-coder** teammate clears gate failures; then the **code-quality-agent** teammate audits. This mirrors “CI green, then review,” without relying on GitHub Actions — the script is the gate.
191
+ 1. From `last_action` / `last_findings`:
192
+ - `last_action == "audited"` and `last_findings.total == 0` → exit `converged`
193
+ - `last_action == "fixed"` and `git rev-parse HEAD` unchanged since pre-FIX → exit `stuck`
194
+ - `last_action in {"fresh", "fixed"}` → **pre-audit** then **AUDIT**
195
+ - `last_action == "audited"` and `last_findings.total > 0` → **FIX**
233
196
 
234
- 1. Decide the next action from `last_action` and `last_findings`:
235
- - `last_action == "audited"` and `last_findings.total == 0` → exit reason = `converged`
236
- - `last_action == "fixed"` and `git rev-parse HEAD` did not change since pre-FIX → exit reason = `stuck` (see FIX action)
237
- - `last_action in {"fresh", "fixed"}` → go to **pre-audit path** (below), then **AUDIT**
238
- - `last_action == "audited"` and `last_findings.total > 0` → go to **FIX** (below)
197
+ 2. **Pre-audit** (only when the next step is AUDIT):
239
198
 
240
- 2. **Pre-audit path** (only when the next step is **AUDIT**):
241
- 1. From the repository root, run the gate script (align `--base` with the PR base branch from Step 1, e.g. `origin/main` or `origin/develop`):
242
-
243
- ```bash
244
- python "${CLAUDE_SKILL_DIR}/scripts/bugteam_code_rules_gate.py" --base origin/<baseRefName>
245
- ```
199
+ ```bash
200
+ python "${CLAUDE_SKILL_DIR}/scripts/bugteam_code_rules_gate.py" --base origin/<baseRefName>
201
+ ```
246
202
 
247
- Use `git merge-base` + `git diff --name-only` inside the script; see [`scripts/README.md`](scripts/README.md). The lead runs this (not a teammate).
248
- 2. If exit code **0** → continue to step 3 (AUDIT spawn) below.
249
- 3. If exit code **non-zero** → spawn a NEW **clean-coder** teammate — **standards-fix pass** — with instructions: read the script’s stderr, edit the repo until a **re-run** of the **same** gate command exits **0**, then one commit, `git push`, shutdown. Repeat standards-fix spawns until the gate exits **0** or **5** failed gate rounds (each round = one teammate session after a non-zero gate). If still non-zero after 5 rounds → exit reason = `error: code rules gate failed pre-audit`.
250
- 4. After gate exit **0**, increment `loop_count`. If `loop_count > 10`, exit reason = `cap reached` (counts **audits**, not standards-only rounds).
251
- 5. Execute **AUDIT action** (spawn bugfind). Print progress: `Loop <N> audit: ...`
203
+ Lead only; merge-base / diff details in [`scripts/README.md`](scripts/README.md). Non-zero spawn **clean-coder** standards-fix (read stderr, edit, re-run **this same** command, one commit, `git push`, shutdown) until exit **0** or **5** failed gate rounds → `error: code rules gate failed pre-audit`. After **0**: `loop_count += 1`; if `loop_count > 10` → `cap reached`. Then **AUDIT** (bugfind); print `Loop <N> audit: ...`.
252
204
 
253
- 3. **FIX path** (when `last_action == "audited"` and `last_findings.total > 0`):
254
- 1. Increment `loop_count`. If `loop_count > 10`, exit reason = `cap reached`.
255
- 2. Execute **FIX action** (spawn bugfix clean-coder for audit findings). Print: `Loop <N> fix: commit ...`
256
- 3. Set `last_action = "fixed"`, update `audit_log`, loop to step 1 (next iteration will hit **pre-audit path** before the next AUDIT).
205
+ 3. **FIX** (`last_action == "audited"` and `last_findings.total > 0`): `loop_count += 1`; if `loop_count > 10` → `cap reached`; **FIX** (bugfix); print `Loop <N> fix: ...`; `last_action = "fixed"`, update `audit_log`; loop to step 1.
257
206
 
258
- 4. After **AUDIT**, update `last_action`, `last_findings`, `audit_log`; print the audit progress line if not already printed.
207
+ 4. After **AUDIT**: update `last_action`, `last_findings`, `audit_log`; print audit line if not already printed.
259
208
 
260
209
  5. Loop.
261
210
 
262
- **Note:** The first iteration uses **pre-audit path** then **AUDIT**. After a **FIX** for audit findings, the next iteration runs **pre-audit path** again (gate then AUDIT), so `validate_content` stays green before semantic audit.
263
-
264
- ### AUDIT action (clean-room teammate, fresh per loop)
211
+ First pass: pre-audit AUDIT. After a FIX, the next pass runs pre-audit again before the next AUDIT.
265
212
 
266
- Capture a fresh PR diff for this loop into the per-team scoped directory so each concurrent `/bugteam` run keeps its patches isolated. Use the literal `<team_temp_dir>` resolved once in Step 2 — Claude resolves the absolute path, and every shell receives the same literal value:
213
+ ### AUDIT action
267
214
 
268
- ```
215
+ ```bash
269
216
  mkdir -p "<team_temp_dir>"
270
217
  gh pr diff <number> -R <owner>/<repo> > "<team_temp_dir>/loop-<N>.patch"
271
218
  ```
272
219
 
273
- `<team_temp_dir>` is the absolute path captured in Step 2 (already includes the sanitized team_name and timestamp suffix, and `team_name` itself is already prefixed with `bugteam-`). Claude resolves the portable temp root once via `Path(tempfile.gettempdir()) / team_name` (requires `import tempfile`) and passes the literal absolute path to every shell command. `tempfile.gettempdir()` honors `TMPDIR`, `TEMP`, and `TMP` in the platform-correct order and falls back to `C:\Users\<user>\AppData\Local\Temp` on Windows or `/tmp` on Unix, so this works identically on macOS, Linux, Windows cmd.exe, and PowerShell: Claude resolves the literal path once and every shell receives the same absolute value.
274
-
275
- Spawn a fresh `bugfind` teammate for this loop by calling the `Agent` tool with these exact arguments:
276
-
277
220
  ```
278
221
  Agent(
279
222
  subagent_type="code-quality-agent",
@@ -281,60 +224,28 @@ Agent(
281
224
  team_name="<team_name>",
282
225
  model="sonnet",
283
226
  description="Bugfind audit loop <N>",
284
- prompt="<audit XML from the block below, with placeholders substituted>"
227
+ prompt="<audit XML; see PROMPTS.md>"
285
228
  )
286
229
  ```
287
230
 
288
- Each loop calls `Agent` again with a fresh `Agent` invocation so the teammate starts with its own context window. The docs guarantee this: *"The lead's conversation history does not carry over."* Spawning per loop keeps every audit independent.
289
-
290
- See [`PROMPTS.md`](PROMPTS.md) for the AUDIT spawn-prompt XML and bugfind outcome schema. Substitute placeholders (repo, branch, base_branch, pr_url, loop, diff_path) and pass the result as the `prompt` argument.
231
+ Fresh `Agent` each loop; teammate context excludes lead history ([`sources.md`](sources.md) § Teammate context isolation). [`PROMPTS.md`](PROMPTS.md): XML + outcome schema. Lead reads `.bugteam-loop-<N>.outcomes.xml`, fills `loop_comment_index`.
291
232
 
292
- After the teammate writes the XML and returns, the lead reads `.bugteam-loop-<N>.outcomes.xml` with the `Read` tool, parses it, and populates `loop_comment_index` from `<finding>` elements.
293
-
294
- **Expected path: self-termination.** In practice, teammates self-terminate when their task is complete — the `Agent` call returns and the teammate's session ends automatically. When that happens, no `SendMessage` shutdown is needed and the cycle proceeds directly to the next action.
295
-
296
- **Fallback path: lead-initiated shutdown.** If the teammate has not self-terminated after the `Agent` call returns (observable as the teammate still appearing in the active-teammates list), send a shutdown message:
233
+ **Shutdown:** If `Agent` returned and the teammate already ended, skip. Otherwise:
297
234
 
298
235
  ```
299
236
  SendMessage(
300
237
  to="bugfind",
301
- message={
302
- "type": "shutdown_request",
303
- "reason": "audit loop <N> complete; outcome XML captured"
304
- }
238
+ message={"type": "shutdown_request", "reason": "audit loop <N> complete; outcome XML captured"}
305
239
  )
306
240
  ```
307
241
 
308
- The teammate replies with `{type: "shutdown_response", approve: true}` and exits. If `approve` comes back `false`, treat this as a fatal error: set exit reason = `error: bugfind teammate refused shutdown` and jump to Step 4 teardown followed by Step 5 revoke.
309
-
310
- `last_action = "audited"`. `last_findings = parsed`. Append `(loop=N, action="audit", counts={P0,P1,P2}, sha=current_HEAD, review_url=<url>, finding_count=<n>, fallback_count=<n>)` to `audit_log`.
311
-
312
- **Parallel auditors from loop 4 onward (`loop_count >= 4`).** The pre-audit code rules gate must still pass immediately before this step (Step 3). After three full audit/fix rounds without convergence, spawn three bugfind teammates concurrently by issuing three `Agent` calls in a single assistant message so they run in parallel:
313
-
314
- ```
315
- Agent(subagent_type="code-quality-agent", name="bugfind-loop-<N>-a", team_name="<team_name>", model="sonnet", description="Bugfind audit loop <N> variant a", prompt="<audit XML; write outcome to .bugteam-loop-<N>.outcomes.xml; post the per-loop review; read and merge b/c outcomes from <team_temp_dir>/loop-<N>-b.outcomes.xml and <team_temp_dir>/loop-<N>-c.outcomes.xml>")
316
- Agent(subagent_type="code-quality-agent", name="bugfind-loop-<N>-b", team_name="<team_name>", model="sonnet", description="Bugfind audit loop <N> variant b", prompt="<audit XML; write outcome to <team_temp_dir>/loop-<N>-b.outcomes.xml; skip PR posting>")
317
- Agent(subagent_type="code-quality-agent", name="bugfind-loop-<N>-c", team_name="<team_name>", model="sonnet", description="Bugfind audit loop <N> variant c", prompt="<audit XML; write outcome to <team_temp_dir>/loop-<N>-c.outcomes.xml; skip PR posting>")
318
- ```
319
-
320
- Teammate `-a` is the post-owner: it reads all three outcome XML files using their explicit absolute paths — its own outcome at `.bugteam-loop-<N>.outcomes.xml` (working directory), and the sibling outcomes at `<team_temp_dir>/loop-<N>-b.outcomes.xml` and `<team_temp_dir>/loop-<N>-c.outcomes.xml` — then merges findings by `(file, line, category_letter)` (same tuple collapses to one finding, keeping the longest description and the highest severity of the group), re-assigns merged-finding IDs as `loopN-K`, and posts the single per-loop review per the standard posting protocol above. The `-a` spawn prompt must include both sibling paths as literal absolute values so `-a` can read them with the `Read` tool by path without any discovery step.
242
+ `approve: false` `error: bugfind teammate refused shutdown` Step 4 then 5.
321
243
 
322
- Shut down `-b` and `-c` first with two parallel `SendMessage` calls, then shut down `-a` after its post completes:
244
+ `last_action = "audited"`; append audit line to `audit_log`.
323
245
 
324
- ```
325
- SendMessage(to="bugfind-loop-<N>-b", message={"type": "shutdown_request", "reason": "variant XML captured"})
326
- SendMessage(to="bugfind-loop-<N>-c", message={"type": "shutdown_request", "reason": "variant XML captured"})
327
- ```
246
+ **Parallel auditors (`loop_count >= 4`):** gate passes immediately before; after three full audit/fix rounds without convergence, issue three `Agent` calls in one assistant message (parallel). `-a` posts the review and merges outcomes from `-b`/`-c` (read `.bugteam-loop-<N>.outcomes.xml` plus `<team_temp_dir>/loop-<N>-b.outcomes.xml` and `...-c...`); merge key `(file, line, category_letter)`; re-id `loopN-K`. `-b`/`-c` write sibling XML only; prompts must pass literal absolute sibling paths. Shutdown: parallel `SendMessage` to `b` and `c`, then `a`.
328
247
 
329
- then
330
-
331
- ```
332
- SendMessage(to="bugfind-loop-<N>-a", message={"type": "shutdown_request", "reason": "merged review posted"})
333
- ```
334
-
335
- ### FIX action (fresh teammate, only sees latest audit)
336
-
337
- Spawn a fresh `bugfix` teammate for this loop by calling the `Agent` tool with these exact arguments:
248
+ ### FIX action
338
249
 
339
250
  ```
340
251
  Agent(
@@ -343,127 +254,45 @@ Agent(
343
254
  team_name="<team_name>",
344
255
  model="sonnet",
345
256
  description="Bugfix loop <N>",
346
- prompt="<fix XML from the block below, with placeholders substituted>"
257
+ prompt="<fix XML; see PROMPTS.md>"
347
258
  )
348
259
  ```
349
260
 
350
- The teammate sees only the most recent audit's findings — each `Agent` call starts with a fresh context window, so prior-loop findings, prior-loop fix history, and prior chat history stay inside the lead.
261
+ Pass finding comment URLs/ids from `loop_comment_index` in XML. Replies: `Fixed in <sha>` or `Could not address this loop: <reason>`.
351
262
 
352
- Pass the **finding comment URL and id for each finding** (from `loop_comment_index`) inside the XML prompt so the teammate owns reply posting. After committing fixes, the teammate posts one reply per finding: `Fixed in <commit_sha>` for addressed findings, `Could not address this loop: <one-line reason>` for skipped or failed findings. Same one-identity model as bugfind: teammate posts, lead waits.
263
+ **Shutdown:** same as bugfind; else `SendMessage(to="bugfix", message={"type": "shutdown_request", "reason": "fix loop <N> complete; commit <sha7> pushed"})`. `approve: false` `error: bugfix teammate refused shutdown` Step 4 then 5.
353
264
 
354
- After all replies are posted, the teammate writes its own outcome XML (see schema below) and returns.
265
+ [`PROMPTS.md`](PROMPTS.md): fix XML + schema. Verify: `git rev-parse HEAD` advanced; `git fetch origin <branch> && git rev-parse origin/<branch>` matches `HEAD`. Unchanged HEAD → `stuck — bugfix teammate could not address findings`.
355
266
 
356
- **Expected path: self-termination.** In practice, teammates self-terminate when their task is complete — the `Agent` call returns and the teammate's session ends automatically. When that happens, no `SendMessage` shutdown is needed and the cycle proceeds directly to the next action.
267
+ ### Step 4: Teardown
357
268
 
358
- **Fallback path: lead-initiated shutdown.** If the teammate has not self-terminated after the `Agent` call returns, send a shutdown message:
269
+ 1. For each live teammate: `SendMessage(to="<name>", message={"type": "shutdown_request", "reason": "bugteam cycle ending"})`. `approve: false` on cleanup log and continue.
359
270
 
360
- ```
361
- SendMessage(
362
- to="bugfix",
363
- message={
364
- "type": "shutdown_request",
365
- "reason": "fix loop <N> complete; commit <sha7> pushed"
366
- }
367
- )
368
- ```
271
+ 2. `TeamDelete()`
369
272
 
370
- If the shutdown response returns `approve: false`, treat it the same as the bugfind refusal case above: exit reason = `error: bugfix teammate refused shutdown`, jump to Step 4 teardown then Step 5 revoke.
273
+ 3. `python -c "import shutil; shutil.rmtree(r'<team_temp_dir>', ignore_errors=True)"`
371
274
 
372
- See [`PROMPTS.md`](PROMPTS.md) for the FIX spawn-prompt XML and bugfix outcome schema. Substitute placeholders (repo, branch, base_branch, pr_url, loop, and the per-finding bug entries built from `last_findings`) and pass the result as the `prompt` argument.
275
+ ### Step 4.5: PR description
373
276
 
374
- Verify the fix actually committed and pushed:
277
+ Lead only; cumulative product narrative (not process). Delegate body to `pr-description-writer` via `Agent` (else `general-purpose`) so the mandatory-pr-description hook accepts `gh pr edit`.
375
278
 
376
- - `git rev-parse HEAD` after fix should differ from before
377
- - The new HEAD should be present on `origin/<branch>` (`git fetch origin <branch> && git rev-parse origin/<branch>` matches HEAD)
279
+ 1. `gh pr diff <number> -R <owner>/<repo> > .bugteam-final.diff`
280
+ 2. `gh pr view <number> -R <owner>/<repo> --json body --jq .body > .bugteam-original-body.md`
281
+ 3. Agent brief: paths + branch names; describe merge-ready change from diff; keep curated original sections intact; return markdown body.
282
+ 4. Write `.bugteam-final-body.md`; `gh pr edit <number> -R <owner>/<repo> --body-file .bugteam-final-body.md`
283
+ 5. Delete the three temp files.
378
284
 
379
- If `git rev-parse HEAD` did not change, exit reason = `stuck — bugfix teammate could not address findings`. The fix teammate ran but produced no commit; further loops will not converge.
285
+ On failure: log in final report; continue to Step 5.
380
286
 
381
- `last_action = "fixed"`. Append `(loop=N, action="fix", commit_sha=new_HEAD, files_changed, lines_added, lines_removed)` to `audit_log`.
382
-
383
- ### Step 4: Tear down the team and clean working tree
384
-
385
- When the cycle exits (any reason), run these steps in order from THIS session (the lead):
386
-
387
- 1. **Confirm every teammate has shut down.** Any teammate still alive (for example, from an aborted shutdown mid-loop) must receive a shutdown message first. For each remaining teammate name:
388
-
389
- ```
390
- SendMessage(to="<teammate_name>", message={"type": "shutdown_request", "reason": "bugteam cycle ending"})
391
- ```
392
-
393
- The docs state: *"When the lead runs cleanup, it checks for active teammates and fails if any are still running, so shut them down first."*
394
-
395
- If any teammate returns `approve: false` during this cleanup shutdown, log the refusing teammate name (e.g., `cleanup warning: <teammate_name> refused shutdown_request`) and force-proceed to step 2 (`TeamDelete`) anyway. `TeamDelete` may fail if active teammates remain; if it does, surface the error in the final report with the refusing teammate name so the user can manually clean up. Do not abort the cleanup sequence — continue through temp-dir deletion, Step 4.5, and Step 5 regardless.
396
-
397
- 2. **Clean up the team** by calling `TeamDelete` with no arguments — it reads `<team_name>` from the current session's team context:
398
-
399
- ```
400
- TeamDelete()
401
- ```
402
-
403
- The docs state: *"When you're done, ask the lead to clean up: 'Clean up the team'."* `TeamDelete` is the tool that resolves that sentence.
404
-
405
- 3. **Delete the per-team scoped temp directory** by running this Python one-liner through the `Bash` tool (same literal `<team_temp_dir>` path resolved at Step 2):
406
-
407
- ```
408
- python -c "import shutil; shutil.rmtree(r'<team_temp_dir>', ignore_errors=True)"
409
- ```
410
-
411
- `shutil.rmtree(..., ignore_errors=True)` works identically on Windows and Unix, so the lead uses one command regardless of platform.
412
-
413
- ### Step 4.5: Finalize the PR description (mandatory)
414
-
415
- After teardown and before permission revoke, the lead rewrites the PR body to reflect the PR's **final cumulative state** — the change the PR delivers, not the bugteam process. This is the **only** PR-write the lead performs (audit and fix comments belong to the teammates).
416
-
417
- The lead delegates the body authoring to the `pr-description-writer` agent so the global mandatory-pr-description-writer hook accepts the subsequent `gh pr edit`. The lead does NOT compose the body inline.
418
-
419
- `pr-description-writer` is provided by the global git-workflow rule in `claude-code-config`. Invoke it with the `Agent` tool:
420
-
421
- ```
422
- Agent(
423
- subagent_type="pr-description-writer",
424
- description="Rewrite PR <number> body from cumulative diff",
425
- prompt="<brief from step 3 below>"
426
- )
427
- ```
428
-
429
- If `pr-description-writer` is not in the available agents list for the current environment, fall back to `general-purpose` with the same brief — the global hook treats agent-authored bodies the same regardless of the specific agent type:
430
-
431
- ```
432
- Agent(
433
- subagent_type="general-purpose",
434
- description="Rewrite PR <number> body from cumulative diff",
435
- prompt="<brief from step 3 below>"
436
- )
437
- ```
438
-
439
- When neither agent is available, log a warning in the final report and skip Step 4.5 so the original PR body stays in place.
440
-
441
- Steps:
442
-
443
- 1. Capture the cumulative diff: `gh pr diff <number> -R <owner>/<repo> > .bugteam-final.diff`.
444
- 2. Capture the original body: `gh pr view <number> -R <owner>/<repo> --json body --jq .body > .bugteam-original-body.md`.
445
- 3. Invoke the `pr-description-writer` agent (or `general-purpose` fallback) with this brief:
446
- - **Inputs:** the diff path, the original body path, the head branch name, the base branch name.
447
- - **Constraint:** describe what the PR delivers based on the cumulative diff — code behavior, user-facing effect, and merge rationale. Process metadata (audit loops, fix commit counts, finding counts) lives in the finding comments. The description speaks to the merge audience.
448
- - **Preservation rule:** if the original body contains sections that look manually curated (linked issues, screenshots, a populated test plan, "Risk Assessment" sections), preserve those verbatim and only rewrite the prose narrative around them.
449
- - **Output:** the new body markdown.
450
- 4. Write the agent's returned body to `.bugteam-final-body.md`.
451
- 5. Apply: `gh pr edit <number> -R <owner>/<repo> --body-file .bugteam-final-body.md`.
452
- 6. Delete `.bugteam-final.diff`, `.bugteam-original-body.md`, and `.bugteam-final-body.md`.
453
-
454
- If Step 4.5 fails for any reason (agent error, hook block, network), surface the failure in the final report and continue to Step 5. The original PR body remains; the rest of the cycle's work (commits, comments, replies) is unaffected.
455
-
456
- ### Step 5: Revoke project permissions (mandatory, runs always)
457
-
458
- After team cleanup completes — including on error, cap-reached, or stuck exits — run:
287
+ ### Step 5: Revoke permissions (always)
459
288
 
460
289
  ```bash
461
- python "${CLAUDE_SKILL_DIR}/revoke_project_claude_permissions.py"
290
+ python "${CLAUDE_SKILL_DIR}/scripts/revoke_project_claude_permissions.py"
462
291
  ```
463
292
 
464
- This removes the allow rules and additionalDirectories entry that Step 0 added. Revoke is non-negotiable: leaving the grant in place means future sessions inherit elevated permissions on this project's `.claude/**` tree without the user opting in. Run this even if Step 4 cleanup partially failed; surface the cleanup error separately in the final report.
293
+ Removes Step 0 grant run even if Step 4 partially failed (log separately).
465
294
 
466
- ### Step 6: Print the final report
295
+ ### Step 6: Final report
467
296
 
468
297
  ```
469
298
  /bugteam exit: <converged | cap reached | stuck | error>
@@ -474,13 +303,10 @@ Net change: <total_files> files, +<total_add>/-<total_del>
474
303
 
475
304
  Loop log:
476
305
  1 audit: 3P0 2P1 0P2
477
- 1 fix: commit a1b2c3d (4 files, +12/-3)
478
- 2 audit: 1P0 0P1 0P2
479
- 2 fix: commit e4f5g6h (1 file, +2/-1)
480
- 3 audit: 0P0 0P1 0P2 → converged
306
+ ...
481
307
  ```
482
308
 
483
- If exit = `cap reached`, name the remaining bug count and recommend `/findbugs` for human triage. If exit = `stuck`, name which findings the fix agent could not resolve. If exit = `error`, surface the error and the loop number.
309
+ `cap reached` suggest `/findbugs`. `stuck` which findings. `error` detail + loop.
484
310
 
485
311
  ## Constraints
486
312
 
@@ -489,3 +315,11 @@ See [`CONSTRAINTS.md`](CONSTRAINTS.md).
489
315
  ## Examples
490
316
 
491
317
  See [`EXAMPLES.md`](EXAMPLES.md).
318
+
319
+ ## Reference
320
+
321
+ See [`reference/README.md`](reference/README.md).
322
+
323
+ ## Sources
324
+
325
+ See [`sources.md`](sources.md).