claude-dev-env 2.4.0 → 2.5.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.
Files changed (195) hide show
  1. package/CLAUDE.md +53 -49
  2. package/_shared/pr-loop/scripts/_claude_permissions_common.py +84 -0
  3. package/_shared/pr-loop/scripts/code_rules_gate.py +4 -2
  4. package/_shared/pr-loop/scripts/grant_project_claude_permissions.py +306 -306
  5. package/_shared/pr-loop/scripts/pr_loop_shared_constants/claude_permissions_constants.py +44 -0
  6. package/_shared/pr-loop/scripts/pr_loop_shared_constants/copilot_quota_constants.py +24 -24
  7. package/_shared/pr-loop/scripts/pr_loop_shared_constants/stale_worktree_rule_sweep_constants.py +107 -107
  8. package/_shared/pr-loop/scripts/revoke_project_claude_permissions.py +290 -48
  9. package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +42 -2
  10. package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +36 -0
  11. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +100 -1
  12. package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -497
  13. package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +311 -2
  14. package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep.py +301 -301
  15. package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep_constants.py +85 -85
  16. package/_shared/pr-loop/worker-spawn.md +1 -1
  17. package/agents/CLAUDE.md +2 -1
  18. package/agents/caveman.md +0 -1
  19. package/agents/clasp-deployment-orchestrator.md +0 -1
  20. package/agents/clean-coder.md +0 -1
  21. package/agents/code-advisor.md +0 -1
  22. package/agents/code-quality-agent.md +1 -2
  23. package/agents/code-verifier.md +0 -1
  24. package/agents/deep-research.md +0 -1
  25. package/agents/docs-agent.md +0 -1
  26. package/agents/git-commit-crafter.md +0 -1
  27. package/agents/issue-tracker.md +42 -0
  28. package/agents/plan-packet-validator.md +0 -1
  29. package/agents/pr-description-writer.md +0 -1
  30. package/agents/test_agent_frontmatter.py +67 -18
  31. package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +143 -141
  32. package/bin/CLAUDE.md +68 -5
  33. package/bin/ever-shipped-skills.mjs +1 -0
  34. package/bin/install-constants.mjs +88 -0
  35. package/bin/install.mjs +1138 -114
  36. package/bin/install.prune.test.mjs +869 -19
  37. package/bin/install.test.mjs +906 -2
  38. package/commands/implement.md +1 -1
  39. package/commands/right-size.md +1 -1
  40. package/docs/CLAUDE.md +1 -0
  41. package/docs/host-pool-health-monitor.md +102 -0
  42. package/docs/references/CLAUDE.md +4 -2
  43. package/docs/references/advisor-tool.md +13 -0
  44. package/docs/references/code-review-enforcement.md +10 -0
  45. package/docs/references/team-advisor-skill.md +14 -0
  46. package/hooks/blocking/CLAUDE.md +1 -0
  47. package/hooks/blocking/code_review_pr_create_gate.py +7 -3
  48. package/hooks/blocking/code_review_push_gate.py +9 -4
  49. package/hooks/blocking/code_review_stamp_directory_write_blocker.py +8 -0
  50. package/hooks/blocking/config/__init__.py +5 -5
  51. package/hooks/blocking/config/code_review_enforcement_constants.py +4 -1
  52. package/hooks/blocking/config/test_code_review_enforcement_constants.py +5 -0
  53. package/hooks/blocking/config/verified_commit_constants.py +160 -159
  54. package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +256 -0
  55. package/hooks/blocking/pre_tool_use_dispatcher.py +24 -24
  56. package/hooks/blocking/test_code_review_pr_create_gate.py +14 -0
  57. package/hooks/blocking/test_code_review_push_gate.py +16 -0
  58. package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +19 -0
  59. package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +231 -0
  60. package/hooks/blocking/test_pre_tool_use_dispatcher.py +10 -1
  61. package/hooks/blocking/test_verdict_directory_write_blocker.py +808 -808
  62. package/hooks/blocking/test_verification_verdict_store.py +54 -0
  63. package/hooks/blocking/test_verified_commit_gate.py +581 -581
  64. package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +131 -131
  65. package/hooks/blocking/verdict_directory_write_blocker.py +687 -687
  66. package/hooks/blocking/verification_verdict_store.py +1039 -1036
  67. package/hooks/blocking/verified_commit_message_accuracy_blocker.py +167 -167
  68. package/hooks/blocking/verifier_verdict_minter.py +280 -280
  69. package/hooks/git-hooks/test_pre_push.py +25 -0
  70. package/hooks/hooks.json +10 -0
  71. package/hooks/hooks_constants/CLAUDE.md +2 -1
  72. package/hooks/hooks_constants/enter_worktree_prefetch_constants.py +18 -18
  73. package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +48 -0
  74. package/hooks/hooks_constants/ruff_integration_constants.py +16 -0
  75. package/hooks/lifecycle/enter_worktree_origin_prefetch.py +163 -146
  76. package/hooks/lifecycle/test_enter_worktree_origin_prefetch.py +185 -178
  77. package/hooks/pyproject.toml +1 -0
  78. package/hooks/validators/CLAUDE.md +1 -0
  79. package/hooks/validators/config/__init__.py +0 -0
  80. package/hooks/validators/config/directory_exemption_constants.py +183 -0
  81. package/hooks/validators/config/test_directory_exemption_constants.py +21 -0
  82. package/hooks/validators/conftest.py +4 -0
  83. package/hooks/validators/ruff_integration.py +49 -5
  84. package/hooks/validators/run_all_validators.py +206 -9
  85. package/hooks/validators/test_directory_exemption_constants.py +185 -0
  86. package/hooks/validators/test_python_antipattern_checks.py +110 -5
  87. package/hooks/validators/test_ruff_integration.py +92 -1
  88. package/hooks/validators/test_run_all_validators.py +115 -68
  89. package/hooks/validators/test_run_all_validators_pretooluse.py +159 -1
  90. package/package.json +10 -2
  91. package/rules/CLAUDE.md +1 -0
  92. package/rules/docstring-prose-matches-implementation.md +45 -44
  93. package/rules/state-what-is.md +25 -0
  94. package/rules/verified-commit-gate-skip.md +1 -1
  95. package/scripts/CLAUDE.md +1 -0
  96. package/scripts/Capture-PoolHealth.ps1 +410 -0
  97. package/scripts/_code_review_test_support.py +404 -0
  98. package/scripts/claude_chain_runner.py +141 -1
  99. package/scripts/conftest.py +16 -1
  100. package/scripts/dev_env_scripts_constants/CLAUDE.md +1 -1
  101. package/scripts/dev_env_scripts_constants/claude_chain_constants.py +9 -0
  102. package/scripts/resolve_worker_spawn.py +626 -626
  103. package/scripts/spawn_grok_batch.py +672 -672
  104. package/scripts/test_claude_chain_runner.py +131 -0
  105. package/scripts/test_invoke_code_review_chain.py +70 -0
  106. package/scripts/test_invoke_code_review_cli.py +192 -0
  107. package/scripts/test_invoke_code_review_contract.py +256 -0
  108. package/scripts/test_invoke_code_review_git.py +123 -0
  109. package/scripts/test_invoke_code_review_mode.py +99 -0
  110. package/scripts/test_resolve_worker_spawn.py +1014 -1014
  111. package/skills/CLAUDE.md +2 -0
  112. package/skills/auditing-claude-config/SKILL.md +114 -114
  113. package/skills/autoconverge/SKILL.md +427 -427
  114. package/skills/autoconverge/reference/convergence.md +24 -3
  115. package/skills/autoconverge/workflow/CLAUDE.md +1 -0
  116. package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +3 -3
  117. package/skills/autoconverge/workflow/converge.contract.test.mjs +1263 -1263
  118. package/skills/autoconverge/workflow/converge.mjs +167 -0
  119. package/skills/autoconverge/workflow/converge.p2-advance.test.mjs +202 -0
  120. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +2 -2
  121. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +2 -2
  122. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +2 -2
  123. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +2 -2
  124. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +2 -2
  125. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +2 -2
  126. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +2 -2
  127. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +2 -2
  128. package/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +265 -265
  129. package/skills/closeout/SKILL.md +33 -50
  130. package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +8 -0
  131. package/skills/codex-review/scripts/run_codex_review.py +233 -1
  132. package/skills/codex-review/scripts/test_run_codex_review.py +189 -0
  133. package/skills/condensing-instructions/SKILL.md +81 -0
  134. package/skills/copilot-review/SKILL.md +119 -119
  135. package/skills/e-code-review/SKILL.md +52 -0
  136. package/skills/e-code-review/reference/fix.md +54 -0
  137. package/skills/e-code-review/reference/loop.md +43 -0
  138. package/skills/e-code-review/reference/low.md +57 -0
  139. package/skills/e-code-review/reference/medium.md +153 -0
  140. package/skills/e-code-review/reference/xhigh.md +182 -0
  141. package/skills/e-simplify/SKILL.md +97 -0
  142. package/skills/issue-tracker/SKILL.md +92 -0
  143. package/skills/issue-tracker/reference/epic-and-sub-issue-model.md +55 -0
  144. package/skills/issue-tracker/reference/handoff-schema.md +64 -0
  145. package/skills/issue-tracker/reference/operation-matrix.md +41 -0
  146. package/skills/orchestrator/SKILL.md +162 -21
  147. package/skills/orchestrator/scripts/status_gate.py +625 -0
  148. package/skills/orchestrator/scripts/status_gate_constants/__init__.py +1 -0
  149. package/skills/orchestrator/scripts/status_gate_constants/config/__init__.py +1 -0
  150. package/skills/orchestrator/scripts/status_gate_constants/config/constants.py +47 -0
  151. package/skills/orchestrator/scripts/test_status_gate.py +439 -0
  152. package/skills/orchestrator-refresh/SKILL.md +110 -35
  153. package/skills/plan-to-pr/SKILL.md +155 -0
  154. package/skills/plan-to-pr/reference/final-validation-tasks.md +15 -0
  155. package/skills/plan-to-pr/reference/model-routing.md +36 -0
  156. package/skills/plan-to-pr/reference/packet-contract.md +43 -0
  157. package/skills/plan-to-pr/reference/packet-schema.json +57 -0
  158. package/skills/plan-to-pr/reference/process-inventory.md +22 -0
  159. package/skills/plan-to-pr/reference/review-loop.md +33 -0
  160. package/skills/plan-to-pr/reference/run-record.schema.json +27 -0
  161. package/skills/plan-to-pr/reference/self-audit-tasks.md +15 -0
  162. package/skills/plan-to-pr/reference/task-seeds.md +14 -0
  163. package/skills/plan-to-pr/reference/task-ticket.md +38 -0
  164. package/skills/plan-to-pr/scripts/config/__init__.py +1 -0
  165. package/skills/plan-to-pr/scripts/config/constants.py +193 -0
  166. package/skills/plan-to-pr/scripts/create_packet.py +173 -0
  167. package/skills/plan-to-pr/scripts/test_create_packet.py +102 -0
  168. package/skills/plan-to-pr/scripts/test_validate_packet.py +256 -0
  169. package/skills/plan-to-pr/scripts/test_validate_protocol.py +135 -0
  170. package/skills/plan-to-pr/scripts/test_validate_run.py +158 -0
  171. package/skills/plan-to-pr/scripts/validate_packet.py +655 -0
  172. package/skills/plan-to-pr/scripts/validate_protocol.py +622 -0
  173. package/skills/plan-to-pr/scripts/validate_run.py +173 -0
  174. package/skills/plan-to-pr/test_skill_contract.py +207 -0
  175. package/skills/plan-to-pr/test_task_ticket_contract.py +151 -0
  176. package/skills/pr-converge/SKILL.md +472 -469
  177. package/skills/pr-converge/reference/examples.md +3 -3
  178. package/skills/pr-converge/reference/fix-protocol.md +1 -1
  179. package/skills/pr-converge/reference/ground-rules.md +7 -4
  180. package/skills/pr-converge/reference/multi-pr-orchestration.md +4 -1
  181. package/skills/pr-converge/reference/per-tick.md +5 -5
  182. package/skills/pr-converge/reference/progress-checklist.md +1 -1
  183. package/skills/pr-converge/scripts/check_convergence_gates.py +279 -279
  184. package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -507
  185. package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -84
  186. package/skills/pr-converge/test_step5_host_branch.py +1 -1
  187. package/skills/pr-fix-protocol/SKILL.md +1 -1
  188. package/skills/privacy-hygiene/SKILL.md +68 -68
  189. package/skills/prototype/workflows/promotion.md +1 -1
  190. package/skills/release-notes-html/SKILL.md +164 -0
  191. package/skills/task-build/CLAUDE.md +8 -7
  192. package/skills/task-build/SKILL.md +16 -8
  193. package/skills/task-build/reference/tool-routing.md +19 -0
  194. package/scripts/test_invoke_code_review.py +0 -966
  195. package/skills/closeout/reference/issue-body-templates.md +0 -108
@@ -1,469 +1,472 @@
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
- A code-review ↔ bugteam ↔ Bugbot ↔ Copilot loop on a draft PR: the internal
13
- code-review and bugteam passes drive the code to clean first, then Cursor
14
- Bugbot and Copilot run as terminal confirmation gates, until all are clean on
15
- the same `HEAD` and mergeable. On `pacer=schedule_wakeup`, one tick runs per
16
- invocation and the next tick is scheduled. On `pacer=portable`, ticks run
17
- continuously in-session (see
18
- [`../_shared/pr-loop/portable-driver.md`](../_shared/pr-loop/portable-driver.md)).
19
-
20
- ## Transport check (before any GitHub step)
21
-
22
- Run `command -v gh`; when it succeeds, run `gh auth status`; once the PR
23
- scope is resolved, run `gh api repos/<owner>/<repo> --jq .permissions.push`
24
- and take `true` as the pass. When any check fails, run the
25
- `pr-loop-cloud-transport` skill first and route every `gh` operation in this
26
- skill through its substitution matrix.
27
-
28
- ## Pre-flight
29
-
30
- ### Task list (step 1)
31
-
32
- After down/opt-out flags are known, build the work list with
33
- `build_converge_task_list.py` only (same script as autoconverge). Register every
34
- task; **final task** is always `all_runnable_reviews_clean_same_head`. Complete
35
- that final task only when every runnable review is CLEAN on the same HEAD.
36
- On `pacer=portable`, `open-run` returns the same list.
37
-
38
- ### Pacer selection
39
-
40
- Scan the tool list for `ScheduleWakeup`. Record whether it is present, then
41
- select the pacer:
42
-
43
- ```
44
- python "$HOME/.claude/skills/_shared/pr-loop/scripts/select_converge_pacer.py" \
45
- --skill pr-converge \
46
- --has-workflow <0|1> \
47
- --has-schedule-wakeup <0|1>
48
- ```
49
-
50
- - `pacer=schedule_wakeup` — native tick pacing via `ScheduleWakeup` (see
51
- [`workflows/schedule-wakeup-loop.md`](workflows/schedule-wakeup-loop.md)).
52
- - `pacer=portable` — continuous in-session driver on
53
- [`../_shared/pr-loop/portable-driver.md`](../_shared/pr-loop/portable-driver.md).
54
- **Do not abort** because `ScheduleWakeup` is missing.
55
-
56
- ### Worktree isolation
57
-
58
- When the tool list includes `EnterWorktree`, call it with no arguments before
59
- any API call, file read, or edit. Agent-view sessions start in the shared
60
- checkout; Bash (`gh`, `git`) does not auto-isolate. Do not proceed until the
61
- working directory contains `.claude/worktrees/`. If `EnterWorktree` fails,
62
- report the failure and stop.
63
-
64
- When `EnterWorktree` is absent, isolate with git worktree machinery per
65
- [`../_shared/pr-loop/portable-driver.md`](../_shared/pr-loop/portable-driver.md)
66
- § Isolation and worktree, then run strict
67
- `preflight_worktree.py` for the PR's owner/repo. Fail closed only when the
68
- checkout is not the PR's repo on the PR head ref.
69
-
70
- `EnterWorktree` (or the portable worktree path) isolates the session repo.
71
- When the PR lives in a different repo, Step 1.5 routes the working directory
72
- into a **PR worktree** of that repo on its head branch (routine, never a
73
- pause). See [`reference/per-tick.md` § Step 1.5](reference/per-tick.md).
74
-
75
- ## Resume from a prior run
76
-
77
- Before Step 0, check
78
- `~/.claude/runtime/pr-loop/bugteam-pr-<PR number>/handoff.json`. When
79
- `$CLAUDE_JOB_DIR/pr-converge-state.json` is absent but that handoff exists, seed
80
- `phase`, `tick_count`, and the clean-at SHAs from the run's `state-copy.json`, so
81
- a fresh session continues where the last one stopped rather than restarting at
82
- CODE_REVIEW.
83
-
84
- ## Copilot quota pre-check (start of run)
85
-
86
- On the first tick, apply the `reviewer-gates` skill's Copilot quota gate
87
- (`../reviewer-gates/SKILL.md` § Gate 2) — once per run, never per tick. The
88
- flag lives in `pr-converge-state.json` as `copilot_down`, and every tick reads
89
- it. While `copilot_down` is true, the tick skips all Copilot work — no fetch,
90
- no request, no poll, no agent — and exports
91
- `CLAUDE_REVIEWS_DISABLED="copilot"` in that tick's shell before
92
- `check_convergence.py`, so the check bypasses the Copilot review gate and the
93
- pending-requested-reviews gate and the run still marks ready on the remaining
94
- signals.
95
-
96
- ## Copilot findings — tier, verify, then route
97
-
98
- Tier, verify, and route each Copilot finding via
99
- [`copilot-finding-triage`](../copilot-finding-triage/SKILL.md) (self-healing
100
- auto-fix; code-concern verify then confirmed / refuted / inconclusive).
101
-
102
- **pr-converge phase routing after triage:**
103
-
104
- - Self-healing and **confirmed** findings join the fix tick on `current_head`;
105
- after push, reset clean-at SHAs, set `phase = CODE_REVIEW`, return to Step 5.
106
- - **Refuted** findings resolve clean on the thread; no phase change.
107
- - One or more **inconclusive** findings: do not mark ready. Page via the
108
- triage skill's ntfy step, then hold by pacer:
109
- - **`pacer=schedule_wakeup`** — triage user gate (`ScheduleWakeup` /
110
- `send_later` 45-minute hold across ticks; persist the deadline so each tick
111
- reads it on entry).
112
- - **`pacer=portable`** — in-session deadline poll or handoff per
113
- [`../_shared/pr-loop/portable-driver.md`](../_shared/pr-loop/portable-driver.md);
114
- never `ScheduleWakeup` or `send_later`.
115
- Act on the user's direction inside the window; on timeout, teardown and report
116
- the findings un-reviewed.
117
- - Enter `COPILOT_WAIT` only from gate (d) after requesting a Copilot review
118
- (Step 7 → 7a). Stay on `COPILOT_WAIT` until a review surfaces at
119
- `current_head`, `copilot_wait_count >= 3` hard-blocks, or `copilot_down`
120
- skips the Copilot path entirely.
121
-
122
- ## Budget-aware tick boundaries
123
-
124
- Before starting any tick, estimate whether the remaining session/usage
125
- budget covers one full clean tick (worst case: a BUGBOT fetch + a
126
- full-diff CODE_REVIEW + a fix commit + replies). If it does not, do not
127
- start the tick. Stop at the current tick boundary: write updated state to
128
- `$CLAUDE_JOB_DIR/pr-converge-state.json`, write the durable handoff (see
129
- [State persistence](#state-persistence)), then report the exact resume
130
- command (`/pr-converge <PR URL>`) and the persisted `phase`/`tick_count`.
131
- A tick cut off mid-flight poisons the resume state — clean SHAs recorded
132
- against work that never landed — so an unstarted tick is always cheaper
133
- than a half-finished one.
134
-
135
- ## Findings discipline
136
-
137
- Every finding, reply, and report states verified facts only — no hedging
138
- language (`likely`, `probably`, `should`, `appears to`). Verify each
139
- claim against the code on `current_head` before stating it; the
140
- anti-hallucination Stop hook rejects hedged output, forcing a rework
141
- pass. A claim that cannot be verified is reported as unverified, not
142
- softened.
143
-
144
- ## State persistence
145
-
146
- Single-PR mode persists loop state to `$CLAUDE_JOB_DIR/pr-converge-state.json`.
147
- On tick entry, read this file if it exists to restore phase, tick_count, and
148
- clean-at SHAs. On tick exit, write updated state before the next pacer step
149
- (`ScheduleWakeup` when `pacer=schedule_wakeup`; continuous continue or
150
- in-session poll when `pacer=portable`) so the next tick resumes with accurate
151
- state.
152
-
153
- After the state write and before the next pacer step, write the durable handoff
154
- so a fresh session in a new job can resume this run:
155
-
156
- ```
157
- python "$HOME/.claude/skills/_shared/pr-loop/scripts/write_handoff.py" \
158
- --pr-number <N> --head-ref <branch> --phase <phase> \
159
- --resume-command "/pr-converge <PR URL>" \
160
- --state-file "$CLAUDE_JOB_DIR/pr-converge-state.json" \
161
- --completed-steps "<clean phases this run>"
162
- ```
163
-
164
- It writes `handoff.json`, `HANDOFF.md`, and `state-copy.json` under
165
- `~/.claude/runtime/pr-loop/<run-name>/`. The job-dir state stays the source of
166
- truth for a resumed tick; the handoff copy is the pointer a fresh session reads
167
- when `$CLAUDE_JOB_DIR` is gone.
168
-
169
- Fields: `phase`, `tick_count`, `bugbot_clean_at`, `code_review_clean_at`,
170
- `bugteam_clean_at`, `copilot_clean_at`, `merge_state_status`, `current_head`,
171
- `bugbot_acknowledged_at`, `bugbot_down`, `copilot_down`,
172
- `bugteam_skill_invoked_at_head`, `bugteam_skill_invoked_at_tick`,
173
- `agents_session_id`, `persistent_agents`.
174
-
175
- ## Persistent per-step agents
176
-
177
- Three step-scoped agents persist across ticks so their context carries
178
- forward: `fix_executor`, `thread_sweep`, and `copilot_watch`, recorded in
179
- the `persistent_agents` map
180
- ([`reference/state-schema.md`](reference/state-schema.md)).
181
-
182
- - **Resume:** read `persistent_agents.<key>`. When an entry exists,
183
- `SendMessage` to the stored `agent_id` with a tick payload that restates
184
- the PR scope, `current_head`, the PR worktree path, this tick's findings
185
- or threads, and the report-back contract. Bump `last_used_tick`, then
186
- await completion.
187
- - **First spawn:** `Agent(subagent_type: "clean-coder", name:
188
- "prc-fix-<PR#>")` — the `name` makes the agent a persistent teammate
189
- that idles awaiting messages. Record `{agent_id, created_tick,
190
- last_used_tick}` under the step key. Keep the spawn prompt fix-shaped,
191
- never audit-shaped: the `pr_converge_bugteam_enforcer` hook blocks
192
- audit-shaped clean-coder spawns during the BUGTEAM phase.
193
- - **Stale or dead id:** on a `SendMessage` failure, or no acknowledgment
194
- within one bounded wait, drop the map entry, spawn a fresh named agent,
195
- record it, and continue the tick. Never abort a tick on a stale id;
196
- never retry the same dead id.
197
- - **Fresh every round (never persisted):** the Step 5 host-aware
198
- `invoke_code_review.py` / `/code-review xhigh --fix` pass and the Step 6
199
- bugteam audit (unbiased eyes each round; the enforcer needs the formal
200
- Skill call), and every `code-verifier` a named code-verifier never fires
201
- `SubagentStop`, so no verdict mints (see the named-`code-verifier` entry
202
- in the Gotchas list below).
203
- - **Shutdown:** at loop end (convergence or a stop condition), send each
204
- persistent agent a shutdown request and clear `persistent_agents` before
205
- the `pr-loop-lifecycle` Close.
206
-
207
- ## Gotchas
208
-
209
- Highest-signal content. Append a bullet each time a tick fails in a new
210
- way — these are the hard-won lessons that keep the loop honest. Once this
211
- grows to 5 or more items, suggest spinning up a subagent to investigate, fix,
212
- post a fresh PR in a fresh branch based on origin main to the user.
213
-
214
- - **`ScheduleWakeup` not in subagent tool registries** — background
215
- `general-purpose` tick cannot schedule re-entry; only the parent session
216
- with `ScheduleWakeup` (or the portable continuous driver on the parent)
217
- may pace the next tick.
218
- - **Portable host has no durable wake outside the session** — when
219
- `pacer=portable`, budget and context bound how many ticks complete; write
220
- handoff at a tick boundary and resume with `/pr-converge <PR URL>`.
221
- - **`state.json` without §Concurrency lock loses merges** when teammates
222
- finish in same wall-clock window.
223
- - **`tick_count` must not double-increment** conversation state line
224
- only when **no** `state.json`; with `state.json`, only the
225
- orchestrator bump increments.
226
- - **Bugbot trigger and detection** — CI check-run based. Full flow at
227
- [`per-tick.md` Step 3](reference/per-tick.md); see also
228
- `~/.claude/skills/pr-converge/scripts/check_bugbot_ci.py --help`.
229
- - **Bot login fields differ by endpoint** — `get_reviews` returns
230
- `.user.login` (object), but `get_review_comments` returns `.author`
231
- (string, not an object). Always check the correct fields and use
232
- case-insensitive substring matching on login values, never strict
233
- equality.
234
- - **`isOutdated` has dual scope** GitHub marks a thread `isOutdated=true`
235
- when the line it anchors to has changed since the comment was posted. The
236
- machine gate (`check_convergence.py` / [convergence-gates.md](reference/convergence-gates.md)
237
- gate (e)) excludes `isOutdated == true` bot threads from the fail count
238
- only non-outdated unresolved bot threads fail the gate. The agent-side
239
- unresolved-thread sweep in the shared fix protocol
240
- ([`../../_shared/pr-loop/fix-protocol.md`](../../_shared/pr-loop/fix-protocol.md)
241
- step 12; skill deltas in [`reference/fix-protocol.md`](reference/fix-protocol.md))
242
- still verifies outdated unresolved threads against HEAD before resolve when
243
- the protocol requires it (the original concern can still apply when the fix
244
- moved rather than landed).
245
- - **Tilde paths fail on Windows Git Bash** `~/.claude/skills/...`
246
- resolves to the wrong home directory in Bash-tool contexts. Use
247
- `$HOME/.claude/skills/...` in shell invocations or `Path.home() /
248
- ".claude/skills/..."` in Python scripts. Script invocations through
249
- Bash that reference `~` produce "file not found" errors
250
- indistinguishable from actual script failures.
251
- - **PowerShell cmdlets fail in Bash tool** `.ps1` scripts and
252
- `pwsh` calls run through the PowerShell tool, not Bash. Bash on
253
- Windows is Git Bash which cannot execute PowerShell cmdlets. Route all
254
- PowerShell work through the PowerShell tool or `pwsh -NoProfile -File`.
255
- - **Cross-repo PR: route cwd into the PR worktree before Step 5 review** —
256
- `invoke_code_review.py` and `/code-review xhigh --fix` audit the repo of the
257
- cwd (the helper's `--cwd`). When the session is rooted in a different repo
258
- than the PR, `EnterWorktree` cannot re-root (it is scoped to the session's
259
- repo); resolve the PR worktree and `cd` into it per
260
- [Step 1.5](reference/per-tick.md). Skipping this reviews and edits the
261
- wrong repo. The route is routine and automatic never a material fork
262
- to pause on.
263
- - **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.
264
-
265
- ## Progress checklist
266
-
267
- State variables (`phase`, `bugbot_clean_at`, `code_review_clean_at`,
268
- `bugteam_clean_at`, `copilot_clean_at`, `merge_state_status`, counters) are
269
- defined in [`reference/state-schema.md`](reference/state-schema.md). Ground rules
270
- in [`reference/ground-rules.md`](reference/ground-rules.md).
271
-
272
- Each step references its spoke file for full procedural detail — MCP calls,
273
- script invocations, decision criteria. Every "return to Step N" means the next
274
- tick starts fresh from that step.
275
-
276
- **Hard gate: do not advance from any step while ANY unresolved review
277
- thread exists on the PR.** Sweep semantics and per-thread handling live in
278
- the shared fix protocol
279
- ([`../../_shared/pr-loop/fix-protocol.md`](../../_shared/pr-loop/fix-protocol.md)
280
- step 12; skill deltas in [`reference/fix-protocol.md`](reference/fix-protocol.md)).
281
-
282
- **Full-PR-diff rule: every CODE-REVIEW round (Step 5) and every BUGTEAM
283
- round (Step 6) covers the FULL `origin/main...HEAD` diff — every file
284
- the PR touches.** A round that scopes to a subset — only the last commit,
285
- only files touched since the prior clean SHA, only bugbot-flagged paths,
286
- or any other delta cut does not satisfy the gate, and a "clean" verdict
287
- against a partial diff is not a valid clean. Re-run the round against the
288
- full diff before recording `code_review_clean_at` or treating the bugteam
289
- round as converged. This rule holds every tick, every loop, every PR.
290
-
291
- - [ ] **Step 0: Open the run**
292
- Apply the `pr-loop-lifecycle` skill's Open section
293
- (`../pr-loop-lifecycle/SKILL.md`): permission grant + worktree preflight.
294
-
295
- - [ ] **Step 1: Resolve PR scope + PR worktree**
296
- Apply Step 1.5 scope resolution in
297
- [`reference/per-tick.md`](reference/per-tick.md). Resolve the **PR
298
- worktree** the local checkout every local step this tick targets:
299
- the `EnterWorktree` checkout when the PR shares the session's repo,
300
- else a checkout of the PR's repo that the working directory routes
301
- into via `cd`. Cross-repo routing is automatic, not a fork.
302
- See: [`reference/per-tick.md` § Step 1](reference/per-tick.md)
303
- and Step 1.5](reference/per-tick.md)
304
-
305
- - [ ] **Step 2: Initialize loop state**
306
- `phase = CODE_REVIEW`; all counters at zero; `run_name` resolved.
307
-
308
- - [ ] **Step 3: Mergeability check**
309
- See: [`reference/convergence-gates.md` § (c)](reference/convergence-gates.md)
310
-
311
- ```
312
- pull_request_read(method="get") .mergeable_state, .mergeable
313
- ```
314
-
315
- - [ ] mergeable → advance to Step 4
316
- - [ ] not mergeable → rebase → force-push → return to Step 1
317
-
318
- - [ ] **Step 4: BUGBOT terminal Bugbot confirmation gate**
319
- Step 6 routes here after BUGTEAM converges. Bugbot confirms code the
320
- internal passes already drove to clean.
321
- See: [`reference/per-tick.md` § BUGBOT terminal gate + Step 3](reference/per-tick.md);
322
- availability and trigger via `reviewer-gates`
323
- (`../reviewer-gates/SKILL.md` § Gate 1 / Gate 3).
324
-
325
- - [ ] **disabled / down** → `bugbot_down = true` → Step 7
326
- - [ ] **dirty on `current_head`** → apply shared fix protocol
327
- ([`../../_shared/pr-loop/fix-protocol.md`](../../_shared/pr-loop/fix-protocol.md);
328
- skill deltas in [`reference/fix-protocol.md`](reference/fix-protocol.md))
329
- push reset push-invalidated markers`phase = CODE_REVIEW` → Step 5
330
- - [ ] **clean on `current_head`** → zero unresolved threads (else fix + resolve first)
331
- `bugbot_clean_at = current_head` → Step 7
332
- - [ ] **no review / commit_id mismatch** `reviewer-gates` Bugbot flow (Gate 3):
333
- silent pass stamp + Step 7; queued/triggered ScheduleWakeup 360s or portable in-session poll Step 4;
334
- down → `bugbot_down = true` → Step 7
335
-
336
- - [ ] **Step 5: CODE-REVIEW static sweep, review, fix, advance**
337
- Entry phase every tick; re-entered after any fix push.
338
- See: [`reference/per-tick.md` § CODE_REVIEW entry](reference/per-tick.md).
339
- Pre-condition: cwd is the Step 1.5 PR worktree on `current_head`.
340
- Scope: FULL `origin/main...HEAD` diff every tick (no path args, no delta cut).
341
- Review always runs at effort high on model opus through
342
- `invoke_code_review.py`. Mode decision inputs: host profile + session
343
- model. Call:
344
- `python "$HOME/.claude/scripts/invoke_code_review.py" --cwd <PR-worktree>
345
- --session-model <alias>`. Chain mode uses that cwd and empty stdin; the
346
- chain process never commits and never pushes. JSON stdout carries
347
- `mode` (`in_session` | `chain`), `served_command`, `returncode`, and
348
- `dirty_tree`. Config/host errors still emit that JSON with non-zero
349
- `returncode` (no traceback-only failure).
350
-
351
- - [ ] **Static sweep fails** apply shared fix protocol → push → reset markers
352
- stay CODE_REVIEW → Step 5
353
- - [ ] **`mode == in_session`** (Claude host, session model opus) → run
354
- `/code-review xhigh --fix` in-session (no path args)
355
- - [ ] **`mode == chain`** (any other host or non-opus session) → helper
356
- already ran the headless review; read `returncode`,
357
- `served_command`, and `dirty_tree` from JSON
358
- - [ ] **failed review** (`returncode != 0`, or chain with null
359
- `served_command`) do not set `code_review_clean_at` → stay
360
- CODE_REVIEW Step 5
361
- - [ ] **fixes applied** (`dirty_tree` true / working tree dirty)
362
- commit + push via shared fix protocol reset markers → stay
363
- CODE_REVIEW → Step 5
364
- - [ ] **clean** (successful serve: `returncode == 0`, chain
365
- `served_command` non-null when chain, and `dirty_tree` false)
366
- zero unresolved threads (else fix + resolve) →
367
- `code_review_clean_at = current_head` `phase = BUGTEAM` Step 6
368
-
369
- - [ ] **Step 6: BUGTEAM — run, decide, fix, reply, resolve**
370
- See: [`reference/per-tick.md` § Step 2 BUGTEAM](reference/per-tick.md);
371
- [`../bugteam/SKILL.md`](../bugteam/SKILL.md).
372
- Pre-condition: `code_review_clean_at == current_head`.
373
- Mandatory: `Skill({skill: "bugteam", args: "<PR URL>"})` this tick
374
- (enforcer-blocked otherwise; `qbug` is not a substitute). Scope: FULL
375
- `origin/main...HEAD` diff. Re-resolve HEAD after bugteam.
376
-
377
- - [ ] **bugteam pushed** verify threads replied + resolved → reset markers
378
- `phase = CODE_REVIEW` ScheduleWakeup 360s or portable in-session poll Step 5
379
- - [ ] **converged, no push** → zero unresolved threads →
380
- `bugteam_clean_at = current_head``phase = BUGBOT`Step 4
381
- - [ ] **findings without committed fixes** apply shared fix protocol push
382
- reset markers `phase = CODE_REVIEW`Step 5
383
-
384
- - [ ] **Step 7: Convergence gates**
385
- Full procedure: [`reference/convergence-gates.md`](reference/convergence-gates.md).
386
-
387
- Pre-condition: Step 6 converged AND (`bugbot_clean_at == current_head` OR
388
- `bugbot_down`). The terminal Bugbot gate (Step 4) sets that state just
389
- before these gates run. Count unresolved threads before each gate.
390
- Every gate records evidence; gate (f) cites evidence from (a)–(e).
391
-
392
- - [ ] **(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
393
- - [ ] **(b) Claude reviewer** fetch Claude on `current_head`; dirty → fix + return to Step 5; clean or absent → continue
394
- - [ ] **(c) Mergeability** — `mergeable_state == "clean"` and `mergeable == true`; dirty → rebase + return to Step 1; blocked/behind/unknown/unstable → hard blocker
395
- - [ ] **(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)
396
- - [ ] **(e) Thread-resolution** — zero unresolved threads across the PR; else sweep + fix/resolve
397
- - [ ] **(f) Mark ready** — run `check_convergence.py`; exit 0 `update_pull_request(draft=false)`Step 8; exit 1 fix path; exit 2 retry/stop
398
-
399
- - [ ] **Step 7a: COPILOT_WAIT fetch Copilot, decide**
400
- See: [`reference/per-tick.md` § Step 2 COPILOT_WAIT](reference/per-tick.md).
401
- Skipped entirely when `copilot_down` (gate (d) never enters this phase).
402
-
403
- - [ ] **clean** → `copilot_clean_at = current_head` Step 7 (re-validate (b), (c), (e), (f))
404
- - [ ] **dirty** apply shared fix protocol
405
- ([`../../_shared/pr-loop/fix-protocol.md`](../../_shared/pr-loop/fix-protocol.md);
406
- skill deltas in [`reference/fix-protocol.md`](reference/fix-protocol.md))
407
- push reset markers `phase = CODE_REVIEW` → Step 5
408
- - [ ] **no review yet** → increment `copilot_wait_count` → ≥ 3 hard-blocks;
409
- else ScheduleWakeup 360s or portable in-session poll → Step 7a next tick
410
-
411
- - [ ] **Step 8: Clean working tree**
412
- `pr-loop-lifecycle` Close (`../pr-loop-lifecycle/SKILL.md`).
413
- See: [`pr-loop-lifecycle/reference/teardown-publish-permissions.md` § Clean working tree](../pr-loop-lifecycle/reference/teardown-publish-permissions.md)
414
-
415
- - [ ] **Step 9: Rewrite PR description**
416
- `pr-loop-lifecycle` Close.
417
- See: [`pr-loop-lifecycle/reference/teardown-publish-permissions.md` § Publish the final PR description](../pr-loop-lifecycle/reference/teardown-publish-permissions.md)
418
-
419
- - [ ] **Step 10: Revoke project permissions (always)**
420
- `pr-loop-lifecycle` Close (`../pr-loop-lifecycle/SKILL.md` § Close).
421
-
422
- - [ ] **Step 11: Print final report**
423
- Print this block verbatim — no paraphrase, no extra commentary:
424
- ```
425
- /pr-converge exit: converged
426
- Loops: <N>
427
- Final commit: <SHA>
428
- ```
429
-
430
- ## Edge cases
431
-
432
- | Situation | Read |
433
- |---|---|
434
- | Multi-PR session (`state.json` exists) | [`reference/multi-pr-orchestration.md`](reference/multi-pr-orchestration.md) |
435
- | Hard blocker or user stops loop | [`reference/stop-conditions.md`](reference/stop-conditions.md) |
436
- | Tick is ambiguous against the steps above | [`reference/examples.md`](reference/examples.md) |
437
-
438
- ## Depends on / invokes
439
-
440
- | Skill / path | Role |
441
- |---|---|
442
- | `pr-loop-lifecycle` | Open (permission grant + worktree preflight) and Close (cleanup, PR description, revoke) |
443
- | `reviewer-gates` | Copilot quota gate, Bugbot availability, Bugbot trigger flow |
444
- | `copilot-finding-triage` | Tier, verify, and route each Copilot finding |
445
- | `bugteam` | Full-diff bug audit in Step 6 (Skill invocation mandatory) |
446
- | `pr-loop-cloud-transport` | When `gh` is unavailable or unauthenticated |
447
- | [`../_shared/pr-loop/portable-driver.md`](../_shared/pr-loop/portable-driver.md) | Continuous in-session pacer when `ScheduleWakeup` is absent |
448
- | [`../../_shared/pr-loop/fix-protocol.md`](../../_shared/pr-loop/fix-protocol.md) | Shared 13-step fix sequence (TDD, commit, push, reply, resolve) |
449
- | [`reference/fix-protocol.md`](reference/fix-protocol.md) | pr-converge deltas on the shared fix protocol |
450
-
451
- ## Package file index
452
-
453
- | Path | Purpose |
454
- |---|---|
455
- | `SKILL.md` | Hub: pre-flight, state, progress checklist, gotchas |
456
- | `CLAUDE.md` | Package map for agents working in this skill |
457
- | `pr_converge_skill_constants/` | Importable constants for skill scripts |
458
- | `reference/per-tick.md` | Full per-tick procedure (phases, cwd routing, pacing) |
459
- | `reference/convergence-gates.md` | Six gates before mark-ready |
460
- | `reference/fix-protocol.md` | pr-converge fix-protocol deltas |
461
- | `reference/ground-rules.md` | Non-negotiable loop constraints |
462
- | `reference/state-schema.md` | `pr-converge-state.json` fields |
463
- | `reference/stop-conditions.md` | Hard stops without convergence |
464
- | `reference/multi-pr-orchestration.md` | Multi-PR session orchestration |
465
- | `reference/examples.md` | Worked tick sequences |
466
- | `reference/obstacles/` | Per-obstacle fix runbooks |
467
- | `scripts/` | Convergence helpers, Copilot fetch, fix-reply poster, tests |
468
- | `workflows/schedule-wakeup-loop.md` | ScheduleWakeup pacing (`pacer=schedule_wakeup`) |
469
- | [`../_shared/pr-loop/portable-driver.md`](../_shared/pr-loop/portable-driver.md) | Portable continuous pacer (`pacer=portable`) |
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
+ A code-review ↔ bugteam ↔ Bugbot ↔ Copilot loop on a draft PR: the internal
13
+ code-review and bugteam passes drive the code to clean first, then Cursor
14
+ Bugbot and Copilot run as terminal confirmation gates, until all are clean on
15
+ the same `HEAD` and mergeable. On `pacer=schedule_wakeup`, one tick runs per
16
+ invocation and the next tick is scheduled. On `pacer=portable`, ticks run
17
+ continuously in-session (see
18
+ [`../_shared/pr-loop/portable-driver.md`](../_shared/pr-loop/portable-driver.md)).
19
+
20
+ ## Transport check (before any GitHub step)
21
+
22
+ Run `command -v gh`; when it succeeds, run `gh auth status`; once the PR
23
+ scope is resolved, run `gh api repos/<owner>/<repo> --jq .permissions.push`
24
+ and take `true` as the pass. When any check fails, run the
25
+ `pr-loop-cloud-transport` skill first and route every `gh` operation in this
26
+ skill through its substitution matrix.
27
+
28
+ ## Pre-flight
29
+
30
+ ### Task list (step 1)
31
+
32
+ After down/opt-out flags are known, build the work list with
33
+ `build_converge_task_list.py` only (same script as autoconverge). Register every
34
+ task; **final task** is always `all_runnable_reviews_clean_same_head`. Complete
35
+ that final task only when every runnable review is CLEAN on the same HEAD.
36
+ On `pacer=portable`, `open-run` returns the same list.
37
+
38
+ ### Pacer selection
39
+
40
+ Scan the tool list for `ScheduleWakeup`. Record whether it is present, then
41
+ select the pacer:
42
+
43
+ ```
44
+ python "$HOME/.claude/skills/_shared/pr-loop/scripts/select_converge_pacer.py" \
45
+ --skill pr-converge \
46
+ --has-workflow <0|1> \
47
+ --has-schedule-wakeup <0|1>
48
+ ```
49
+
50
+ - `pacer=schedule_wakeup` — native tick pacing via `ScheduleWakeup` (see
51
+ [`workflows/schedule-wakeup-loop.md`](workflows/schedule-wakeup-loop.md)).
52
+ - `pacer=portable` — continuous in-session driver on
53
+ [`../_shared/pr-loop/portable-driver.md`](../_shared/pr-loop/portable-driver.md).
54
+ **Do not abort** because `ScheduleWakeup` is missing.
55
+
56
+ ### Worktree isolation
57
+
58
+ When the tool list includes `EnterWorktree`, call it with no arguments before
59
+ any API call, file read, or edit. Agent-view sessions start in the shared
60
+ checkout; Bash (`gh`, `git`) does not auto-isolate. Do not proceed until the
61
+ working directory contains `.claude/worktrees/`. If `EnterWorktree` fails,
62
+ report the failure and stop.
63
+
64
+ When `EnterWorktree` is absent, isolate with git worktree machinery per
65
+ [`../_shared/pr-loop/portable-driver.md`](../_shared/pr-loop/portable-driver.md)
66
+ § Isolation and worktree, then run strict
67
+ `preflight_worktree.py` for the PR's owner/repo. Fail closed only when the
68
+ checkout is not the PR's repo on the PR head ref.
69
+
70
+ `EnterWorktree` (or the portable worktree path) isolates the session repo.
71
+ When the PR lives in a different repo, Step 1.5 routes the working directory
72
+ into a **PR worktree** of that repo on its head branch (routine, never a
73
+ pause). See [`reference/per-tick.md` § Step 1.5](reference/per-tick.md).
74
+
75
+ ## Resume from a prior run
76
+
77
+ Before Step 0, check
78
+ `~/.claude/runtime/pr-loop/bugteam-pr-<PR number>/handoff.json`. When
79
+ `$CLAUDE_JOB_DIR/pr-converge-state.json` is absent but that handoff exists, seed
80
+ `phase`, `tick_count`, and the clean-at SHAs from the run's `state-copy.json`, so
81
+ a fresh session continues where the last one stopped rather than restarting at
82
+ CODE_REVIEW.
83
+
84
+ ## Copilot quota pre-check (start of run)
85
+
86
+ On the first tick, apply the `reviewer-gates` skill's Copilot quota gate
87
+ (`../reviewer-gates/SKILL.md` § Gate 2) — once per run, never per tick. The
88
+ flag lives in `pr-converge-state.json` as `copilot_down`, and every tick reads
89
+ it. While `copilot_down` is true, the tick skips all Copilot work — no fetch,
90
+ no request, no poll, no agent — and exports
91
+ `CLAUDE_REVIEWS_DISABLED="copilot"` in that tick's shell before
92
+ `check_convergence.py`, so the check bypasses the Copilot review gate and the
93
+ pending-requested-reviews gate and the run still marks ready on the remaining
94
+ signals.
95
+
96
+ ## Copilot findings — tier, verify, then route
97
+
98
+ Tier, verify, and route each Copilot finding via
99
+ [`copilot-finding-triage`](../copilot-finding-triage/SKILL.md) (self-healing
100
+ auto-fix; code-concern verify then confirmed / refuted / inconclusive).
101
+
102
+ **pr-converge phase routing after triage:**
103
+
104
+ - Self-healing and **confirmed** findings join the fix tick on `current_head`;
105
+ after push, reset clean-at SHAs, set `phase = CODE_REVIEW`, return to Step 5.
106
+ - **Refuted** findings resolve clean on the thread; no phase change.
107
+ - One or more **inconclusive** findings: do not mark ready. Page via the
108
+ triage skill's ntfy step, then hold by pacer:
109
+ - **`pacer=schedule_wakeup`** — triage user gate (`ScheduleWakeup` /
110
+ `send_later` 45-minute hold across ticks; persist the deadline so each tick
111
+ reads it on entry).
112
+ - **`pacer=portable`** — in-session deadline poll or handoff per
113
+ [`../_shared/pr-loop/portable-driver.md`](../_shared/pr-loop/portable-driver.md);
114
+ never `ScheduleWakeup` or `send_later`.
115
+ Act on the user's direction inside the window; on timeout, teardown and report
116
+ the findings un-reviewed.
117
+ - Enter `COPILOT_WAIT` only from gate (d) after requesting a Copilot review
118
+ (Step 7 → 7a). Stay on `COPILOT_WAIT` until a review surfaces at
119
+ `current_head`, `copilot_wait_count >= 3` hard-blocks, or `copilot_down`
120
+ skips the Copilot path entirely.
121
+
122
+ ## Budget-aware tick boundaries
123
+
124
+ Before starting any tick, estimate whether the remaining session/usage
125
+ budget covers one full clean tick (worst case: a BUGBOT fetch + a
126
+ full-diff CODE_REVIEW + a fix commit + replies). If it does not, do not
127
+ start the tick. Stop at the current tick boundary: write updated state to
128
+ `$CLAUDE_JOB_DIR/pr-converge-state.json`, write the durable handoff (see
129
+ [State persistence](#state-persistence)), then report the exact resume
130
+ command (`/pr-converge <PR URL>`) and the persisted `phase`/`tick_count`.
131
+ A tick cut off mid-flight poisons the resume state — clean SHAs recorded
132
+ against work that never landed — so an unstarted tick is always cheaper
133
+ than a half-finished one.
134
+
135
+ ## Findings discipline
136
+
137
+ Every finding, reply, and report states verified facts only — no hedging
138
+ language (`likely`, `probably`, `should`, `appears to`). Verify each
139
+ claim against the code on `current_head` before stating it; the
140
+ anti-hallucination Stop hook rejects hedged output, forcing a rework
141
+ pass. A claim that cannot be verified is reported as unverified, not
142
+ softened.
143
+
144
+ ## State persistence
145
+
146
+ Single-PR mode persists loop state to `$CLAUDE_JOB_DIR/pr-converge-state.json`.
147
+ On tick entry, read this file if it exists to restore phase, tick_count, and
148
+ clean-at SHAs. On tick exit, write updated state before the next pacer step
149
+ (`ScheduleWakeup` when `pacer=schedule_wakeup`; continuous continue or
150
+ in-session poll when `pacer=portable`) so the next tick resumes with accurate
151
+ state.
152
+
153
+ After the state write and before the next pacer step, write the durable handoff
154
+ so a fresh session in a new job can resume this run:
155
+
156
+ ```
157
+ python "$HOME/.claude/skills/_shared/pr-loop/scripts/write_handoff.py" \
158
+ --pr-number <N> --head-ref <branch> --phase <phase> \
159
+ --resume-command "/pr-converge <PR URL>" \
160
+ --state-file "$CLAUDE_JOB_DIR/pr-converge-state.json" \
161
+ --completed-steps "<clean phases this run>"
162
+ ```
163
+
164
+ It writes `handoff.json`, `HANDOFF.md`, and `state-copy.json` under
165
+ `~/.claude/runtime/pr-loop/<run-name>/`. The job-dir state stays the source of
166
+ truth for a resumed tick; the handoff copy is the pointer a fresh session reads
167
+ when `$CLAUDE_JOB_DIR` is gone.
168
+
169
+ Fields: `phase`, `tick_count`, `bugbot_clean_at`, `code_review_clean_at`,
170
+ `bugteam_clean_at`, `copilot_clean_at`, `merge_state_status`, `current_head`,
171
+ `bugbot_acknowledged_at`, `bugbot_down`, `copilot_down`,
172
+ `bugteam_skill_invoked_at_head`, `bugteam_skill_invoked_at_tick`,
173
+ `agents_session_id`, `persistent_agents`.
174
+
175
+ ## Persistent per-step agents
176
+
177
+ Three step-scoped agents persist across ticks so their context carries
178
+ forward: `fix_executor`, `thread_sweep`, and `copilot_watch`, recorded in
179
+ the `persistent_agents` map
180
+ ([`reference/state-schema.md`](reference/state-schema.md)).
181
+
182
+ - **Resume:** read `persistent_agents.<key>`. When an entry exists,
183
+ `SendMessage` to the stored `agent_id` with a tick payload that restates
184
+ the PR scope, `current_head`, the PR worktree path, this tick's findings
185
+ or threads, and the report-back contract. Bump `last_used_tick`, then
186
+ await completion.
187
+ - **First spawn:** `Agent(subagent_type: "clean-coder", name:
188
+ "prc-fix-<PR#>", model: "sonnet")` — worker-model routing per
189
+ [`skills/orchestrator/SKILL.md`](../orchestrator/SKILL.md#workflow-agent-routing);
190
+ resolver-supplied sonnet-equivalent on third-party hosts. The `name`
191
+ makes the agent a persistent teammate
192
+ that idles awaiting messages. Record `{agent_id, created_tick,
193
+ last_used_tick}` under the step key. Keep the spawn prompt fix-shaped,
194
+ never audit-shaped: the `pr_converge_bugteam_enforcer` hook blocks
195
+ audit-shaped clean-coder spawns during the BUGTEAM phase.
196
+ - **Stale or dead id:** on a `SendMessage` failure, or no acknowledgment
197
+ within one bounded wait, drop the map entry, spawn a fresh named agent,
198
+ record it, and continue the tick. Never abort a tick on a stale id;
199
+ never retry the same dead id.
200
+ - **Fresh every round (never persisted):** the Step 5 host-aware
201
+ `invoke_code_review.py` / `/code-review ultra --fix` pass and the Step 6
202
+ bugteam audit (unbiased eyes each round; the enforcer needs the formal
203
+ Skill call), and every `code-verifier` a named code-verifier never fires
204
+ `SubagentStop`, so no verdict mints (see the named-`code-verifier` entry
205
+ in the Gotchas list below).
206
+ - **Shutdown:** at loop end (convergence or a stop condition), send each
207
+ persistent agent a shutdown request and clear `persistent_agents` before
208
+ the `pr-loop-lifecycle` Close.
209
+
210
+ ## Gotchas
211
+
212
+ Highest-signal content. Append a bullet each time a tick fails in a new
213
+ way — these are the hard-won lessons that keep the loop honest. Once this
214
+ grows to 5 or more items, suggest spinning up a subagent to investigate, fix,
215
+ post a fresh PR in a fresh branch based on origin main to the user.
216
+
217
+ - **`ScheduleWakeup` not in subagent tool registries** — background
218
+ `general-purpose` tick cannot schedule re-entry; only the parent session
219
+ with `ScheduleWakeup` (or the portable continuous driver on the parent)
220
+ may pace the next tick.
221
+ - **Portable host has no durable wake outside the session** when
222
+ `pacer=portable`, budget and context bound how many ticks complete; write
223
+ handoff at a tick boundary and resume with `/pr-converge <PR URL>`.
224
+ - **`state.json` without §Concurrency lock loses merges** when teammates
225
+ finish in same wall-clock window.
226
+ - **`tick_count` must not double-increment** — conversation state line
227
+ only when **no** `state.json`; with `state.json`, only the
228
+ orchestrator bump increments.
229
+ - **Bugbot trigger and detection** — CI check-run based. Full flow at
230
+ [`per-tick.md` Step 3](reference/per-tick.md); see also
231
+ `~/.claude/skills/pr-converge/scripts/check_bugbot_ci.py --help`.
232
+ - **Bot login fields differ by endpoint** — `get_reviews` returns
233
+ `.user.login` (object), but `get_review_comments` returns `.author`
234
+ (string, not an object). Always check the correct fields and use
235
+ case-insensitive substring matching on login values, never strict
236
+ equality.
237
+ - **`isOutdated` has dual scope** GitHub marks a thread `isOutdated=true`
238
+ when the line it anchors to has changed since the comment was posted. The
239
+ machine gate (`check_convergence.py` / [convergence-gates.md](reference/convergence-gates.md)
240
+ gate (e)) excludes `isOutdated == true` bot threads from the fail count —
241
+ only non-outdated unresolved bot threads fail the gate. The agent-side
242
+ unresolved-thread sweep in the shared fix protocol
243
+ ([`../../_shared/pr-loop/fix-protocol.md`](../../_shared/pr-loop/fix-protocol.md)
244
+ step 12; skill deltas in [`reference/fix-protocol.md`](reference/fix-protocol.md))
245
+ still verifies outdated unresolved threads against HEAD before resolve when
246
+ the protocol requires it (the original concern can still apply when the fix
247
+ moved rather than landed).
248
+ - **Tilde paths fail on Windows Git Bash** — `~/.claude/skills/...`
249
+ resolves to the wrong home directory in Bash-tool contexts. Use
250
+ `$HOME/.claude/skills/...` in shell invocations or `Path.home() /
251
+ ".claude/skills/..."` in Python scripts. Script invocations through
252
+ Bash that reference `~` produce "file not found" errors
253
+ indistinguishable from actual script failures.
254
+ - **PowerShell cmdlets fail in Bash tool** `.ps1` scripts and
255
+ `pwsh` calls run through the PowerShell tool, not Bash. Bash on
256
+ Windows is Git Bash which cannot execute PowerShell cmdlets. Route all
257
+ PowerShell work through the PowerShell tool or `pwsh -NoProfile -File`.
258
+ - **Cross-repo PR: route cwd into the PR worktree before Step 5 review** —
259
+ `invoke_code_review.py` and `/code-review ultra --fix` audit the repo of the
260
+ cwd (the helper's `--cwd`). When the session is rooted in a different repo
261
+ than the PR, `EnterWorktree` cannot re-root (it is scoped to the session's
262
+ repo); resolve the PR worktree and `cd` into it per
263
+ [Step 1.5](reference/per-tick.md). Skipping this reviews and edits the
264
+ wrong repo. The route is routine and automatic — never a material fork
265
+ to pause on.
266
+ - **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.
267
+
268
+ ## Progress checklist
269
+
270
+ State variables (`phase`, `bugbot_clean_at`, `code_review_clean_at`,
271
+ `bugteam_clean_at`, `copilot_clean_at`, `merge_state_status`, counters) are
272
+ defined in [`reference/state-schema.md`](reference/state-schema.md). Ground rules
273
+ in [`reference/ground-rules.md`](reference/ground-rules.md).
274
+
275
+ Each step references its spoke file for full procedural detail — MCP calls,
276
+ script invocations, decision criteria. Every "return to Step N" means the next
277
+ tick starts fresh from that step.
278
+
279
+ **Hard gate: do not advance from any step while ANY unresolved review
280
+ thread exists on the PR.** Sweep semantics and per-thread handling live in
281
+ the shared fix protocol
282
+ ([`../../_shared/pr-loop/fix-protocol.md`](../../_shared/pr-loop/fix-protocol.md)
283
+ step 12; skill deltas in [`reference/fix-protocol.md`](reference/fix-protocol.md)).
284
+
285
+ **Full-PR-diff rule: every CODE-REVIEW round (Step 5) and every BUGTEAM
286
+ round (Step 6) covers the FULL `origin/main...HEAD` diff every file
287
+ the PR touches.** A round that scopes to a subset only the last commit,
288
+ only files touched since the prior clean SHA, only bugbot-flagged paths,
289
+ or any other delta cut does not satisfy the gate, and a "clean" verdict
290
+ against a partial diff is not a valid clean. Re-run the round against the
291
+ full diff before recording `code_review_clean_at` or treating the bugteam
292
+ round as converged. This rule holds every tick, every loop, every PR.
293
+
294
+ - [ ] **Step 0: Open the run**
295
+ Apply the `pr-loop-lifecycle` skill's Open section
296
+ (`../pr-loop-lifecycle/SKILL.md`): permission grant + worktree preflight.
297
+
298
+ - [ ] **Step 1: Resolve PR scope + PR worktree**
299
+ Apply Step 1.5 scope resolution in
300
+ [`reference/per-tick.md`](reference/per-tick.md). Resolve the **PR
301
+ worktree** the local checkout every local step this tick targets:
302
+ the `EnterWorktree` checkout when the PR shares the session's repo,
303
+ else a checkout of the PR's repo that the working directory routes
304
+ into via `cd`. Cross-repo routing is automatic, not a fork.
305
+ See: [`reference/per-tick.md` § Step 1](reference/per-tick.md)
306
+ and Step 1.5](reference/per-tick.md)
307
+
308
+ - [ ] **Step 2: Initialize loop state**
309
+ `phase = CODE_REVIEW`; all counters at zero; `run_name` resolved.
310
+
311
+ - [ ] **Step 3: Mergeability check**
312
+ See: [`reference/convergence-gates.md` § (c)](reference/convergence-gates.md)
313
+
314
+ ```
315
+ pull_request_read(method="get") .mergeable_state, .mergeable
316
+ ```
317
+
318
+ - [ ] mergeable advance to Step 4
319
+ - [ ] not mergeable rebase force-push return to Step 1
320
+
321
+ - [ ] **Step 4: BUGBOT terminal Bugbot confirmation gate**
322
+ Step 6 routes here after BUGTEAM converges. Bugbot confirms code the
323
+ internal passes already drove to clean.
324
+ See: [`reference/per-tick.md` § BUGBOT terminal gate + Step 3](reference/per-tick.md);
325
+ availability and trigger via `reviewer-gates`
326
+ (`../reviewer-gates/SKILL.md` § Gate 1 / Gate 3).
327
+
328
+ - [ ] **disabled / down** → `bugbot_down = true` → Step 7
329
+ - [ ] **dirty on `current_head`**apply shared fix protocol
330
+ ([`../../_shared/pr-loop/fix-protocol.md`](../../_shared/pr-loop/fix-protocol.md);
331
+ skill deltas in [`reference/fix-protocol.md`](reference/fix-protocol.md))
332
+ push reset push-invalidated markers `phase = CODE_REVIEW` Step 5
333
+ - [ ] **clean on `current_head`**zero unresolved threads (else fix + resolve first)
334
+ → `bugbot_clean_at = current_head` → Step 7
335
+ - [ ] **no review / commit_id mismatch** → `reviewer-gates` Bugbot flow (Gate 3):
336
+ silent pass stamp + Step 7; queued/triggered ScheduleWakeup 360s or portable in-session poll → Step 4;
337
+ down `bugbot_down = true` Step 7
338
+
339
+ - [ ] **Step 5: CODE-REVIEW static sweep, review, fix, advance**
340
+ Entry phase every tick; re-entered after any fix push.
341
+ See: [`reference/per-tick.md` § CODE_REVIEW entry](reference/per-tick.md).
342
+ Pre-condition: cwd is the Step 1.5 PR worktree on `current_head`.
343
+ Scope: FULL `origin/main...HEAD` diff every tick (no path args, no delta cut).
344
+ Review always runs at effort high on model opus through
345
+ `invoke_code_review.py`. Mode decision inputs: host profile + session
346
+ model. Call:
347
+ `python "$HOME/.claude/scripts/invoke_code_review.py" --cwd <PR-worktree>
348
+ --session-model <alias>`. Chain mode uses that cwd and empty stdin; the
349
+ chain process never commits and never pushes. JSON stdout carries
350
+ `mode` (`in_session` | `chain`), `served_command`, `returncode`, and
351
+ `dirty_tree`. Config/host errors still emit that JSON with non-zero
352
+ `returncode` (no traceback-only failure).
353
+
354
+ - [ ] **Static sweep fails** → apply shared fix protocol push → reset markers
355
+ stay CODE_REVIEW Step 5
356
+ - [ ] **`mode == in_session`** (Claude host, session model opus) → run
357
+ `/code-review ultra --fix` in-session (no path args)
358
+ - [ ] **`mode == chain`** (any other host or non-opus session) → helper
359
+ already ran the headless review; read `returncode`,
360
+ `served_command`, and `dirty_tree` from JSON
361
+ - [ ] **failed review** (`returncode != 0`, or chain with null
362
+ `served_command`) do not set `code_review_clean_at` → stay
363
+ CODE_REVIEW → Step 5
364
+ - [ ] **fixes applied** (`dirty_tree` true / working tree dirty) →
365
+ commit + push via shared fix protocol reset markers → stay
366
+ CODE_REVIEW Step 5
367
+ - [ ] **clean** (successful serve: `returncode == 0`, chain
368
+ `served_command` non-null when chain, and `dirty_tree` false) →
369
+ zero unresolved threads (else fix + resolve) →
370
+ `code_review_clean_at = current_head``phase = BUGTEAM` → Step 6
371
+
372
+ - [ ] **Step 6: BUGTEAM run, decide, fix, reply, resolve**
373
+ See: [`reference/per-tick.md` § Step 2 BUGTEAM](reference/per-tick.md);
374
+ [`../bugteam/SKILL.md`](../bugteam/SKILL.md).
375
+ Pre-condition: `code_review_clean_at == current_head`.
376
+ Mandatory: `Skill({skill: "bugteam", args: "<PR URL>"})` this tick
377
+ (enforcer-blocked otherwise; `qbug` is not a substitute). Scope: FULL
378
+ `origin/main...HEAD` diff. Re-resolve HEAD after bugteam.
379
+
380
+ - [ ] **bugteam pushed** verify threads replied + resolved reset markers
381
+ `phase = CODE_REVIEW` ScheduleWakeup 360s or portable in-session pollStep 5
382
+ - [ ] **converged, no push**zero unresolved threads →
383
+ `bugteam_clean_at = current_head` → `phase = BUGBOT` → Step 4
384
+ - [ ] **findings without committed fixes** → apply shared fix protocol → push →
385
+ reset markers `phase = CODE_REVIEW` → Step 5
386
+
387
+ - [ ] **Step 7: Convergence gates**
388
+ Full procedure: [`reference/convergence-gates.md`](reference/convergence-gates.md).
389
+
390
+ Pre-condition: Step 6 converged AND (`bugbot_clean_at == current_head` OR
391
+ `bugbot_down`). The terminal Bugbot gate (Step 4) sets that state just
392
+ before these gates run. Count unresolved threads before each gate.
393
+ Every gate records evidence; gate (f) cites evidence from (a)–(e).
394
+
395
+ - [ ] **(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
396
+ - [ ] **(b) Claude reviewer** — fetch Claude on `current_head`; dirty → fix + return to Step 5; clean or absent → continue
397
+ - [ ] **(c) Mergeability** — `mergeable_state == "clean"` and `mergeable == true`; dirty rebase + return to Step 1; blocked/behind/unknown/unstablehard blocker
398
+ - [ ] **(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)
399
+ - [ ] **(e) Thread-resolution**zero unresolved threads across the PR; else sweep + fix/resolve
400
+ - [ ] **(f) Mark ready** — run `check_convergence.py`; exit 0 → `update_pull_request(draft=false)` Step 8; exit 1 → fix path; exit 2 → retry/stop
401
+
402
+ - [ ] **Step 7a: COPILOT_WAIT — fetch Copilot, decide**
403
+ See: [`reference/per-tick.md` § Step 2 COPILOT_WAIT](reference/per-tick.md).
404
+ Skipped entirely when `copilot_down` (gate (d) never enters this phase).
405
+
406
+ - [ ] **clean** → `copilot_clean_at = current_head` → Step 7 (re-validate (b), (c), (e), (f))
407
+ - [ ] **dirty**apply shared fix protocol
408
+ ([`../../_shared/pr-loop/fix-protocol.md`](../../_shared/pr-loop/fix-protocol.md);
409
+ skill deltas in [`reference/fix-protocol.md`](reference/fix-protocol.md))
410
+ → push → reset markers → `phase = CODE_REVIEW` → Step 5
411
+ - [ ] **no review yet** increment `copilot_wait_count` → ≥ 3 hard-blocks;
412
+ else ScheduleWakeup 360s or portable in-session poll → Step 7a next tick
413
+
414
+ - [ ] **Step 8: Clean working tree**
415
+ `pr-loop-lifecycle` Close (`../pr-loop-lifecycle/SKILL.md`).
416
+ See: [`pr-loop-lifecycle/reference/teardown-publish-permissions.md` § Clean working tree](../pr-loop-lifecycle/reference/teardown-publish-permissions.md)
417
+
418
+ - [ ] **Step 9: Rewrite PR description**
419
+ `pr-loop-lifecycle` Close.
420
+ See: [`pr-loop-lifecycle/reference/teardown-publish-permissions.md` § Publish the final PR description](../pr-loop-lifecycle/reference/teardown-publish-permissions.md)
421
+
422
+ - [ ] **Step 10: Revoke project permissions (always)**
423
+ `pr-loop-lifecycle` Close (`../pr-loop-lifecycle/SKILL.md` § Close).
424
+
425
+ - [ ] **Step 11: Print final report**
426
+ Print this block verbatim — no paraphrase, no extra commentary:
427
+ ```
428
+ /pr-converge exit: converged
429
+ Loops: <N>
430
+ Final commit: <SHA>
431
+ ```
432
+
433
+ ## Edge cases
434
+
435
+ | Situation | Read |
436
+ |---|---|
437
+ | Multi-PR session (`state.json` exists) | [`reference/multi-pr-orchestration.md`](reference/multi-pr-orchestration.md) |
438
+ | Hard blocker or user stops loop | [`reference/stop-conditions.md`](reference/stop-conditions.md) |
439
+ | Tick is ambiguous against the steps above | [`reference/examples.md`](reference/examples.md) |
440
+
441
+ ## Depends on / invokes
442
+
443
+ | Skill / path | Role |
444
+ |---|---|
445
+ | `pr-loop-lifecycle` | Open (permission grant + worktree preflight) and Close (cleanup, PR description, revoke) |
446
+ | `reviewer-gates` | Copilot quota gate, Bugbot availability, Bugbot trigger flow |
447
+ | `copilot-finding-triage` | Tier, verify, and route each Copilot finding |
448
+ | `bugteam` | Full-diff bug audit in Step 6 (Skill invocation mandatory) |
449
+ | `pr-loop-cloud-transport` | When `gh` is unavailable or unauthenticated |
450
+ | [`../_shared/pr-loop/portable-driver.md`](../_shared/pr-loop/portable-driver.md) | Continuous in-session pacer when `ScheduleWakeup` is absent |
451
+ | [`../../_shared/pr-loop/fix-protocol.md`](../../_shared/pr-loop/fix-protocol.md) | Shared 13-step fix sequence (TDD, commit, push, reply, resolve) |
452
+ | [`reference/fix-protocol.md`](reference/fix-protocol.md) | pr-converge deltas on the shared fix protocol |
453
+
454
+ ## Package file index
455
+
456
+ | Path | Purpose |
457
+ |---|---|
458
+ | `SKILL.md` | Hub: pre-flight, state, progress checklist, gotchas |
459
+ | `CLAUDE.md` | Package map for agents working in this skill |
460
+ | `pr_converge_skill_constants/` | Importable constants for skill scripts |
461
+ | `reference/per-tick.md` | Full per-tick procedure (phases, cwd routing, pacing) |
462
+ | `reference/convergence-gates.md` | Six gates before mark-ready |
463
+ | `reference/fix-protocol.md` | pr-converge fix-protocol deltas |
464
+ | `reference/ground-rules.md` | Non-negotiable loop constraints |
465
+ | `reference/state-schema.md` | `pr-converge-state.json` fields |
466
+ | `reference/stop-conditions.md` | Hard stops without convergence |
467
+ | `reference/multi-pr-orchestration.md` | Multi-PR session orchestration |
468
+ | `reference/examples.md` | Worked tick sequences |
469
+ | `reference/obstacles/` | Per-obstacle fix runbooks |
470
+ | `scripts/` | Convergence helpers, Copilot fetch, fix-reply poster, tests |
471
+ | `workflows/schedule-wakeup-loop.md` | ScheduleWakeup pacing (`pacer=schedule_wakeup`) |
472
+ | [`../_shared/pr-loop/portable-driver.md`](../_shared/pr-loop/portable-driver.md) | Portable continuous pacer (`pacer=portable`) |