claude-dev-env 1.92.0 → 1.93.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 (216) hide show
  1. package/CLAUDE.md +5 -39
  2. package/_shared/CLAUDE.md +2 -1
  3. package/_shared/advisor/CLAUDE.md +21 -0
  4. package/_shared/advisor/advisor-protocol.md +142 -0
  5. package/_shared/advisor/scripts/config/advisor_scripts_constants/__init__.py +1 -0
  6. package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +86 -0
  7. package/_shared/advisor/scripts/model_tier_run_validator.py +246 -0
  8. package/_shared/advisor/scripts/pyproject.toml +3 -0
  9. package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +302 -0
  10. package/_shared/advisor/scripts/tests/test_tier_model_ids.py +180 -0
  11. package/_shared/advisor/scripts/tier_model_ids.py +164 -0
  12. package/_shared/pr-loop/CLAUDE.md +1 -0
  13. package/_shared/pr-loop/precatch-rubric.md +65 -0
  14. package/_shared/pr-loop/scripts/CLAUDE.md +1 -1
  15. package/_shared/pr-loop/scripts/code_rules_gate.py +137 -5
  16. package/_shared/pr-loop/scripts/post_audit_thread.py +1 -1
  17. package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -1
  18. package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +16 -0
  19. package/_shared/pr-loop/scripts/pr_loop_shared_constants/reviews_disabled_constants.py +2 -1
  20. package/_shared/pr-loop/scripts/reviewer_availability.py +42 -12
  21. package/_shared/pr-loop/scripts/reviews_disabled.py +65 -21
  22. package/_shared/pr-loop/scripts/terminology_sweep.py +69 -21
  23. package/_shared/pr-loop/scripts/tests/CLAUDE.md +1 -1
  24. package/_shared/pr-loop/scripts/tests/fixtures/{copilot_internal_user_jonecho.json → copilot_internal_user_example.json} +1 -1
  25. package/_shared/pr-loop/scripts/tests/test_agent_config_carveout.py +1 -1
  26. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +146 -2
  27. package/_shared/pr-loop/scripts/tests/test_copilot_quota.py +8 -8
  28. package/_shared/pr-loop/scripts/tests/test_reviewer_availability.py +18 -5
  29. package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +56 -5
  30. package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +104 -17
  31. package/agents/CLAUDE.md +3 -2
  32. package/agents/clasp-deployment-orchestrator.md +2 -2
  33. package/agents/code-advisor.md +7 -5
  34. package/agents/code-verifier.md +6 -2
  35. package/agents/session-advisor.md +27 -0
  36. package/audit-rubrics/category_rubrics/category-j-code-rules-compliance.md +1 -1
  37. package/audit-rubrics/category_rubrics/category-k-codebase-conflicts.md +1 -1
  38. package/audit-rubrics/prompts/category-a-api-contracts.md +2 -2
  39. package/audit-rubrics/prompts/category-b-selector-engine-compat.md +2 -2
  40. package/audit-rubrics/prompts/category-c-resource-cleanup.md +2 -2
  41. package/audit-rubrics/prompts/category-d-scoping-and-ordering.md +2 -2
  42. package/audit-rubrics/prompts/category-e-dead-code.md +2 -2
  43. package/audit-rubrics/prompts/category-f-silent-failures.md +2 -2
  44. package/audit-rubrics/prompts/category-g-bounds-and-overflow.md +2 -2
  45. package/audit-rubrics/prompts/category-h-security-boundaries.md +2 -2
  46. package/audit-rubrics/prompts/category-i-concurrency.md +2 -2
  47. package/audit-rubrics/prompts/category-j-code-rules-compliance.md +2 -2
  48. package/audit-rubrics/prompts/category-k-codebase-conflicts.md +2 -2
  49. package/audit-rubrics/prompts/category-l-behavior-equivalence.md +2 -2
  50. package/audit-rubrics/prompts/category-m-producer-consumer-cardinality.md +2 -2
  51. package/audit-rubrics/prompts/category-n-test-name-scenario-verifier.md +2 -2
  52. package/audit-rubrics/prompts/category-o-docstring-vs-impl-drift.md +2 -2
  53. package/audit-rubrics/prompts/category-p-name-vs-behavior-contract.md +2 -2
  54. package/bin/CLAUDE.md +1 -0
  55. package/bin/expand_home_directory_tokens.mjs +75 -0
  56. package/bin/install.mjs +21 -7
  57. package/bin/install.test.mjs +220 -10
  58. package/hooks/CLAUDE.md +1 -1
  59. package/hooks/blocking/CLAUDE.md +4 -1
  60. package/hooks/blocking/_path_setup.py +13 -0
  61. package/hooks/blocking/bash_pre_tool_use_dispatcher.py +281 -0
  62. package/hooks/blocking/code_rules_dead_module_constant.py +88 -16
  63. package/hooks/blocking/code_rules_enforcer.py +6 -0
  64. package/hooks/blocking/code_rules_imports_logging.py +34 -1
  65. package/hooks/blocking/code_rules_paths_syspath.py +1 -1
  66. package/hooks/blocking/code_rules_test_layout.py +272 -0
  67. package/hooks/blocking/nas_ssh_binary_enforcer.py +22 -12
  68. package/hooks/blocking/pii_prevention_blocker.py +781 -0
  69. package/hooks/blocking/pii_scanner.py +340 -0
  70. package/hooks/blocking/pre_tool_use_dispatcher.py +3 -3
  71. package/hooks/blocking/stop_dispatcher.py +84 -0
  72. package/hooks/blocking/test__path_setup.py +39 -0
  73. package/hooks/blocking/test_bash_dispatcher_interpreter_starts.py +61 -0
  74. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +294 -0
  75. package/hooks/blocking/test_code_rules_enforcer_cross_skill_duplicate.py +1 -1
  76. package/hooks/blocking/test_code_rules_enforcer_dead_module_constant.py +51 -0
  77. package/hooks/blocking/test_code_rules_enforcer_dead_module_constant_read_cap.py +103 -0
  78. package/hooks/blocking/test_code_rules_enforcer_dot_test_pattern.py +1 -1
  79. package/hooks/blocking/test_code_rules_enforcer_file_global_constants.py +1 -1
  80. package/hooks/blocking/test_code_rules_enforcer_hardcoded_user_path.py +11 -11
  81. package/hooks/blocking/test_code_rules_enforcer_naive_datetime.py +1 -1
  82. package/hooks/blocking/test_code_rules_enforcer_same_file_inline_duplicate.py +1 -1
  83. package/hooks/blocking/test_code_rules_enforcer_test_layout.py +111 -0
  84. package/hooks/blocking/test_code_rules_enforcer_type_checking_scope.py +76 -0
  85. package/hooks/blocking/test_destructive_command_blocker.py +29 -9
  86. package/hooks/blocking/test_gh_body_arg_blocker.py +3 -3
  87. package/hooks/blocking/test_nas_ssh_binary_enforcer.py +96 -39
  88. package/hooks/blocking/test_pii_prevention_blocker.py +905 -0
  89. package/hooks/blocking/test_pii_prevention_windows_git_path.py +41 -0
  90. package/hooks/blocking/test_pii_scanner.py +165 -0
  91. package/hooks/blocking/test_pr_description_enforcer_pr_number.py +1 -1
  92. package/hooks/blocking/test_pre_tool_use_dispatcher.py +14 -13
  93. package/hooks/blocking/test_stop_dispatcher.py +187 -0
  94. package/hooks/blocking/test_verdict_directory_write_blocker.py +1 -1
  95. package/hooks/blocking/test_volatile_path_in_post_blocker.py +3 -3
  96. package/hooks/blocking/volatile_path_in_post_blocker.py +54 -5
  97. package/hooks/diagnostic/migrations/README.md +25 -26
  98. package/hooks/diagnostic/test_hook_log_extractor.py +10 -10
  99. package/hooks/hooks.json +10 -120
  100. package/hooks/hooks_constants/CLAUDE.md +12 -3
  101. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +78 -0
  102. package/hooks/hooks_constants/dead_module_constant_constants.py +1 -0
  103. package/hooks/hooks_constants/hosted_hook_runner.py +73 -0
  104. package/hooks/hooks_constants/local_identity.py +182 -0
  105. package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +4 -37
  106. package/hooks/hooks_constants/pii_prevention_constants.py +299 -0
  107. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +4 -0
  108. package/hooks/hooks_constants/python_style_checks_constants.py +15 -0
  109. package/hooks/hooks_constants/stop_dispatcher_constants.py +26 -0
  110. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +87 -0
  111. package/hooks/hooks_constants/test_hardcoded_user_path_constants.py +2 -2
  112. package/hooks/hooks_constants/test_hosted_hook_runner.py +101 -0
  113. package/hooks/hooks_constants/test_layout_constants.py +28 -0
  114. package/hooks/hooks_constants/test_local_identity.py +88 -0
  115. package/hooks/hooks_constants/test_session_env_cleanup_constants.py +1 -1
  116. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +32 -0
  117. package/hooks/validators/README.md +5 -6
  118. package/hooks/validators/__init__.py +2 -2
  119. package/hooks/validators/python_style_checks.py +298 -243
  120. package/hooks/validators/run_all_validators.py +6 -0
  121. package/hooks/validators/test_python_style_checks.py +278 -163
  122. package/package.json +2 -2
  123. package/rules/CLAUDE.md +7 -1
  124. package/rules/bdd.md +1 -1
  125. package/rules/nas-ssh-invocation.md +6 -4
  126. package/rules/no-justification-noise.md +61 -0
  127. package/rules/testing.md +0 -2
  128. package/scripts/test_setup_project_paths.py +1 -1
  129. package/skills/CLAUDE.md +6 -3
  130. package/skills/_shared/pr-loop/scripts/_path_resolver.py +2 -2
  131. package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +1 -1
  132. package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +1 -1
  133. package/skills/_shared/pr-loop/scripts/test__path_resolver.py +42 -3
  134. package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +11 -11
  135. package/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +26 -3
  136. package/skills/_shared/pr-loop/scripts/test_preflight_worktree.py +49 -25
  137. package/skills/autoconverge/CLAUDE.md +3 -3
  138. package/skills/autoconverge/SKILL.md +113 -35
  139. package/skills/autoconverge/reference/CLAUDE.md +2 -2
  140. package/skills/autoconverge/reference/convergence.md +44 -22
  141. package/skills/autoconverge/reference/gotchas.md +11 -0
  142. package/skills/autoconverge/reference/stop-conditions.md +23 -13
  143. package/skills/autoconverge/workflow/CLAUDE.md +2 -1
  144. package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +15 -19
  145. package/skills/autoconverge/workflow/converge.contract.test.mjs +43 -42
  146. package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +141 -16
  147. package/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +2 -2
  148. package/skills/autoconverge/workflow/converge.mjs +329 -68
  149. package/skills/autoconverge/workflow/converge.path-aware.test.mjs +1 -1
  150. package/skills/autoconverge/workflow/converge.precatch.test.mjs +152 -0
  151. package/skills/autoconverge/workflow/converge.run-input.test.mjs +3 -3
  152. package/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +15 -15
  153. package/skills/autoconverge/workflow/test_convergence_summary.py +6 -6
  154. package/skills/bdd-protocol/SKILL.md +4 -5
  155. package/skills/bdd-protocol/references/anti-patterns.md +1 -1
  156. package/skills/bugteam/SKILL.md +8 -0
  157. package/skills/bugteam/reference/copilot-gap-analysis.md +20 -20
  158. package/skills/bugteam/reference/obstacles/audit-walk-categories.md +1 -1
  159. package/skills/bugteam/reference/team-setup.md +7 -5
  160. package/skills/bugteam/scripts/reflow_skill_md.py +1 -1
  161. package/skills/copilot-finding-triage/SKILL.md +124 -0
  162. package/skills/copilot-finding-triage/reference/tier-rubric.md +49 -0
  163. package/skills/copilot-finding-triage/scripts/conftest.py +8 -0
  164. package/skills/copilot-finding-triage/scripts/copilot_finding_triage_constants/__init__.py +0 -0
  165. package/skills/copilot-finding-triage/scripts/copilot_finding_triage_constants/config/__init__.py +0 -0
  166. package/skills/copilot-finding-triage/scripts/copilot_finding_triage_constants/config/notify_ntfy_constants.py +32 -0
  167. package/skills/copilot-finding-triage/scripts/notify_ntfy.py +192 -0
  168. package/skills/copilot-finding-triage/scripts/test_notify_ntfy.py +141 -0
  169. package/skills/copilot-finding-triage/templates/notification.md +34 -0
  170. package/skills/copilot-review/SKILL.md +8 -6
  171. package/skills/findbugs/SKILL.md +4 -0
  172. package/skills/fixbugs/SKILL.md +8 -7
  173. package/skills/gotcha/CLAUDE.md +2 -2
  174. package/skills/gotcha/SKILL.md +4 -4
  175. package/skills/log-audit/SKILL.md +4 -6
  176. package/skills/monitor-open-prs/CLAUDE.md +1 -1
  177. package/skills/monitor-open-prs/SKILL.md +6 -1
  178. package/skills/orchestrator/SKILL.md +184 -0
  179. package/skills/orchestrator-refresh/SKILL.md +37 -0
  180. package/skills/post-audit-findings/SKILL.md +5 -9
  181. package/skills/pr-consistency-audit/SKILL.md +5 -1
  182. package/skills/pr-converge/CLAUDE.md +1 -1
  183. package/skills/pr-converge/SKILL.md +86 -47
  184. package/skills/pr-converge/reference/CLAUDE.md +1 -1
  185. package/skills/pr-converge/reference/convergence-gates.md +20 -17
  186. package/skills/pr-converge/reference/examples.md +63 -47
  187. package/skills/pr-converge/reference/fix-protocol.md +15 -15
  188. package/skills/pr-converge/reference/ground-rules.md +11 -7
  189. package/skills/pr-converge/reference/multi-pr-orchestration.md +6 -5
  190. package/skills/pr-converge/reference/per-tick.md +129 -107
  191. package/skills/pr-converge/reference/state-schema.md +15 -10
  192. package/skills/pr-converge/scripts/check_convergence.py +5 -4
  193. package/skills/pr-converge/scripts/test_check_convergence.py +6 -4
  194. package/skills/pr-fix-protocol/SKILL.md +3 -8
  195. package/skills/pr-loop-cloud-transport/SKILL.md +102 -0
  196. package/skills/pr-loop-cloud-transport/reference/identity-and-hooks.md +40 -0
  197. package/skills/pr-loop-cloud-transport/reference/substitution-matrix.md +48 -0
  198. package/skills/pr-loop-lifecycle/SKILL.md +5 -9
  199. package/skills/pr-scope-resolve/SKILL.md +3 -6
  200. package/skills/privacy-hygiene/SKILL.md +114 -0
  201. package/skills/qbug/SKILL.md +8 -8
  202. package/skills/rebase/SKILL.md +5 -1
  203. package/skills/refine/SKILL.md +4 -5
  204. package/skills/reviewer-gates/SKILL.md +7 -11
  205. package/skills/session-log/SKILL.md +4 -1
  206. package/skills/skill-builder/SKILL.md +3 -6
  207. package/skills/structure-prompt/SKILL.md +4 -5
  208. package/skills/team-advisor/SKILL.md +32 -164
  209. package/skills/test_markdown_link_integrity.py +10 -6
  210. package/skills/update/SKILL.md +5 -1
  211. package/skills/usage-pause/SKILL.md +14 -5
  212. package/skills/usage-pause/scripts/resolve_usage_window.py +83 -5
  213. package/skills/usage-pause/scripts/test_resolve_usage_window.py +185 -17
  214. package/skills/usage-pause/scripts/usage_pause_constants/resolve_usage_window_constants.py +4 -3
  215. package/skills/verified-build/SKILL.md +4 -9
  216. package/skills/team-advisor-refresh/SKILL.md +0 -25
@@ -48,8 +48,8 @@ Capture `number`, `head.sha` (= `current_head`), owner/repo, branch.
48
48
  ## Step 1.5: Resolve the PR worktree (cwd routing)
49
49
 
50
50
  The **PR worktree** is the local working tree of the PR's repo on its head
51
- branch. Every local operation this tick runs there: the CODE_REVIEW
52
- `/code-review --fix`, every `clean-coder` fix spawn, and every commit and
51
+ branch. Every local operation this tick runs there: the CODE_REVIEW static sweep
52
+ and `/code-review high --fix`, every `clean-coder` fix spawn, and every commit and
53
53
  push. `/code-review` and `git` both act on the repo of the current working
54
54
  directory, so the working directory must be the PR worktree before any local
55
55
  work begins. Re-resolve it every tick — a rebase or a fresh HEAD can move the
@@ -90,7 +90,7 @@ outcome (no git work tree, or no readable origin) exits non-zero. Route on the
90
90
 
91
91
  - **`PREFLIGHT_OUTCOME=different_repo`** (the session is rooted in another repo
92
92
  — for example, the PR lives in `llm-settings` while the session runs from
93
- `claude-code-config`): route the working directory into a checkout of the
93
+ `claude-dev-env`): route the working directory into a checkout of the
94
94
  PR's repo. This is routine and automatic — never pause, and never raise it as
95
95
  a fork (see [ground-rules.md](ground-rules.md)). `EnterWorktree` is scoped to
96
96
  the session's own repo and cannot re-root into the PR's repo.
@@ -150,75 +150,28 @@ outcome (no git work tree, or no readable origin) exits non-zero. Route on the
150
150
 
151
151
  ## Step 2: Branch on `phase`
152
152
 
153
- ### `phase == BUGBOT`
154
-
155
- **Opt-out gate (runs first, before any fetch or trigger).**
156
- `python "$HOME/.claude/_shared/pr-loop/scripts/reviews_disabled.py" --reviewer bugbot`
157
-
158
- - Exit 0 (`CLAUDE_REVIEWS_DISABLED` lists `bugbot`) → set `bugbot_down = true`,
159
- `phase = CODE_REVIEW`, continue CODE_REVIEW in the same tick; skip steps a–c below.
160
- - Exit 1 → proceed to step a.
161
-
162
- Because `bugbot_down` resets on every push, this gate re-runs on every
163
- BUGBOT entry and keeps Cursor Bugbot skipped for the entire run.
164
-
165
- a. Fetch Cursor Bugbot reviews newest-first, walk back until first clean:
166
-
167
- ```
168
- pull_request_read(owner=OWNER, repo=REPO, pullNumber=NUMBER, method="get_reviews")
169
- → filter `.user.login` for cursor/bugbot, sort by `.submitted_at` descending
170
- ```
171
-
172
- Track dirty entries (review body contains `BUGBOT_REVIEW` markers with finding content); Fix protocol reads them back later this tick.
173
-
174
- Iterate from index 0 (most recent) toward older:
175
-
176
- - Dirty review → append JSON line with `{review_id, commit_id,
177
- submitted_at, body}`.
178
- - Stop at first clean. Older reviews presumed addressed at that
179
- checkpoint.
180
- - Index 0 clean → `$dirty_reviews_path` stays empty.
181
-
182
- Capture `commit_id`, `submitted_at`, body, `classification` of index-0
183
- review for decisions below. When branch routes to **Fix protocol**, address
184
- **every** entry in `$dirty_reviews_path` — not just index 0.
185
-
186
- b. Fetch ALL unresolved inline comment threads on the PR:
187
-
188
- ```
189
- pull_request_read(owner=OWNER, repo=REPO, pullNumber=NUMBER, method="get_review_comments")
190
- → filter threads where `is_resolved == false`
191
- ```
192
-
193
- Per-thread handling lives in the `pr-fix-protocol` skill's
194
- unresolved-thread sweep (`../../pr-fix-protocol/SKILL.md`).
195
-
196
- c. Decide (four branches; match first whose predicate holds):
197
- - **No bugbot review yet, OR latest review's `commit_id` ≠
198
- `current_head`:** Re-trigger bugbot (Step 3), set `bugbot_clean_at =
199
- null`, reset `inline_lag_streak = 0`, schedule next wakeup, return.
200
- - **`commit_id == current_head` AND zero unaddressed inline AND review
201
- body clean:** Set `bugbot_clean_at = current_head`, reset
202
- `inline_lag_streak = 0`, `phase = CODE_REVIEW`. Continue CODE_REVIEW
203
- in same tick — back-to-back convergence requires code-review then
204
- bugteam on same HEAD before next wakeup.
205
- - **`commit_id == current_head` with unaddressed inline findings:**
206
- Apply the `pr-fix-protocol` skill (`../../pr-fix-protocol/SKILL.md`).
207
- Reset `inline_lag_streak = 0`. With `state.json`: the clean-coder
208
- teammate executes it, writes `state.json`, goes idle; Step 3 on new
209
- HEAD runs after via orchestrator-spawned follow-up agent (§Fix result
210
- → general-purpose). No `state.json` (single-PR): the lead executes it
211
- → Step 3 in same tick. Schedule next wakeup, return.
153
+ The internal passes drive the code to clean first. CODE_REVIEW is the entry phase
154
+ each tick; BUGTEAM follows; the terminal Bugbot gate confirms; then the
155
+ convergence gates and the terminal Copilot gate run. Every fix push re-enters at
156
+ CODE_REVIEW.
212
157
 
213
158
  ### `phase == CODE_REVIEW`
214
159
 
215
- Local correctness/quality pass between BUGBOT clean and BUGTEAM. Enters
216
- after BUGBOT reports clean on `current_head` (or `bugbot_down == true`).
217
- Runs Claude Code's built-in `/code-review --fix` on the full
218
- `origin/main...HEAD` diff; it produces no GitHub review artifact, so there
219
- are no code-review threads to resolve.
220
-
221
- a. Run Claude Code's built-in `/code-review --fix` on the FULL
160
+ The entry phase of every convergence tick, re-entered after any fix push. It runs
161
+ a deterministic static sweep, then Claude Code's built-in `/code-review high
162
+ --fix` on the full `origin/main...HEAD` diff; `/code-review` produces no GitHub
163
+ review artifact, so there are no code-review threads to resolve.
164
+
165
+ a. **Static sweep — runs first, before `/code-review`.** Run the deterministic
166
+ gates over the full `origin/main...HEAD` changed files:
167
+ `python "$HOME/.claude/_shared/pr-loop/scripts/code_rules_gate.py" --base origin/main`,
168
+ `ruff`, `mypy`, and stem-matched `pytest`. On any failure, apply the
169
+ `pr-fix-protocol` skill (`../../pr-fix-protocol/SKILL.md`), commit and push,
170
+ reset `bugbot_clean_at = null` and `code_review_clean_at = null`, stay
171
+ `phase = CODE_REVIEW`, and re-run the sweep. When the sweep is clean, run
172
+ `/code-review` below.
173
+
174
+ b. Run Claude Code's built-in `/code-review high --fix` on the FULL
222
175
  `origin/main...HEAD` diff — every file the PR touches — via the
223
176
  [local diff review](https://code.claude.com/docs/en/code-review#review-a-diff-locally).
224
177
  It reviews the diff and applies its findings to the working tree.
@@ -229,26 +182,25 @@ a. Run Claude Code's built-in `/code-review --fix` on the FULL
229
182
  `current_head` — with no uncommitted edits. When the session is rooted in a
230
183
  different repo than the PR, the `cd` from Step 1.5 supplies this; the
231
184
  persisted working directory is what `/code-review` audits. Then invoke
232
- `/code-review --fix` with no path arguments so it audits the whole branch
185
+ `/code-review high --fix` with no path arguments so it audits the whole branch
233
186
  diff against `origin/main`. Do not delta-scope to commits added since the
234
187
  prior clean SHA, do not scope to a single file, do not scope to bugbot's
235
188
  flagged paths. A partial-scope round does not count and cannot set
236
- `code_review_clean_at`. Pass no effort argument, so the review uses
237
- the session's current effort.
189
+ `code_review_clean_at`. Invoke `/code-review high --fix` so the pre-catch pass
190
+ gets broad coverage regardless of the session's current effort.
238
191
 
239
- b. Decide (two branches; match first whose predicate holds):
192
+ c. Decide (two branches; match first whose predicate holds):
240
193
 
241
194
  - **`/code-review` applied fixes (working tree changed):** Commit the
242
195
  applied fixes in one commit → push, following the `pr-fix-protocol`
243
196
  skill's commit and push steps (`../../pr-fix-protocol/SKILL.md`). Reset
244
- `bugbot_clean_at = null` AND `code_review_clean_at = null`. Re-trigger
245
- bugbot (Step 3) so the new HEAD enters the queue. Set `phase = BUGBOT`,
246
- schedule next wakeup, return. A code-review fix push requires a full
247
- back-to-back-clean cycle on the new HEAD.
197
+ `bugbot_clean_at = null` AND `code_review_clean_at = null`. Stay
198
+ `phase = CODE_REVIEW`, schedule next wakeup, return. Every fix push
199
+ re-enters the internal passes on the new HEAD.
248
200
  - **Clean (no changes applied):** Set
249
201
  `code_review_clean_at = current_head`, `phase = BUGTEAM`. Continue
250
- BUGTEAM in same tick — back-to-back convergence requires bugbot,
251
- code-review, and bugteam all clean on the same HEAD.
202
+ BUGTEAM in same tick — back-to-back convergence requires code-review and
203
+ bugteam clean on the same HEAD before the terminal gates run.
252
204
 
253
205
  ### `phase == BUGTEAM`
254
206
 
@@ -300,27 +252,94 @@ c. Inspect bugteam outcome. Reports `convergence (zero findings)` or list
300
252
  of unfixed findings with file:line.
301
253
 
302
254
  d. Decide based on post-bugteam state — order matters. Check
303
- pushed-during-bugteam FIRST so convergence report against stale HEAD
255
+ pushed-during-bugteam FIRST so a convergence report against a stale HEAD
304
256
  never falsely terminates:
305
257
  - **Audit pushed this tick (`bugbot_clean_at` reset in step b):**
306
- Re-trigger bugbot same tick (Step 3) so new HEAD enters queue, `phase
307
- = BUGBOT`, schedule next wakeup, return.
308
- - **Convergence AND `bugbot_clean_at == current_head` (no push):**
309
- Back-to-back clean necessary, not sufficient. Run **[convergence-gates.md](convergence-gates.md)** to clear all six gates: Copilot findings,
310
- Claude reviewer, mergeability, post-convergence Copilot request,
311
- thread resolution. Only when all six gates pass mark PR ready and
312
- **omit loop pacing** per **Convergence** of active pacing workflow.
313
- - **Convergence BUT `bugbot_clean_at != current_head` (no push):**
314
- `phase = BUGBOT`, schedule next wakeup, return.
258
+ Reset `code_review_clean_at = null`, `phase = CODE_REVIEW`, schedule next
259
+ wakeup, return. Every fix push re-enters the internal passes on the new
260
+ HEAD.
261
+ - **Convergence AND no push:** the internal passes are clean on
262
+ `current_head`. `phase = BUGBOT` route into the terminal Bugbot gate,
263
+ which confirms and then runs the convergence gates. Continue BUGBOT in the
264
+ same tick.
315
265
  - **Findings without committed fixes:** apply the `pr-fix-protocol`
316
- skill (`../../pr-fix-protocol/SKILL.md`).
317
- `phase = BUGBOT`, schedule next wakeup, return.
266
+ skill (`../../pr-fix-protocol/SKILL.md`). Reset `code_review_clean_at =
267
+ null`, `phase = CODE_REVIEW`, schedule next wakeup, return.
268
+
269
+ ### `phase == BUGBOT` (terminal gate)
270
+
271
+ The terminal external confirmation gate. BUGTEAM routes here once the internal
272
+ passes are clean; Bugbot confirms the HEAD, then the convergence gates run.
273
+
274
+ **Availability gate (runs first, before any fetch or trigger).**
275
+ `python "$HOME/.claude/_shared/pr-loop/scripts/reviews_disabled.py" --reviewer bugbot`
276
+
277
+ - Exit 0 (Bugbot disabled for this run — the default, unless
278
+ `CLAUDE_REVIEWS_ENABLED` lists `bugbot`) → set `bugbot_down = true`, advance to
279
+ the [convergence gates](convergence-gates.md) in the same tick with the Bugbot
280
+ gate bypassed; skip steps a–c below.
281
+ - Exit 1 (`CLAUDE_REVIEWS_ENABLED` lists `bugbot` and `CLAUDE_REVIEWS_DISABLED`
282
+ does not) → go to step a.
283
+
284
+ Because `bugbot_down` resets on every push, this gate re-runs on every
285
+ BUGBOT entry. Cursor Bugbot is off by default and runs only when
286
+ `CLAUDE_REVIEWS_ENABLED` lists `bugbot`; a `bugbot` token in
287
+ `CLAUDE_REVIEWS_DISABLED` keeps it off even then.
288
+
289
+ a. Fetch Cursor Bugbot reviews newest-first, walk back until first clean:
290
+
291
+ ```
292
+ pull_request_read(owner=OWNER, repo=REPO, pullNumber=NUMBER, method="get_reviews")
293
+ → filter `.user.login` for cursor/bugbot, sort by `.submitted_at` descending
294
+ ```
295
+
296
+ Track dirty entries (review body has `BUGBOT_REVIEW` markers with finding content); Fix protocol reads them back later this tick.
297
+
298
+ Iterate from index 0 (most recent) toward older:
299
+
300
+ - Dirty review → append JSON line with `{review_id, commit_id,
301
+ submitted_at, body}`.
302
+ - Stop at first clean. Older reviews presumed addressed at that
303
+ checkpoint.
304
+ - Index 0 clean → `$dirty_reviews_path` stays empty.
305
+
306
+ Capture `commit_id`, `submitted_at`, body, `classification` of index-0
307
+ review for decisions below. When branch routes to **Fix protocol**, address
308
+ **every** entry in `$dirty_reviews_path` — not just index 0.
309
+
310
+ b. Fetch ALL unresolved inline comment threads on the PR:
311
+
312
+ ```
313
+ pull_request_read(owner=OWNER, repo=REPO, pullNumber=NUMBER, method="get_review_comments")
314
+ → filter threads where `is_resolved == false`
315
+ ```
316
+
317
+ Per-thread handling lives in the `pr-fix-protocol` skill's
318
+ unresolved-thread sweep (`../../pr-fix-protocol/SKILL.md`).
319
+
320
+ c. Decide (four branches; match first whose predicate holds):
321
+ - **No bugbot review yet, OR latest review's `commit_id` ≠
322
+ `current_head`:** Re-trigger bugbot (Step 3), set `bugbot_clean_at =
323
+ null`, reset `inline_lag_streak = 0`, schedule next wakeup, return to the
324
+ Bugbot gate next tick.
325
+ - **`commit_id == current_head` AND zero unaddressed inline AND review
326
+ body clean:** Set `bugbot_clean_at = current_head`, reset
327
+ `inline_lag_streak = 0`, advance to the [convergence
328
+ gates](convergence-gates.md) in the same tick.
329
+ - **`commit_id == current_head` with unaddressed inline findings:**
330
+ Apply the `pr-fix-protocol` skill (`../../pr-fix-protocol/SKILL.md`).
331
+ Reset `inline_lag_streak = 0`, `bugbot_clean_at = null`,
332
+ `code_review_clean_at = null`, `phase = CODE_REVIEW`. With `state.json`:
333
+ the clean-coder teammate executes the fix, writes `state.json`, goes idle;
334
+ the next tick re-enters CODE_REVIEW on the new HEAD. No `state.json`
335
+ (single-PR): the lead executes it, stays `phase = CODE_REVIEW`. Schedule
336
+ next wakeup, return.
318
337
 
319
338
  ### `phase == COPILOT_WAIT`
320
339
 
321
- Post-convergence Copilot re-check. Enters after gate (d) requests Copilot
322
- review. Do **not** run bugteam here — that only happens after BUGBOT clean
323
- on this HEAD.
340
+ Post-convergence Copilot re-check. Enters after the convergence gates request a
341
+ Copilot review. Do **not** run bugteam here — the internal code-review and
342
+ bugteam passes already converged before this terminal gate.
324
343
 
325
344
  a. Fetch latest Copilot review at `current_head` plus unaddressed inline
326
345
  comments:
@@ -343,9 +362,10 @@ b. Decide (three branches; match first whose predicate holds):
343
362
  at `current_head`:** Apply the `pr-fix-protocol` skill
344
363
  (`../../pr-fix-protocol/SKILL.md`) — it covers body-only findings with
345
364
  no inline threads. Reset
346
- `bugbot_clean_at = null` AND `copilot_clean_at = null`. **Set
347
- `phase = BUGBOT`** (NOT COPILOT_WAIT) — every fix push requires a full
348
- back-to-back-clean cycle on the new HEAD. Schedule next wakeup, return.
365
+ `bugbot_clean_at = null`, `code_review_clean_at = null`, AND
366
+ `copilot_clean_at = null`. **Set `phase = CODE_REVIEW`** (NOT
367
+ COPILOT_WAIT) every fix push re-enters the internal passes on the new
368
+ HEAD. Schedule next wakeup, return.
349
369
  - **No Copilot review at `current_head` yet:** Increment
350
370
  `copilot_wait_count` (init 0 on COPILOT_WAIT entry; reset to 0 on
351
371
  every push and on every successful Copilot review). `>= 3` → hard
@@ -353,25 +373,27 @@ b. Decide (three branches; match first whose predicate holds):
353
373
  schedule next wakeup (360s), return.
354
374
 
355
375
  **Non-negotiable:** After any Copilot fix push, `phase` MUST route to
356
- `BUGBOT`. Never cycle COPILOT_WAIT → fix → COPILOT_WAIT. The
357
- back-to-back-clean guarantee (bugbot bugteam both clean on same HEAD
358
- before gates re-open) only holds when every fix commit re-enters through
359
- BUGBOT.
376
+ `CODE_REVIEW`. Never cycle COPILOT_WAIT → fix → COPILOT_WAIT. The
377
+ back-to-back-clean guarantee (the internal code-review and bugteam passes both
378
+ clean on the same HEAD before the terminal gates re-open) only holds when every
379
+ fix commit re-enters through CODE_REVIEW.
360
380
 
361
381
  ## Step 3: Re-trigger bugbot
362
382
 
363
- - [ ] **Opt-out gate.** Enforced at BUGBOT entry (see `### phase == BUGBOT`).
364
- When `CLAUDE_REVIEWS_DISABLED` lists `bugbot`, the entry gate sets
365
- `bugbot_down = true` and routes to BUGTEAM before any trigger flow runs,
366
- so the flow below is skipped.
383
+ - [ ] **Availability gate.** Enforced at BUGBOT entry (see `### phase == BUGBOT`).
384
+ When Bugbot is disabled for the run the default unless
385
+ `CLAUDE_REVIEWS_ENABLED` lists `bugbot`, and always when
386
+ `CLAUDE_REVIEWS_DISABLED` lists `bugbot` — the entry gate sets
387
+ `bugbot_down = true` and advances to the convergence gates before any trigger
388
+ flow runs, so the flow below is skipped.
367
389
  - [ ] Apply the `reviewer-gates` skill's Bugbot flow
368
390
  (`../../reviewer-gates/SKILL.md` § Gate 3) against `current_head` — the
369
391
  silent-pass pre-check, the already-queued check, the trigger comment, and
370
392
  the acknowledge check, with their rationale. Map its outcomes:
371
- - [ ] Silent pass → set `bugbot_clean_at = current_head`, `phase = CODE_REVIEW`, continue CODE_REVIEW same tick
393
+ - [ ] Silent pass → set `bugbot_clean_at = current_head`, advance to the [convergence gates](convergence-gates.md) same tick
372
394
  - [ ] Already queued → skip posting, wait for completion, advance to Step 4
373
395
  - [ ] Trigger acknowledged (`bugbot_acknowledged_at` recorded) → advance to Step 4
374
- - [ ] Bugbot down → set `bugbot_down = true`, `phase = CODE_REVIEW`, continue CODE_REVIEW same tick
396
+ - [ ] Bugbot down → set `bugbot_down = true`, advance to the [convergence gates](convergence-gates.md) same tick
375
397
 
376
398
  ## Step 4: Loop pacing
377
399
 
@@ -10,7 +10,9 @@ live ONLY in the single-PR `$CLAUDE_JOB_DIR/pr-converge-state.json` file
10
10
  (see those field entries below for details).
11
11
 
12
12
  - `phase`: `BUGBOT`, `CODE_REVIEW`, `BUGTEAM`, or `COPILOT_WAIT`. Start
13
- `BUGBOT` on first tick.
13
+ `CODE_REVIEW` on first tick. `BUGBOT` is the terminal external-confirmation
14
+ phase reached after BUGTEAM converges; the internal `CODE_REVIEW` and
15
+ `BUGTEAM` passes drive the code to clean before it runs.
14
16
  - `bugbot_clean_at`: HEAD SHA where bugbot last reported clean, or `null`.
15
17
  Reset to `null` on every push.
16
18
  - `code_review_clean_at`: HEAD SHA where the `/code-review` pass last
@@ -37,16 +39,19 @@ live ONLY in the single-PR `$CLAUDE_JOB_DIR/pr-converge-state.json` file
37
39
  body shows findings against `current_head` but inline API returns zero
38
40
  matching. Reset to `0` on any other branch outcome.
39
41
  - `bugbot_down`: boolean, init `false`. Set `true` when bugbot fails to
40
- acknowledge a trigger comment; forces phase to BUGTEAM. Also set `true`
41
- at every BUGBOT-phase entry when `CLAUDE_REVIEWS_DISABLED` lists the
42
- `bugbot` token (env opt-out via the BUGBOT entry gate in `per-tick.md`),
43
- which routes straight to BUGTEAM before any bugbot fetch or trigger. Also
44
- set `true` when an acknowledged trigger has been outstanding more than 30
45
- minutes with no surfaced review at `current_head` (per Step 2 BUGBOT (c)
46
- 30-minute budget — see `per-tick.md`). Reset to `false` on every push;
47
- the entry gate re-applies the env opt-out on the next BUGBOT entry.
42
+ acknowledge a trigger comment; the convergence gates then run with the Bugbot
43
+ gate bypassed. Also set `true` at the terminal BUGBOT-phase entry whenever the
44
+ availability gate reports Bugbot disabled for the run the default unless
45
+ `CLAUDE_REVIEWS_ENABLED` lists the `bugbot` token, and always when
46
+ `CLAUDE_REVIEWS_DISABLED` lists it (the BUGBOT entry gate in `per-tick.md`),
47
+ which advances to the convergence gates before any bugbot fetch or trigger.
48
+ Also set `true` when an acknowledged trigger has been outstanding more than 30
49
+ minutes with no surfaced review at `current_head` (per the terminal BUGBOT gate
50
+ 30-minute budget — see `per-tick.md`). Reset to `false` on every push; the
51
+ entry gate re-applies the availability check on the next BUGBOT entry.
48
52
  Once set, remains `true` until the next push; if bugbot stays down
49
- across ticks, the flag persists and BUGTEAM continues.
53
+ across ticks, the flag persists and the convergence gates keep the Bugbot gate
54
+ bypassed.
50
55
  - `bugbot_acknowledged_at`: ISO 8601 timestamp string or `null`. Records
51
56
  the wall-clock moment Cursor Bugbot acknowledged the most recent
52
57
  `bugbot run` trigger comment (i.e. the trigger comment carries an
@@ -747,15 +747,16 @@ def parse_arguments(all_argv: list[str]) -> argparse.Namespace:
747
747
 
748
748
 
749
749
  def _resolve_bugbot_down(bugbot_down_flag: bool) -> bool:
750
- """Combine the explicit flag with the CLAUDE_REVIEWS_DISABLED env opt-out.
750
+ """Combine the explicit flag with the env availability gate for Bugbot.
751
751
 
752
752
  Args:
753
753
  bugbot_down_flag: Value of the ``--bugbot-down`` CLI flag.
754
754
 
755
755
  Returns:
756
- True when the flag is set OR ``CLAUDE_REVIEWS_DISABLED`` lists the
757
- ``bugbot`` token, so the env opt-out bypasses the bugbot gates even
758
- when the caller omits the flag.
756
+ True when the flag is set, or when Bugbot is disabled for the run:
757
+ off by default unless ``CLAUDE_REVIEWS_ENABLED`` lists ``bugbot``, and
758
+ always when ``CLAUDE_REVIEWS_DISABLED`` lists ``bugbot``. The env gate
759
+ bypasses the bugbot gates even when the caller omits the flag.
759
760
  """
760
761
  return bugbot_down_flag or is_bugbot_disabled_via_env()
761
762
 
@@ -231,17 +231,19 @@ def should_resolve_bugbot_down_true_when_env_disables_bugbot(
231
231
  assert check_convergence._resolve_bugbot_down(False) is True
232
232
 
233
233
 
234
- def should_resolve_bugbot_down_false_when_flag_unset_and_env_empty(
234
+ def should_resolve_bugbot_down_true_when_flag_unset_and_env_empty(
235
235
  monkeypatch: pytest.MonkeyPatch,
236
236
  ) -> None:
237
237
  monkeypatch.delenv("CLAUDE_REVIEWS_DISABLED", raising=False)
238
- assert check_convergence._resolve_bugbot_down(False) is False
238
+ monkeypatch.delenv("CLAUDE_REVIEWS_ENABLED", raising=False)
239
+ assert check_convergence._resolve_bugbot_down(False) is True
239
240
 
240
241
 
241
- def should_resolve_bugbot_down_false_when_env_disables_only_bugteam(
242
+ def should_resolve_bugbot_down_false_when_enabled_lists_bugbot(
242
243
  monkeypatch: pytest.MonkeyPatch,
243
244
  ) -> None:
244
- monkeypatch.setenv("CLAUDE_REVIEWS_DISABLED", "bugteam")
245
+ monkeypatch.delenv("CLAUDE_REVIEWS_DISABLED", raising=False)
246
+ monkeypatch.setenv("CLAUDE_REVIEWS_ENABLED", "bugbot")
245
247
  assert check_convergence._resolve_bugbot_down(False) is False
246
248
 
247
249
 
@@ -2,14 +2,9 @@
2
2
  name: pr-fix-protocol
3
3
  description: >-
4
4
  Applies reviewer findings to a PR as verified fixes and drives unresolved
5
- review threads to zero: executor choice (clean-coder spawn or multi-PR
6
- teammate with a worktree-path handoff), the shared 13-step fix sequence
7
- (TDD, one commit, fast-forward push), an atomic reply-and-resolve per
8
- thread, the unresolved-thread sweep hard gate (is_resolved == false is the
9
- only filter), and post-push state resets. Invoked by PR-loop orchestrators
10
- (pr-converge, autoconverge, bugteam, qbug, copilot-review) whenever a
11
- reviewer reports findings or unresolved threads exist; not for ad-hoc "fix
12
- this bug" requests outside a PR review loop.
5
+ review threads to zero. Invoked by PR-loop orchestrators (pr-converge,
6
+ autoconverge, bugteam, qbug, copilot-review) when a reviewer reports
7
+ findings; not for ad-hoc 'fix this bug' requests outside a PR loop.
13
8
  ---
14
9
 
15
10
  # PR Fix Protocol
@@ -0,0 +1,102 @@
1
+ ---
2
+ name: pr-loop-cloud-transport
3
+ description: Runs any PR-loop skill in a Claude Code session whose gh CLI cannot act on the PR — the binary is absent, gh auth status fails, or the active login cannot act on the PR's owner. A six-step transport workflow loads the GitHub MCP schemas, fixes origin/HEAD before pushes, keys review rules to the live MCP identity, routes every GitHub operation through the gh-to-MCP substitution matrix, reads Copilot status from what lands on the PR, and self-checks the posts the gh-text hooks cannot gate. Use at run start when pr-converge, autoconverge, bugteam, qbug, findbugs, fixbugs, monitor-open-prs, or copilot-review runs in a cloud session, when `command -v gh` or `gh auth status` fails, or when an MCP tool call fails with InputValidationError.
4
+ ---
5
+
6
+ # PR-loop cloud transport
7
+
8
+ One workflow that makes a cloud session able to run the PR-loop skill family. A cloud session ships no `gh` binary and cannot fetch one, so every `gh` step in a PR-loop skill routes through the GitHub MCP tools; this skill is the setup and routing contract for that substitution. The same routing serves a session whose `gh` is present but unauthenticated, or authenticated as an account that cannot act on the PR's owner — a binary that cannot act on the PR is as unusable as a binary that is not there. The evidence base is `docs/references/cloud-pr-loop-compatibility.md` in the source repo — every rule here traces to a live probe, the operation inventory recorded there, or the auth rule the calling skills state.
9
+
10
+ ## Decide the transport first
11
+
12
+ Three checks, in order; the first failure routes to the cloud transport:
13
+
14
+ 1. `command -v gh` — the binary exists.
15
+ 2. `gh auth status` — an active authenticated account exists; a non-zero exit fails the check.
16
+ 3. Once the PR scope is resolved (owner and repo known): `gh api repos/<owner>/<repo> --jq .permissions.push` must print `true`. The `permissions` object in that response reports the authenticated account's own rights on the repo, `push` included (live probe: the Section 7 appendix row in the runbook), so one command proves the binary, the auth, and the account's write access together. An error, a 404, or `false` fails the check.
17
+
18
+ - **All three pass** → local session. Stop here; follow the calling skill's own steps unchanged.
19
+ - **Any check fails** → cloud transport. Run the workflow below, then follow the calling skill's steps with every `gh` operation swapped for its cloud path.
20
+
21
+ A read-scoped account passes checks 1 and 2 and fails check 3 — the PR-loop skills push fix commits, so `push: false` means the local path stalls at its first push while the MCP transport carries the run.
22
+
23
+ ## Cloud transport workflow
24
+
25
+ Copy this checklist and check off items as you complete them:
26
+
27
+ ```
28
+ Cloud transport setup:
29
+ - [ ] Step 1: Load MCP schemas (once per session)
30
+ - [ ] Step 2: Fix origin/HEAD in every repo root
31
+ - [ ] Step 3: Read the MCP identity and key the review rules to it
32
+ - [ ] Step 4: Route every GitHub operation through the substitution matrix
33
+ - [ ] Step 5: Read Copilot status from what lands on the PR
34
+ - [ ] Step 6: Self-check the posts the quiet hooks would have gated
35
+ ```
36
+
37
+ **Step 1: Load MCP schemas (once per session)**
38
+
39
+ MCP tool schemas are deferred: a call before its schema loads fails with `InputValidationError`. Run exactly:
40
+
41
+ ```
42
+ ToolSearch "select:mcp__github__pull_request_read,mcp__github__pull_request_review_write,mcp__github__add_comment_to_pending_review,mcp__github__add_reply_to_pull_request_comment,mcp__github__add_issue_comment,mcp__github__request_copilot_review,mcp__github__update_pull_request,mcp__github__create_pull_request,mcp__github__issue_write,mcp__github__search_pull_requests,mcp__github__get_me,mcp__github__actions_list,mcp__github__actions_get,mcp__github__get_job_logs,mcp__Claude_Code_Remote__add_repo"
43
+ ```
44
+
45
+ Every name carries its full `mcp__` prefix — a bare name matches nothing and loads no schema. The list mixes servers on purpose: the `mcp__github__*` tools plus `mcp__Claude_Code_Remote__add_repo` for cross-repo checkout.
46
+
47
+ Verify: call `mcp__github__get_me`. A returned login confirms the load; an `InputValidationError` means a schema is missing — re-run the load with the failing tool named, then verify again.
48
+
49
+ **Step 2: Fix origin/HEAD in every repo root**
50
+
51
+ Cloud clones do not set `origin/HEAD`, and the global pre-push hook resolves its base ref from it, so every `git push` fails with `fatal: Not a valid object name origin/HEAD` until the ref exists. For each repo root the run touches:
52
+
53
+ ```
54
+ git -C <repo-root> remote set-head origin -a
55
+ ```
56
+
57
+ Feedback loop: a push that fails with the `origin/HEAD` message means this step has not run in that repo — run it there and push again. Never route around the hook with `--no-verify`.
58
+
59
+ **Step 3: Read the MCP identity and key the review rules to it**
60
+
61
+ Call `mcp__github__get_me` and record the login. Key every identity rule to that live login, not to a fixed account name:
62
+
63
+ - On a PR the MCP login authored, post `COMMENT` reviews only — GitHub blocks `APPROVE` and `REQUEST_CHANGES` on one's own PR. On a PR another account authored, any review type works.
64
+ - Leave `BUGTEAM_REVIEWER_ACCOUNT` unset and skip every account-swap step; the swap path has no cloud form.
65
+ - A second identity rides raw REST: a body posted with `GH_TOKEN` lands as the `claude[bot]` GitHub App. Self-PR checks and bot filters account for the split.
66
+
67
+ Full identity rules, the REST fallback scope, and the two-identity model: see [reference/identity-and-hooks.md](reference/identity-and-hooks.md).
68
+
69
+ **Step 4: Route every GitHub operation through the substitution matrix**
70
+
71
+ Every `gh` operation in the calling skill has one cloud path. The full operation-by-operation table: see [reference/substitution-matrix.md](reference/substitution-matrix.md). The rules that carry the most weight:
72
+
73
+ - Read review threads with `mcp__github__pull_request_read(method="get_review_comments")` and resolve them with `mcp__github__pull_request_review_write(method="resolve_thread", threadId="PRRT_...")`. Custom GraphQL is pinned to a served set, so hand-written `gh api graphql` queries have no cloud path.
74
+ - Reply to an inline comment with `mcp__github__add_reply_to_pull_request_comment` using the numeric id from the comment's `discussion_r` anchor.
75
+ - Always pass `perPage` on `mcp__github__search_pull_requests` — an unpaginated owner-wide search overflows the tool-result limit.
76
+ - Helper scripts that spawn `gh` subprocesses fail in cloud; run their steps through the matrix and keep their decision rules.
77
+
78
+ **Step 5: Read Copilot status from what lands on the PR**
79
+
80
+ Skip the `gh api copilot_internal/user` quota read; treat Copilot quota as unknown. Call `mcp__github__request_copilot_review` — its silent completion confirms nothing either way. Status comes from the PR afterward:
81
+
82
+ - A Copilot review on the HEAD, clean or with findings, means Copilot is up.
83
+ - An out-of-usage notice on the HEAD, or no review within the caller's poll budget, means Copilot is down — bypass the gate and say plainly in the run report that the status is an environment limit.
84
+
85
+ **Step 6: Self-check the posts the quiet hooks would have gated**
86
+
87
+ The `gh`-text hooks read risk from literal `gh ...` command text, and an MCP post carries none, so those checks go quiet in cloud. Before each MCP post, check by hand:
88
+
89
+ - No volatile scratch path in a post body (job dirs, temp roots, worktrees).
90
+ - A proof-of-work comment carries all five parts the proof standard names.
91
+ - A PR title follows Conventional Commits.
92
+ - Markdown bodies go through the structured `body` parameter so backticks show as formatting.
93
+
94
+ The commit and push gates still fire on cloud Bash git commands — follow them normally. The full hook roster and which side of the split each hook sits on: see [reference/identity-and-hooks.md](reference/identity-and-hooks.md).
95
+
96
+ ## Layout
97
+
98
+ | File | Role |
99
+ |---|---|
100
+ | `SKILL.md` | This workflow: the transport decision, the six setup and routing steps, and the progress checklist |
101
+ | `reference/substitution-matrix.md` | The gh-to-MCP operation substitution matrix, pagination rules, and the REST fallback scope |
102
+ | `reference/identity-and-hooks.md` | The two-identity model, self-PR review rules, and the hook-coverage split in cloud sessions |
@@ -0,0 +1,40 @@
1
+ # Identity model and hook coverage in cloud sessions
2
+
3
+ The identity rules a PR-loop run keys its reviews to, and the split between hooks that still gate a cloud run and hooks that go quiet on the MCP path. Every rule traces to a live probe recorded in `docs/references/cloud-pr-loop-compatibility.md` in the source repo.
4
+
5
+ ## Contents
6
+
7
+ - The two-identity model
8
+ - Self-PR review rules
9
+ - Hooks that still gate a cloud run
10
+ - Hooks that go quiet on the MCP path
11
+
12
+ ## The two-identity model
13
+
14
+ Two identities run side by side in a cloud session:
15
+
16
+ - **The MCP identity.** Every `mcp__github__*` tool acts as the user the `mcp__github__get_me` login names. Read it at run start and key every identity rule to that live login, not to a fixed account name.
17
+ - **The REST identity.** A body posted over raw REST with `GH_TOKEN` lands as the `claude[bot]` GitHub App (`author_association` NONE), with a server-appended `Generated by Claude Code` footer. `GET /user` still answers with the MCP user login.
18
+
19
+ Author-keyed logic accounts for the split: a self-PR check keys on the login that opened the PR, and a bot filter treats `claude[bot]` as the session's own REST author.
20
+
21
+ ## Self-PR review rules
22
+
23
+ - On a PR the MCP login authored, post `COMMENT` reviews. GitHub blocks `APPROVE` and `REQUEST_CHANGES` on one's own PR.
24
+ - On a PR another account authored, a review of any type from the MCP login works — including the APPROVE that a CLEAN bugteam audit posts.
25
+ - Leave `BUGTEAM_REVIEWER_ACCOUNT` unset. The account-swap path (`gh auth switch`, the swap helpers) has no cloud form.
26
+
27
+ ## Hooks that still gate a cloud run
28
+
29
+ The commit and push gates read git commands, which a cloud run still issues through Bash, so they fire normally and the run follows them: `verified_commit_gate`, `session_edit_stage_gate`, `block_main_commit`, `precommit_code_rules_gate`, `test_preflight_check`, and the pre-push base-ref check that needs `origin/HEAD` set (SKILL.md Step 2).
30
+
31
+ ## Hooks that go quiet on the MCP path
32
+
33
+ The `gh`-text hooks read risk from literal `gh ...` command text: `pr_description_enforcer`, `gh_body_arg_blocker`, `conventional_pr_title_gate`, `gh_pr_author_enforcer`, the `gh` branch of `volatile_path_in_post_blocker`, and the `gh pr ready` branch of `convergence_gate_blocker`. An MCP post carries no `gh` text, so these checks see nothing to gate while the settings matchers name only the local tool prefix. The run covers the gap by hand before each MCP post — the Step 6 self-check in SKILL.md:
34
+
35
+ - No volatile scratch path in a post body (job dirs, temp roots, worktrees).
36
+ - A proof-of-work comment carries all five parts the proof standard names.
37
+ - A PR title follows Conventional Commits.
38
+ - Markdown bodies go through the structured `body` parameter so backticks show as formatting.
39
+
40
+ `gh_pr_author_enforcer` shells to `gh`, catches the missing-binary error, and does nothing in cloud — it fails open by design.
@@ -0,0 +1,48 @@
1
+ # gh-to-MCP operation substitution matrix
2
+
3
+ Route every `gh` operation a PR-loop skill names through its cloud path. Load the MCP schemas first (SKILL.md Step 1) — a call before its schema loads fails with `InputValidationError`. Every row traces to a live probe or the operation inventory recorded in `docs/references/cloud-pr-loop-compatibility.md` in the source repo.
4
+
5
+ ## Contents
6
+
7
+ - The operation matrix
8
+ - Pagination rules
9
+ - The REST fallback and its scope
10
+
11
+ ## The operation matrix
12
+
13
+ | Operation | Local mechanism | Cloud path |
14
+ |---|---|---|
15
+ | Read PR / mergeability / draft state | `gh pr view`, `gh api pulls/N` | `mcp__github__pull_request_read(method="get")` |
16
+ | PR diff / files / commits / checks | `gh pr diff`, `gh api ...` | `mcp__github__pull_request_read(method="get_diff"/"get_files"/"get_commits"/"get_check_runs")` |
17
+ | List reviews / review threads | `gh api --paginate --slurp \| jq` | `mcp__github__pull_request_read(method="get_reviews"/"get_review_comments")` |
18
+ | Post / update / close issue | `gh issue create/edit/close` | `mcp__github__issue_write(method="create"/"update")`; close an issue with `method="update", state="closed"` |
19
+ | Post issue or PR comment | `gh issue comment`, `gh pr comment` | `mcp__github__add_issue_comment` |
20
+ | Post review with inline comments | `gh api pulls/N/reviews` POST | `mcp__github__pull_request_review_write(method="create")` → `mcp__github__add_comment_to_pending_review` per finding → `mcp__github__pull_request_review_write(method="submit_pending", event="COMMENT")` |
21
+ | Reply to a review comment | `gh api pulls/N/comments/ID/replies` | `mcp__github__add_reply_to_pull_request_comment` (numeric `discussion_r` id) |
22
+ | Resolve / unresolve a thread | `gh api graphql resolveReviewThread` | `mcp__github__pull_request_review_write(method="resolve_thread"/"unresolve_thread", threadId="PRRT_...")` |
23
+ | Request the Copilot reviewer | `gh api POST pulls/N/requested_reviewers` | `mcp__github__request_copilot_review`. The call completes with no output and no in-band confirmation either way; confirm by a Copilot review landing on the PR. |
24
+ | Mark ready / send to draft | `gh pr ready`, `gh pr ready --undo` | `mcp__github__update_pull_request(draft=false / draft=true)`, with a read-back confirming the draft state each way |
25
+ | Create a PR | `gh pr create --draft` | `mcp__github__create_pull_request(draft=true)` |
26
+ | Edit a PR body or title | `gh pr edit` | `mcp__github__update_pull_request(body=..., title=...)` |
27
+ | Cross-repo PR search | `gh search prs --owner X --state open` | `mcp__github__search_pull_requests(query="user:X is:open", perPage=30)` |
28
+ | Check runs on a SHA | `gh api commits/SHA/check-runs` | `mcp__github__pull_request_read(method="get_check_runs")`, or REST for owners the app connection covers |
29
+ | CI logs | `gh run view --log` | `mcp__github__actions_list` / `mcp__github__actions_get` / `mcp__github__get_job_logs` |
30
+ | Clone another repo | `gh repo clone` | `git clone https://github.com/owner/repo` (session-scoped), or `mcp__Claude_Code_Remote__add_repo` first |
31
+ | Copilot quota | `gh api copilot_internal/user` | None. Treat quota as unknown; call `mcp__github__request_copilot_review`, then read `copilot_down` from what lands on the PR (SKILL.md Step 5). |
32
+ | Second reviewer identity | `gh auth switch`, `BUGTEAM_REVIEWER_ACCOUNT` | None. One MCP identity — the `mcp__github__get_me` login. `COMMENT` reviews on own PRs work; `APPROVE`/`REQUEST_CHANGES` on own PRs are blocked by GitHub. |
33
+
34
+ ## Pagination rules
35
+
36
+ - `mcp__github__pull_request_read(method="get_reviews")` paginates with `page` + `perPage`.
37
+ - `mcp__github__pull_request_read(method="get_review_comments")` paginates with `perPage` + an `after` cursor.
38
+ - Always pass `perPage` on `mcp__github__search_pull_requests`: an unpaginated owner-wide search overflows the tool-result limit.
39
+
40
+ ## The REST fallback and its scope
41
+
42
+ Raw REST through the agent proxy works only for owners the Claude GitHub App connection covers; a repo outside that coverage answers 403. The client shape:
43
+
44
+ ```
45
+ curl -H "Authorization: Bearer $GH_TOKEN" --cacert /root/.ccr/ca-bundle.crt https://api.github.com/repos/<owner>/<repo>/...
46
+ ```
47
+
48
+ GraphQL through the proxy is pinned to a served set of PR-review operations, so a hand-written `gh api graphql` query has no cloud path — read review threads through `mcp__github__pull_request_read(method="get_review_comments")` and resolve them through `mcp__github__pull_request_review_write(method="resolve_thread", ...)`.