claude-dev-env 1.92.1 → 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.
- package/CLAUDE.md +6 -2
- package/_shared/CLAUDE.md +2 -1
- package/_shared/advisor/CLAUDE.md +21 -0
- package/_shared/advisor/advisor-protocol.md +142 -0
- package/_shared/advisor/scripts/config/advisor_scripts_constants/__init__.py +1 -0
- package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +86 -0
- package/_shared/advisor/scripts/model_tier_run_validator.py +246 -0
- package/_shared/advisor/scripts/pyproject.toml +3 -0
- package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +302 -0
- package/_shared/advisor/scripts/tests/test_tier_model_ids.py +180 -0
- package/_shared/advisor/scripts/tier_model_ids.py +164 -0
- package/_shared/pr-loop/CLAUDE.md +1 -0
- package/_shared/pr-loop/precatch-rubric.md +65 -0
- package/_shared/pr-loop/scripts/CLAUDE.md +1 -1
- package/_shared/pr-loop/scripts/code_rules_gate.py +137 -5
- package/_shared/pr-loop/scripts/post_audit_thread.py +1 -1
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -1
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +16 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/reviews_disabled_constants.py +2 -1
- package/_shared/pr-loop/scripts/reviewer_availability.py +42 -12
- package/_shared/pr-loop/scripts/reviews_disabled.py +65 -21
- package/_shared/pr-loop/scripts/terminology_sweep.py +69 -21
- package/_shared/pr-loop/scripts/tests/CLAUDE.md +1 -1
- package/_shared/pr-loop/scripts/tests/fixtures/{copilot_internal_user_jonecho.json → copilot_internal_user_example.json} +1 -1
- package/_shared/pr-loop/scripts/tests/test_agent_config_carveout.py +1 -1
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +146 -2
- package/_shared/pr-loop/scripts/tests/test_copilot_quota.py +8 -8
- package/_shared/pr-loop/scripts/tests/test_reviewer_availability.py +18 -5
- package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +56 -5
- package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +104 -17
- package/agents/CLAUDE.md +3 -2
- package/agents/clasp-deployment-orchestrator.md +2 -2
- package/agents/code-advisor.md +7 -5
- package/agents/code-verifier.md +6 -2
- package/agents/session-advisor.md +27 -0
- package/audit-rubrics/category_rubrics/category-j-code-rules-compliance.md +1 -1
- package/audit-rubrics/category_rubrics/category-k-codebase-conflicts.md +1 -1
- package/audit-rubrics/prompts/category-a-api-contracts.md +2 -2
- package/audit-rubrics/prompts/category-b-selector-engine-compat.md +2 -2
- package/audit-rubrics/prompts/category-c-resource-cleanup.md +2 -2
- package/audit-rubrics/prompts/category-d-scoping-and-ordering.md +2 -2
- package/audit-rubrics/prompts/category-e-dead-code.md +2 -2
- package/audit-rubrics/prompts/category-f-silent-failures.md +2 -2
- package/audit-rubrics/prompts/category-g-bounds-and-overflow.md +2 -2
- package/audit-rubrics/prompts/category-h-security-boundaries.md +2 -2
- package/audit-rubrics/prompts/category-i-concurrency.md +2 -2
- package/audit-rubrics/prompts/category-j-code-rules-compliance.md +2 -2
- package/audit-rubrics/prompts/category-k-codebase-conflicts.md +2 -2
- package/audit-rubrics/prompts/category-l-behavior-equivalence.md +2 -2
- package/audit-rubrics/prompts/category-m-producer-consumer-cardinality.md +2 -2
- package/audit-rubrics/prompts/category-n-test-name-scenario-verifier.md +2 -2
- package/audit-rubrics/prompts/category-o-docstring-vs-impl-drift.md +2 -2
- package/audit-rubrics/prompts/category-p-name-vs-behavior-contract.md +2 -2
- package/bin/CLAUDE.md +1 -0
- package/bin/expand_home_directory_tokens.mjs +75 -0
- package/bin/install.mjs +21 -7
- package/bin/install.test.mjs +220 -10
- package/hooks/CLAUDE.md +1 -1
- package/hooks/blocking/CLAUDE.md +4 -1
- package/hooks/blocking/_path_setup.py +13 -0
- package/hooks/blocking/bash_pre_tool_use_dispatcher.py +281 -0
- package/hooks/blocking/code_rules_dead_module_constant.py +88 -16
- package/hooks/blocking/code_rules_enforcer.py +6 -0
- package/hooks/blocking/code_rules_imports_logging.py +34 -1
- package/hooks/blocking/code_rules_paths_syspath.py +1 -1
- package/hooks/blocking/code_rules_test_layout.py +272 -0
- package/hooks/blocking/nas_ssh_binary_enforcer.py +22 -12
- package/hooks/blocking/pii_prevention_blocker.py +781 -0
- package/hooks/blocking/pii_scanner.py +340 -0
- package/hooks/blocking/pre_tool_use_dispatcher.py +3 -3
- package/hooks/blocking/stop_dispatcher.py +84 -0
- package/hooks/blocking/test__path_setup.py +39 -0
- package/hooks/blocking/test_bash_dispatcher_interpreter_starts.py +61 -0
- package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +294 -0
- package/hooks/blocking/test_code_rules_enforcer_cross_skill_duplicate.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_dead_module_constant.py +51 -0
- package/hooks/blocking/test_code_rules_enforcer_dead_module_constant_read_cap.py +103 -0
- package/hooks/blocking/test_code_rules_enforcer_dot_test_pattern.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_file_global_constants.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_hardcoded_user_path.py +11 -11
- package/hooks/blocking/test_code_rules_enforcer_naive_datetime.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_same_file_inline_duplicate.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_test_layout.py +111 -0
- package/hooks/blocking/test_code_rules_enforcer_type_checking_scope.py +76 -0
- package/hooks/blocking/test_destructive_command_blocker.py +29 -9
- package/hooks/blocking/test_gh_body_arg_blocker.py +3 -3
- package/hooks/blocking/test_nas_ssh_binary_enforcer.py +96 -39
- package/hooks/blocking/test_pii_prevention_blocker.py +905 -0
- package/hooks/blocking/test_pii_prevention_windows_git_path.py +41 -0
- package/hooks/blocking/test_pii_scanner.py +165 -0
- package/hooks/blocking/test_pr_description_enforcer_pr_number.py +1 -1
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +14 -13
- package/hooks/blocking/test_stop_dispatcher.py +187 -0
- package/hooks/blocking/test_verdict_directory_write_blocker.py +1 -1
- package/hooks/blocking/test_volatile_path_in_post_blocker.py +3 -3
- package/hooks/blocking/volatile_path_in_post_blocker.py +54 -5
- package/hooks/diagnostic/migrations/README.md +25 -26
- package/hooks/diagnostic/test_hook_log_extractor.py +10 -10
- package/hooks/hooks.json +10 -120
- package/hooks/hooks_constants/CLAUDE.md +12 -3
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +78 -0
- package/hooks/hooks_constants/dead_module_constant_constants.py +1 -0
- package/hooks/hooks_constants/hosted_hook_runner.py +73 -0
- package/hooks/hooks_constants/local_identity.py +182 -0
- package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +4 -37
- package/hooks/hooks_constants/pii_prevention_constants.py +299 -0
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +4 -0
- package/hooks/hooks_constants/python_style_checks_constants.py +15 -0
- package/hooks/hooks_constants/stop_dispatcher_constants.py +26 -0
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +87 -0
- package/hooks/hooks_constants/test_hardcoded_user_path_constants.py +2 -2
- package/hooks/hooks_constants/test_hosted_hook_runner.py +101 -0
- package/hooks/hooks_constants/test_layout_constants.py +28 -0
- package/hooks/hooks_constants/test_local_identity.py +88 -0
- package/hooks/hooks_constants/test_session_env_cleanup_constants.py +1 -1
- package/hooks/hooks_constants/test_stop_dispatcher_constants.py +32 -0
- package/hooks/validators/README.md +5 -6
- package/hooks/validators/__init__.py +2 -2
- package/hooks/validators/python_style_checks.py +298 -243
- package/hooks/validators/run_all_validators.py +6 -0
- package/hooks/validators/test_python_style_checks.py +278 -163
- package/package.json +2 -2
- package/rules/CLAUDE.md +2 -1
- package/rules/bdd.md +1 -1
- package/rules/nas-ssh-invocation.md +6 -4
- package/rules/no-justification-noise.md +61 -0
- package/scripts/test_setup_project_paths.py +1 -1
- package/skills/CLAUDE.md +6 -3
- package/skills/_shared/pr-loop/scripts/_path_resolver.py +2 -2
- package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +1 -1
- package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +1 -1
- package/skills/_shared/pr-loop/scripts/test__path_resolver.py +42 -3
- package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +11 -11
- package/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +26 -3
- package/skills/_shared/pr-loop/scripts/test_preflight_worktree.py +49 -25
- package/skills/autoconverge/CLAUDE.md +3 -3
- package/skills/autoconverge/SKILL.md +113 -35
- package/skills/autoconverge/reference/CLAUDE.md +2 -2
- package/skills/autoconverge/reference/convergence.md +44 -22
- package/skills/autoconverge/reference/gotchas.md +11 -0
- package/skills/autoconverge/reference/stop-conditions.md +23 -13
- package/skills/autoconverge/workflow/CLAUDE.md +2 -1
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +15 -19
- package/skills/autoconverge/workflow/converge.contract.test.mjs +43 -42
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +141 -16
- package/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +2 -2
- package/skills/autoconverge/workflow/converge.mjs +329 -68
- package/skills/autoconverge/workflow/converge.path-aware.test.mjs +1 -1
- package/skills/autoconverge/workflow/converge.precatch.test.mjs +152 -0
- package/skills/autoconverge/workflow/converge.run-input.test.mjs +3 -3
- package/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +15 -15
- package/skills/autoconverge/workflow/test_convergence_summary.py +6 -6
- package/skills/bdd-protocol/SKILL.md +4 -5
- package/skills/bdd-protocol/references/anti-patterns.md +1 -1
- package/skills/bugteam/SKILL.md +8 -0
- package/skills/bugteam/reference/copilot-gap-analysis.md +20 -20
- package/skills/bugteam/reference/obstacles/audit-walk-categories.md +1 -1
- package/skills/bugteam/reference/team-setup.md +7 -5
- package/skills/bugteam/scripts/reflow_skill_md.py +1 -1
- package/skills/copilot-finding-triage/SKILL.md +124 -0
- package/skills/copilot-finding-triage/reference/tier-rubric.md +49 -0
- package/skills/copilot-finding-triage/scripts/conftest.py +8 -0
- package/skills/copilot-finding-triage/scripts/copilot_finding_triage_constants/__init__.py +0 -0
- package/skills/copilot-finding-triage/scripts/copilot_finding_triage_constants/config/__init__.py +0 -0
- package/skills/copilot-finding-triage/scripts/copilot_finding_triage_constants/config/notify_ntfy_constants.py +32 -0
- package/skills/copilot-finding-triage/scripts/notify_ntfy.py +192 -0
- package/skills/copilot-finding-triage/scripts/test_notify_ntfy.py +141 -0
- package/skills/copilot-finding-triage/templates/notification.md +34 -0
- package/skills/copilot-review/SKILL.md +8 -6
- package/skills/findbugs/SKILL.md +4 -0
- package/skills/fixbugs/SKILL.md +8 -7
- package/skills/gotcha/CLAUDE.md +2 -2
- package/skills/gotcha/SKILL.md +4 -4
- package/skills/log-audit/SKILL.md +4 -6
- package/skills/monitor-open-prs/CLAUDE.md +1 -1
- package/skills/monitor-open-prs/SKILL.md +6 -1
- package/skills/orchestrator/SKILL.md +74 -78
- package/skills/orchestrator-refresh/SKILL.md +24 -12
- package/skills/post-audit-findings/SKILL.md +5 -9
- package/skills/pr-consistency-audit/SKILL.md +5 -1
- package/skills/pr-converge/CLAUDE.md +1 -1
- package/skills/pr-converge/SKILL.md +86 -47
- package/skills/pr-converge/reference/CLAUDE.md +1 -1
- package/skills/pr-converge/reference/convergence-gates.md +20 -17
- package/skills/pr-converge/reference/examples.md +63 -47
- package/skills/pr-converge/reference/fix-protocol.md +15 -15
- package/skills/pr-converge/reference/ground-rules.md +11 -7
- package/skills/pr-converge/reference/multi-pr-orchestration.md +6 -5
- package/skills/pr-converge/reference/per-tick.md +129 -107
- package/skills/pr-converge/reference/state-schema.md +15 -10
- package/skills/pr-converge/scripts/check_convergence.py +5 -4
- package/skills/pr-converge/scripts/test_check_convergence.py +6 -4
- package/skills/pr-fix-protocol/SKILL.md +3 -8
- package/skills/pr-loop-cloud-transport/SKILL.md +102 -0
- package/skills/pr-loop-cloud-transport/reference/identity-and-hooks.md +40 -0
- package/skills/pr-loop-cloud-transport/reference/substitution-matrix.md +48 -0
- package/skills/pr-loop-lifecycle/SKILL.md +5 -9
- package/skills/pr-scope-resolve/SKILL.md +3 -6
- package/skills/privacy-hygiene/SKILL.md +114 -0
- package/skills/qbug/SKILL.md +8 -8
- package/skills/rebase/SKILL.md +5 -1
- package/skills/refine/SKILL.md +4 -5
- package/skills/reviewer-gates/SKILL.md +7 -11
- package/skills/session-log/SKILL.md +4 -1
- package/skills/skill-builder/SKILL.md +3 -6
- package/skills/structure-prompt/SKILL.md +4 -5
- package/skills/team-advisor/SKILL.md +56 -0
- package/skills/test_markdown_link_integrity.py +10 -6
- package/skills/update/SKILL.md +5 -1
- package/skills/usage-pause/SKILL.md +14 -5
- package/skills/usage-pause/scripts/resolve_usage_window.py +83 -5
- package/skills/usage-pause/scripts/test_resolve_usage_window.py +185 -17
- package/skills/usage-pause/scripts/usage_pause_constants/resolve_usage_window_constants.py +4 -3
- package/skills/verified-build/SKILL.md +4 -9
|
@@ -36,7 +36,7 @@ Create once at session start. Each teammate writes result before going idle.
|
|
|
36
36
|
"prs": {
|
|
37
37
|
"289": {
|
|
38
38
|
"owner": "jl-cmd",
|
|
39
|
-
"repo": "claude-
|
|
39
|
+
"repo": "claude-dev-env",
|
|
40
40
|
"branch": "feat/shared-pr-loop-extraction",
|
|
41
41
|
"phase": "BUGBOT",
|
|
42
42
|
"current_head": "f9a7d49e",
|
|
@@ -85,10 +85,11 @@ file contents, or subagent-owned fields except two exceptions. Uses same
|
|
|
85
85
|
refresh `last_updated`. Observability only — no ceiling; loop ends on
|
|
86
86
|
convergence or **Stop conditions**.
|
|
87
87
|
2. **`phase` when only orchestrator decides:** Orchestrator applies a
|
|
88
|
-
Step 2 phase transition (including BUGTEAM §(d) `phase = BUGBOT`
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
Step 2 phase transition (including BUGTEAM §(d) — `phase = BUGBOT` on
|
|
89
|
+
convergence with no push, routing into the terminal Bugbot gate, or
|
|
90
|
+
`phase = CODE_REVIEW` on a fix push — without a subagent write) and no
|
|
91
|
+
subagent merge occurs that tick → orchestrator performs one locked merge
|
|
92
|
+
setting only `prs[<pr_number>].phase` and `last_updated`.
|
|
92
93
|
|
|
93
94
|
Orchestrator reads file at start of every tick for cross-PR state, not
|
|
94
95
|
conversation context.
|
|
@@ -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-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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`.
|
|
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
|
-
|
|
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`.
|
|
245
|
-
|
|
246
|
-
|
|
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
|
|
251
|
-
|
|
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
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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 =
|
|
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
|
|
322
|
-
review. Do **not** run bugteam here —
|
|
323
|
-
|
|
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
|
|
347
|
-
`phase =
|
|
348
|
-
|
|
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
|
-
`
|
|
357
|
-
back-to-back-clean guarantee (
|
|
358
|
-
before gates re-open) only holds when every
|
|
359
|
-
|
|
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
|
-
- [ ] **
|
|
364
|
-
When
|
|
365
|
-
`
|
|
366
|
-
|
|
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`,
|
|
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`,
|
|
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
|
-
`
|
|
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;
|
|
41
|
-
at
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|
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
|
|
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
|
|
757
|
-
|
|
758
|
-
when
|
|
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
|
|
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
|
-
|
|
238
|
+
monkeypatch.delenv("CLAUDE_REVIEWS_ENABLED", raising=False)
|
|
239
|
+
assert check_convergence._resolve_bugbot_down(False) is True
|
|
239
240
|
|
|
240
241
|
|
|
241
|
-
def
|
|
242
|
+
def should_resolve_bugbot_down_false_when_enabled_lists_bugbot(
|
|
242
243
|
monkeypatch: pytest.MonkeyPatch,
|
|
243
244
|
) -> None:
|
|
244
|
-
monkeypatch.
|
|
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
|
|
6
|
-
|
|
7
|
-
|
|
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.
|