claude-dev-env 1.35.0 → 1.36.1

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 (115) hide show
  1. package/agents/clean-coder.md +109 -1
  2. package/bin/install.mjs +28 -8
  3. package/bin/install.test.mjs +9 -1
  4. package/docs/CODE_RULES.md +3 -0
  5. package/docs/agents-md-alignment-plan.md +123 -0
  6. package/hooks/blocking/code_rules_enforcer.py +451 -39
  7. package/hooks/blocking/es_exe_path_rewriter.py +10 -4
  8. package/hooks/blocking/test_code_rules_enforcer.py +182 -0
  9. package/hooks/blocking/test_code_rules_enforcer_banned_identifier.py +106 -0
  10. package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +173 -0
  11. package/hooks/blocking/test_code_rules_enforcer_collection_prefix.py +191 -0
  12. package/hooks/blocking/test_code_rules_enforcer_constant_equality.py +40 -0
  13. package/hooks/blocking/test_code_rules_enforcer_hardcoded_user_path.py +291 -0
  14. package/hooks/blocking/test_code_rules_enforcer_loop_variable_naming.py +87 -3
  15. package/hooks/blocking/test_code_rules_enforcer_naming_pattern.py +49 -0
  16. package/hooks/blocking/test_code_rules_enforcer_sys_path_insert.py +157 -0
  17. package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +244 -0
  18. package/hooks/blocking/test_es_exe_path_rewriter.py +81 -3
  19. package/hooks/blocking/test_windows_rmtree_blocker.py +120 -8
  20. package/hooks/blocking/windows_rmtree_blocker.py +23 -6
  21. package/hooks/config/banned_identifiers_constants.py +24 -0
  22. package/hooks/config/hardcoded_user_path_constants.py +12 -0
  23. package/hooks/config/hook_log_extractor_constants.py +1 -1
  24. package/hooks/config/pre_tool_use_stdin.py +48 -0
  25. package/hooks/config/setup_project_paths_constants.py +4 -0
  26. package/hooks/config/stuttering_check_config.py +14 -0
  27. package/hooks/config/stuttering_import_binding_constants.py +11 -0
  28. package/hooks/config/sys_path_insert_constants.py +4 -0
  29. package/hooks/config/test_banned_identifiers_constants.py +48 -0
  30. package/hooks/config/test_hardcoded_user_path_constants.py +78 -0
  31. package/hooks/config/test_hook_log_extractor_constants.py +3 -3
  32. package/hooks/config/test_pre_tool_use_stdin.py +80 -0
  33. package/hooks/config/unused_module_import_constants.py +7 -0
  34. package/hooks/config/windows_rmtree_blocker_constants.py +3 -0
  35. package/hooks/diagnostic/hook_log_stop_wrapper.py +7 -4
  36. package/hooks/git-hooks/config.py +3 -3
  37. package/hooks/git-hooks/test_gate_utils.py +10 -10
  38. package/hooks/mypy.ini +2 -0
  39. package/package.json +1 -1
  40. package/rules/gh-paginate.md +125 -0
  41. package/skills/bugteam/CONSTRAINTS.md +12 -6
  42. package/skills/bugteam/SKILL.md +364 -154
  43. package/skills/bugteam/SKILL_EVALS.md +25 -23
  44. package/skills/bugteam/reference/README.md +2 -0
  45. package/skills/bugteam/reference/audit-and-teammates.md +2 -2
  46. package/skills/bugteam/reference/teardown-publish-permissions.md +1 -1
  47. package/skills/bugteam/reference/workflow-path-a-orchestrated-teams.md +113 -0
  48. package/skills/bugteam/reference/workflow-path-b-task-harness.md +48 -0
  49. package/skills/bugteam/scripts/reflow_skill_md.py +298 -0
  50. package/skills/bugteam/test_skill_additions.py +13 -4
  51. package/skills/bugteam/test_team_lifecycle.py +103 -0
  52. package/skills/findbugs/SKILL.md +3 -3
  53. package/skills/fixbugs/SKILL.md +4 -4
  54. package/skills/monitor-open-prs/SKILL.md +32 -2
  55. package/skills/monitor-open-prs/test_team_lifecycle.py +46 -0
  56. package/skills/pr-converge/SKILL.md +1206 -131
  57. package/skills/pr-converge/scripts/README.md +145 -0
  58. package/skills/pr-converge/scripts/caller-window-pid.ps1 +86 -0
  59. package/skills/pr-converge/scripts/check_pr_mergeability.py +79 -0
  60. package/skills/pr-converge/scripts/config/pr_converge_constants.py +65 -0
  61. package/skills/pr-converge/scripts/config/test_pr_converge_constants.py +176 -0
  62. package/skills/pr-converge/scripts/cursor-agents-continue-caller.cmd +9 -0
  63. package/skills/pr-converge/scripts/cursor-agents-continue-stop-others.ps1 +16 -0
  64. package/skills/pr-converge/scripts/cursor-agents-continue.ahk +172 -0
  65. package/skills/pr-converge/scripts/cursor-agents-continue.cmd +2 -0
  66. package/skills/pr-converge/scripts/evict_cached_config_modules.py +20 -0
  67. package/skills/pr-converge/scripts/fetch_bugbot_inline_comments.py +110 -0
  68. package/skills/pr-converge/scripts/fetch_bugbot_reviews.py +103 -0
  69. package/skills/pr-converge/scripts/fetch_copilot_inline_comments.py +112 -0
  70. package/skills/pr-converge/scripts/fetch_copilot_reviews.py +121 -0
  71. package/skills/pr-converge/scripts/mark_pr_ready.py +54 -0
  72. package/skills/pr-converge/scripts/open_followup_copilot_pr.py +136 -0
  73. package/skills/pr-converge/scripts/post-bugbot-run.helpers.ps1 +49 -0
  74. package/skills/pr-converge/scripts/post-bugbot-run.ps1 +33 -0
  75. package/skills/pr-converge/scripts/reflow_skill_md.py +288 -0
  76. package/skills/pr-converge/scripts/reply_to_inline_comment.py +84 -0
  77. package/skills/pr-converge/scripts/request_copilot_review.py +71 -0
  78. package/skills/pr-converge/scripts/resolve_pr_head.py +58 -0
  79. package/skills/pr-converge/scripts/review_field_helpers.py +43 -0
  80. package/skills/pr-converge/scripts/test_check_pr_mergeability.py +126 -0
  81. package/skills/pr-converge/scripts/test_evict_cached_config_modules.py +22 -0
  82. package/skills/pr-converge/scripts/test_fetch_bugbot_inline_comments.py +342 -0
  83. package/skills/pr-converge/scripts/test_fetch_bugbot_reviews.py +220 -0
  84. package/skills/pr-converge/scripts/test_fetch_copilot_inline_comments.py +372 -0
  85. package/skills/pr-converge/scripts/test_fetch_copilot_reviews.py +280 -0
  86. package/skills/pr-converge/scripts/test_mark_pr_ready.py +69 -0
  87. package/skills/pr-converge/scripts/test_open_followup_copilot_pr.py +236 -0
  88. package/skills/pr-converge/scripts/test_post_bugbot_run.py +195 -0
  89. package/skills/pr-converge/scripts/test_reply_to_inline_comment.py +159 -0
  90. package/skills/pr-converge/scripts/test_request_copilot_review.py +101 -0
  91. package/skills/pr-converge/scripts/test_resolve_pr_head.py +79 -0
  92. package/skills/pr-converge/scripts/test_review_field_helpers.py +80 -0
  93. package/skills/pr-converge/scripts/test_trigger_bugbot.py +139 -0
  94. package/skills/pr-converge/scripts/test_view_pr_context.py +111 -0
  95. package/skills/pr-converge/scripts/trigger_bugbot.py +77 -0
  96. package/skills/pr-converge/scripts/view_pr_context.py +47 -0
  97. package/skills/pr-converge/test_team_lifecycle.py +56 -0
  98. package/skills/pr-converge/workflows/ahk-auto-continue-loop.md +108 -0
  99. package/skills/pr-converge/workflows/schedule-wakeup-loop.md +37 -0
  100. package/skills/qbug/SKILL.md +4 -4
  101. package/skills/qbug/test_qbug_skill_post_fix_audit.py +2 -2
  102. package/skills/resume-review/SKILL.md +261 -0
  103. package/skills/bugteam/scripts/README.md +0 -58
  104. package/skills/bugteam/scripts/_claude_permissions_common.py +0 -219
  105. package/skills/bugteam/scripts/bugteam_code_rules_gate.py +0 -633
  106. package/skills/bugteam/scripts/bugteam_fix_hookspath.py +0 -260
  107. package/skills/bugteam/scripts/bugteam_preflight.py +0 -201
  108. package/skills/bugteam/scripts/config/bugteam_fix_hookspath_constants.py +0 -17
  109. package/skills/bugteam/scripts/grant_project_claude_permissions.py +0 -109
  110. package/skills/bugteam/scripts/revoke_project_claude_permissions.py +0 -135
  111. package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +0 -271
  112. package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +0 -267
  113. package/skills/bugteam/scripts/test_bugteam_preflight.py +0 -189
  114. package/skills/bugteam/scripts/test_claude_permissions_common.py +0 -44
  115. /package/skills/{bugteam → pr-converge}/scripts/config/__init__.py +0 -0
@@ -1,34 +1,120 @@
1
1
  ---
2
2
  name: bugteam
3
3
  description: >-
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, opus) and a fix pass
6
- (clean-coder, opus), 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'.
4
+ Open pull request audit–fix until convergence: CODE_RULES gate, clean-room
5
+ audit (`code-quality-agent`, opus) and fix (`clean-coder`, opus), per-loop
6
+ GitHub reviews, 10-audit cap; grant then revoke `.claude/**`. **Path A** when
7
+ `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` (orchestrated teams, `TeamCreate`).
8
+ **Path B** otherwise (Task harness workflow in
9
+ `reference/workflow-path-b-task-harness.md`). **This `SKILL.md` holds only
10
+ shared steps**; per-path harness lives in `reference/workflow-path-*.md`.
11
+ Triggers: '/bugteam', 'run the bug team', 'auto-fix the PR until clean', 'loop
12
+ audit and fix'.
13
13
  ---
14
14
 
15
15
  # Bugteam
16
16
 
17
- **Core principle:** Agent team runs audit–fix 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
-
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).
17
+ **Core principle:** Audit–fix until convergence. **Bugfind:**
18
+ `code-quality-agent`, fresh context each loop. **Bugfix:** `clean-coder`. Hard
19
+ cap: 10 audit loops. Grant `.claude/**` at start, revoke always at end.
20
+
21
+ **Path routing** picks **Path A** (orchestrated teams) vs **Path B** (Task
22
+ harness). Harness execution — `TeamCreate`, `Agent`/`Task` spawns,
23
+ `SendMessage`, `TeamDelete`, who runs Step 2.5 `gh api` — lives only in
24
+ [`reference/workflow-path-a-orchestrated-teams.md`][path-a] and
25
+ [`reference/workflow-path-b-task-harness.md`][path-b]. Verbatim doc quotes and
26
+ URLs: [`sources.md`](sources.md).
27
+
28
+ ## Path routing (mandatory first branch)
29
+
30
+ At `/bugteam` entry, evaluate **once** (same rule as pr-converge §Team
31
+ infrastructure detection):
32
+
33
+ - **Path A** — `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` set and equals **`1`**
34
+ after trim → load and follow
35
+ [`reference/workflow-path-a-orchestrated-teams.md`][path-a] for every harness
36
+ step (with this `SKILL.md` for shared material).
37
+ - **Path B** — otherwise → load and follow
38
+ [`reference/workflow-path-b-task-harness.md`][path-b] for every harness step
39
+ (with this `SKILL.md` for shared material).
40
+
41
+ Shared material is **everything else in this file** plus
42
+ [`PROMPTS.md`](PROMPTS.md), [`EXAMPLES.md`](EXAMPLES.md),
43
+ [`CONSTRAINTS.md`](CONSTRAINTS.md) — agent types, models, XML, gates, cycle
44
+ state machine, Step 2.5 payload shapes, shared teardown `rmtree`, revoke, final
45
+ report.
46
+
47
+ ## Team lifecycle (Path A only)
48
+
49
+ The `TeamCreate` / `TeamDelete` pair has historically been bound to a single
50
+ `/bugteam` invocation. That coupling fails when an orchestrator (`pr-converge`
51
+ multi-PR mode, `monitor-open-prs`) needs to call `/bugteam` repeatedly inside
52
+ one parent session: only one team can be led at a time, and a missed Step 4
53
+ leaks the team. To decouple, every Path A invocation reads
54
+ `BUGTEAM_TEAM_LIFECYCLE` (defaults to `auto`) and may also read
55
+ `BUGTEAM_TEAM_NAME`.
56
+
57
+ **`owned`**
58
+
59
+ - **Step 2:** `TeamCreate(<computed_team_name>)`. If the runtime returns
60
+ `Already leading team "<existing>". A leader can only manage one team at a
61
+ time.` → **error**: `Already leading team <existing>; rerun with
62
+ BUGTEAM_TEAM_LIFECYCLE=attach BUGTEAM_TEAM_NAME=<existing>`.
63
+ - **Step 4:** `TeamDelete()` (lead-owned).
64
+ - **Use case:** Pre-decoupling behavior. Use only when you know the session
65
+ leads no other team.
66
+
67
+ **`attach`**
68
+
69
+ - **Step 2:** Require `BUGTEAM_TEAM_NAME`. Treat that team as already-led; do
70
+ **not** call `TeamCreate`.
71
+ - **Step 4:** **Skip** `TeamDelete` — the orchestrator owns teardown.
72
+ - **Use case:** Orchestrators (pr-converge multi-PR, monitor-open-prs) that
73
+ explicitly created a team and will tear it down themselves.
74
+
75
+ ### `auto` (**default: `auto`**)
76
+
77
+ - **Step 2:** Try `TeamCreate(<computed_team_name>)`. On `Already leading team
78
+ "<existing>"` → parse `<existing>`, attach (do **not** call `TeamCreate`
79
+ again), set `team_owned=false`. On success → set `team_owned=true`.
80
+ - **Step 4:** If `team_owned=true` → `TeamDelete()`. Else → **skip**
81
+ `TeamDelete`.
82
+ - **Use case:** All callers when in doubt. Solo invocations behave like `owned`;
83
+ nested or repeated invocations attach safely.
84
+
85
+ **`team_owned` flag** — set in Step 2 by all three modes
86
+ (`owned` always `true`; `attach` always `false`; `auto` reflects the
87
+ `TeamCreate` outcome). Read in Step
88
+ 4 to decide whether to call `TeamDelete`. The same flag also gates
89
+ `<team_temp_dir>` `rmtree`: when `team_owned=false`, only the per-PR subfolders
90
+ this invocation created (`<team_temp_dir>/pr-<N>/`) are removed; the
91
+ orchestrator's parent directory survives.
92
+
93
+ **Path B note:** Path B does not use `TeamCreate` / `TeamDelete`, so
94
+ `BUGTEAM_TEAM_LIFECYCLE` is read but only its `team_temp_dir` cleanup behavior
95
+ applies. `team_owned` is treated as `true` by default in Path B; orchestrators
96
+ driving Path B that share a temp directory should set
97
+ `BUGTEAM_TEAM_LIFECYCLE=attach` so the per-PR subfolder cleanup rule applies.
20
98
 
21
99
  ## Contents
22
100
 
23
- Orchestration lives here; companion files hold prompts, invariants, examples, citations, and domain reference notes. Scan this list before a partial read.
24
-
25
- - When this skill applies — refusal cases (4) and trigger conditions
101
+ Orchestration lives here; companion files hold prompts, invariants, examples,
102
+ citations, and domain reference notes. Scan this list before a partial read.
103
+
104
+ - [Path routing](#path-routing-mandatory-first-branch) — Path A vs Path B
105
+ - [Team lifecycle](#team-lifecycle-path-a-only) — `owned` / `attach` / `auto`
106
+ modes; orchestrator-owned teams
107
+ - [`reference/workflow-path-a-orchestrated-teams.md`][path-a] — Path A harness
108
+ (orchestrated teams)
109
+ - [`reference/workflow-path-b-task-harness.md`][path-b] — Path B harness (Task
110
+ harness)
111
+ - When this skill applies — refusal cases and trigger conditions
26
112
  - Utility scripts — pre-flight (`scripts/`, executed not inlined)
27
113
  - Pre-audit gate — `validate_content` before each AUDIT
28
114
  - The Process — checklist + Steps 0–6
29
115
  - Step 0 — Grant project permissions
30
116
  - Step 1 — Resolve PR scope
31
- - Step 2 — Create the agent team
117
+ - Step 2 — Path harness + loop state
32
118
  - Step 2.5 — PR comment lifecycle (per-loop review + fix replies)
33
119
  - Step 3 — Cycle (AUDIT ↔ FIX, exits)
34
120
  - Step 4 — Teardown + clean tree
@@ -39,7 +125,8 @@ Orchestration lives here; companion files hold prompts, invariants, examples, ci
39
125
  - [`EXAMPLES.md`](EXAMPLES.md) — exit scenarios
40
126
  - [`CONSTRAINTS.md`](CONSTRAINTS.md) — invariants and design rationale
41
127
  - [`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)
128
+ - [`reference/README.md`](reference/README.md) — expanded prose by topic
129
+ (design, team setup, GitHub reviews, cycle, teardown)
43
130
 
44
131
  ## When this skill applies
45
132
 
@@ -47,31 +134,53 @@ Orchestration lives here; companion files hold prompts, invariants, examples, ci
47
134
 
48
135
  Refusals — first match wins; respond with the quoted line exactly and stop:
49
136
 
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
137
  - **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
- - **Lead role must be held by the orchestrator.** Run /bugteam in the session that received the user's command. The orchestrator session calls TeamCreate directly. Runtime confirms a single lead per team: `Already leading team "<name>". A leader can only manage one team at a time.`
138
+ - **Dirty tree.** `Uncommitted changes detected. Stash, commit, or revert before
139
+ /bugteam.`
140
+ - **Missing subagents.** Before Step 0, confirm `code-quality-agent` and
141
+ `clean-coder` exist. Else: `Required subagent type <name> not installed.
142
+ /bugteam needs both code-quality-agent and clean-coder available.`
143
+ - **Lead role must be held by the orchestrator.** Run /bugteam in the session
144
+ that received the user's command. **Path A:** lead calls `TeamCreate` per
145
+ [`reference/workflow-path-a-orchestrated-teams.md`][path-a]; runtime may
146
+ return `Already leading team "<name>". A leader can only manage one team at a
147
+ time.` **Path B:** lead runs the Task harness per
148
+ [`reference/workflow-path-b-task-harness.md`][path-b]; no `TeamCreate`.
55
149
 
56
150
  ## Utility scripts
57
151
 
58
- 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).
152
+ Shell-heavy steps live under
153
+ [`_shared/pr-loop/scripts/`](../../_shared/pr-loop/scripts/) (run, do not paste
154
+ into context). Utility scripts are **executed**, not loaded as primary context
155
+ ([`sources.md`](sources.md) § Progressive disclosure and utility scripts).
59
156
 
60
157
  ### Pre-flight (before Step 0)
61
158
 
62
159
  ```bash
63
- python "${CLAUDE_SKILL_DIR}/scripts/bugteam_preflight.py"
160
+ python "${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/preflight.py"
64
161
  ```
65
162
 
66
- Non-zero → fix before grant. `BUGTEAM_PREFLIGHT_SKIP=1` emergency only. `--pre-commit` if `.pre-commit-config.yaml` exists.
163
+ Non-zero → fix before grant. `BUGTEAM_PREFLIGHT_SKIP=1` emergency only.
164
+ `--pre-commit` if `.pre-commit-config.yaml` exists.
67
165
 
68
- **Auto-remediation for `core.hooksPath`:** when preflight fails with stderr containing `core.hooksPath` (the message starts with `bugteam_preflight: core.hooksPath is`, or `Git-side CODE_RULES enforcement is not active`), Claude must auto-invoke the fix script — do not fall through to `AskUserQuestion`, do not punt to the user, do not ask for confirmation:
166
+ **Auto-remediation for `core.hooksPath`:** when preflight fails with stderr
167
+ containing `core.hooksPath` (the message starts with `bugteam_preflight:
168
+ core.hooksPath is`, or `Git-side CODE_RULES enforcement is not active`), Claude
169
+ must auto-invoke the fix script — do not fall through to `AskUserQuestion`, do
170
+ not punt to the user, do not ask for confirmation:
69
171
 
70
172
  ```bash
71
- python "${CLAUDE_SKILL_DIR}/scripts/bugteam_fix_hookspath.py"
173
+ python "${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/fix_hookspath.py"
72
174
  ```
73
175
 
74
- The fix script removes any non-canonical local-scope override on the active repository, sets the global `core.hooksPath` to `~/.claude/hooks/git-hooks` if missing or wrong, and re-runs `bugteam_preflight.py`. Its exit code becomes the preflight outcome. Exit 0 → continue to Step 0. Non-zero only when the canonical hooks directory is missing (run `npx claude-dev-env .` first) or `git config --global` writes are blocked. Other preflight failures (pytest, pre-commit) still require manual fixes — the auto-remediation only applies to the `core.hooksPath` failure mode.
176
+ The fix script removes any non-canonical local-scope override on the active
177
+ repository, sets the global `core.hooksPath` to `~/.claude/hooks/git-hooks` if
178
+ missing or wrong, and re-runs `preflight.py`. Its exit code becomes the
179
+ preflight outcome. Exit 0 → continue to Step 0. Non-zero only when the
180
+ canonical hooks directory is missing (run `npx claude-dev-env .` first) or
181
+ `git config --global` writes are blocked. Other preflight failures (pytest,
182
+ pre-commit) still require manual fixes —
183
+ the auto-remediation only applies to the `core.hooksPath` failure mode.
75
184
 
76
185
  ## The Process
77
186
 
@@ -80,7 +189,7 @@ The fix script removes any non-canonical local-scope override on the active repo
80
189
  ```
81
190
  [ ] Step 0: project permissions granted
82
191
  [ ] Step 1: PR scope resolved
83
- [ ] Step 2: agent team created + loop state set
192
+ [ ] Step 2: loop state set + path harness applied
84
193
  [ ] Step 3: cycle complete (converged | cap reached | stuck | error)
85
194
  [ ] Step 4: team torn down + working tree clean
86
195
  [ ] Step 4.5: PR description rewritten (or skip warning logged)
@@ -91,14 +200,22 @@ The fix script removes any non-canonical local-scope override on the active repo
91
200
  ### Step 0: Grant project permissions (once, first)
92
201
 
93
202
  ```bash
94
- python "${CLAUDE_SKILL_DIR}/scripts/grant_project_claude_permissions.py"
203
+ python
204
+ "${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/grant_project_claude_permis \
205
+ sions.py"
95
206
  ```
96
207
 
97
- `${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.
208
+ `${CLAUDE_SKILL_DIR}` is host-substituted before the shell runs (unlike normal
209
+ env expansion). Idempotent writes to `~/.claude/settings.json` from repo root.
210
+ Non-zero → stop. Revoke in Step 5 on every exit path.
98
211
 
99
212
  ### Step 1: Resolve PR scope (once)
100
213
 
101
- Accept one or more PR numbers from the invocation. For each PR, run `gh pr view --json number,baseRefName,headRefName,url` (falling back to the merge-base diff path when no PR exists). Capture `all_prs = [{number, owner, repo, baseRef, headRef, url}, ...]`. A single-PR invocation produces a one-element list and follows the same downstream rules.
214
+ Accept one or more PR numbers from the invocation. For each PR, run `gh pr view
215
+ --json number,baseRefName,headRefName,url` (falling back to the merge-base diff
216
+ path when no PR exists). Capture `all_prs = [{number, owner, repo, baseRef,
217
+ headRef, url}, ...]`. A single-PR invocation produces a one-element list and
218
+ follows the same downstream rules.
102
219
 
103
220
  Keep: owner/repo, branches, PR number, URL — for all loops.
104
221
 
@@ -110,31 +227,29 @@ For each PR in all_prs:
110
227
  2. Run `git worktree add "<team_temp_dir>/pr-<N>/worktree" origin/<headRef>`.
111
228
  3. Record the absolute worktree path alongside the PR's other fields.
112
229
 
113
- Teammates spawned for a PR operate inside that PR's worktree. Step 4 teardown runs `git worktree remove "<team_temp_dir>/pr-<N>/worktree"` for each PR before `TeamDelete`.
114
-
115
- ### Step 2: Create the agent team
116
-
117
- **This session is the lead.** The orchestrator calls `TeamCreate` directly:
118
-
119
- ```
120
- TeamCreate(
121
- team_name="<team_name>",
122
- description="Bugteam audit/fix loop for PR <number> (<owner>/<repo>)",
123
- agent_type="team-lead"
124
- )
125
- ```
126
-
127
- **Team name:** For a single-PR invocation use `bugteam-pr-<number>-<YYYYMMDDHHMMSS>`. For a multi-PR invocation use `bugteam-<YYYYMMDDHHMMSS>`. The timestamp is captured once at team-creation time. Apply the no-PR fallback (`bugteam-<sanitized-head>-<YYYYMMDDHHMMSS>`) only when no PR resolves at all. `TeamCreate` implements natural-language team creation ([`sources.md`](sources.md) § Team creation in natural language).
128
-
129
- **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.
130
-
131
- **`<team_temp_dir>`:** `Path(tempfile.gettempdir()) / team_name` (lead resolves once to an absolute path; every shell gets that literal string).
132
-
133
- **Roles (spawned per loop, not here):** bugfind → `code-quality-agent` opus (4.7) at xhigh effort; bugfix → `clean-coder` opus (4.7) at xhigh effort. `model="opus"` resolves to Opus 4.7 on the Anthropic API and runs at the model's default `xhigh` effort level — see [`CONSTRAINTS.md`](CONSTRAINTS.md) § **Opus 4.7 at xhigh effort for both teammates** for rationale. **Display:** inherit `teammateMode` from `~/.claude.json`. Reference subagent types by name when spawning teammates ([`sources.md`](sources.md) § Referencing subagent types when spawning teammates).
134
-
135
- **Optional Groq-backed FIX path (explicit opt-in only):** the default flow above always uses Opus teammates. A separate optional path exists only when the user explicitly sets `BUGTEAM_FIX_IMPLEMENTER=groq-coder` before invocation: spawn the FIX teammate with `subagent_type="groq-coder"`. Before Step 3, `groq_bugteam.py` loads `packages/claude-dev-env/.env` when that file exists (gitignored; start from `packages/claude-dev-env/.env.example`). If `GROQ_API_KEY` is still unset after that load, stop and prompt the user to create `packages/claude-dev-env/.env` from the example path above—do not continue the Groq path without a key. Any other `BUGTEAM_FIX_IMPLEMENTER` value (or unset) keeps `clean-coder` on Opus. The FIX spawn XML in [`PROMPTS.md`](PROMPTS.md) is identical for both implementers.
136
-
137
- **`--bugbot-retrigger` flag:** when present on the `/bugteam` invocation, after every successful FIX push in Step 3, post an additional `bugbot run` issue comment via the Step 2.5 issue-comments fallback endpoint (`POST .../issues/{issue}/comments`) to re-trigger Cursor's bugbot on the new commit. Omit when the flag is absent.
230
+ Teammates or Task workers for a PR operate inside that PR's worktree. Step 4
231
+ teardown runs `git worktree remove "<team_temp_dir>/pr-<N>/worktree"` for each
232
+ PR, then path-specific harness teardown per
233
+ [`reference/workflow-path-a-orchestrated-teams.md`][path-a] or
234
+ [`reference/workflow-path-b-task-harness.md`][path-b] § Step 4.
235
+
236
+ ### Step 2: Path harness + loop state
237
+
238
+ Apply the path you chose in [Path
239
+ routing](#path-routing-mandatory-first-branch): **Path A**
240
+ [`reference/workflow-path-a-orchestrated-teams.md`][path-a] § Step 2
241
+ (`TeamCreate`, team name, `team_temp_dir`, roles, optional Groq FIX,
242
+ `--bugbot-retrigger`). **Path B** —
243
+ [`reference/workflow-path-b-task-harness.md`][path-b] § Step 2 (no `TeamCreate`
244
+ / `TeamDelete`; same worktrees and variables).
245
+
246
+ Path A also resolves the team lifecycle here per [Team
247
+ lifecycle](#team-lifecycle-path-a-only): pick the mode (`owned` / `attach` /
248
+ `auto`) from `BUGTEAM_TEAM_LIFECYCLE`, set `team_name` (computed for
249
+ `owned`/`auto` create paths; required `BUGTEAM_TEAM_NAME` for `attach` and
250
+ `auto`'s attach branch), and set `team_owned` (`true` when `TeamCreate`
251
+ succeeded in this invocation; `false` when attaching to an existing team). Step
252
+ 4 reads `team_owned` to decide whether to call `TeamDelete`.
138
253
 
139
254
  **Loop state (lead; not a single script):**
140
255
 
@@ -146,46 +261,63 @@ audit_log=""
146
261
  starting_sha="$(git rev-parse HEAD)"
147
262
  team_name="bugteam-pr-<number>-<YYYYMMDDHHMMSS>"
148
263
  team_temp_dir="<absolute-path>/<team_name>"
264
+ team_owned="true" # set by Step 2 lifecycle resolution; see Team lifecycle table
149
265
  loop_comment_index=""
150
266
  ```
151
267
 
152
- **`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}`.
268
+ **`loop_comment_index`:** reset each AUDIT start; filled during AUDIT; FIX
269
+ consumes for replies; cleared after FIX. Entries: `{loop, finding_id,
270
+ finding_comment_id, finding_comment_url, used_fallback, fix_status}`.
153
271
 
154
272
  ### Step 2.5: PR comments (one review per loop)
155
273
 
156
- 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.
274
+ **Who posts:** Path A vs Path B
275
+ [`reference/workflow-path-a-orchestrated-teams.md`][path-a] § Step 2.5 and
276
+ [`reference/workflow-path-b-task-harness.md`][path-b] § Step 2.5. Payloads and
277
+ endpoints below are identical for both paths.
157
278
 
158
- 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`.
279
+ Order: audit → buffer → validate anchors vs diff → single review POST.
280
+ Review body states counts; zero findings → still one review, `comments: []`,
281
+ body `## /bugteam loop <N> audit: 0P0 / 0P1 / 0P2 → clean`.
159
282
 
160
- **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.
283
+ **Payloads:** build JSON with `jq --rawfile` / `-Rs`, pipe to `gh api ...
284
+ --input -` (avoids shell-quoting; satisfies `gh-body-backtick-guard`). Write
285
+ each markdown body to a temp file first.
161
286
 
162
- **Review POST** (one `comments[]` object per anchored finding; single-line `{path, line, side: "RIGHT", body}`; multi-line add `start_line`, `start_side: "RIGHT"`):
287
+ **Review POST** (one `comments[]` object per anchored finding; single-line
288
+ `{path, line, side: "RIGHT", body}`; multi-line add `start_line`, `start_side:
289
+ "RIGHT"`):
163
290
 
164
291
  ```
165
292
  jq -n \
166
- --rawfile review_body <tmp_review_body.md> \
167
- --arg commit_id "$(git rev-parse HEAD)" \
168
- --rawfile finding_body_1 <tmp_finding_1.md> \
169
- --arg path_1 "<file_1>" \
170
- --argjson line_1 <line_1> \
171
- [... one finding_body_K / path_K / line_K triple per finding ...] \
172
- '{
173
- commit_id: $commit_id,
174
- event: "COMMENT",
175
- body: $review_body,
176
- comments: [
177
- {path: $path_1, line: $line_1, side: "RIGHT", body: $finding_body_1}
178
- [, ... ]
179
- ]
180
- }' \
293
+ --rawfile review_body <tmp_review_body.md> \
294
+ --arg commit_id "$(git rev-parse HEAD)" \
295
+ --rawfile finding_body_1 <tmp_finding_1.md> \
296
+ --arg path_1 "<file_1>" \
297
+ --argjson line_1 <line_1> \
298
+ [... one finding_body_K / path_K / line_K triple per finding ...] \
299
+ '{
300
+ commit_id: $commit_id,
301
+ event: "COMMENT",
302
+ body: $review_body,
303
+ comments: [
304
+ {path: $path_1, line: $line_1, side: "RIGHT", body: $finding_body_1}
305
+ [, ... ]
306
+ ]
307
+ }' \
181
308
  | gh api repos/<owner>/<repo>/pulls/<number>/reviews -X POST --input -
182
309
  ```
183
310
 
184
- **Fix reply:** `jq -Rs '{body: .}' <tmp_reply.md | gh api repos/<owner>/<repo>/pulls/<number>/comments/<finding_comment_id>/replies -X POST --input -`
311
+ **Fix reply:** `jq -Rs '{body: .}' <tmp_reply.md | gh api
312
+ repos/<owner>/<repo>/pulls/<number>/comments/<finding_comment_id>/replies -X
313
+ POST --input -`
185
314
 
186
- **Review POST fails:** issue comment fallback: `jq -Rs '{body: .}' <tmp_fallback.md | gh api repos/<owner>/<repo>/issues/<number>/comments -X POST --input -`
315
+ **Review POST fails:** issue comment fallback: `jq -Rs '{body: .}'
316
+ <tmp_fallback.md | gh api repos/<owner>/<repo>/issues/<number>/comments -X POST
317
+ --input -`
187
318
 
188
- `<head_sha_at_post_time>`: `git rev-parse HEAD` in teammate cwd immediately before POST.
319
+ `<head_sha_at_post_time>`: `git rev-parse HEAD` in teammate cwd immediately
320
+ before POST.
189
321
 
190
322
  **Review body template (`<tmp_review_body.md>`):**
191
323
 
@@ -197,55 +329,105 @@ jq -n \
197
329
  - **[severity] title** — <file>:<line> — <one-line description>
198
330
  ```
199
331
 
200
- **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.
332
+ **Anchor fallback:** lines not in diff → omit from `comments[]`, list under
333
+ `### Findings without a diff anchor`; outcome `used_fallback="true"`, empty
334
+ `finding_comment_id`, `finding_comment_url` = parent review URL.
201
335
 
202
- **POST failure fallback:** one issue comment with full text; all findings `used_fallback="true"`, URLs = issue comment.
336
+ **POST failure fallback:** one issue comment with full text; all findings
337
+ `used_fallback="true"`, URLs = issue comment.
203
338
 
204
- **Endpoints:** `POST .../pulls/{pull}/reviews`; `POST .../pulls/{pull}/comments/{id}/replies`; fallback `POST .../issues/{issue}/comments` (`issue` = PR number).
339
+ **Endpoints:** `POST .../pulls/{pull}/reviews`; `POST
340
+ .../pulls/{pull}/comments/{id}/replies`; fallback `POST
341
+ .../issues/{issue}/comments` (`issue` = PR number).
205
342
 
206
343
  ### Step 3: The cycle
207
344
 
208
- Run the AUDIT-FIX cycle for each PR in all_prs, reusing the same team across PRs. The 10-loop cap applies per PR. Exit reasons (converged, cap reached, stuck, error) are tracked per PR; the final report lists one outcome line per PR.
345
+ Run the AUDIT-FIX cycle for each PR in all_prs, reusing the same team across
346
+ PRs. The 10-loop cap applies per PR. Exit reasons (converged, cap reached,
347
+ stuck, error) are tracked per PR; the final report lists one outcome line per
348
+ PR.
209
349
 
210
- **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.
350
+ **Gate:** `validate_content` / `hooks/blocking/code_rules_enforcer.py` on
351
+ PR-scoped files before every AUDIT
352
+ (`_shared/pr-loop/scripts/code_rules_gate.py`). Lead runs gate; clean-coder
353
+ clears failures; then bugfind audits.
211
354
 
212
- **Pre-cycle: walk prior bugteam reviews end-first** (once per PR, after Step 2 and before iteration begins, when `last_action == "fresh"`). A re-invocation of `/bugteam` on a PR with prior loops detects whether the most recent loop already cleaned this HEAD (short-circuit) and otherwise records that prior loops were dirty so the AUDIT runs against the latest diff with that signal in mind:
355
+ **Pre-cycle: walk prior bugteam reviews end-first** (once per PR, after Step 2
356
+ and before iteration begins, when `last_action == "fresh"`). A re-invocation of
357
+ `/bugteam` on a PR with prior loops detects whether the most recent loop already
358
+ cleaned this HEAD (short-circuit) and otherwise records that prior loops were
359
+ dirty so the AUDIT runs against the latest diff with that signal in mind:
213
360
 
214
361
  ```bash
215
362
  dirty_review_count=0
216
363
  gh api "repos/<owner>/<repo>/pulls/<number>/reviews" \
217
- --jq '[.[] | select(.body | startswith("## /bugteam loop "))] | sort_by(.submitted_at) | reverse'
364
+ --jq '[.[] | select(.body | startswith("## /bugteam loop "))] |
365
+ sort_by(.submitted_at) | reverse'
218
366
  ```
219
367
 
220
368
  Iterate from index 0 (most recent) toward older entries:
221
369
 
222
- - A bugteam review body that ends with `→ clean` is **clean**; any other `## /bugteam loop ...` body is **dirty**.
223
- - For a dirty review, increment `dirty_review_count` by one. The review's specific finding bodies are not carried forward — bugteam's AUDIT regenerates findings against the current HEAD's diff each loop, so prior bodies are stale by definition. The count alone is the carried signal.
224
- - Stop at the first clean review. Older reviews are presumed addressed at that clean checkpoint and are not re-read.
225
- - When index 0 is itself clean AND its `commit_id` matches `git rev-parse HEAD`, the PR is already converged on this HEAD set `last_action="audited"`, `last_findings='{"total": 0}'`, fall through to step 1's `converged` exit, skip Step 3 iteration entirely.
226
- - When `dirty_review_count > 0`, log the count and proceed into the normal iteration; the next AUDIT regenerates anchored findings against the current HEAD so `loop_comment_index` stays correct. Unlike `pr-converge` — where Cursor Bugbot's prior dirty-review *bodies* are read back by the Fix protocol because each dirty body lists specific findings the loop must still address — bugteam's per-loop bodies are anchored to the diff at *that loop's* HEAD, so re-applying them against a newer diff would be incorrect. The count is sufficient signal that "prior loops did not converge here."
370
+ - A bugteam review body that ends with `→ clean` is **clean**; any other `##
371
+ /bugteam loop ...` body is **dirty**.
372
+ - For a dirty review, increment `dirty_review_count` by one. The review's
373
+ specific finding bodies are not carried forward
374
+ bugteam's AUDIT regenerates
375
+ findings against the current HEAD's diff each loop, so prior bodies are stale
376
+ by definition. The count alone is the carried signal.
377
+ - Stop at the first clean review. Older reviews are presumed addressed at that
378
+ clean checkpoint and are not re-read.
379
+ - When index 0 is itself clean AND its `commit_id` matches `git rev-parse HEAD`,
380
+ the PR is already converged on this HEAD — set `last_action="audited"`,
381
+ `last_findings='{"total": 0}'`, fall through to step 1's `converged` exit,
382
+ skip Step 3 iteration entirely.
383
+ - When `dirty_review_count > 0`, log the count and proceed into the normal
384
+ iteration; the next AUDIT regenerates anchored findings against the current
385
+ HEAD so `loop_comment_index` stays correct. Unlike `pr-converge` — where
386
+ Cursor Bugbot's prior dirty-review *bodies* are read back by the Fix protocol
387
+ because each dirty body lists specific findings the loop must still address
388
+
389
+ bugteam's per-loop bodies are anchored to the diff at *that loop's* HEAD, so
390
+ re-applying them against a newer diff would be incorrect. The count is
391
+ sufficient signal that "prior loops did not converge here."
227
392
 
228
393
  1. From `last_action` / `last_findings`:
229
- - `last_action == "audited"` and `last_findings.total == 0` → exit `converged`
230
- - `last_action == "fixed"` and `git rev-parse HEAD` unchanged since pre-FIX → exit `stuck`
394
+ - `last_action == "audited"` and `last_findings.total == 0` → exit
395
+ `converged`
396
+ - `last_action == "fixed"` and `git rev-parse HEAD` unchanged since
397
+ pre-FIX → exit `stuck`
231
398
  - `last_action in {"fresh", "fixed"}` → **pre-audit** then **AUDIT**
232
399
  - `last_action == "audited"` and `last_findings.total > 0` → **FIX**
233
400
 
234
401
  2. **Pre-audit** (only when the next step is AUDIT):
235
402
 
236
403
  ```bash
237
- python "${CLAUDE_SKILL_DIR}/scripts/bugteam_code_rules_gate.py" --base origin/<baseRefName>
404
+ python \
405
+ "${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/code_rules_gate.py" \
406
+ --base origin/<baseRefName>
238
407
  ```
239
408
 
240
- 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: ...`.
409
+ Lead only; merge-base / diff semantics:
410
+ [`../../_shared/pr-loop/code-rules-gate.md`][path-code-rules]; shared script
411
+ inventory: [`../../_shared/pr-loop/scripts/README.md`][path-scripts-readme].
412
+ Non-zero → spawn **clean-coder** standards-fix (read stderr, edit, re-run
413
+ **this same** command, one commit, `git push`, shutdown) until exit **0** or
414
+ **5**
415
+ failed gate rounds → `error: code rules gate failed pre-audit`. After **0**:
416
+ `loop_count += 1`; if `loop_count > 10` → `cap reached`. Then **AUDIT**
417
+ (bugfind); print `Loop <N> audit: ...`.
241
418
 
242
- 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.
419
+ 3. **FIX** (`last_action == "audited"` and `last_findings.total > 0`):
420
+ `loop_count += 1`; if `loop_count > 10` → `cap reached`; **FIX** (bugfix);
421
+ print `Loop <N> fix: ...`; `last_action = "fixed"`, update `audit_log`; loop
422
+ to step 1.
243
423
 
244
- 4. After **AUDIT**: update `last_action`, `last_findings`, `audit_log`; print audit line if not already printed.
424
+ 4. After **AUDIT**: update `last_action`, `last_findings`, `audit_log`; print
425
+ audit line if not already printed.
245
426
 
246
427
  5. Loop.
247
428
 
248
- First pass: pre-audit → AUDIT. After a FIX, the next pass runs pre-audit again before the next AUDIT.
429
+ First pass: pre-audit → AUDIT. After a FIX, the next pass runs pre-audit again
430
+ before the next AUDIT.
249
431
 
250
432
  ### AUDIT action
251
433
 
@@ -254,75 +436,95 @@ mkdir -p "<team_temp_dir>/pr-<N>"
254
436
  gh pr diff <N> -R <owner>/<repo> > "<team_temp_dir>/pr-<N>/loop-<L>.patch"
255
437
  ```
256
438
 
257
- ```
258
- Agent(
259
- subagent_type="code-quality-agent",
260
- name="bugfind-pr<N>-loop<L>",
261
- team_name="<team_name>",
262
- model="opus",
263
- description="Bugfind audit PR <N> loop <L>",
264
- prompt="<audit XML; see PROMPTS.md>"
265
- )
266
- ```
439
+ **Spawn and shutdown:** Path A —
440
+ [`reference/workflow-path-a-orchestrated-teams.md`][path-a] § AUDIT. Path B —
441
+ [`reference/workflow-path-b-task-harness.md`][path-b] § AUDIT. Same
442
+ `prompt="<audit XML; see PROMPTS.md>"` and outcome files.
267
443
 
268
- 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-pr<N>-loop<L>.outcomes.xml`, fills `loop_comment_index`.
269
-
270
- **Shutdown:** If `Agent` returned and the teammate already ended, skip. Otherwise:
271
-
272
- ```
273
- SendMessage(
274
- to="bugfind-pr<N>-loop<L>",
275
- message={"type": "shutdown_request", "reason": "audit PR <N> loop <L> complete; outcome XML captured"}
276
- )
277
- ```
278
-
279
- `approve: false` → `error: bugfind teammate refused shutdown` → Step 4 then 5.
444
+ Fresh spawn each loop; Path A teammate context excludes lead history
445
+ ([`sources.md`](sources.md) § Teammate context isolation). Path B: fresh Task
446
+ per loop for the same clean-room intent. [`PROMPTS.md`](PROMPTS.md): XML +
447
+ outcome schema. Lead reads `.bugteam-pr<N>-loop<L>.outcomes.xml`, fills
448
+ `loop_comment_index`.
280
449
 
281
450
  `last_action = "audited"`; append audit line to `audit_log`.
282
451
 
283
- **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-pr<N>-loop<L>.outcomes.xml` plus `<team_temp_dir>/pr-<N>/loop-<L>-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`.
452
+ **Parallel auditors (`loop_count >= 4`):** gate passes immediately before;
453
+ after three full audit/fix rounds without convergence, issue three spawns in
454
+ one assistant message (parallel): Path A — three `Agent` calls; Path B —
455
+ three `Task` calls — full rules in the workflow files § parallel auditors.
456
+ `-a` posts
457
+ the review and merges outcomes from `-b`/`-c` (read
458
+ `.bugteam-pr<N>-loop<L>.outcomes.xml` plus
459
+ `<team_temp_dir>/pr-<N>/loop-<L>-b.outcomes.xml` and `...-c...`); merge key
460
+ `(file, line, category_letter)`; re-id `loopN-K`. `-b`/`-c` write sibling XML
461
+ only; prompts must pass literal absolute sibling paths. Shutdown order: Path A
462
+ workflow § parallel auditors; Path B: await all three Tasks.
284
463
 
285
464
  ### FIX action
286
465
 
287
- ```
288
- Agent(
289
- subagent_type="clean-coder",
290
- name="bugfix-pr<N>-loop<L>",
291
- team_name="<team_name>",
292
- model="opus",
293
- description="Bugfix PR <N> loop <L>",
294
- prompt="<fix XML; see PROMPTS.md>"
295
- )
296
- ```
297
-
298
- Pass finding comment URLs/ids from `loop_comment_index` in XML. Replies: `Fixed in <sha>` or `Could not address this loop: <reason>`.
466
+ **Spawn and shutdown:** Path A —
467
+ [`reference/workflow-path-a-orchestrated-teams.md`][path-a] § FIX. Path B —
468
+ [`reference/workflow-path-b-task-harness.md`][path-b] § FIX.
299
469
 
300
- **Shutdown:** same as bugfind; else `SendMessage(to="bugfix-pr<N>-loop<L>", message={"type": "shutdown_request", "reason": "fix PR <N> loop <L> complete; commit <sha7> pushed"})`. `approve: false` `error: bugfix teammate refused shutdown` → Step 4 then 5.
470
+ Pass finding comment URLs/ids from `loop_comment_index` in XML. Replies: `Fixed
471
+ in <sha>` or `Could not address this loop: <reason>`.
301
472
 
302
- [`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`.
473
+ [`PROMPTS.md`](PROMPTS.md): fix XML + schema. Verify: `git rev-parse HEAD`
474
+ advanced; `git fetch origin <branch> && git rev-parse origin/<branch>` matches
475
+ `HEAD`. Unchanged HEAD →
476
+ `stuck — bugfix teammate could not address findings`.
303
477
 
304
478
  ### Step 4: Teardown
305
479
 
306
- 1. For each live teammate: `SendMessage(to="<name>", message={"type": "shutdown_request", "reason": "bugteam cycle ending"})`. `approve: false` on cleanup → log and continue.
307
-
308
- 2. `TeamDelete()`
309
-
310
- 3. Windows-safe teardown `ignore_errors=True` silently swallows ReadOnly-attribute failures on Windows (see `~/.claude/rules/windows-filesystem-safe.md`). Use the inline `force_rmtree` helper:
311
-
312
- ```bash
313
- python -c "import os, shutil, stat, sys; \
314
- h = lambda f, p, *_: (os.chmod(p, stat.S_IWRITE), f(p)); \
315
- shutil.rmtree(r'<team_temp_dir>', **({'onexc': h} if sys.version_info >= (3, 12) else {'onerror': h}))"
316
- ```
480
+ 1. For each PR in `all_prs`: `git worktree remove
481
+ "<team_temp_dir>/pr-<N>/worktree"` (from Step 1) before tearing down the team
482
+ harness — tolerate already-removed worktrees.
483
+
484
+ 2. Path-specific harness
485
+ [`reference/workflow-path-a-orchestrated-teams.md`][path-a] § Step 4
486
+ (teammate `SendMessage`, `TeamDelete` **only when `team_owned=true`**) or
487
+ [`reference/workflow-path-b-task-harness.md`][path-b] § Step 4 (omit those).
488
+
489
+ 3. **Windows-safe `rmtree` gated by `team_owned` from [Team
490
+ lifecycle](#team-lifecycle-path-a-only).** The Windows-safe handler strips
491
+ the Windows ReadOnly attribute and retries the failing syscall (see
492
+ `~/.claude/rules/windows-filesystem-safe.md`).
493
+
494
+ - `team_owned=true` → remove the full `<team_temp_dir>`:
495
+
496
+ ```bash
497
+ python -c "import os, shutil, stat, sys; \
498
+ h = lambda f, p, *_: (os.chmod(p, stat.S_IWRITE), f(p)); \
499
+ shutil.rmtree(r'<team_temp_dir>', **({'onexc': h} if sys.version_info >= (3, 12)
500
+ else {'onerror': h}))"
501
+ ```
502
+
503
+ - `team_owned=false` (attach mode) → for each PR in `all_prs`, remove only
504
+ that PR's `<team_temp_dir>/pr-<N>/` subfolder. The orchestrator-owned
505
+ parent `<team_temp_dir>` survives so the next attached invocation can write
506
+ its own per-PR subfolders without colliding.
507
+
508
+ ```bash
509
+ python -c "import os, shutil, stat, sys; \
510
+ h = lambda f, p, *_: (os.chmod(p, stat.S_IWRITE), f(p)); \
511
+ shutil.rmtree(r'<team_temp_dir>/pr-<N>', **({'onexc': h} if sys.version_info >=
512
+ (3, 12) else {'onerror': h}))"
513
+ ```
317
514
 
318
515
  ### Step 4.5: PR description
319
516
 
320
- 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`.
517
+ Lead only; cumulative product narrative (not process). Delegate body to
518
+ `pr-description-writer` via `Agent` (else `general-purpose`) so the
519
+ mandatory-pr-description hook accepts `gh pr edit`.
321
520
 
322
521
  1. `gh pr diff <number> -R <owner>/<repo> > .bugteam-final.diff`
323
- 2. `gh pr view <number> -R <owner>/<repo> --json body --jq .body > .bugteam-original-body.md`
324
- 3. Agent brief: paths + branch names; describe merge-ready change from diff; keep curated original sections intact; return markdown body.
325
- 4. Write `.bugteam-final-body.md`; `gh pr edit <number> -R <owner>/<repo> --body-file .bugteam-final-body.md`
522
+ 2. `gh pr view <number> -R <owner>/<repo> --json body --jq .body >
523
+ .bugteam-original-body.md`
524
+ 3. Agent brief: paths + branch names; describe merge-ready change from diff;
525
+ keep curated original sections intact; return markdown body.
526
+ 4. Write `.bugteam-final-body.md`; `gh pr edit <number> -R <owner>/<repo>
527
+ --body-file .bugteam-final-body.md`
326
528
  5. Delete the three temp files.
327
529
 
328
530
  On failure: log in final report; continue to Step 5.
@@ -330,7 +532,9 @@ On failure: log in final report; continue to Step 5.
330
532
  ### Step 5: Revoke permissions (always)
331
533
 
332
534
  ```bash
333
- python "${CLAUDE_SKILL_DIR}/scripts/revoke_project_claude_permissions.py"
535
+ python
536
+ "${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/revoke_project_claude_permi \
537
+ ssions.py"
334
538
  ```
335
539
 
336
540
  Removes Step 0 grant — run even if Step 4 partially failed (log separately).
@@ -345,11 +549,12 @@ Final commit: <current_HEAD_sha7>
345
549
  Net change: <total_files> files, +<total_add>/-<total_del>
346
550
 
347
551
  Loop log:
348
- 1 audit: 3P0 2P1 0P2
349
- ...
552
+ 1 audit: 3P0 2P1 0P2
553
+ ...
350
554
  ```
351
555
 
352
- `cap reached` → suggest `/findbugs`. `stuck` → which findings. `error` → detail + loop.
556
+ `cap reached` → suggest `/findbugs`. `stuck` → which findings. `error` →
557
+ detail + loop.
353
558
 
354
559
  ## Constraints
355
560
 
@@ -366,3 +571,8 @@ See [`reference/README.md`](reference/README.md).
366
571
  ## Sources
367
572
 
368
573
  See [`sources.md`](sources.md).
574
+
575
+ [path-a]: reference/workflow-path-a-orchestrated-teams.md
576
+ [path-b]: reference/workflow-path-b-task-harness.md
577
+ [path-code-rules]: ../../_shared/pr-loop/code-rules-gate.md
578
+ [path-scripts-readme]: ../../_shared/pr-loop/scripts/README.md