claude-dev-env 1.36.2 → 1.37.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/_shared/pr-loop/scripts/config/preflight_constants.py +29 -8
- package/_shared/pr-loop/scripts/preflight.py +242 -20
- package/_shared/pr-loop/scripts/tests/test_preflight.py +362 -25
- package/_shared/pr-loop/scripts/tests/test_preflight_constants.py +9 -14
- package/hooks/blocking/code_rules_enforcer.py +269 -23
- package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +157 -1
- package/hooks/config/test_unused_module_import_constants.py +48 -0
- package/hooks/config/unused_module_import_constants.py +41 -0
- package/package.json +1 -1
- package/rules/gh-paginate.md +4 -50
- package/rules/no-historical-clutter.md +36 -0
- package/skills/bg-agent/SKILL.md +69 -0
- package/skills/bugteam/CONSTRAINTS.md +10 -19
- package/skills/bugteam/PROMPTS.md +21 -14
- package/skills/bugteam/SKILL.md +122 -208
- package/skills/bugteam/SKILL_EVALS.md +75 -114
- package/skills/bugteam/reference/README.md +2 -4
- package/skills/bugteam/reference/audit-and-teammates.md +21 -48
- package/skills/bugteam/reference/audit-contract.md +7 -7
- package/skills/bugteam/reference/design-rationale.md +3 -8
- package/skills/bugteam/reference/team-setup.md +11 -19
- package/skills/bugteam/reference/teardown-publish-permissions.md +2 -14
- package/skills/bugteam/scripts/config/__init__.py +0 -0
- package/skills/bugteam/scripts/config/reflow_skill_md_constants.py +12 -0
- package/skills/bugteam/scripts/reflow_skill_md.py +51 -47
- package/skills/bugteam/sources.md +1 -25
- package/skills/bugteam/test_skill_additions.py +4 -13
- package/skills/fresh-branch/SKILL.md +71 -0
- package/skills/gotcha/SKILL.md +73 -0
- package/skills/monitor-open-prs/SKILL.md +4 -37
- package/skills/monitor-open-prs/test_skill_contract.py +0 -5
- package/skills/pr-converge/SKILL.md +60 -1298
- package/skills/pr-converge/reference/convergence-gates.md +122 -0
- package/skills/pr-converge/reference/examples.md +76 -0
- package/skills/pr-converge/reference/fix-protocol.md +56 -0
- package/skills/pr-converge/reference/ground-rules.md +13 -0
- package/skills/pr-converge/reference/multi-pr-orchestration.md +204 -0
- package/skills/pr-converge/reference/per-tick.md +204 -0
- package/skills/pr-converge/reference/state-schema.md +19 -0
- package/skills/pr-converge/reference/stop-conditions.md +26 -0
- package/skills/pr-converge/scripts/README.md +36 -9
- package/skills/pr-converge/scripts/check_pr_mergeability.py +1 -2
- package/skills/pr-converge/scripts/config/pr_converge_constants.py +74 -5
- package/skills/pr-converge/scripts/config/reflow_skill_md_constants.py +13 -0
- package/skills/pr-converge/scripts/config/test_pr_converge_constants.py +0 -24
- package/skills/pr-converge/scripts/cursor-agents-continue.ahk +22 -2
- package/skills/pr-converge/scripts/fetch_bugbot_inline_comments.py +19 -59
- package/skills/pr-converge/scripts/fetch_bugbot_reviews.py +15 -61
- package/skills/pr-converge/scripts/fetch_claude_inline_comments.py +70 -0
- package/skills/pr-converge/scripts/fetch_claude_reviews.py +61 -0
- package/skills/pr-converge/scripts/fetch_copilot_inline_comments.py +19 -61
- package/skills/pr-converge/scripts/fetch_copilot_reviews.py +14 -74
- package/skills/pr-converge/scripts/reflow_skill_md.py +71 -50
- package/skills/pr-converge/scripts/reviewer_fetch_core.py +153 -0
- package/skills/pr-converge/scripts/reviewer_specs.py +98 -0
- package/skills/pr-converge/scripts/test_cursor_agents_continue.py +65 -0
- package/skills/pr-converge/scripts/test_fetch_bugbot_inline_comments.py +107 -6
- package/skills/pr-converge/scripts/test_fetch_bugbot_reviews.py +85 -6
- package/skills/pr-converge/scripts/test_fetch_claude_inline_comments.py +485 -0
- package/skills/pr-converge/scripts/test_fetch_claude_reviews.py +368 -0
- package/skills/pr-converge/scripts/test_fetch_copilot_inline_comments.py +74 -6
- package/skills/pr-converge/scripts/test_fetch_copilot_reviews.py +94 -8
- package/skills/pr-converge/scripts/test_reflow_skill_md.py +162 -0
- package/skills/pr-converge/scripts/test_reviewer_fetch_core.py +448 -0
- package/skills/pr-converge/scripts/test_reviewer_specs.py +107 -0
- package/skills/pr-converge/scripts/test_view_pr_context.py +44 -0
- package/skills/pr-converge/scripts/view_pr_context.py +35 -4
- package/skills/pr-converge/workflows/schedule-wakeup-loop.md +24 -22
- package/skills/bugteam/reference/workflow-path-a-orchestrated-teams.md +0 -113
- package/skills/bugteam/reference/workflow-path-b-task-harness.md +0 -48
- package/skills/bugteam/test_team_lifecycle.py +0 -103
- package/skills/monitor-open-prs/test_team_lifecycle.py +0 -46
- package/skills/pr-converge/scripts/open_followup_copilot_pr.py +0 -136
- package/skills/pr-converge/scripts/test_open_followup_copilot_pr.py +0 -236
- package/skills/pr-converge/test_team_lifecycle.py +0 -56
- package/skills/pr-converge/workflows/ahk-auto-continue-loop.md +0 -108
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Convergence gates
|
|
2
|
+
|
|
3
|
+
Run **only** when Step 2 BUGTEAM reports `convergence (zero findings)` AND
|
|
4
|
+
`bugbot_clean_at == current_head` AND no push during bugteam tick. Gates run
|
|
5
|
+
in order; first failure determines next-tick behavior. Mark PR ready only
|
|
6
|
+
when all four pass.
|
|
7
|
+
|
|
8
|
+
## (a) Copilot findings gate
|
|
9
|
+
|
|
10
|
+
Fetch latest Copilot reviewer (`copilot-pull-request-reviewer[bot]`) review
|
|
11
|
+
plus inline comments anchored to most recent Copilot review on
|
|
12
|
+
`current_head`:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
python "${CLAUDE_SKILL_DIR}/scripts/fetch_copilot_reviews.py" \
|
|
16
|
+
--owner <OWNER> --repo <REPO> --number <NUMBER>
|
|
17
|
+
|
|
18
|
+
python "${CLAUDE_SKILL_DIR}/scripts/fetch_copilot_inline_comments.py" \
|
|
19
|
+
--owner <OWNER> --repo <REPO> --number <NUMBER> --commit "$current_head"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Decide (four branches; match first whose predicate holds):
|
|
23
|
+
|
|
24
|
+
- **`classification == "dirty"` with non-empty inline comments matching
|
|
25
|
+
`pull_request_review_id`:** Fix protocol input (same shape as bugbot
|
|
26
|
+
dirty). Spawn Agent (subagent_type: clean-coder) to implement → push → reply inline on each thread via
|
|
27
|
+
`reply_to_inline_comment.py` → Step 3 in same tick (see
|
|
28
|
+
[Single-PR fix workflow](fix-protocol.md#single-pr-fix-workflow) for
|
|
29
|
+
full contract).
|
|
30
|
+
Reset `bugbot_clean_at = null` AND `copilot_clean_at = null`, `phase =
|
|
31
|
+
BUGBOT`, schedule next wakeup, return. Full back-to-back-clean cycle
|
|
32
|
+
plus all four gates must hold again on new HEAD.
|
|
33
|
+
- **`classification == "dirty"` with empty inline comments matching
|
|
34
|
+
`pull_request_review_id`:** Copilot posted findings only in review body
|
|
35
|
+
(`CHANGES_REQUESTED` or `COMMENTED` with non-empty body, no inline
|
|
36
|
+
threads). Parse body for actionable findings. Spawn Agent (subagent_type: clean-coder) to implement → push → post
|
|
37
|
+
top-level review reply citing new HEAD SHA → Step 3 in same tick (see
|
|
38
|
+
[Single-PR fix workflow](fix-protocol.md#single-pr-fix-workflow) for
|
|
39
|
+
full contract).
|
|
40
|
+
Reset
|
|
41
|
+
`bugbot_clean_at = null` AND
|
|
42
|
+
`copilot_clean_at = null`, `phase = BUGBOT`, Step 3 on new HEAD,
|
|
43
|
+
schedule next wakeup, return. Convergence requires full
|
|
44
|
+
back-to-back-clean on new HEAD.
|
|
45
|
+
- **`classification == "clean"` (state `APPROVED`):** Set
|
|
46
|
+
`copilot_clean_at = current_head`. Continue to gate (b).
|
|
47
|
+
- **No Copilot review on `current_head` yet:** Skip — gate (c) issues
|
|
48
|
+
proactive request. Continue to gate (b).
|
|
49
|
+
|
|
50
|
+
## (b) Mergeability gate
|
|
51
|
+
|
|
52
|
+
Resolve PR's mergeability state:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
python "${CLAUDE_SKILL_DIR}/scripts/check_pr_mergeability.py" \
|
|
56
|
+
--owner <OWNER> --repo <REPO> --number <NUMBER>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Persist `mergeStateStatus` into `merge_state_status`. Decide:
|
|
60
|
+
|
|
61
|
+
- **`mergeStateStatus == "CLEAN"` AND `mergeable == "MERGEABLE"`:**
|
|
62
|
+
Continue to gate (c).
|
|
63
|
+
- **`mergeStateStatus == "DIRTY"` (or `mergeable == "CONFLICTING"`):** Do
|
|
64
|
+
**not** mark ready. Invoke **`rebase`** skill
|
|
65
|
+
([`../../rebase/SKILL.md`](../../rebase/SKILL.md)) Phase 1–4 against PR's
|
|
66
|
+
base ref. After rebase + force-with-lease push, new HEAD invalidates
|
|
67
|
+
every prior clean state — reset `bugbot_clean_at = null`,
|
|
68
|
+
`copilot_clean_at = null`, `merge_state_status = null`, `phase = BUGBOT`,
|
|
69
|
+
Step 3 on new HEAD, schedule next wakeup, return. Loop re-runs from
|
|
70
|
+
scratch on new HEAD.
|
|
71
|
+
- **`mergeStateStatus` is `BLOCKED`, `BEHIND`, or `UNKNOWN` for
|
|
72
|
+
non-conflict reasons** (required checks pending, branch behind base
|
|
73
|
+
without conflicts GitHub cannot auto-resolve): **hard blocker** per
|
|
74
|
+
[stop-conditions.md](stop-conditions.md) — do not invent a fix. Report specific
|
|
75
|
+
`mergeStateStatus`, omit loop pacing.
|
|
76
|
+
|
|
77
|
+
## (c) Post-convergence Copilot review request
|
|
78
|
+
|
|
79
|
+
Once gates (a) and (b) both pass (Copilot clean at `current_head` *or* no
|
|
80
|
+
Copilot review yet, AND `mergeStateStatus == "CLEAN"`), request Copilot
|
|
81
|
+
review:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
python "${CLAUDE_SKILL_DIR}/scripts/request_copilot_review.py" \
|
|
85
|
+
--owner <OWNER> --repo <REPO> --number <NUMBER>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
After request, schedule next wakeup and return — next tick checks response.
|
|
89
|
+
|
|
90
|
+
Next tick with `phase == BUGTEAM` and prior state preserved → re-run gate
|
|
91
|
+
(a) first. Decide:
|
|
92
|
+
|
|
93
|
+
- **Copilot review `clean` (state `APPROVED`):** Set `copilot_clean_at =
|
|
94
|
+
current_head`. Mark PR ready (`mark_pr_ready.py`), report convergence
|
|
95
|
+
per §(d), terminate per [stop-conditions.md](stop-conditions.md) / Convergence.
|
|
96
|
+
- **Copilot review `dirty`:** Treat identically to gate (a) dirty path —
|
|
97
|
+
spawn Agent (subagent_type: clean-coder) to fix in same PR, restart convergence from BUGBOT. Follow [Single-PR fix workflow](fix-protocol.md#single-pr-fix-workflow).
|
|
98
|
+
For body-only findings with empty inline, spawn Agent (subagent_type: clean-coder) to implement, then post top-level review reply
|
|
99
|
+
citing new HEAD SHA. Reset `bugbot_clean_at = null` AND
|
|
100
|
+
`copilot_clean_at = null`, `phase = BUGBOT`, schedule next wakeup,
|
|
101
|
+
return. Full back-to-back-clean cycle plus all four gates must hold
|
|
102
|
+
again on new HEAD.
|
|
103
|
+
- **No Copilot review at `current_head` yet (still propagating):**
|
|
104
|
+
Schedule one more wakeup (270s), re-check next tick. After three consecutive empty waits,
|
|
105
|
+
escalate as hard blocker per [stop-conditions.md](stop-conditions.md).
|
|
106
|
+
|
|
107
|
+
## (d) Mark ready and report
|
|
108
|
+
|
|
109
|
+
Only when all four gates pass — bugbot CLEAN ∧ bugteam CLEAN ∧
|
|
110
|
+
`mergeStateStatus == "CLEAN"` ∧ Copilot CLEAN at HEAD — run:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
python "${CLAUDE_SKILL_DIR}/scripts/mark_pr_ready.py" \
|
|
114
|
+
--owner <OWNER> --repo <REPO> --number <NUMBER>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
When scripts unavailable, `gh pr ready <NUMBER> --repo <OWNER>/<REPO>` is
|
|
118
|
+
equivalent. With `state.json`, append convergence row to
|
|
119
|
+
`<TMPDIR>/pr-converge-<session_id>/converged.log` per `multi-pr-orchestration.md` §Memory; else skip.
|
|
120
|
+
Report: `PR #<NUMBER> converged: bugbot CLEAN at <SHA>, bugteam CLEAN at
|
|
121
|
+
<SHA>, mergeStateStatus CLEAN, copilot CLEAN at <SHA>; marked ready for
|
|
122
|
+
review`. **Omit loop pacing** per **Convergence** of active pacing workflow.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
Worked examples for `pr-converge`. Read on demand when a tick's
|
|
4
|
+
classification is novel or ambiguous against the in-skill rules. Cross-refs
|
|
5
|
+
into `SKILL.md` use `§Section name` notation.
|
|
6
|
+
|
|
7
|
+
<example> User: `/pr-converge` Claude: [PR context + one tick bugbot/bugteam
|
|
8
|
+
work; Step 4 per `workflows/schedule-wakeup-loop.md` — default loop until
|
|
9
|
+
convergence or stop]
|
|
10
|
+
</example>
|
|
11
|
+
|
|
12
|
+
<example> BUGBOT tick, latest bugbot review against older commit. Claude:
|
|
13
|
+
[posts `bugbot run`, sets `bugbot_clean_at = null`, Step 4 per
|
|
14
|
+
`workflows/schedule-wakeup-loop.md` (e.g. 270s wakeup), returns]
|
|
15
|
+
</example>
|
|
16
|
+
|
|
17
|
+
<example> BUGBOT tick, bugbot has 2 unaddressed findings on HEAD. Claude:
|
|
18
|
+
[TDD-fixes both, one commit, pushes, replies inline on both threads, posts
|
|
19
|
+
`bugbot run`, Step 4 at 270s, returns]
|
|
20
|
+
</example>
|
|
21
|
+
|
|
22
|
+
<example> BUGBOT tick, bugbot clean against HEAD. Claude: [sets
|
|
23
|
+
`bugbot_clean_at = HEAD`, `phase = BUGTEAM`, runs `Skill({skill: "bugteam",
|
|
24
|
+
...})` in same tick]
|
|
25
|
+
</example>
|
|
26
|
+
|
|
27
|
+
<example> BUGTEAM phase, bugteam reports convergence and `bugbot_clean_at
|
|
28
|
+
== current_head`. Claude: [runs `gh pr ready <NUMBER>`, reports "PR
|
|
29
|
+
converged: bugbot CLEAN at <SHA>, bugteam CLEAN at <SHA>; marked ready for
|
|
30
|
+
review", applies **Convergence** from `workflows/schedule-wakeup-loop.md`]
|
|
31
|
+
</example>
|
|
32
|
+
|
|
33
|
+
<example> BUGTEAM phase, bugteam pushed fix commit during run. Claude:
|
|
34
|
+
[re-resolves HEAD, sets `bugbot_clean_at = null`, posts `bugbot run` in
|
|
35
|
+
same tick, `phase = BUGBOT`, Step 4 at 270s]
|
|
36
|
+
</example>
|
|
37
|
+
|
|
38
|
+
<example> BUGBOT tick, review body says "found 3 potential issues" against
|
|
39
|
+
HEAD but inline API returns zero matching for `current_head`. Claude:
|
|
40
|
+
[increments `inline_lag_streak` to 1, Step 4 inline-lag rules (90s
|
|
41
|
+
`ScheduleWakeup`), returns]
|
|
42
|
+
</example>
|
|
43
|
+
|
|
44
|
+
<example> Back-to-back clean reached, but `mergeStateStatus: DIRTY` (base
|
|
45
|
+
advanced, merge conflicts). Claude: [runs §Convergence gates (b); does NOT
|
|
46
|
+
mark ready; invokes `rebase` skill per `../../rebase/SKILL.md` Phase 1–4;
|
|
47
|
+
after force-with-lease push, resets `bugbot_clean_at = null`,
|
|
48
|
+
`copilot_clean_at = null`, `merge_state_status = null`, `phase = BUGBOT`,
|
|
49
|
+
posts `bugbot run` on new HEAD, schedules next wakeup]
|
|
50
|
+
</example>
|
|
51
|
+
|
|
52
|
+
<example> Back-to-back clean, mergeability CLEAN, Copilot review at
|
|
53
|
+
`current_head` `state == "CHANGES_REQUESTED"` with two unaddressed inline
|
|
54
|
+
findings. Claude: [runs §Convergence gates (a); applies Fix protocol (TDD
|
|
55
|
+
test → fix → push → reply inline both threads), resets `bugbot_clean_at`
|
|
56
|
+
and `copilot_clean_at` null, `phase = BUGBOT`, posts `bugbot run` on new
|
|
57
|
+
HEAD, schedules next wakeup]
|
|
58
|
+
</example>
|
|
59
|
+
|
|
60
|
+
<example> Back-to-back clean, mergeability CLEAN, no Copilot review on
|
|
61
|
+
`current_head`. Claude requests Copilot via `request_copilot_review.py`,
|
|
62
|
+
waits one tick. Next tick: Copilot review `state: APPROVED`. Claude: [sets
|
|
63
|
+
`copilot_clean_at = current_head`; runs `mark_pr_ready.py`; reports "PR
|
|
64
|
+
#N converged: bugbot CLEAN at <SHA>, bugteam CLEAN at <SHA>,
|
|
65
|
+
mergeStateStatus CLEAN, copilot CLEAN; marked ready for review"]
|
|
66
|
+
</example>
|
|
67
|
+
|
|
68
|
+
<example> Back-to-back clean, mergeability CLEAN, post-convergence Copilot
|
|
69
|
+
review returned `state: CHANGES_REQUESTED` with inline findings on
|
|
70
|
+
`current_head`. Claude: [does NOT mark PR ready — gate (4) failed;
|
|
71
|
+
applies Fix protocol on every confirmed Copilot finding (TDD test → fix →
|
|
72
|
+
push → reply inline on each thread); resets `bugbot_clean_at = null` and
|
|
73
|
+
`copilot_clean_at = null`; `phase = BUGBOT`; posts `bugbot run` on new
|
|
74
|
+
HEAD; schedules next wakeup. Full back-to-back-clean cycle plus all four
|
|
75
|
+
gates must hold again on new HEAD.]
|
|
76
|
+
</example>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Fix protocol
|
|
2
|
+
|
|
3
|
+
Single-PR (no `state.json`): production edits run in main session via
|
|
4
|
+
`Agent` (`subagent_type: "clean-coder"`). Multi-PR (`state.json`):
|
|
5
|
+
clean-coder teammate; orchestrator never edits inline. Hook handling
|
|
6
|
+
per [ground-rules.md](ground-rules.md).
|
|
7
|
+
|
|
8
|
+
**Multi-PR (`state.json`) teammate obligations** (plus TDD, commit, push):
|
|
9
|
+
|
|
10
|
+
- Replies inline on each addressed finding via
|
|
11
|
+
`reply_to_inline_comment.py` (what changed + commit identifier),
|
|
12
|
+
matching §Audit result → fix worker step 4 — **before** writing
|
|
13
|
+
`state.json` and going idle.
|
|
14
|
+
- Writes `last_action: "fix_pushed"`, `current_head: <new SHA>`,
|
|
15
|
+
`bugbot_clean_at: null`, `phase: "BUGBOT"`, `status: "awaiting_bugbot"`,
|
|
16
|
+
`last_updated` (ISO-8601 UTC) to `state.json` (per §Concurrency).
|
|
17
|
+
- Goes idle. Orchestrator spawns follow-up `general-purpose` agent for
|
|
18
|
+
bugbot trigger and monitoring.
|
|
19
|
+
|
|
20
|
+
Orchestrator does not reply inline, trigger bugbot, or read repo source
|
|
21
|
+
files during fix phase in multi-PR mode.
|
|
22
|
+
|
|
23
|
+
### Single-PR fix workflow
|
|
24
|
+
|
|
25
|
+
**Single-PR (no `state.json`) — same gates, main session executor:**
|
|
26
|
+
|
|
27
|
+
- Read each referenced file:line.
|
|
28
|
+
- Write failing test first when finding has behavior to test. Pure doc /
|
|
29
|
+
comment / naming nits with no behavior → straight to fix.
|
|
30
|
+
- **Implement** via `Agent` (`subagent_type: "clean-coder"`).
|
|
31
|
+
Full-stop if `Agent` is unavailable.
|
|
32
|
+
- Stage affected files and create one new commit on existing branch:
|
|
33
|
+
```bash
|
|
34
|
+
git add <files> && git commit -m "fix(review): <brief summary>"
|
|
35
|
+
```
|
|
36
|
+
**Pre-commit gate:** honor hooks; full-stop on bypass.
|
|
37
|
+
- Push the new commit:
|
|
38
|
+
```bash
|
|
39
|
+
git push origin <BRANCH>
|
|
40
|
+
```
|
|
41
|
+
**Pre-push gate:** honor hooks; full-stop on bypass. Capture new HEAD
|
|
42
|
+
only after both gates pass; set `current_head`, `bugbot_clean_at = null`.
|
|
43
|
+
- Reply inline on each addressed comment thread using `--body-file` (per
|
|
44
|
+
gh-body-file rule):
|
|
45
|
+
```bash
|
|
46
|
+
python "${CLAUDE_SKILL_DIR}/scripts/reply_to_inline_comment.py" \
|
|
47
|
+
--owner <OWNER> --repo <REPO> --number <NUMBER> \
|
|
48
|
+
--comment-id <COMMENT_ID> --body-file <path/to/reply.md>
|
|
49
|
+
```
|
|
50
|
+
- **After pushing a fix, always run Step 3 (`bugbot run`) in the same
|
|
51
|
+
tick** regardless of phase. New commit **resets full convergence cycle**:
|
|
52
|
+
prior bugbot clean and prior bugteam clean on older SHA do **not**
|
|
53
|
+
count toward convergence on new `HEAD`. Must re-obtain bugbot CLEAN on
|
|
54
|
+
`current_head`, then bugteam CLEAN on same `HEAD` with no
|
|
55
|
+
intervening push. Re-triggering in same tick saves a wakeup cycle vs
|
|
56
|
+
deferring Step 3.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Ground rules
|
|
2
|
+
|
|
3
|
+
- **Append commits.** Each tick adds at most one fix commit.
|
|
4
|
+
- **Bugbot findings on current SHA mean fix-then-push-then-`bugbot run`,
|
|
5
|
+
not another naked `bugbot run`.**
|
|
6
|
+
- **All `*_clean_at` and `merge_state_status` reset on every push.**
|
|
7
|
+
- **`bugbot run` comment is load-bearing.** Literal phrase exactly —
|
|
8
|
+
empirically the only re-trigger Cursor Bugbot recognizes.
|
|
9
|
+
- **Honor pre-push and pre-commit hooks.** Read output, fix the cause,
|
|
10
|
+
retry. Full-stop on bypass.
|
|
11
|
+
- **Adapt when reality contradicts on-disk state.** If `state.json`,
|
|
12
|
+
`git`, or `gh` disagree with live PR, escalate as hard blocker per
|
|
13
|
+
[stop-conditions.md](stop-conditions.md).
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Multi-PR orchestration model
|
|
2
|
+
|
|
3
|
+
Loaded by `pr-converge` only when `state.json` exists at
|
|
4
|
+
`<TMPDIR>/pr-converge-<session_id>/state.json`. Single-PR runs ignore.
|
|
5
|
+
|
|
6
|
+
## Core rule: orchestrator is traffic controller only
|
|
7
|
+
|
|
8
|
+
Orchestrator (main session) **never** reads repo source files, writes code,
|
|
9
|
+
audits findings, or does per-PR codebase work inline. Reads `state.json` for
|
|
10
|
+
traffic state, writes only narrow fields per §Orchestrator `state.json`
|
|
11
|
+
writes, receives teammate handoffs, spawns next worker. Every audit/fix unit
|
|
12
|
+
runs inside dedicated teammate.
|
|
13
|
+
|
|
14
|
+
[Workflow-style skill](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices#use-workflows-for-complex-tasks):
|
|
15
|
+
orchestrator splits multi-PR problem into parallel per-PR subworkflows
|
|
16
|
+
owned by short-lived teammates. Orchestrator job: keep state file
|
|
17
|
+
consistent, spawn next agent.
|
|
18
|
+
|
|
19
|
+
## Per-PR state file
|
|
20
|
+
|
|
21
|
+
Create once at session start. Each teammate writes result before going idle.
|
|
22
|
+
|
|
23
|
+
**Path:** `<TMPDIR>/pr-converge-<session_id>/state.json`. **Session ID:**
|
|
24
|
+
`YYYYMMDDHHMMSS` captured once when loop starts.
|
|
25
|
+
|
|
26
|
+
**Directory lifecycle:** Keep `<TMPDIR>/pr-converge-<session_id>/` until every
|
|
27
|
+
`prs[...]` is `converged` or `blocked`, or user stops. Then delete folder.
|
|
28
|
+
`mark_pr_ready.py` / `gh pr ready` on GitHub is canonical record. See
|
|
29
|
+
[Memory](#memory) for optional append-only log.
|
|
30
|
+
|
|
31
|
+
**Barebones schema:**
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"session_id": "20260502050000",
|
|
36
|
+
"prs": {
|
|
37
|
+
"289": {
|
|
38
|
+
"owner": "jl-cmd",
|
|
39
|
+
"repo": "claude-code-config",
|
|
40
|
+
"branch": "feat/shared-pr-loop-extraction",
|
|
41
|
+
"phase": "BUGBOT",
|
|
42
|
+
"current_head": "f9a7d49e",
|
|
43
|
+
"bugbot_clean_at": null,
|
|
44
|
+
"inline_lag_streak": 0,
|
|
45
|
+
"tick_count": 5,
|
|
46
|
+
"last_action": "bugbot_triggered",
|
|
47
|
+
"status": "in_progress",
|
|
48
|
+
"last_updated": "2026-05-02T10:00:00Z"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**`status` values:** `fresh` | `in_progress` | `awaiting_bugbot` |
|
|
55
|
+
`awaiting_bugteam` | `converged` | `blocked`
|
|
56
|
+
|
|
57
|
+
**Write rule:** Subagents read current file, merge **only** their PR's entry
|
|
58
|
+
under `prs`, write back. Writes keyed on `pr_number`; other PRs untouched.
|
|
59
|
+
|
|
60
|
+
**Concurrency (mandatory):** Naive read–modify–write loses updates when
|
|
61
|
+
multiple subagents finish in same wall-clock window (10+ idle notifications
|
|
62
|
+
together). Every subagent write **must** use serialized access plus atomic
|
|
63
|
+
publish:
|
|
64
|
+
|
|
65
|
+
1. **Acquire** exclusive lock at sibling path `state.json.lock` via atomic
|
|
66
|
+
create-only primitive (`mkdir` on Unix; on Windows `New-Item` / `md`
|
|
67
|
+
guarded so only one creator succeeds, or host file lock API). On
|
|
68
|
+
contention, sleep with jitter and retry. Cap retries and escalate per
|
|
69
|
+
**Stop conditions** if lock never clears (stuck subagent).
|
|
70
|
+
2. **Read** `state.json`, merge `prs[<pr_number>]` only, write full merged
|
|
71
|
+
JSON to `state.json.tmp`.
|
|
72
|
+
3. **Replace** `state.json` atomically from `state.json.tmp` (`os.replace` /
|
|
73
|
+
same-volume rename so readers never see half-written file).
|
|
74
|
+
4. **Release** lock (`rmdir` / `Remove-Item`).
|
|
75
|
+
|
|
76
|
+
**Orchestrator `state.json` writes (traffic metadata only):** Subagents
|
|
77
|
+
own audit/fix payloads. Orchestrator **must not** merge finding bodies,
|
|
78
|
+
file contents, or subagent-owned fields except two exceptions. Uses same
|
|
79
|
+
§Concurrency lock.
|
|
80
|
+
|
|
81
|
+
1. **Per-tick `tick_count` bump (mandatory):** At start of each tick, one
|
|
82
|
+
locked read–merge–publish: every `prs[<pr_number>]` whose `status` is
|
|
83
|
+
not `converged` or `blocked` → increment `tick_count` by 1 (init `0`),
|
|
84
|
+
refresh `last_updated`. Observability only — no ceiling; loop ends on
|
|
85
|
+
convergence or **Stop conditions**.
|
|
86
|
+
2. **`phase` when only orchestrator decides:** Orchestrator applies a
|
|
87
|
+
Step 2 phase transition (including BUGTEAM §(d) `phase = BUGBOT`
|
|
88
|
+
without immediate subagent write) and no subagent merge occurs that
|
|
89
|
+
tick → orchestrator performs one locked merge setting only
|
|
90
|
+
`prs[<pr_number>].phase` and `last_updated`.
|
|
91
|
+
|
|
92
|
+
Orchestrator reads file at start of every tick for cross-PR state, not
|
|
93
|
+
conversation context.
|
|
94
|
+
|
|
95
|
+
## Subagent spawning rules
|
|
96
|
+
|
|
97
|
+
Multiple PRs returning simultaneously (10+ idle notifications) → spawn
|
|
98
|
+
one agent per PR in single parallel message. Never process any PR inline.
|
|
99
|
+
|
|
100
|
+
### Audit result → fix worker per PR
|
|
101
|
+
|
|
102
|
+
Bugfind subagent completes (findings or clean):
|
|
103
|
+
|
|
104
|
+
- **PRs with findings:** spawn one fix worker per PR via
|
|
105
|
+
`Agent(subagent_type="clean-coder", run_in_background=true)`. Worker:
|
|
106
|
+
1. Reads outcomes XML.
|
|
107
|
+
2. Applies TDD fixes (test first, then production).
|
|
108
|
+
3. Commits, pushes one fix commit.
|
|
109
|
+
4. Replies inline on each addressed finding via
|
|
110
|
+
`reply_to_inline_comment.py`.
|
|
111
|
+
5. Writes `state.json` (per §Concurrency): `last_action: "fix_pushed"`,
|
|
112
|
+
`current_head: <new SHA>`, `bugbot_clean_at: null`, `phase:
|
|
113
|
+
"BUGBOT"`, `status: "awaiting_bugbot"`, `last_updated` ISO-8601 UTC.
|
|
114
|
+
6. Goes idle.
|
|
115
|
+
|
|
116
|
+
- **PRs with zero findings:** spawn one `general-purpose` subagent per PR via
|
|
117
|
+
`Agent(subagent_type="general-purpose", run_in_background=true)`. Subagent:
|
|
118
|
+
1. `bugbot_clean_at == current_head` (back-to-back clean): run
|
|
119
|
+
`mark_pr_ready.py`, append convergence row to
|
|
120
|
+
`<TMPDIR>/pr-converge-<session_id>/converged.log` per §Memory, then
|
|
121
|
+
write `state.json` (per §Concurrency) with `status: "converged"`,
|
|
122
|
+
`last_action: "converged"` (or `marked_ready`), `phase: "BUGBOT"`,
|
|
123
|
+
`last_updated` ISO-8601 UTC — **before** going idle. Skipping leaves
|
|
124
|
+
orchestrator with stale `awaiting_bugteam` / `in_progress` row, risks
|
|
125
|
+
duplicate work.
|
|
126
|
+
2. Else: update `state.json` (per §Concurrency) with `last_action:
|
|
127
|
+
"audit_clean"`, `status: "awaiting_bugbot"`, `phase: "BUGBOT"`, then
|
|
128
|
+
trigger bugbot via `trigger_bugbot.py`.
|
|
129
|
+
3. Goes idle.
|
|
130
|
+
|
|
131
|
+
### Fix result → general-purpose per PR
|
|
132
|
+
|
|
133
|
+
When bugfix (clean-coder) subagent completes after push:
|
|
134
|
+
|
|
135
|
+
- Spawn one `general-purpose` subagent per PR via
|
|
136
|
+
`Agent(subagent_type="general-purpose", run_in_background=true)`. Subagent:
|
|
137
|
+
1. Reads `state.json` for its PR.
|
|
138
|
+
2. Triggers bugbot via `trigger_bugbot.py`.
|
|
139
|
+
3. Polls `fetch_bugbot_reviews.py` every 60s (up to 10 polls) until review
|
|
140
|
+
anchored to `current_head` appears.
|
|
141
|
+
4. **Poll / classify loop** (repeat from 4a whenever 4c retries):
|
|
142
|
+
- **4a.** Fetch inline comments via `fetch_bugbot_inline_comments.py`.
|
|
143
|
+
- **4b.** Classify — three outcomes (same as `SKILL.md` Step 2 BUGBOT):
|
|
144
|
+
- **`clean`:** review body clean, zero unaddressed inline findings.
|
|
145
|
+
- **`dirty`:** ≥1 unaddressed inline finding for `current_head`
|
|
146
|
+
(actionable for Fix protocol / `clean-coder`).
|
|
147
|
+
- **`inline_lag`:** review body shows findings, inline API returns
|
|
148
|
+
zero matching for `current_head` (transient desync — `SKILL.md`
|
|
149
|
+
Step 2 BUGBOT fourth bullet).
|
|
150
|
+
- **4c. `inline_lag`:** locked merge: increment `inline_lag_streak`
|
|
151
|
+
(missing → `0` first); set `last_action: "inline_lag_wait"`,
|
|
152
|
+
`phase: "BUGBOT"`, `last_updated`; keep `status` consistent (e.g.
|
|
153
|
+
`awaiting_bugbot`). `inline_lag_streak >= 3` → **hard blocker** per
|
|
154
|
+
`SKILL.md` §Stop conditions (structurally inconsistent review);
|
|
155
|
+
report and go idle **without** classifying as `dirty`. Else sleep
|
|
156
|
+
90s and repeat from 4a (re-fetch inline only).
|
|
157
|
+
- **4d. `clean`:** exit. Locked merge: `bugbot_clean_at =
|
|
158
|
+
current_head`, reset `inline_lag_streak`, update `last_action`,
|
|
159
|
+
`status`, `phase: BUGTEAM`.
|
|
160
|
+
- **4e. `dirty`:** exit. Locked merge: reset `inline_lag_streak`,
|
|
161
|
+
record findings count, update `last_action`, `status`, `phase:
|
|
162
|
+
BUGBOT`.
|
|
163
|
+
5. Reports one-line outcome to orchestrator.
|
|
164
|
+
|
|
165
|
+
- Orchestrator reads updated `state.json`, spawns next agent:
|
|
166
|
+
- `clean` → `general-purpose` runs BUGTEAM phase (bugteam via `Skill`
|
|
167
|
+
when available, else inline by reading bugteam `SKILL.md`).
|
|
168
|
+
- Exited on `dirty` (4e) with actionable inline threads → spawn same
|
|
169
|
+
fix worker as "audit result with findings". Do **not** spawn
|
|
170
|
+
`clean-coder` when monitor only saw `inline_lag` (4c retries) without
|
|
171
|
+
reaching 4e — that path retries or escalates via `inline_lag_streak ≥
|
|
172
|
+
3` hard blocker, not fix pass.
|
|
173
|
+
|
|
174
|
+
## What orchestrator does per tick
|
|
175
|
+
|
|
176
|
+
1. Per-tick `tick_count` bump for every non-terminal PR under `prs`.
|
|
177
|
+
2. Read `state.json`.
|
|
178
|
+
3. Each PR with new subagent results → spawn next agent per rules, all
|
|
179
|
+
in one parallel message.
|
|
180
|
+
4. Re-read `state.json` if needed for scheduling.
|
|
181
|
+
5. Call `ScheduleWakeup` with appropriate delay.
|
|
182
|
+
6. Nothing else.
|
|
183
|
+
|
|
184
|
+
## Memory
|
|
185
|
+
|
|
186
|
+
Run directory `<TMPDIR>/pr-converge-<session_id>/` holds `state.json` and
|
|
187
|
+
optional `converged.log`. Keep from first create until every PR under `prs`
|
|
188
|
+
is `converged` or `blocked`, or **Stop conditions** ends loop. Safe to
|
|
189
|
+
delete folder after — `mark_pr_ready.py` / `gh pr ready` on GitHub is
|
|
190
|
+
canonical record. Folder skill, not a plugin package; do **not** rely
|
|
191
|
+
on `${CLAUDE_PLUGIN_DATA}`. OS/disk cleanup of `<TMPDIR>` (reboot, policy)
|
|
192
|
+
can remove files mid-run — environmental risk.
|
|
193
|
+
|
|
194
|
+
**`converged.log` (multi-PR only — requires `state.json`):**
|
|
195
|
+
|
|
196
|
+
- **Path:** sibling of `state.json`.
|
|
197
|
+
- **Format:** one tab-separated row per converged PR: ISO8601 UTC,
|
|
198
|
+
owner/repo#number, bugbot SHA, bugteam SHA.
|
|
199
|
+
- **Append site:** agent running `mark_pr_ready.py`. Append **before**
|
|
200
|
+
locked `state.json` publish so log row survives failed merge.
|
|
201
|
+
- **Never read inside loop.** User / follow-up tooling only.
|
|
202
|
+
|
|
203
|
+
Single-PR runs without `state.json`: do **not** append `converged.log`;
|
|
204
|
+
in-conversation summary plus GitHub ready state suffice.
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Per-tick work
|
|
2
|
+
|
|
3
|
+
Use on **draft PR**. Cursor Bugbot and `/bugteam` re-run after each push. Fix
|
|
4
|
+
findings between rounds until back-to-back clean on same `HEAD`, then mark
|
|
5
|
+
PR ready for review.
|
|
6
|
+
|
|
7
|
+
Run every tick in parent harness session. Pacing lives in
|
|
8
|
+
[`../workflows/schedule-wakeup-loop.md`](../workflows/schedule-wakeup-loop.md) (read before Step 4); see [Pacing
|
|
9
|
+
workflow](#pacing-workflow).
|
|
10
|
+
|
|
11
|
+
Every BUGTEAM tick runs **bugteam** — never hand-rolled substitute. Fix
|
|
12
|
+
protocol per [fix-protocol.md](fix-protocol.md). Pacing stays in main session via
|
|
13
|
+
`ScheduleWakeup` (pre-flight aborts when absent).
|
|
14
|
+
|
|
15
|
+
## Invocation modes
|
|
16
|
+
|
|
17
|
+
- **`/pr-converge`** runs one tick, then Step 4 schedules the next via
|
|
18
|
+
`ScheduleWakeup`. Omit the next wakeup only on convergence or **Stop
|
|
19
|
+
conditions**.
|
|
20
|
+
|
|
21
|
+
## Pacing workflow
|
|
22
|
+
|
|
23
|
+
Read [`../workflows/schedule-wakeup-loop.md`](../workflows/schedule-wakeup-loop.md)
|
|
24
|
+
(installed copy under `$HOME/.claude/skills/pr-converge/workflows/`) before
|
|
25
|
+
Step 4. The pre-flight gate guarantees `ScheduleWakeup` is invokable; the
|
|
26
|
+
workflow file specifies delays, prompts, convergence cleanup, and
|
|
27
|
+
inline-lag handling.
|
|
28
|
+
|
|
29
|
+
- **`/pr-converge`** (default): loops until convergence. After each tick
|
|
30
|
+
(unless converged or stopped), run **Step 4**.
|
|
31
|
+
|
|
32
|
+
## Step 1: Resolve current HEAD and PR context
|
|
33
|
+
|
|
34
|
+
Read prior tick's state line from most recent assistant message (or
|
|
35
|
+
initialize fields if none). Increment `tick_count` by 1 in conversation
|
|
36
|
+
state line when **no** `state.json` (single-PR only). With `state.json`, do
|
|
37
|
+
**not** increment here — orchestrator's per-tick bump is sole increment.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
python "${CLAUDE_SKILL_DIR}/scripts/view_pr_context.py" --owner <OWNER> --repo <REPO> --number <NUMBER>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
If owner/repo/number are not yet known, extract them from the PR URL or run without flags in a repo checkout.
|
|
44
|
+
|
|
45
|
+
Capture `number`, `headRefOid` (= `current_head`), owner/repo, branch.
|
|
46
|
+
|
|
47
|
+
## Step 2: Branch on `phase`
|
|
48
|
+
|
|
49
|
+
### `phase == BUGBOT`
|
|
50
|
+
|
|
51
|
+
a. Fetch Cursor Bugbot reviews newest-first, walk back until first clean:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
python "${CLAUDE_SKILL_DIR}/scripts/fetch_bugbot_reviews.py" \
|
|
55
|
+
--owner <OWNER> --repo <REPO> --number <NUMBER>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Track dirty entries in a temp file; Fix protocol reads it back later
|
|
59
|
+
this tick.
|
|
60
|
+
|
|
61
|
+
Iterate from index 0 (most recent) toward older:
|
|
62
|
+
|
|
63
|
+
- Dirty review → append JSON line with `{review_id, commit_id,
|
|
64
|
+
submitted_at, body}`.
|
|
65
|
+
- Stop at first clean. Older reviews presumed addressed at that
|
|
66
|
+
checkpoint.
|
|
67
|
+
- Index 0 clean → `$dirty_reviews_path` stays empty.
|
|
68
|
+
|
|
69
|
+
Capture `commit_id`, `submitted_at`, body, `classification` of index-0
|
|
70
|
+
review for decisions below. When branch routes to **Fix protocol**, address
|
|
71
|
+
**every** entry in `$dirty_reviews_path` — not just index 0.
|
|
72
|
+
|
|
73
|
+
b. Fetch unaddressed inline comments from `cursor[bot]` for newest Bugbot
|
|
74
|
+
review on `current_head`. Script uses same `--paginate --slurp` pattern,
|
|
75
|
+
resolves review via reviews list, returns only inline rows whose
|
|
76
|
+
`pull_request_review_id` matches that review (excludes stale threads from
|
|
77
|
+
older reviews on same SHA).
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
python "${CLAUDE_SKILL_DIR}/scripts/fetch_bugbot_inline_comments.py" \
|
|
81
|
+
--owner <OWNER> --repo <REPO> --number <NUMBER> --commit "$current_head"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
c. Decide (four branches; match first whose predicate holds):
|
|
85
|
+
- **No bugbot review yet, OR latest review's `commit_id` ≠
|
|
86
|
+
`current_head`:** Re-trigger bugbot (Step 3), set `bugbot_clean_at =
|
|
87
|
+
null`, reset `inline_lag_streak = 0`, schedule next wakeup, return.
|
|
88
|
+
- **`commit_id == current_head` AND zero unaddressed inline AND review
|
|
89
|
+
body clean:** Set `bugbot_clean_at = current_head`, reset
|
|
90
|
+
`inline_lag_streak = 0`, `phase = BUGTEAM`. Continue BUGTEAM in same
|
|
91
|
+
tick — back-to-back convergence requires bugteam on same HEAD
|
|
92
|
+
before next wakeup.
|
|
93
|
+
- **`commit_id == current_head` with unaddressed inline findings:**
|
|
94
|
+
Apply **Fix protocol**. Reset `inline_lag_streak = 0`. With
|
|
95
|
+
`state.json`: clean-coder teammate pushes, replies inline, writes
|
|
96
|
+
`state.json`, goes idle; Step 3 on new HEAD runs after via
|
|
97
|
+
orchestrator-spawned follow-up agent (§Fix result → general-purpose).
|
|
98
|
+
No `state.json` (single-PR): spawn Agent (subagent_type: clean-coder) to implement → push → reply inline on each thread
|
|
99
|
+
via `reply_to_inline_comment.py` → Step 3 in same tick (see
|
|
100
|
+
[Single-PR fix workflow](fix-protocol.md#single-pr-fix-workflow) for
|
|
101
|
+
full contract).
|
|
102
|
+
Schedule next wakeup, return.
|
|
103
|
+
- **`commit_id == current_head` AND review body findings AND inline
|
|
104
|
+
API zero matching for `current_head`:** Transient API lag. Increment
|
|
105
|
+
`inline_lag_streak`. `>= 3` → hard blocker; report and terminate with
|
|
106
|
+
no loop pacing. Else Step 4 uses the BUGBOT inline-lag section of
|
|
107
|
+
`../workflows/schedule-wakeup-loop.md` (`delaySeconds: 90`).
|
|
108
|
+
|
|
109
|
+
### `phase == BUGTEAM`
|
|
110
|
+
|
|
111
|
+
a. Run **bugteam** on current PR.
|
|
112
|
+
|
|
113
|
+
- **`Skill` invokable**: invoke bugteam
|
|
114
|
+
with `Skill`.
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
Skill({skill: "bugteam", args:
|
|
118
|
+
"https://github.com/<OWNER>/<REPO>/pull/<NUMBER>"})
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
- **`Skill` not invokable** (typical delegated teammate): worker executes
|
|
122
|
+
bugteam by reading [`../../bugteam/SKILL.md`](../../bugteam/SKILL.md). Same
|
|
123
|
+
loop and gates; only harness steps differ.
|
|
124
|
+
|
|
125
|
+
b. **Re-resolve current HEAD** — bugteam may have pushed commits during
|
|
126
|
+
its run. `current_head` from Step 1 is potentially stale:
|
|
127
|
+
```bash
|
|
128
|
+
new_head=$(python "${CLAUDE_SKILL_DIR}/scripts/resolve_pr_head.py" \
|
|
129
|
+
--owner <OWNER> --repo <REPO> --number <NUMBER>)
|
|
130
|
+
```
|
|
131
|
+
If `new_head != current_head`, set `current_head = new_head` AND
|
|
132
|
+
`bugbot_clean_at = null`. New commits invalidate bugbot's prior clean.
|
|
133
|
+
|
|
134
|
+
c. Inspect bugteam outcome. Reports `convergence (zero findings)` or list
|
|
135
|
+
of unfixed findings with file:line.
|
|
136
|
+
|
|
137
|
+
d. Decide based on post-bugteam state — order matters. Check
|
|
138
|
+
pushed-during-bugteam FIRST so convergence report against stale HEAD
|
|
139
|
+
never falsely terminates:
|
|
140
|
+
- **Audit pushed this tick (`bugbot_clean_at` reset in step b):**
|
|
141
|
+
Re-trigger bugbot same tick (Step 3) so new HEAD enters queue, `phase
|
|
142
|
+
= BUGBOT`, schedule next wakeup, return.
|
|
143
|
+
- **Convergence AND `bugbot_clean_at == current_head` (no push):**
|
|
144
|
+
Back-to-back clean — necessary, not sufficient. Run **[convergence-gates.md](convergence-gates.md)** to clear Copilot-findings, mergeability, post-convergence
|
|
145
|
+
Copilot-request. Only when all four gates pass mark PR ready and
|
|
146
|
+
**omit loop pacing** per **Convergence** of active pacing workflow.
|
|
147
|
+
- **Convergence BUT `bugbot_clean_at != current_head` (no push):**
|
|
148
|
+
`phase = BUGBOT`, schedule next wakeup, return.
|
|
149
|
+
- **Findings without committed fixes:** spawn Agent (subagent_type: clean-coder) to implement fixes and push, then reply inline via `reply_to_inline_comment.py`, following [Single-PR fix workflow](fix-protocol.md#single-pr-fix-workflow).
|
|
150
|
+
`phase = BUGBOT`, schedule next wakeup, return.
|
|
151
|
+
|
|
152
|
+
## Step 3: Re-trigger bugbot
|
|
153
|
+
|
|
154
|
+
Prefer portable script (temp body file, `gh pr comment --body-file`):
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
python "${CLAUDE_SKILL_DIR}/scripts/trigger_bugbot.py" \
|
|
158
|
+
--owner <OWNER> --repo <REPO> --number <NUMBER>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Bundled PowerShell alternative** (same gh-body-file contract):
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
POST_BUGBOT_RUN="$HOME/.claude/skills/pr-converge/scripts/post-bugbot-run.ps1"
|
|
165
|
+
pwsh -NoProfile -ExecutionPolicy Bypass -File "$POST_BUGBOT_RUN" \
|
|
166
|
+
"https://github.com/<OWNER>/<REPO>/pull/<NUMBER>"
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
`bugbot run` is empirically the only re-trigger Cursor Bugbot recognizes;
|
|
170
|
+
alternative phrasings (`re-review`, `bugbot please`, etc.) silently no-op.
|
|
171
|
+
|
|
172
|
+
**Gotcha (duplicate `bugbot run` while review queued):** Skip Step 3 when
|
|
173
|
+
the latest `bugbot run` PR comment has an `:eyes:` or `:+1:` reaction; wait
|
|
174
|
+
for review or HEAD change before re-triggering.
|
|
175
|
+
|
|
176
|
+
## Step 4: Loop pacing
|
|
177
|
+
|
|
178
|
+
**`ScheduleWakeup` field hints** (prefer [Pacing
|
|
179
|
+
workflow](#pacing-workflow)):
|
|
180
|
+
|
|
181
|
+
- `delaySeconds: 270` after bugbot re-trigger. Bugbot finishes in 1–4
|
|
182
|
+
min; 270s stays under 5-min prompt-cache TTL with margin. Exception:
|
|
183
|
+
BUGBOT inline-lag branch uses `delaySeconds: 90` (no re-trigger;
|
|
184
|
+
awaiting GitHub inline API).
|
|
185
|
+
- `reason`: short sentence on what is awaited, including `phase` and
|
|
186
|
+
`bugbot_clean_at` SHA.
|
|
187
|
+
- `prompt: "/pr-converge"`.
|
|
188
|
+
|
|
189
|
+
**On convergence:** apply **Convergence** section of
|
|
190
|
+
`../workflows/schedule-wakeup-loop.md` (omit wakeups).
|
|
191
|
+
|
|
192
|
+
## Bugteam execution
|
|
193
|
+
|
|
194
|
+
**Second audit** (BUGTEAM phase) is **always** **bugteam** skill: preflight,
|
|
195
|
+
CODE_RULES gate, **`code-quality-agent`** / **`clean-coder`** loop, audit
|
|
196
|
+
rubric, outcome shape, Step 2 BUGTEAM §(b)–(d) contract — all in
|
|
197
|
+
[`../../bugteam/SKILL.md`](../../bugteam/SKILL.md) plus `PROMPTS.md` / `EXAMPLES.md` /
|
|
198
|
+
`CONSTRAINTS.md`. Do not re-spec.
|
|
199
|
+
|
|
200
|
+
**pr-converge rule:** Prefer **`Skill({skill: "bugteam", args: "<PR URL or
|
|
201
|
+
args>"})`** wherever registry exposes `Skill`. When `Skill` not invokable
|
|
202
|
+
(typical delegated teammate), worker runs **bugteam** by loading
|
|
203
|
+
`../../bugteam/SKILL.md` from the same checkout. If bugteam cannot run, cancel the
|
|
204
|
+
convergence loop fully and report the issue to the user.
|