claude-dev-env 1.36.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.
- package/package.json +1 -1
- package/skills/bugteam/SKILL.md +332 -108
- package/skills/bugteam/scripts/reflow_skill_md.py +298 -0
- package/skills/bugteam/test_team_lifecycle.py +9 -0
- package/skills/pr-converge/SKILL.md +1005 -395
- package/skills/pr-converge/scripts/reflow_skill_md.py +288 -0
- package/skills/pr-converge/test_team_lifecycle.py +9 -0
package/skills/bugteam/SKILL.md
CHANGED
|
@@ -3,50 +3,111 @@ name: bugteam
|
|
|
3
3
|
description: >-
|
|
4
4
|
Open pull request audit–fix until convergence: CODE_RULES gate, clean-room
|
|
5
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**
|
|
7
|
-
|
|
8
|
-
**Path B** otherwise (Task harness — workflow in
|
|
9
|
-
**This `SKILL.md` holds only
|
|
10
|
-
|
|
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'.
|
|
11
13
|
---
|
|
12
14
|
|
|
13
15
|
# Bugteam
|
|
14
16
|
|
|
15
|
-
**Core principle:** Audit–fix until convergence. **Bugfind:**
|
|
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.
|
|
16
20
|
|
|
17
|
-
**Path routing** picks **Path A** (orchestrated teams) vs **Path B** (Task
|
|
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).
|
|
18
27
|
|
|
19
28
|
## Path routing (mandatory first branch)
|
|
20
29
|
|
|
21
|
-
At `/bugteam` entry, evaluate **once** (same rule as pr-converge §Team
|
|
30
|
+
At `/bugteam` entry, evaluate **once** (same rule as pr-converge §Team
|
|
31
|
+
infrastructure detection):
|
|
22
32
|
|
|
23
|
-
- **Path A** — `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` set and equals **`1`**
|
|
24
|
-
|
|
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).
|
|
25
40
|
|
|
26
|
-
Shared material is **everything else in this file** plus
|
|
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.
|
|
27
46
|
|
|
28
47
|
## Team lifecycle (Path A only)
|
|
29
48
|
|
|
30
|
-
The `TeamCreate` / `TeamDelete` pair has historically been bound to a single
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
**`
|
|
39
|
-
|
|
40
|
-
**
|
|
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.
|
|
41
98
|
|
|
42
99
|
## Contents
|
|
43
100
|
|
|
44
|
-
Orchestration lives here; companion files hold prompts, invariants, examples,
|
|
101
|
+
Orchestration lives here; companion files hold prompts, invariants, examples,
|
|
102
|
+
citations, and domain reference notes. Scan this list before a partial read.
|
|
45
103
|
|
|
46
104
|
- [Path routing](#path-routing-mandatory-first-branch) — Path A vs Path B
|
|
47
|
-
- [Team lifecycle](#team-lifecycle-path-a-only) — `owned` / `attach` / `auto`
|
|
48
|
-
|
|
49
|
-
- [`reference/workflow-path-
|
|
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)
|
|
50
111
|
- When this skill applies — refusal cases and trigger conditions
|
|
51
112
|
- Utility scripts — pre-flight (`scripts/`, executed not inlined)
|
|
52
113
|
- Pre-audit gate — `validate_content` before each AUDIT
|
|
@@ -64,7 +125,8 @@ Orchestration lives here; companion files hold prompts, invariants, examples, ci
|
|
|
64
125
|
- [`EXAMPLES.md`](EXAMPLES.md) — exit scenarios
|
|
65
126
|
- [`CONSTRAINTS.md`](CONSTRAINTS.md) — invariants and design rationale
|
|
66
127
|
- [`sources.md`](sources.md) — doc URLs and verbatim quotes
|
|
67
|
-
- [`reference/README.md`](reference/README.md) — expanded prose by topic
|
|
128
|
+
- [`reference/README.md`](reference/README.md) — expanded prose by topic
|
|
129
|
+
(design, team setup, GitHub reviews, cycle, teardown)
|
|
68
130
|
|
|
69
131
|
## When this skill applies
|
|
70
132
|
|
|
@@ -73,13 +135,24 @@ Orchestration lives here; companion files hold prompts, invariants, examples, ci
|
|
|
73
135
|
Refusals — first match wins; respond with the quoted line exactly and stop:
|
|
74
136
|
|
|
75
137
|
- **No PR or upstream diff.** `No PR or upstream diff. /bugteam needs a target.`
|
|
76
|
-
- **Dirty tree.** `Uncommitted changes detected. Stash, commit, or revert before
|
|
77
|
-
|
|
78
|
-
- **
|
|
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`.
|
|
79
149
|
|
|
80
150
|
## Utility scripts
|
|
81
151
|
|
|
82
|
-
Shell-heavy steps live under
|
|
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).
|
|
83
156
|
|
|
84
157
|
### Pre-flight (before Step 0)
|
|
85
158
|
|
|
@@ -87,15 +160,27 @@ Shell-heavy steps live under [`_shared/pr-loop/scripts/`](../../_shared/pr-loop/
|
|
|
87
160
|
python "${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/preflight.py"
|
|
88
161
|
```
|
|
89
162
|
|
|
90
|
-
Non-zero → fix before grant. `BUGTEAM_PREFLIGHT_SKIP=1` emergency only.
|
|
163
|
+
Non-zero → fix before grant. `BUGTEAM_PREFLIGHT_SKIP=1` emergency only.
|
|
164
|
+
`--pre-commit` if `.pre-commit-config.yaml` exists.
|
|
91
165
|
|
|
92
|
-
**Auto-remediation for `core.hooksPath`:** when preflight fails with stderr
|
|
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:
|
|
93
171
|
|
|
94
172
|
```bash
|
|
95
173
|
python "${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/fix_hookspath.py"
|
|
96
174
|
```
|
|
97
175
|
|
|
98
|
-
The fix script removes any non-canonical local-scope override on the active
|
|
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.
|
|
99
184
|
|
|
100
185
|
## The Process
|
|
101
186
|
|
|
@@ -115,14 +200,22 @@ The fix script removes any non-canonical local-scope override on the active repo
|
|
|
115
200
|
### Step 0: Grant project permissions (once, first)
|
|
116
201
|
|
|
117
202
|
```bash
|
|
118
|
-
python
|
|
203
|
+
python
|
|
204
|
+
"${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/grant_project_claude_permis \
|
|
205
|
+
sions.py"
|
|
119
206
|
```
|
|
120
207
|
|
|
121
|
-
`${CLAUDE_SKILL_DIR}` is host-substituted before the shell runs (unlike normal
|
|
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.
|
|
122
211
|
|
|
123
212
|
### Step 1: Resolve PR scope (once)
|
|
124
213
|
|
|
125
|
-
Accept one or more PR numbers from the invocation. For each PR, run `gh pr view
|
|
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.
|
|
126
219
|
|
|
127
220
|
Keep: owner/repo, branches, PR number, URL — for all loops.
|
|
128
221
|
|
|
@@ -134,13 +227,29 @@ For each PR in all_prs:
|
|
|
134
227
|
2. Run `git worktree add "<team_temp_dir>/pr-<N>/worktree" origin/<headRef>`.
|
|
135
228
|
3. Record the absolute worktree path alongside the PR's other fields.
|
|
136
229
|
|
|
137
|
-
Teammates or Task workers for a PR operate inside that PR's worktree. Step 4
|
|
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.
|
|
138
235
|
|
|
139
236
|
### Step 2: Path harness + loop state
|
|
140
237
|
|
|
141
|
-
Apply the path you chose in [Path
|
|
142
|
-
|
|
143
|
-
|
|
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`.
|
|
144
253
|
|
|
145
254
|
**Loop state (lead; not a single script):**
|
|
146
255
|
|
|
@@ -152,47 +261,63 @@ audit_log=""
|
|
|
152
261
|
starting_sha="$(git rev-parse HEAD)"
|
|
153
262
|
team_name="bugteam-pr-<number>-<YYYYMMDDHHMMSS>"
|
|
154
263
|
team_temp_dir="<absolute-path>/<team_name>"
|
|
155
|
-
team_owned="true"
|
|
264
|
+
team_owned="true" # set by Step 2 lifecycle resolution; see Team lifecycle table
|
|
156
265
|
loop_comment_index=""
|
|
157
266
|
```
|
|
158
267
|
|
|
159
|
-
**`loop_comment_index`:** reset each AUDIT start; filled during AUDIT; FIX
|
|
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}`.
|
|
160
271
|
|
|
161
272
|
### Step 2.5: PR comments (one review per loop)
|
|
162
273
|
|
|
163
|
-
**Who posts:** Path A vs Path B —
|
|
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.
|
|
164
278
|
|
|
165
|
-
Order: audit → buffer → validate anchors vs diff → single review POST.
|
|
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`.
|
|
166
282
|
|
|
167
|
-
**Payloads:** build JSON with `jq --rawfile` / `-Rs`, pipe to `gh api ...
|
|
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.
|
|
168
286
|
|
|
169
|
-
**Review POST** (one `comments[]` object per anchored finding; single-line
|
|
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"`):
|
|
170
290
|
|
|
171
291
|
```
|
|
172
292
|
jq -n \
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
+
}' \
|
|
188
308
|
| gh api repos/<owner>/<repo>/pulls/<number>/reviews -X POST --input -
|
|
189
309
|
```
|
|
190
310
|
|
|
191
|
-
**Fix reply:** `jq -Rs '{body: .}' <tmp_reply.md | gh api
|
|
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 -`
|
|
192
314
|
|
|
193
|
-
**Review POST fails:** issue comment fallback: `jq -Rs '{body: .}'
|
|
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 -`
|
|
194
318
|
|
|
195
|
-
`<head_sha_at_post_time>`: `git rev-parse HEAD` in teammate cwd immediately
|
|
319
|
+
`<head_sha_at_post_time>`: `git rev-parse HEAD` in teammate cwd immediately
|
|
320
|
+
before POST.
|
|
196
321
|
|
|
197
322
|
**Review body template (`<tmp_review_body.md>`):**
|
|
198
323
|
|
|
@@ -204,55 +329,105 @@ jq -n \
|
|
|
204
329
|
- **[severity] title** — <file>:<line> — <one-line description>
|
|
205
330
|
```
|
|
206
331
|
|
|
207
|
-
**Anchor fallback:** lines not in diff → omit from `comments[]`, list under
|
|
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.
|
|
208
335
|
|
|
209
|
-
**POST failure fallback:** one issue comment with full text; all findings
|
|
336
|
+
**POST failure fallback:** one issue comment with full text; all findings
|
|
337
|
+
`used_fallback="true"`, URLs = issue comment.
|
|
210
338
|
|
|
211
|
-
**Endpoints:** `POST .../pulls/{pull}/reviews`; `POST
|
|
339
|
+
**Endpoints:** `POST .../pulls/{pull}/reviews`; `POST
|
|
340
|
+
.../pulls/{pull}/comments/{id}/replies`; fallback `POST
|
|
341
|
+
.../issues/{issue}/comments` (`issue` = PR number).
|
|
212
342
|
|
|
213
343
|
### Step 3: The cycle
|
|
214
344
|
|
|
215
|
-
Run the AUDIT-FIX cycle for each PR in all_prs, reusing the same team across
|
|
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.
|
|
216
349
|
|
|
217
|
-
**Gate:** `validate_content` / `hooks/blocking/code_rules_enforcer.py` on
|
|
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.
|
|
218
354
|
|
|
219
|
-
**Pre-cycle: walk prior bugteam reviews end-first** (once per PR, after Step 2
|
|
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:
|
|
220
360
|
|
|
221
361
|
```bash
|
|
222
362
|
dirty_review_count=0
|
|
223
363
|
gh api "repos/<owner>/<repo>/pulls/<number>/reviews" \
|
|
224
|
-
|
|
364
|
+
--jq '[.[] | select(.body | startswith("## /bugteam loop "))] |
|
|
365
|
+
sort_by(.submitted_at) | reverse'
|
|
225
366
|
```
|
|
226
367
|
|
|
227
368
|
Iterate from index 0 (most recent) toward older entries:
|
|
228
369
|
|
|
229
|
-
- A bugteam review body that ends with `→ clean` is **clean**; any other `##
|
|
230
|
-
|
|
231
|
-
-
|
|
232
|
-
|
|
233
|
-
|
|
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."
|
|
234
392
|
|
|
235
393
|
1. From `last_action` / `last_findings`:
|
|
236
|
-
- `last_action == "audited"` and `last_findings.total == 0` → exit
|
|
237
|
-
|
|
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`
|
|
238
398
|
- `last_action in {"fresh", "fixed"}` → **pre-audit** then **AUDIT**
|
|
239
399
|
- `last_action == "audited"` and `last_findings.total > 0` → **FIX**
|
|
240
400
|
|
|
241
401
|
2. **Pre-audit** (only when the next step is AUDIT):
|
|
242
402
|
|
|
243
403
|
```bash
|
|
244
|
-
python
|
|
404
|
+
python \
|
|
405
|
+
"${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/code_rules_gate.py" \
|
|
406
|
+
--base origin/<baseRefName>
|
|
245
407
|
```
|
|
246
408
|
|
|
247
|
-
|
|
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: ...`.
|
|
248
418
|
|
|
249
|
-
3. **FIX** (`last_action == "audited"` and `last_findings.total > 0`):
|
|
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.
|
|
250
423
|
|
|
251
|
-
4. After **AUDIT**: update `last_action`, `last_findings`, `audit_log`; print
|
|
424
|
+
4. After **AUDIT**: update `last_action`, `last_findings`, `audit_log`; print
|
|
425
|
+
audit line if not already printed.
|
|
252
426
|
|
|
253
427
|
5. Loop.
|
|
254
428
|
|
|
255
|
-
First pass: pre-audit → AUDIT. After a FIX, the next pass runs pre-audit again
|
|
429
|
+
First pass: pre-audit → AUDIT. After a FIX, the next pass runs pre-audit again
|
|
430
|
+
before the next AUDIT.
|
|
256
431
|
|
|
257
432
|
### AUDIT action
|
|
258
433
|
|
|
@@ -261,54 +436,95 @@ mkdir -p "<team_temp_dir>/pr-<N>"
|
|
|
261
436
|
gh pr diff <N> -R <owner>/<repo> > "<team_temp_dir>/pr-<N>/loop-<L>.patch"
|
|
262
437
|
```
|
|
263
438
|
|
|
264
|
-
**Spawn and shutdown:** Path A —
|
|
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.
|
|
265
443
|
|
|
266
|
-
Fresh spawn each loop; Path A teammate context excludes lead history
|
|
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`.
|
|
267
449
|
|
|
268
450
|
`last_action = "audited"`; append audit line to `audit_log`.
|
|
269
451
|
|
|
270
|
-
**Parallel auditors (`loop_count >= 4`):** gate passes immediately before;
|
|
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.
|
|
271
463
|
|
|
272
464
|
### FIX action
|
|
273
465
|
|
|
274
|
-
**Spawn and shutdown:** Path A —
|
|
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.
|
|
275
469
|
|
|
276
|
-
Pass finding comment URLs/ids from `loop_comment_index` in XML. Replies: `Fixed
|
|
470
|
+
Pass finding comment URLs/ids from `loop_comment_index` in XML. Replies: `Fixed
|
|
471
|
+
in <sha>` or `Could not address this loop: <reason>`.
|
|
277
472
|
|
|
278
|
-
[`PROMPTS.md`](PROMPTS.md): fix XML + schema. Verify: `git rev-parse HEAD`
|
|
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`.
|
|
279
477
|
|
|
280
478
|
### Step 4: Teardown
|
|
281
479
|
|
|
282
|
-
1. For each PR in `all_prs`: `git worktree remove
|
|
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.
|
|
283
483
|
|
|
284
|
-
2. Path-specific harness —
|
|
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).
|
|
285
488
|
|
|
286
|
-
3. **Windows-safe `rmtree` — gated by `team_owned` from [Team
|
|
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`).
|
|
287
493
|
|
|
288
494
|
- `team_owned=true` → remove the full `<team_temp_dir>`:
|
|
289
495
|
|
|
290
496
|
```bash
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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}))"
|
|
294
501
|
```
|
|
295
502
|
|
|
296
|
-
- `team_owned=false` (attach mode) → for each PR in `all_prs`, remove only
|
|
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.
|
|
297
507
|
|
|
298
508
|
```bash
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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}))"
|
|
302
513
|
```
|
|
303
514
|
|
|
304
515
|
### Step 4.5: PR description
|
|
305
516
|
|
|
306
|
-
Lead only; cumulative product narrative (not process). Delegate body to
|
|
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`.
|
|
307
520
|
|
|
308
521
|
1. `gh pr diff <number> -R <owner>/<repo> > .bugteam-final.diff`
|
|
309
|
-
2. `gh pr view <number> -R <owner>/<repo> --json body --jq .body >
|
|
310
|
-
|
|
311
|
-
|
|
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`
|
|
312
528
|
5. Delete the three temp files.
|
|
313
529
|
|
|
314
530
|
On failure: log in final report; continue to Step 5.
|
|
@@ -316,7 +532,9 @@ On failure: log in final report; continue to Step 5.
|
|
|
316
532
|
### Step 5: Revoke permissions (always)
|
|
317
533
|
|
|
318
534
|
```bash
|
|
319
|
-
python
|
|
535
|
+
python
|
|
536
|
+
"${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/revoke_project_claude_permi \
|
|
537
|
+
ssions.py"
|
|
320
538
|
```
|
|
321
539
|
|
|
322
540
|
Removes Step 0 grant — run even if Step 4 partially failed (log separately).
|
|
@@ -331,11 +549,12 @@ Final commit: <current_HEAD_sha7>
|
|
|
331
549
|
Net change: <total_files> files, +<total_add>/-<total_del>
|
|
332
550
|
|
|
333
551
|
Loop log:
|
|
334
|
-
|
|
335
|
-
|
|
552
|
+
1 audit: 3P0 2P1 0P2
|
|
553
|
+
...
|
|
336
554
|
```
|
|
337
555
|
|
|
338
|
-
`cap reached` → suggest `/findbugs`. `stuck` → which findings. `error` →
|
|
556
|
+
`cap reached` → suggest `/findbugs`. `stuck` → which findings. `error` →
|
|
557
|
+
detail + loop.
|
|
339
558
|
|
|
340
559
|
## Constraints
|
|
341
560
|
|
|
@@ -352,3 +571,8 @@ See [`reference/README.md`](reference/README.md).
|
|
|
352
571
|
## Sources
|
|
353
572
|
|
|
354
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
|