claude-dev-env 2.0.1 → 2.1.0
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/CLAUDE.md +1 -1
- package/agents/clean-coder.md +31 -1
- package/audit-rubrics/prompts/category-e-dead-code.md +2 -2
- package/bin/install.test.mjs +2 -2
- package/docs/CLAUDE.md +6 -3
- package/docs/CODE_RULES.md +5 -1
- package/docs/agent-spawn-protocol.md +39 -0
- package/docs/nas-ssh-invocation.md +23 -0
- package/docs/worker-completion-gate.md +33 -0
- package/hooks/blocking/CLAUDE.md +1 -0
- package/hooks/blocking/code_rules_dead_module_constant.py +11 -5
- package/hooks/blocking/code_rules_shared.py +126 -47
- package/hooks/blocking/config/CLAUDE.md +2 -0
- package/hooks/blocking/config/verified_commit_constants.py +8 -1
- package/hooks/blocking/config/verified_commit_context_constants.py +21 -0
- package/hooks/blocking/config/verified_commit_gate_output_constants.py +14 -0
- package/hooks/blocking/conftest.py +36 -30
- package/hooks/blocking/convergence_gate_blocker.py +76 -8
- package/hooks/blocking/plain_language_blocker.py +8 -0
- package/hooks/blocking/state_description_blocker.py +4 -1
- package/hooks/blocking/test_code_rules_enforcer_dead_module_constant.py +58 -0
- package/hooks/blocking/test_code_rules_enforcer_dead_module_constant_alias.py +133 -0
- package/hooks/blocking/test_code_rules_shared.py +120 -20
- package/hooks/blocking/test_convergence_gate_blocker.py +146 -0
- package/hooks/blocking/test_plain_language_blocker.py +15 -0
- package/hooks/blocking/test_state_description_blocker.py +15 -0
- package/hooks/blocking/test_verification_verdict_store.py +12 -0
- package/hooks/blocking/test_verified_commit_config_bootstrap.py +18 -0
- package/hooks/blocking/test_verified_commit_docs_delta.py +176 -0
- package/hooks/blocking/test_verified_commit_gate_additional_context.py +134 -0
- package/hooks/blocking/tests/test_pii_prevention_blocker.py +0 -1
- package/hooks/blocking/tests/test_verified_commit_gate.py +41 -0
- package/hooks/blocking/verified_commit_config_bootstrap.py +22 -10
- package/hooks/blocking/verified_commit_gate.py +113 -568
- package/hooks/blocking/verified_commit_gate_parts/CLAUDE.md +28 -0
- package/hooks/blocking/verified_commit_gate_parts/__init__.py +1 -0
- package/hooks/blocking/verified_commit_gate_parts/command_tokenization.py +174 -0
- package/hooks/blocking/verified_commit_gate_parts/deny_payload.py +53 -0
- package/hooks/blocking/verified_commit_gate_parts/deny_reason.py +80 -0
- package/hooks/blocking/verified_commit_gate_parts/directory_resolution.py +170 -0
- package/hooks/blocking/verified_commit_gate_parts/gated_invocations.py +205 -0
- package/hooks/blocking/verified_commit_gate_parts/tests/conftest.py +10 -0
- package/hooks/blocking/verified_commit_gate_parts/tests/test_command_tokenization.py +94 -0
- package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_payload.py +17 -0
- package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_reason.py +38 -0
- package/hooks/blocking/verified_commit_gate_parts/tests/test_directory_resolution.py +71 -0
- package/hooks/blocking/verified_commit_gate_parts/tests/test_gated_invocations.py +61 -0
- package/hooks/hooks_constants/CLAUDE.md +4 -1
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -0
- package/hooks/hooks_constants/convergence_gate_blocker_constants.py +36 -0
- package/hooks/hooks_constants/dead_module_constant_constants.py +8 -0
- package/hooks/hooks_constants/harness_scratchpad_constants.py +10 -9
- package/hooks/hooks_constants/mypy_integration_constants.py +16 -0
- package/hooks/hooks_constants/pii_prevention_constants.py +1 -0
- package/hooks/validators/mypy_integration.py +145 -24
- package/hooks/validators/python_antipattern_checks.py +16 -0
- package/hooks/validators/run_all_validators.py +9 -3
- package/hooks/validators/test_mypy_integration.py +154 -0
- package/hooks/validators/test_python_antipattern_checks.py +112 -1
- package/hooks/validators/test_run_all_validators_pretooluse.py +16 -0
- package/package.json +1 -1
- package/rules/CLAUDE.md +3 -3
- package/rules/agent-spawn-protocol.md +5 -43
- package/rules/code-standards.md +1 -36
- package/rules/env-var-table-code-drift.md +2 -21
- package/rules/hook-prose-matches-detector.md +5 -16
- package/rules/nas-ssh-invocation.md +3 -15
- package/rules/no-historical-clutter.md +7 -49
- package/rules/no-inline-destructive-literals.md +3 -5
- package/rules/package-inventory-stale-entry.md +7 -32
- package/rules/re-stage-before-commit.md +6 -23
- package/rules/shell-invocation-policy.md +1 -1
- package/rules/vault-context.md +3 -3
- package/rules/verified-commit-gate-skip.md +28 -0
- package/rules/workers-done-before-complete.md +2 -30
- package/scripts/claude_chain_runner.py +39 -3
- package/scripts/dev_env_scripts_constants/claude_chain_constants.py +3 -0
- package/scripts/test_claude_chain_runner.py +112 -0
- package/scripts/test_grok_headless_runner.py +0 -1
- package/skills/auditing-claude-config/CLAUDE.md +2 -1
- package/skills/auditing-claude-config/SKILL.md +114 -176
- package/skills/auditing-claude-config/reference/probe-hook.md +74 -0
- package/skills/autoconverge/CLAUDE.md +1 -0
- package/skills/autoconverge/SKILL.md +310 -346
- package/skills/autoconverge/reference/CLAUDE.md +1 -0
- package/skills/autoconverge/reference/copilot-findings.md +51 -0
- package/skills/closeout/SKILL.md +7 -9
- package/skills/copilot-finding-triage/SKILL.md +5 -7
- package/skills/copilot-review/CLAUDE.md +3 -2
- package/skills/copilot-review/SKILL.md +119 -155
- package/skills/copilot-review/templates/subagent-prompt.md +49 -0
- package/skills/everything-search/SKILL.md +7 -1
- package/skills/fresh-branch/CLAUDE.md +6 -9
- package/skills/fresh-branch/SKILL.md +84 -33
- package/skills/fresh-branch/scripts/create_fresh_branch.py +445 -0
- package/skills/fresh-branch/scripts/fresh_branch_scripts_constants/__init__.py +1 -0
- package/skills/fresh-branch/scripts/fresh_branch_scripts_constants/fresh_branch_cli_constants.py +74 -0
- package/skills/fresh-branch/scripts/test_create_fresh_branch.py +670 -0
- package/skills/orchestrator/SKILL.md +161 -147
- package/skills/pr-converge/SKILL.md +277 -422
- package/skills/pr-converge/reference/CLAUDE.md +1 -0
- package/skills/pr-converge/reference/progress-checklist.md +168 -0
- package/skills/pr-converge/test_step5_host_branch.py +8 -6
- package/skills/pr-loop-cloud-transport/SKILL.md +1 -1
- package/skills/privacy-hygiene/SKILL.md +68 -115
- package/skills/privacy-hygiene/reference/sweep-procedure.md +62 -0
- package/skills/session-log/CLAUDE.md +2 -1
- package/skills/session-log/SKILL.md +4 -26
- package/skills/session-log/templates/frontmatter.md +40 -0
- package/skills/skill-builder/CLAUDE.md +8 -7
- package/skills/skill-builder/SKILL.md +26 -11
- package/skills/skill-builder/references/CLAUDE.md +3 -1
- package/skills/skill-builder/references/delegation-map.md +21 -12
- package/skills/skill-builder/references/description-field.md +9 -11
- package/skills/skill-builder/references/deterministic-elements.md +218 -0
- package/skills/skill-builder/references/self-audit-checklist.md +62 -45
- package/skills/skill-builder/references/skill-modularity.md +8 -9
- package/skills/skill-builder/templates/CLAUDE.md +2 -2
- package/skills/skill-builder/templates/gap-analysis.md +15 -0
- package/skills/skill-builder/workflows/CLAUDE.md +5 -5
- package/skills/skill-builder/workflows/improve-skill.md +18 -9
- package/skills/skill-builder/workflows/new-skill.md +23 -15
- package/skills/skill-builder/workflows/polish-skill.md +28 -21
- package/system-prompts/CLAUDE.md +3 -3
- package/docs/agents-md-alignment-plan.md +0 -123
- package/docs/emotion-informed-prompt-design.md +0 -362
- package/rules/es-exe-file-search.md +0 -17
|
@@ -1,422 +1,277 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: pr-converge
|
|
3
|
-
description: >-
|
|
4
|
-
Loops Cursor Bugbot, a code review, a bug audit, and Copilot on the current
|
|
5
|
-
PR, applying TDD fixes until all are clean on one HEAD. Use when the user says
|
|
6
|
-
'/pr-converge', 'drive PR to convergence', 'loop bugbot and bugteam', 'babysit
|
|
7
|
-
bugbot and bugteam', 'until both are clean', or 'converge this PR'.
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# PR Converge
|
|
11
|
-
|
|
12
|
-
One tick per invocation. A code-review ↔ bugteam ↔ Bugbot ↔ Copilot loop on
|
|
13
|
-
a draft PR: the internal code-review and bugteam passes drive the code to clean
|
|
14
|
-
first, then Cursor Bugbot and Copilot run as terminal confirmation gates, until
|
|
15
|
-
all are clean on the same `HEAD` and mergeable.
|
|
16
|
-
|
|
17
|
-
## Transport check (before any GitHub step)
|
|
18
|
-
|
|
19
|
-
Run `command -v gh`; when it succeeds, run `gh auth status`; once the PR
|
|
20
|
-
scope is resolved, run `gh api repos/<owner>/<repo> --jq .permissions.push`
|
|
21
|
-
and take `true` as the pass. When any check fails, run the
|
|
22
|
-
`pr-loop-cloud-transport` skill first and route every `gh` operation in this
|
|
23
|
-
skill through its substitution matrix.
|
|
24
|
-
|
|
25
|
-
## Pre-flight
|
|
26
|
-
|
|
27
|
-
Pause and physically scan the tool list at the top of this conversation
|
|
28
|
-
for the literal string `ScheduleWakeup`. If you see it, proceed. If the
|
|
29
|
-
string is absent after scanning, report `pr-converge requires
|
|
30
|
-
ScheduleWakeup; aborting` and stop.
|
|
31
|
-
|
|
32
|
-
Call `EnterWorktree` with no arguments before any API call, file read, or
|
|
33
|
-
edit. Agent-view sessions start in the shared checkout; Bash (`gh`, `git`)
|
|
34
|
-
does not auto-isolate. Do not proceed until the working directory contains
|
|
35
|
-
`.claude/worktrees/`. If `EnterWorktree` fails, report the failure and stop.
|
|
36
|
-
|
|
37
|
-
`EnterWorktree` isolates the session repo. When the PR lives in a different
|
|
38
|
-
repo, Step 1.5 routes the working directory into a **PR worktree** of that
|
|
39
|
-
repo on its head branch (routine, never a pause). See
|
|
40
|
-
[`reference/per-tick.md` § Step 1.5](reference/per-tick.md).
|
|
41
|
-
|
|
42
|
-
## Resume from a prior run
|
|
43
|
-
|
|
44
|
-
Before Step 0, check
|
|
45
|
-
`~/.claude/runtime/pr-loop/bugteam-pr-<PR number>/handoff.json`. When
|
|
46
|
-
`$CLAUDE_JOB_DIR/pr-converge-state.json` is absent but that handoff exists, seed
|
|
47
|
-
`phase`, `tick_count`, and the clean-at SHAs from the run's `state-copy.json`, so
|
|
48
|
-
a fresh session continues where the last one stopped rather than restarting at
|
|
49
|
-
CODE_REVIEW.
|
|
50
|
-
|
|
51
|
-
## Copilot quota pre-check (start of run)
|
|
52
|
-
|
|
53
|
-
On the first tick, apply the `reviewer-gates` skill's Copilot quota gate
|
|
54
|
-
(`../reviewer-gates/SKILL.md` § Gate 2) — once per run, never per tick. The
|
|
55
|
-
flag lives in `pr-converge-state.json` as `copilot_down`, and every tick reads
|
|
56
|
-
it. While `copilot_down` is true, the tick skips all Copilot work — no fetch,
|
|
57
|
-
no request, no poll, no agent — and exports
|
|
58
|
-
`CLAUDE_REVIEWS_DISABLED="copilot"` in that tick's shell before
|
|
59
|
-
`check_convergence.py`, so the check bypasses the Copilot review gate and the
|
|
60
|
-
pending-requested-reviews gate and the run still marks ready on the remaining
|
|
61
|
-
signals.
|
|
62
|
-
|
|
63
|
-
## Copilot findings — tier, verify, then route
|
|
64
|
-
|
|
65
|
-
Tier, verify, and route each Copilot finding via
|
|
66
|
-
[`copilot-finding-triage`](../copilot-finding-triage/SKILL.md) (self-healing
|
|
67
|
-
auto-fix; code-concern verify then confirmed / refuted / inconclusive).
|
|
68
|
-
|
|
69
|
-
**pr-converge phase routing after triage:**
|
|
70
|
-
|
|
71
|
-
- Self-healing and **confirmed** findings join the fix tick on `current_head`;
|
|
72
|
-
after push, reset clean-at SHAs, set `phase = CODE_REVIEW`, return to Step 5.
|
|
73
|
-
- **Refuted** findings resolve clean on the thread; no phase change.
|
|
74
|
-
- One or more **inconclusive** findings: do not mark ready. Run the triage
|
|
75
|
-
skill's user gate (ntfy + 45-minute hold across ticks; persist the deadline so
|
|
76
|
-
each tick reads it on entry). Act on the user's direction inside the window;
|
|
77
|
-
on timeout, teardown and report the findings un-reviewed.
|
|
78
|
-
- Enter `COPILOT_WAIT` only from gate (d) after requesting a Copilot review
|
|
79
|
-
(Step 7 → 7a). Stay on `COPILOT_WAIT` until a review surfaces at
|
|
80
|
-
`current_head`, `copilot_wait_count >= 3` hard-blocks, or `copilot_down`
|
|
81
|
-
skips the Copilot path entirely.
|
|
82
|
-
|
|
83
|
-
## Budget-aware tick boundaries
|
|
84
|
-
|
|
85
|
-
Before starting any tick, estimate whether the remaining session/usage
|
|
86
|
-
budget covers one full clean tick (worst case: a BUGBOT fetch + a
|
|
87
|
-
full-diff CODE_REVIEW + a fix commit + replies). If it does not, do not
|
|
88
|
-
start the tick. Stop at the current tick boundary: write updated state to
|
|
89
|
-
`$CLAUDE_JOB_DIR/pr-converge-state.json`, write the durable handoff (see
|
|
90
|
-
[State persistence](#state-persistence)), then report the exact resume
|
|
91
|
-
command (`/pr-converge <PR URL>`) and the persisted `phase`/`tick_count`.
|
|
92
|
-
A tick cut off mid-flight poisons the resume state — clean SHAs recorded
|
|
93
|
-
against work that never landed — so an unstarted tick is always cheaper
|
|
94
|
-
than a half-finished one.
|
|
95
|
-
|
|
96
|
-
## Findings discipline
|
|
97
|
-
|
|
98
|
-
Every finding, reply, and report states verified facts only — no hedging
|
|
99
|
-
language (`likely`, `probably`, `should`, `appears to`). Verify each
|
|
100
|
-
claim against the code on `current_head` before stating it; the
|
|
101
|
-
anti-hallucination Stop hook rejects hedged output, forcing a rework
|
|
102
|
-
pass. A claim that cannot be verified is reported as unverified, not
|
|
103
|
-
softened.
|
|
104
|
-
|
|
105
|
-
## State persistence
|
|
106
|
-
|
|
107
|
-
Single-PR mode persists loop state to `$CLAUDE_JOB_DIR/pr-converge-state.json`.
|
|
108
|
-
On tick entry, read this file if it exists to restore phase, tick_count, and
|
|
109
|
-
clean-at SHAs. On tick exit, write updated state before calling ScheduleWakeup
|
|
110
|
-
so the next tick resumes with accurate state.
|
|
111
|
-
|
|
112
|
-
After the state write and before ScheduleWakeup, write the durable handoff so a
|
|
113
|
-
fresh session in a new job can resume this run:
|
|
114
|
-
|
|
115
|
-
```
|
|
116
|
-
python "$HOME/.claude/skills/_shared/pr-loop/scripts/write_handoff.py" \
|
|
117
|
-
--pr-number <N> --head-ref <branch> --phase <phase> \
|
|
118
|
-
--resume-command "/pr-converge <PR URL>" \
|
|
119
|
-
--state-file "$CLAUDE_JOB_DIR/pr-converge-state.json" \
|
|
120
|
-
--completed-steps "<clean phases this run>"
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
It writes `handoff.json`, `HANDOFF.md`, and `state-copy.json` under
|
|
124
|
-
`~/.claude/runtime/pr-loop/<run-name>/`. The job-dir state stays the source of
|
|
125
|
-
truth for a resumed tick; the handoff copy is the pointer a fresh session reads
|
|
126
|
-
when `$CLAUDE_JOB_DIR` is gone.
|
|
127
|
-
|
|
128
|
-
Fields: `phase`, `tick_count`, `bugbot_clean_at`, `code_review_clean_at`,
|
|
129
|
-
`bugteam_clean_at`, `copilot_clean_at`, `merge_state_status`, `current_head`,
|
|
130
|
-
`bugbot_acknowledged_at`, `bugbot_down`, `copilot_down`,
|
|
131
|
-
`bugteam_skill_invoked_at_head`, `bugteam_skill_invoked_at_tick`,
|
|
132
|
-
`agents_session_id`, `persistent_agents`.
|
|
133
|
-
|
|
134
|
-
## Persistent per-step agents
|
|
135
|
-
|
|
136
|
-
Three step-scoped agents persist across ticks so their context carries
|
|
137
|
-
forward: `fix_executor`, `thread_sweep`, and `copilot_watch`, recorded in
|
|
138
|
-
the `persistent_agents` map
|
|
139
|
-
([`reference/state-schema.md`](reference/state-schema.md)).
|
|
140
|
-
|
|
141
|
-
- **Resume:** read `persistent_agents.<key>`. When an entry exists,
|
|
142
|
-
`SendMessage` to the stored `agent_id` with a tick payload that restates
|
|
143
|
-
the PR scope, `current_head`, the PR worktree path, this tick's findings
|
|
144
|
-
or threads, and the report-back contract. Bump `last_used_tick`, then
|
|
145
|
-
await completion.
|
|
146
|
-
- **First spawn:** `Agent(subagent_type: "clean-coder", name:
|
|
147
|
-
"prc-fix-<PR#>")` — the `name` makes the agent a persistent teammate
|
|
148
|
-
that idles awaiting messages. Record `{agent_id, created_tick,
|
|
149
|
-
last_used_tick}` under the step key. Keep the spawn prompt fix-shaped,
|
|
150
|
-
never audit-shaped: the `pr_converge_bugteam_enforcer` hook blocks
|
|
151
|
-
audit-shaped clean-coder spawns during the BUGTEAM phase.
|
|
152
|
-
- **Stale or dead id:** on a `SendMessage` failure, or no acknowledgment
|
|
153
|
-
within one bounded wait, drop the map entry, spawn a fresh named agent,
|
|
154
|
-
record it, and continue the tick. Never abort a tick on a stale id;
|
|
155
|
-
never retry the same dead id.
|
|
156
|
-
- **Fresh every round (never persisted):** the Step 5 host-aware
|
|
157
|
-
`invoke_code_review.py` / `/code-review high --fix` pass and the Step 6
|
|
158
|
-
bugteam audit (unbiased eyes each round; the enforcer needs the formal
|
|
159
|
-
Skill call), and every `code-verifier` — a named code-verifier never fires
|
|
160
|
-
`SubagentStop`, so no verdict mints (see the named-`code-verifier` entry
|
|
161
|
-
in the Gotchas list below).
|
|
162
|
-
- **Shutdown:** at loop end (convergence or a stop condition), send each
|
|
163
|
-
persistent agent a shutdown request and clear `persistent_agents` before
|
|
164
|
-
the `pr-loop-lifecycle` Close.
|
|
165
|
-
|
|
166
|
-
## Gotchas
|
|
167
|
-
|
|
168
|
-
Highest-signal content. Append a bullet each time a tick fails in a new
|
|
169
|
-
way — these are the hard-won lessons that keep the loop honest. Once this
|
|
170
|
-
grows to 5 or more items, suggest spinning up a subagent to investigate, fix,
|
|
171
|
-
post a fresh PR in a fresh branch based on origin main to the user.
|
|
172
|
-
|
|
173
|
-
- **`ScheduleWakeup` not in subagent tool registries** — background
|
|
174
|
-
`general-purpose` tick cannot schedule re-entry; only parent session
|
|
175
|
-
with `ScheduleWakeup` in registry can call it.
|
|
176
|
-
- **`state.json` without §Concurrency lock loses merges** when teammates
|
|
177
|
-
finish in same wall-clock window.
|
|
178
|
-
- **`tick_count` must not double-increment** — conversation state line
|
|
179
|
-
only when **no** `state.json`; with `state.json`, only the
|
|
180
|
-
orchestrator bump increments.
|
|
181
|
-
- **Bugbot trigger and detection** — CI check-run based. Full flow at
|
|
182
|
-
[`per-tick.md` Step 3](reference/per-tick.md); see also
|
|
183
|
-
`~/.claude/skills/pr-converge/scripts/check_bugbot_ci.py --help`.
|
|
184
|
-
- **Bot login fields differ by endpoint** — `get_reviews` returns
|
|
185
|
-
`.user.login` (object), but `get_review_comments` returns `.author`
|
|
186
|
-
(string, not an object). Always check the correct fields and use
|
|
187
|
-
case-insensitive substring matching on login values, never strict
|
|
188
|
-
equality.
|
|
189
|
-
- **`isOutdated` has dual scope** — GitHub marks a thread `isOutdated=true`
|
|
190
|
-
when the line it anchors to has changed since the comment was posted. The
|
|
191
|
-
machine gate (`check_convergence.py` / [convergence-gates.md](reference/convergence-gates.md)
|
|
192
|
-
gate (e)) excludes `isOutdated == true` bot threads from the fail count —
|
|
193
|
-
only non-outdated unresolved bot threads fail the gate. The agent-side
|
|
194
|
-
unresolved-thread sweep in the shared fix protocol
|
|
195
|
-
([`../../_shared/pr-loop/fix-protocol.md`](../../_shared/pr-loop/fix-protocol.md)
|
|
196
|
-
step 12; skill deltas in [`reference/fix-protocol.md`](reference/fix-protocol.md))
|
|
197
|
-
still verifies outdated unresolved threads against HEAD before resolve when
|
|
198
|
-
the protocol requires it (the original concern can still apply when the fix
|
|
199
|
-
moved rather than landed).
|
|
200
|
-
- **Tilde paths fail on Windows Git Bash** — `~/.claude/skills/...`
|
|
201
|
-
resolves to the wrong home directory in Bash-tool contexts. Use
|
|
202
|
-
`$HOME/.claude/skills/...` in shell invocations or `Path.home() /
|
|
203
|
-
".claude/skills/..."` in Python scripts. Script invocations through
|
|
204
|
-
Bash that reference `~` produce "file not found" errors
|
|
205
|
-
indistinguishable from actual script failures.
|
|
206
|
-
- **PowerShell cmdlets fail in Bash tool** — `.ps1` scripts and
|
|
207
|
-
`pwsh` calls run through the PowerShell tool, not Bash. Bash on
|
|
208
|
-
Windows is Git Bash which cannot execute PowerShell cmdlets. Route all
|
|
209
|
-
PowerShell work through the PowerShell tool or `pwsh -NoProfile -File`.
|
|
210
|
-
- **Cross-repo PR: route cwd into the PR worktree before Step 5 review** —
|
|
211
|
-
`invoke_code_review.py` and `/code-review high --fix` audit the repo of the
|
|
212
|
-
cwd (the helper's `--cwd`). When the session is rooted in a different repo
|
|
213
|
-
than the PR, `EnterWorktree` cannot re-root (it is scoped to the session's
|
|
214
|
-
repo); resolve the PR worktree and `cd` into it per
|
|
215
|
-
[Step 1.5](reference/per-tick.md). Skipping this reviews and edits the
|
|
216
|
-
wrong repo. The route is routine and automatic — never a material fork
|
|
217
|
-
to pause on.
|
|
218
|
-
- **A named/teammate `code-verifier` never mints a verdict** — In a background-job session, an `Agent`-tool `code-verifier` spawned with a `name` (or otherwise as a persistent teammate) goes idle/"available" awaiting messages rather than terminating, so its `SubagentStop` never fires. `verifier_verdict_minter.py` mints the verdict only on `SubagentStop`, so no verdict file is written and `verified_commit_gate` blocks the `git commit`/`git push` with "no passing verification verdict" even though the verifier emitted `all_pass`. A `shutdown_request` is ignored and `TaskStop` cannot resolve the teammate's id. Spawn the code-verifier as a one-shot agent with NO `name` (a plain async `Agent` call) so it runs to completion and fires `SubagentStop`, minting the verdict bound to the live surface. Keep the work tree frozen between verification and the commit so the minted surface hash still matches.
|
|
219
|
-
|
|
220
|
-
## Progress checklist
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
`
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
**
|
|
232
|
-
thread exists on the PR
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
(`../reviewer-gates/SKILL.md` § Gate 1 / Gate 3).
|
|
279
|
-
|
|
280
|
-
- [ ] **disabled / down** → `bugbot_down = true` → Step 7
|
|
281
|
-
- [ ] **dirty on `current_head`** → apply shared fix protocol
|
|
282
|
-
([`../../_shared/pr-loop/fix-protocol.md`](../../_shared/pr-loop/fix-protocol.md);
|
|
283
|
-
skill deltas in [`reference/fix-protocol.md`](reference/fix-protocol.md))
|
|
284
|
-
→ push → reset push-invalidated markers → `phase = CODE_REVIEW` → Step 5
|
|
285
|
-
- [ ] **clean on `current_head`** → zero unresolved threads (else fix + resolve first)
|
|
286
|
-
→ `bugbot_clean_at = current_head` → Step 7
|
|
287
|
-
- [ ] **no review / commit_id mismatch** → `reviewer-gates` Bugbot flow (Gate 3):
|
|
288
|
-
silent pass → stamp + Step 7; queued/triggered → 360s wakeup → Step 4;
|
|
289
|
-
down → `bugbot_down = true` → Step 7
|
|
290
|
-
|
|
291
|
-
- [ ] **Step 5: CODE-REVIEW — static sweep, review, fix, advance**
|
|
292
|
-
Entry phase every tick; re-entered after any fix push.
|
|
293
|
-
See: [`reference/per-tick.md` § CODE_REVIEW entry](reference/per-tick.md).
|
|
294
|
-
Pre-condition: cwd is the Step 1.5 PR worktree on `current_head`.
|
|
295
|
-
Scope: FULL `origin/main...HEAD` diff every tick (no path args, no delta cut).
|
|
296
|
-
Review always runs at effort high on model opus through
|
|
297
|
-
`invoke_code_review.py`. Mode decision inputs: host profile + session
|
|
298
|
-
model. Call:
|
|
299
|
-
`python "$HOME/.claude/scripts/invoke_code_review.py" --cwd <PR-worktree>
|
|
300
|
-
--session-model <alias>`. Chain mode uses that cwd and empty stdin; the
|
|
301
|
-
chain process never commits and never pushes. JSON stdout carries
|
|
302
|
-
`mode` (`in_session` | `chain`), `served_command`, `returncode`, and
|
|
303
|
-
`dirty_tree`. Config/host errors still emit that JSON with non-zero
|
|
304
|
-
`returncode` (no traceback-only failure).
|
|
305
|
-
|
|
306
|
-
- [ ] **Static sweep fails** → apply shared fix protocol → push → reset markers
|
|
307
|
-
→ stay CODE_REVIEW → Step 5
|
|
308
|
-
- [ ] **`mode == in_session`** (Claude host, session model opus) → run
|
|
309
|
-
`/code-review high --fix` in-session (no path args)
|
|
310
|
-
- [ ] **`mode == chain`** (any other host or non-opus session) → helper
|
|
311
|
-
already ran the headless review; read `returncode`,
|
|
312
|
-
`served_command`, and `dirty_tree` from JSON
|
|
313
|
-
- [ ] **failed review** (`returncode != 0`, or chain with null
|
|
314
|
-
`served_command`) → do not set `code_review_clean_at` → stay
|
|
315
|
-
CODE_REVIEW → Step 5
|
|
316
|
-
- [ ] **fixes applied** (`dirty_tree` true / working tree dirty) →
|
|
317
|
-
commit + push via shared fix protocol → reset markers → stay
|
|
318
|
-
CODE_REVIEW → Step 5
|
|
319
|
-
- [ ] **clean** (successful serve: `returncode == 0`, chain
|
|
320
|
-
`served_command` non-null when chain, and `dirty_tree` false) →
|
|
321
|
-
zero unresolved threads (else fix + resolve) →
|
|
322
|
-
`code_review_clean_at = current_head` → `phase = BUGTEAM` → Step 6
|
|
323
|
-
|
|
324
|
-
- [ ] **Step 6: BUGTEAM — run, decide, fix, reply, resolve**
|
|
325
|
-
See: [`reference/per-tick.md` § Step 2 BUGTEAM](reference/per-tick.md);
|
|
326
|
-
[`../bugteam/SKILL.md`](../bugteam/SKILL.md).
|
|
327
|
-
Pre-condition: `code_review_clean_at == current_head`.
|
|
328
|
-
Mandatory: `Skill({skill: "bugteam", args: "<PR URL>"})` this tick
|
|
329
|
-
(enforcer-blocked otherwise; `qbug` is not a substitute). Scope: FULL
|
|
330
|
-
`origin/main...HEAD` diff. Re-resolve HEAD after bugteam.
|
|
331
|
-
|
|
332
|
-
- [ ] **bugteam pushed** → verify threads replied + resolved → reset markers
|
|
333
|
-
→ `phase = CODE_REVIEW` → 360s wakeup → Step 5
|
|
334
|
-
- [ ] **converged, no push** → zero unresolved threads →
|
|
335
|
-
`bugteam_clean_at = current_head` → `phase = BUGBOT` → Step 4
|
|
336
|
-
- [ ] **findings without committed fixes** → apply shared fix protocol → push →
|
|
337
|
-
reset markers → `phase = CODE_REVIEW` → Step 5
|
|
338
|
-
|
|
339
|
-
- [ ] **Step 7: Convergence gates**
|
|
340
|
-
Full procedure: [`reference/convergence-gates.md`](reference/convergence-gates.md).
|
|
341
|
-
|
|
342
|
-
Pre-condition: Step 6 converged AND (`bugbot_clean_at == current_head` OR
|
|
343
|
-
`bugbot_down`). The terminal Bugbot gate (Step 4) sets that state just
|
|
344
|
-
before these gates run. Count unresolved threads before each gate.
|
|
345
|
-
Every gate records evidence; gate (f) cites evidence from (a)–(e).
|
|
346
|
-
|
|
347
|
-
- [ ] **(a) Copilot findings** — fetch Copilot on `current_head`; dirty → fix + return to Step 5; clean → stamp `copilot_clean_at`; absent → continue; when `copilot_down`, skip
|
|
348
|
-
- [ ] **(b) Claude reviewer** — fetch Claude on `current_head`; dirty → fix + return to Step 5; clean or absent → continue
|
|
349
|
-
- [ ] **(c) Mergeability** — `mergeable_state == "clean"` and `mergeable == true`; dirty → rebase + return to Step 1; blocked/behind/unknown/unstable → hard blocker
|
|
350
|
-
- [ ] **(d) Post-convergence Copilot request** — request Copilot when not pending and not `copilot_down`; enter `COPILOT_WAIT` (Step 7a); when `copilot_down`, skip to (e)
|
|
351
|
-
- [ ] **(e) Thread-resolution** — zero unresolved threads across the PR; else sweep + fix/resolve
|
|
352
|
-
- [ ] **(f) Mark ready** — run `check_convergence.py`; exit 0 → `update_pull_request(draft=false)` → Step 8; exit 1 → fix path; exit 2 → retry/stop
|
|
353
|
-
|
|
354
|
-
- [ ] **Step 7a: COPILOT_WAIT — fetch Copilot, decide**
|
|
355
|
-
See: [`reference/per-tick.md` § Step 2 COPILOT_WAIT](reference/per-tick.md).
|
|
356
|
-
Skipped entirely when `copilot_down` (gate (d) never enters this phase).
|
|
357
|
-
|
|
358
|
-
- [ ] **clean** → `copilot_clean_at = current_head` → Step 7 (re-validate (b), (c), (e), (f))
|
|
359
|
-
- [ ] **dirty** → apply shared fix protocol
|
|
360
|
-
([`../../_shared/pr-loop/fix-protocol.md`](../../_shared/pr-loop/fix-protocol.md);
|
|
361
|
-
skill deltas in [`reference/fix-protocol.md`](reference/fix-protocol.md))
|
|
362
|
-
→ push → reset markers → `phase = CODE_REVIEW` → Step 5
|
|
363
|
-
- [ ] **no review yet** → increment `copilot_wait_count` → ≥ 3 hard-blocks;
|
|
364
|
-
else 360s wakeup → Step 7a next tick
|
|
365
|
-
|
|
366
|
-
- [ ] **Step 8: Clean working tree**
|
|
367
|
-
`pr-loop-lifecycle` Close (`../pr-loop-lifecycle/SKILL.md`).
|
|
368
|
-
See: [`pr-loop-lifecycle/reference/teardown-publish-permissions.md` § Clean working tree](../pr-loop-lifecycle/reference/teardown-publish-permissions.md)
|
|
369
|
-
|
|
370
|
-
- [ ] **Step 9: Rewrite PR description**
|
|
371
|
-
`pr-loop-lifecycle` Close.
|
|
372
|
-
See: [`pr-loop-lifecycle/reference/teardown-publish-permissions.md` § Publish the final PR description](../pr-loop-lifecycle/reference/teardown-publish-permissions.md)
|
|
373
|
-
|
|
374
|
-
- [ ] **Step 10: Revoke project permissions (always)**
|
|
375
|
-
`pr-loop-lifecycle` Close (`../pr-loop-lifecycle/SKILL.md` § Close).
|
|
376
|
-
|
|
377
|
-
- [ ] **Step 11: Print final report**
|
|
378
|
-
Print this block verbatim — no paraphrase, no extra commentary:
|
|
379
|
-
```
|
|
380
|
-
/pr-converge exit: converged
|
|
381
|
-
Loops: <N>
|
|
382
|
-
Final commit: <SHA>
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
## Edge cases
|
|
386
|
-
|
|
387
|
-
| Situation | Read |
|
|
388
|
-
|---|---|
|
|
389
|
-
| Multi-PR session (`state.json` exists) | [`reference/multi-pr-orchestration.md`](reference/multi-pr-orchestration.md) |
|
|
390
|
-
| Hard blocker or user stops loop | [`reference/stop-conditions.md`](reference/stop-conditions.md) |
|
|
391
|
-
| Tick is ambiguous against the steps above | [`reference/examples.md`](reference/examples.md) |
|
|
392
|
-
|
|
393
|
-
## Depends on / invokes
|
|
394
|
-
|
|
395
|
-
| Skill / path | Role |
|
|
396
|
-
|---|---|
|
|
397
|
-
| `pr-loop-lifecycle` | Open (permission grant + worktree preflight) and Close (cleanup, PR description, revoke) |
|
|
398
|
-
| `reviewer-gates` | Copilot quota gate, Bugbot availability, Bugbot trigger flow |
|
|
399
|
-
| `copilot-finding-triage` | Tier, verify, and route each Copilot finding |
|
|
400
|
-
| `bugteam` | Full-diff bug audit in Step 6 (Skill invocation mandatory) |
|
|
401
|
-
| `pr-loop-cloud-transport` | When `gh` is unavailable or unauthenticated |
|
|
402
|
-
| [`../../_shared/pr-loop/fix-protocol.md`](../../_shared/pr-loop/fix-protocol.md) | Shared 13-step fix sequence (TDD, commit, push, reply, resolve) |
|
|
403
|
-
| [`reference/fix-protocol.md`](reference/fix-protocol.md) | pr-converge deltas on the shared fix protocol |
|
|
404
|
-
|
|
405
|
-
## Package file index
|
|
406
|
-
|
|
407
|
-
| Path | Purpose |
|
|
408
|
-
|---|---|
|
|
409
|
-
| `SKILL.md` | Hub: pre-flight, state, progress checklist, gotchas |
|
|
410
|
-
| `CLAUDE.md` | Package map for agents working in this skill |
|
|
411
|
-
| `pr_converge_skill_constants/` | Importable constants for skill scripts |
|
|
412
|
-
| `reference/per-tick.md` | Full per-tick procedure (phases, cwd routing, pacing) |
|
|
413
|
-
| `reference/convergence-gates.md` | Six gates before mark-ready |
|
|
414
|
-
| `reference/fix-protocol.md` | pr-converge fix-protocol deltas |
|
|
415
|
-
| `reference/ground-rules.md` | Non-negotiable loop constraints |
|
|
416
|
-
| `reference/state-schema.md` | `pr-converge-state.json` fields |
|
|
417
|
-
| `reference/stop-conditions.md` | Hard stops without convergence |
|
|
418
|
-
| `reference/multi-pr-orchestration.md` | Multi-PR session orchestration |
|
|
419
|
-
| `reference/examples.md` | Worked tick sequences |
|
|
420
|
-
| `reference/obstacles/` | Per-obstacle fix runbooks |
|
|
421
|
-
| `scripts/` | Convergence helpers, Copilot fetch, fix-reply poster, tests |
|
|
422
|
-
| `workflows/schedule-wakeup-loop.md` | ScheduleWakeup pacing |
|
|
1
|
+
---
|
|
2
|
+
name: pr-converge
|
|
3
|
+
description: >-
|
|
4
|
+
Loops Cursor Bugbot, a code review, a bug audit, and Copilot on the current
|
|
5
|
+
PR, applying TDD fixes until all are clean on one HEAD. Use when the user says
|
|
6
|
+
'/pr-converge', 'drive PR to convergence', 'loop bugbot and bugteam', 'babysit
|
|
7
|
+
bugbot and bugteam', 'until both are clean', or 'converge this PR'.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# PR Converge
|
|
11
|
+
|
|
12
|
+
One tick per invocation. A code-review ↔ bugteam ↔ Bugbot ↔ Copilot loop on
|
|
13
|
+
a draft PR: the internal code-review and bugteam passes drive the code to clean
|
|
14
|
+
first, then Cursor Bugbot and Copilot run as terminal confirmation gates, until
|
|
15
|
+
all are clean on the same `HEAD` and mergeable.
|
|
16
|
+
|
|
17
|
+
## Transport check (before any GitHub step)
|
|
18
|
+
|
|
19
|
+
Run `command -v gh`; when it succeeds, run `gh auth status`; once the PR
|
|
20
|
+
scope is resolved, run `gh api repos/<owner>/<repo> --jq .permissions.push`
|
|
21
|
+
and take `true` as the pass. When any check fails, run the
|
|
22
|
+
`pr-loop-cloud-transport` skill first and route every `gh` operation in this
|
|
23
|
+
skill through its substitution matrix.
|
|
24
|
+
|
|
25
|
+
## Pre-flight
|
|
26
|
+
|
|
27
|
+
Pause and physically scan the tool list at the top of this conversation
|
|
28
|
+
for the literal string `ScheduleWakeup`. If you see it, proceed. If the
|
|
29
|
+
string is absent after scanning, report `pr-converge requires
|
|
30
|
+
ScheduleWakeup; aborting` and stop.
|
|
31
|
+
|
|
32
|
+
Call `EnterWorktree` with no arguments before any API call, file read, or
|
|
33
|
+
edit. Agent-view sessions start in the shared checkout; Bash (`gh`, `git`)
|
|
34
|
+
does not auto-isolate. Do not proceed until the working directory contains
|
|
35
|
+
`.claude/worktrees/`. If `EnterWorktree` fails, report the failure and stop.
|
|
36
|
+
|
|
37
|
+
`EnterWorktree` isolates the session repo. When the PR lives in a different
|
|
38
|
+
repo, Step 1.5 routes the working directory into a **PR worktree** of that
|
|
39
|
+
repo on its head branch (routine, never a pause). See
|
|
40
|
+
[`reference/per-tick.md` § Step 1.5](reference/per-tick.md).
|
|
41
|
+
|
|
42
|
+
## Resume from a prior run
|
|
43
|
+
|
|
44
|
+
Before Step 0, check
|
|
45
|
+
`~/.claude/runtime/pr-loop/bugteam-pr-<PR number>/handoff.json`. When
|
|
46
|
+
`$CLAUDE_JOB_DIR/pr-converge-state.json` is absent but that handoff exists, seed
|
|
47
|
+
`phase`, `tick_count`, and the clean-at SHAs from the run's `state-copy.json`, so
|
|
48
|
+
a fresh session continues where the last one stopped rather than restarting at
|
|
49
|
+
CODE_REVIEW.
|
|
50
|
+
|
|
51
|
+
## Copilot quota pre-check (start of run)
|
|
52
|
+
|
|
53
|
+
On the first tick, apply the `reviewer-gates` skill's Copilot quota gate
|
|
54
|
+
(`../reviewer-gates/SKILL.md` § Gate 2) — once per run, never per tick. The
|
|
55
|
+
flag lives in `pr-converge-state.json` as `copilot_down`, and every tick reads
|
|
56
|
+
it. While `copilot_down` is true, the tick skips all Copilot work — no fetch,
|
|
57
|
+
no request, no poll, no agent — and exports
|
|
58
|
+
`CLAUDE_REVIEWS_DISABLED="copilot"` in that tick's shell before
|
|
59
|
+
`check_convergence.py`, so the check bypasses the Copilot review gate and the
|
|
60
|
+
pending-requested-reviews gate and the run still marks ready on the remaining
|
|
61
|
+
signals.
|
|
62
|
+
|
|
63
|
+
## Copilot findings — tier, verify, then route
|
|
64
|
+
|
|
65
|
+
Tier, verify, and route each Copilot finding via
|
|
66
|
+
[`copilot-finding-triage`](../copilot-finding-triage/SKILL.md) (self-healing
|
|
67
|
+
auto-fix; code-concern verify then confirmed / refuted / inconclusive).
|
|
68
|
+
|
|
69
|
+
**pr-converge phase routing after triage:**
|
|
70
|
+
|
|
71
|
+
- Self-healing and **confirmed** findings join the fix tick on `current_head`;
|
|
72
|
+
after push, reset clean-at SHAs, set `phase = CODE_REVIEW`, return to Step 5.
|
|
73
|
+
- **Refuted** findings resolve clean on the thread; no phase change.
|
|
74
|
+
- One or more **inconclusive** findings: do not mark ready. Run the triage
|
|
75
|
+
skill's user gate (ntfy + 45-minute hold across ticks; persist the deadline so
|
|
76
|
+
each tick reads it on entry). Act on the user's direction inside the window;
|
|
77
|
+
on timeout, teardown and report the findings un-reviewed.
|
|
78
|
+
- Enter `COPILOT_WAIT` only from gate (d) after requesting a Copilot review
|
|
79
|
+
(Step 7 → 7a). Stay on `COPILOT_WAIT` until a review surfaces at
|
|
80
|
+
`current_head`, `copilot_wait_count >= 3` hard-blocks, or `copilot_down`
|
|
81
|
+
skips the Copilot path entirely.
|
|
82
|
+
|
|
83
|
+
## Budget-aware tick boundaries
|
|
84
|
+
|
|
85
|
+
Before starting any tick, estimate whether the remaining session/usage
|
|
86
|
+
budget covers one full clean tick (worst case: a BUGBOT fetch + a
|
|
87
|
+
full-diff CODE_REVIEW + a fix commit + replies). If it does not, do not
|
|
88
|
+
start the tick. Stop at the current tick boundary: write updated state to
|
|
89
|
+
`$CLAUDE_JOB_DIR/pr-converge-state.json`, write the durable handoff (see
|
|
90
|
+
[State persistence](#state-persistence)), then report the exact resume
|
|
91
|
+
command (`/pr-converge <PR URL>`) and the persisted `phase`/`tick_count`.
|
|
92
|
+
A tick cut off mid-flight poisons the resume state — clean SHAs recorded
|
|
93
|
+
against work that never landed — so an unstarted tick is always cheaper
|
|
94
|
+
than a half-finished one.
|
|
95
|
+
|
|
96
|
+
## Findings discipline
|
|
97
|
+
|
|
98
|
+
Every finding, reply, and report states verified facts only — no hedging
|
|
99
|
+
language (`likely`, `probably`, `should`, `appears to`). Verify each
|
|
100
|
+
claim against the code on `current_head` before stating it; the
|
|
101
|
+
anti-hallucination Stop hook rejects hedged output, forcing a rework
|
|
102
|
+
pass. A claim that cannot be verified is reported as unverified, not
|
|
103
|
+
softened.
|
|
104
|
+
|
|
105
|
+
## State persistence
|
|
106
|
+
|
|
107
|
+
Single-PR mode persists loop state to `$CLAUDE_JOB_DIR/pr-converge-state.json`.
|
|
108
|
+
On tick entry, read this file if it exists to restore phase, tick_count, and
|
|
109
|
+
clean-at SHAs. On tick exit, write updated state before calling ScheduleWakeup
|
|
110
|
+
so the next tick resumes with accurate state.
|
|
111
|
+
|
|
112
|
+
After the state write and before ScheduleWakeup, write the durable handoff so a
|
|
113
|
+
fresh session in a new job can resume this run:
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
python "$HOME/.claude/skills/_shared/pr-loop/scripts/write_handoff.py" \
|
|
117
|
+
--pr-number <N> --head-ref <branch> --phase <phase> \
|
|
118
|
+
--resume-command "/pr-converge <PR URL>" \
|
|
119
|
+
--state-file "$CLAUDE_JOB_DIR/pr-converge-state.json" \
|
|
120
|
+
--completed-steps "<clean phases this run>"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
It writes `handoff.json`, `HANDOFF.md`, and `state-copy.json` under
|
|
124
|
+
`~/.claude/runtime/pr-loop/<run-name>/`. The job-dir state stays the source of
|
|
125
|
+
truth for a resumed tick; the handoff copy is the pointer a fresh session reads
|
|
126
|
+
when `$CLAUDE_JOB_DIR` is gone.
|
|
127
|
+
|
|
128
|
+
Fields: `phase`, `tick_count`, `bugbot_clean_at`, `code_review_clean_at`,
|
|
129
|
+
`bugteam_clean_at`, `copilot_clean_at`, `merge_state_status`, `current_head`,
|
|
130
|
+
`bugbot_acknowledged_at`, `bugbot_down`, `copilot_down`,
|
|
131
|
+
`bugteam_skill_invoked_at_head`, `bugteam_skill_invoked_at_tick`,
|
|
132
|
+
`agents_session_id`, `persistent_agents`.
|
|
133
|
+
|
|
134
|
+
## Persistent per-step agents
|
|
135
|
+
|
|
136
|
+
Three step-scoped agents persist across ticks so their context carries
|
|
137
|
+
forward: `fix_executor`, `thread_sweep`, and `copilot_watch`, recorded in
|
|
138
|
+
the `persistent_agents` map
|
|
139
|
+
([`reference/state-schema.md`](reference/state-schema.md)).
|
|
140
|
+
|
|
141
|
+
- **Resume:** read `persistent_agents.<key>`. When an entry exists,
|
|
142
|
+
`SendMessage` to the stored `agent_id` with a tick payload that restates
|
|
143
|
+
the PR scope, `current_head`, the PR worktree path, this tick's findings
|
|
144
|
+
or threads, and the report-back contract. Bump `last_used_tick`, then
|
|
145
|
+
await completion.
|
|
146
|
+
- **First spawn:** `Agent(subagent_type: "clean-coder", name:
|
|
147
|
+
"prc-fix-<PR#>")` — the `name` makes the agent a persistent teammate
|
|
148
|
+
that idles awaiting messages. Record `{agent_id, created_tick,
|
|
149
|
+
last_used_tick}` under the step key. Keep the spawn prompt fix-shaped,
|
|
150
|
+
never audit-shaped: the `pr_converge_bugteam_enforcer` hook blocks
|
|
151
|
+
audit-shaped clean-coder spawns during the BUGTEAM phase.
|
|
152
|
+
- **Stale or dead id:** on a `SendMessage` failure, or no acknowledgment
|
|
153
|
+
within one bounded wait, drop the map entry, spawn a fresh named agent,
|
|
154
|
+
record it, and continue the tick. Never abort a tick on a stale id;
|
|
155
|
+
never retry the same dead id.
|
|
156
|
+
- **Fresh every round (never persisted):** the Step 5 host-aware
|
|
157
|
+
`invoke_code_review.py` / `/code-review high --fix` pass and the Step 6
|
|
158
|
+
bugteam audit (unbiased eyes each round; the enforcer needs the formal
|
|
159
|
+
Skill call), and every `code-verifier` — a named code-verifier never fires
|
|
160
|
+
`SubagentStop`, so no verdict mints (see the named-`code-verifier` entry
|
|
161
|
+
in the Gotchas list below).
|
|
162
|
+
- **Shutdown:** at loop end (convergence or a stop condition), send each
|
|
163
|
+
persistent agent a shutdown request and clear `persistent_agents` before
|
|
164
|
+
the `pr-loop-lifecycle` Close.
|
|
165
|
+
|
|
166
|
+
## Gotchas
|
|
167
|
+
|
|
168
|
+
Highest-signal content. Append a bullet each time a tick fails in a new
|
|
169
|
+
way — these are the hard-won lessons that keep the loop honest. Once this
|
|
170
|
+
grows to 5 or more items, suggest spinning up a subagent to investigate, fix,
|
|
171
|
+
post a fresh PR in a fresh branch based on origin main to the user.
|
|
172
|
+
|
|
173
|
+
- **`ScheduleWakeup` not in subagent tool registries** — background
|
|
174
|
+
`general-purpose` tick cannot schedule re-entry; only parent session
|
|
175
|
+
with `ScheduleWakeup` in registry can call it.
|
|
176
|
+
- **`state.json` without §Concurrency lock loses merges** when teammates
|
|
177
|
+
finish in same wall-clock window.
|
|
178
|
+
- **`tick_count` must not double-increment** — conversation state line
|
|
179
|
+
only when **no** `state.json`; with `state.json`, only the
|
|
180
|
+
orchestrator bump increments.
|
|
181
|
+
- **Bugbot trigger and detection** — CI check-run based. Full flow at
|
|
182
|
+
[`per-tick.md` Step 3](reference/per-tick.md); see also
|
|
183
|
+
`~/.claude/skills/pr-converge/scripts/check_bugbot_ci.py --help`.
|
|
184
|
+
- **Bot login fields differ by endpoint** — `get_reviews` returns
|
|
185
|
+
`.user.login` (object), but `get_review_comments` returns `.author`
|
|
186
|
+
(string, not an object). Always check the correct fields and use
|
|
187
|
+
case-insensitive substring matching on login values, never strict
|
|
188
|
+
equality.
|
|
189
|
+
- **`isOutdated` has dual scope** — GitHub marks a thread `isOutdated=true`
|
|
190
|
+
when the line it anchors to has changed since the comment was posted. The
|
|
191
|
+
machine gate (`check_convergence.py` / [convergence-gates.md](reference/convergence-gates.md)
|
|
192
|
+
gate (e)) excludes `isOutdated == true` bot threads from the fail count —
|
|
193
|
+
only non-outdated unresolved bot threads fail the gate. The agent-side
|
|
194
|
+
unresolved-thread sweep in the shared fix protocol
|
|
195
|
+
([`../../_shared/pr-loop/fix-protocol.md`](../../_shared/pr-loop/fix-protocol.md)
|
|
196
|
+
step 12; skill deltas in [`reference/fix-protocol.md`](reference/fix-protocol.md))
|
|
197
|
+
still verifies outdated unresolved threads against HEAD before resolve when
|
|
198
|
+
the protocol requires it (the original concern can still apply when the fix
|
|
199
|
+
moved rather than landed).
|
|
200
|
+
- **Tilde paths fail on Windows Git Bash** — `~/.claude/skills/...`
|
|
201
|
+
resolves to the wrong home directory in Bash-tool contexts. Use
|
|
202
|
+
`$HOME/.claude/skills/...` in shell invocations or `Path.home() /
|
|
203
|
+
".claude/skills/..."` in Python scripts. Script invocations through
|
|
204
|
+
Bash that reference `~` produce "file not found" errors
|
|
205
|
+
indistinguishable from actual script failures.
|
|
206
|
+
- **PowerShell cmdlets fail in Bash tool** — `.ps1` scripts and
|
|
207
|
+
`pwsh` calls run through the PowerShell tool, not Bash. Bash on
|
|
208
|
+
Windows is Git Bash which cannot execute PowerShell cmdlets. Route all
|
|
209
|
+
PowerShell work through the PowerShell tool or `pwsh -NoProfile -File`.
|
|
210
|
+
- **Cross-repo PR: route cwd into the PR worktree before Step 5 review** —
|
|
211
|
+
`invoke_code_review.py` and `/code-review high --fix` audit the repo of the
|
|
212
|
+
cwd (the helper's `--cwd`). When the session is rooted in a different repo
|
|
213
|
+
than the PR, `EnterWorktree` cannot re-root (it is scoped to the session's
|
|
214
|
+
repo); resolve the PR worktree and `cd` into it per
|
|
215
|
+
[Step 1.5](reference/per-tick.md). Skipping this reviews and edits the
|
|
216
|
+
wrong repo. The route is routine and automatic — never a material fork
|
|
217
|
+
to pause on.
|
|
218
|
+
- **A named/teammate `code-verifier` never mints a verdict** — In a background-job session, an `Agent`-tool `code-verifier` spawned with a `name` (or otherwise as a persistent teammate) goes idle/"available" awaiting messages rather than terminating, so its `SubagentStop` never fires. `verifier_verdict_minter.py` mints the verdict only on `SubagentStop`, so no verdict file is written and `verified_commit_gate` blocks the `git commit`/`git push` with "no passing verification verdict" even though the verifier emitted `all_pass`. A `shutdown_request` is ignored and `TaskStop` cannot resolve the teammate's id. Spawn the code-verifier as a one-shot agent with NO `name` (a plain async `Agent` call) so it runs to completion and fires `SubagentStop`, minting the verdict bound to the live surface. Keep the work tree frozen between verification and the commit so the minted surface hash still matches.
|
|
219
|
+
|
|
220
|
+
## Progress checklist
|
|
221
|
+
|
|
222
|
+
Run the eleven-step per-tick sequence in
|
|
223
|
+
[`reference/progress-checklist.md`](reference/progress-checklist.md): Step 0
|
|
224
|
+
(open the run) through Step 11 (final report), each step pointing to its spoke
|
|
225
|
+
file for MCP calls, script invocations, and decision criteria. State variables
|
|
226
|
+
live in [`reference/state-schema.md`](reference/state-schema.md); ground rules in
|
|
227
|
+
[`reference/ground-rules.md`](reference/ground-rules.md).
|
|
228
|
+
|
|
229
|
+
Two hard gates bind every step:
|
|
230
|
+
|
|
231
|
+
- **No unresolved threads.** Do not advance from any step while any unresolved
|
|
232
|
+
review thread exists on the PR (sweep semantics in the shared fix protocol,
|
|
233
|
+
[`../../_shared/pr-loop/fix-protocol.md`](../../_shared/pr-loop/fix-protocol.md)
|
|
234
|
+
step 12).
|
|
235
|
+
- **Full-PR-diff.** Every CODE-REVIEW (Step 5) and BUGTEAM (Step 6) round covers
|
|
236
|
+
the full `origin/main...HEAD` diff — a "clean" verdict against a partial diff
|
|
237
|
+
is not a valid clean.
|
|
238
|
+
|
|
239
|
+
## Edge cases
|
|
240
|
+
|
|
241
|
+
| Situation | Read |
|
|
242
|
+
|---|---|
|
|
243
|
+
| Multi-PR session (`state.json` exists) | [`reference/multi-pr-orchestration.md`](reference/multi-pr-orchestration.md) |
|
|
244
|
+
| Hard blocker or user stops loop | [`reference/stop-conditions.md`](reference/stop-conditions.md) |
|
|
245
|
+
| Tick is ambiguous against the steps above | [`reference/examples.md`](reference/examples.md) |
|
|
246
|
+
|
|
247
|
+
## Depends on / invokes
|
|
248
|
+
|
|
249
|
+
| Skill / path | Role |
|
|
250
|
+
|---|---|
|
|
251
|
+
| `pr-loop-lifecycle` | Open (permission grant + worktree preflight) and Close (cleanup, PR description, revoke) |
|
|
252
|
+
| `reviewer-gates` | Copilot quota gate, Bugbot availability, Bugbot trigger flow |
|
|
253
|
+
| `copilot-finding-triage` | Tier, verify, and route each Copilot finding |
|
|
254
|
+
| `bugteam` | Full-diff bug audit in Step 6 (Skill invocation mandatory) |
|
|
255
|
+
| `pr-loop-cloud-transport` | When `gh` is unavailable or unauthenticated |
|
|
256
|
+
| [`../../_shared/pr-loop/fix-protocol.md`](../../_shared/pr-loop/fix-protocol.md) | Shared 13-step fix sequence (TDD, commit, push, reply, resolve) |
|
|
257
|
+
| [`reference/fix-protocol.md`](reference/fix-protocol.md) | pr-converge deltas on the shared fix protocol |
|
|
258
|
+
|
|
259
|
+
## Package file index
|
|
260
|
+
|
|
261
|
+
| Path | Purpose |
|
|
262
|
+
|---|---|
|
|
263
|
+
| `SKILL.md` | Hub: pre-flight, state, progress checklist, gotchas |
|
|
264
|
+
| `CLAUDE.md` | Package map for agents working in this skill |
|
|
265
|
+
| `pr_converge_skill_constants/` | Importable constants for skill scripts |
|
|
266
|
+
| `reference/progress-checklist.md` | Per-tick eleven-step checklist (Step 0 open → Step 11 report) |
|
|
267
|
+
| `reference/per-tick.md` | Full per-tick procedure (phases, cwd routing, pacing) |
|
|
268
|
+
| `reference/convergence-gates.md` | Six gates before mark-ready |
|
|
269
|
+
| `reference/fix-protocol.md` | pr-converge fix-protocol deltas |
|
|
270
|
+
| `reference/ground-rules.md` | Non-negotiable loop constraints |
|
|
271
|
+
| `reference/state-schema.md` | `pr-converge-state.json` fields |
|
|
272
|
+
| `reference/stop-conditions.md` | Hard stops without convergence |
|
|
273
|
+
| `reference/multi-pr-orchestration.md` | Multi-PR session orchestration |
|
|
274
|
+
| `reference/examples.md` | Worked tick sequences |
|
|
275
|
+
| `reference/obstacles/` | Per-obstacle fix runbooks |
|
|
276
|
+
| `scripts/` | Convergence helpers, Copilot fetch, fix-reply poster, tests |
|
|
277
|
+
| `workflows/schedule-wakeup-loop.md` | ScheduleWakeup pacing |
|