claude-dev-env 2.0.2 → 2.2.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 (110) hide show
  1. package/CLAUDE.md +1 -1
  2. package/hooks/blocking/CLAUDE.md +1 -0
  3. package/hooks/blocking/code_rules_shared.py +126 -47
  4. package/hooks/blocking/config/CLAUDE.md +2 -0
  5. package/hooks/blocking/config/verified_commit_context_constants.py +21 -0
  6. package/hooks/blocking/config/verified_commit_gate_output_constants.py +14 -0
  7. package/hooks/blocking/conftest.py +36 -30
  8. package/hooks/blocking/convergence_gate_blocker.py +76 -8
  9. package/hooks/blocking/plain_language_blocker.py +8 -0
  10. package/hooks/blocking/state_description_blocker.py +4 -1
  11. package/hooks/blocking/test_code_rules_shared.py +120 -20
  12. package/hooks/blocking/test_convergence_gate_blocker.py +146 -0
  13. package/hooks/blocking/test_plain_language_blocker.py +15 -0
  14. package/hooks/blocking/test_state_description_blocker.py +15 -0
  15. package/hooks/blocking/test_verified_commit_config_bootstrap.py +18 -0
  16. package/hooks/blocking/test_verified_commit_gate_additional_context.py +134 -0
  17. package/hooks/blocking/tests/test_verified_commit_gate.py +41 -0
  18. package/hooks/blocking/verified_commit_config_bootstrap.py +22 -10
  19. package/hooks/blocking/verified_commit_gate.py +113 -568
  20. package/hooks/blocking/verified_commit_gate_parts/CLAUDE.md +28 -0
  21. package/hooks/blocking/verified_commit_gate_parts/__init__.py +1 -0
  22. package/hooks/blocking/verified_commit_gate_parts/command_tokenization.py +174 -0
  23. package/hooks/blocking/verified_commit_gate_parts/deny_payload.py +53 -0
  24. package/hooks/blocking/verified_commit_gate_parts/deny_reason.py +80 -0
  25. package/hooks/blocking/verified_commit_gate_parts/directory_resolution.py +170 -0
  26. package/hooks/blocking/verified_commit_gate_parts/gated_invocations.py +205 -0
  27. package/hooks/blocking/verified_commit_gate_parts/tests/conftest.py +10 -0
  28. package/hooks/blocking/verified_commit_gate_parts/tests/test_command_tokenization.py +94 -0
  29. package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_payload.py +17 -0
  30. package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_reason.py +38 -0
  31. package/hooks/blocking/verified_commit_gate_parts/tests/test_directory_resolution.py +71 -0
  32. package/hooks/blocking/verified_commit_gate_parts/tests/test_gated_invocations.py +61 -0
  33. package/hooks/hooks_constants/CLAUDE.md +4 -1
  34. package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -0
  35. package/hooks/hooks_constants/convergence_gate_blocker_constants.py +36 -0
  36. package/hooks/hooks_constants/harness_scratchpad_constants.py +10 -9
  37. package/hooks/hooks_constants/mypy_integration_constants.py +16 -0
  38. package/hooks/validators/mypy_integration.py +145 -24
  39. package/hooks/validators/python_antipattern_checks.py +16 -0
  40. package/hooks/validators/run_all_validators.py +9 -3
  41. package/hooks/validators/test_mypy_integration.py +154 -0
  42. package/hooks/validators/test_python_antipattern_checks.py +112 -1
  43. package/hooks/validators/test_run_all_validators_pretooluse.py +16 -0
  44. package/package.json +1 -1
  45. package/rules/CLAUDE.md +1 -0
  46. package/rules/verified-commit-gate-skip.md +28 -0
  47. package/skills/_shared/pr-loop/CLAUDE.md +18 -13
  48. package/skills/_shared/pr-loop/portable-driver.md +150 -0
  49. package/skills/_shared/pr-loop/scripts/CLAUDE.md +3 -0
  50. package/skills/_shared/pr-loop/scripts/build_converge_task_list.py +310 -0
  51. package/skills/_shared/pr-loop/scripts/portable_converge_driver.py +1637 -0
  52. package/skills/_shared/pr-loop/scripts/select_converge_pacer.py +215 -0
  53. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/CLAUDE.md +3 -0
  54. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/converge_task_list_constants.py +56 -0
  55. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/pacer_constants.py +47 -0
  56. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +181 -0
  57. package/skills/_shared/pr-loop/scripts/test_build_converge_task_list.py +140 -0
  58. package/skills/_shared/pr-loop/scripts/test_portable_converge_driver.py +1119 -0
  59. package/skills/_shared/pr-loop/scripts/test_select_converge_pacer.py +207 -0
  60. package/skills/auditing-claude-config/CLAUDE.md +2 -1
  61. package/skills/auditing-claude-config/SKILL.md +114 -176
  62. package/skills/auditing-claude-config/reference/probe-hook.md +74 -0
  63. package/skills/autoconverge/CLAUDE.md +6 -3
  64. package/skills/autoconverge/SKILL.md +421 -346
  65. package/skills/autoconverge/reference/CLAUDE.md +1 -0
  66. package/skills/autoconverge/reference/convergence.md +5 -5
  67. package/skills/autoconverge/reference/copilot-findings.md +51 -0
  68. package/skills/autoconverge/reference/multi-pr.md +33 -2
  69. package/skills/autoconverge/reference/stop-conditions.md +9 -6
  70. package/skills/autoconverge/test_portable_pacer_gate.py +54 -0
  71. package/skills/closeout/SKILL.md +7 -9
  72. package/skills/copilot-finding-triage/SKILL.md +21 -11
  73. package/skills/copilot-review/CLAUDE.md +3 -2
  74. package/skills/copilot-review/SKILL.md +119 -155
  75. package/skills/copilot-review/templates/subagent-prompt.md +49 -0
  76. package/skills/fresh-branch/CLAUDE.md +6 -9
  77. package/skills/fresh-branch/SKILL.md +84 -33
  78. package/skills/fresh-branch/scripts/create_fresh_branch.py +445 -0
  79. package/skills/fresh-branch/scripts/fresh_branch_scripts_constants/__init__.py +1 -0
  80. package/skills/fresh-branch/scripts/fresh_branch_scripts_constants/fresh_branch_cli_constants.py +74 -0
  81. package/skills/fresh-branch/scripts/test_create_fresh_branch.py +670 -0
  82. package/skills/pr-converge/CLAUDE.md +4 -3
  83. package/skills/pr-converge/SKILL.md +469 -422
  84. package/skills/pr-converge/reference/CLAUDE.md +1 -0
  85. package/skills/pr-converge/reference/multi-pr-orchestration.md +5 -1
  86. package/skills/pr-converge/reference/per-tick.md +51 -24
  87. package/skills/pr-converge/reference/progress-checklist.md +168 -0
  88. package/skills/pr-converge/test_portable_pacer_gate.py +67 -0
  89. package/skills/pr-converge/test_step5_host_branch.py +8 -6
  90. package/skills/pr-converge/workflows/schedule-wakeup-loop.md +5 -3
  91. package/skills/pr-loop-cloud-transport/SKILL.md +1 -1
  92. package/skills/privacy-hygiene/SKILL.md +68 -115
  93. package/skills/privacy-hygiene/reference/sweep-procedure.md +62 -0
  94. package/skills/session-log/CLAUDE.md +2 -1
  95. package/skills/session-log/SKILL.md +4 -26
  96. package/skills/session-log/templates/frontmatter.md +40 -0
  97. package/skills/skill-builder/CLAUDE.md +8 -7
  98. package/skills/skill-builder/SKILL.md +26 -11
  99. package/skills/skill-builder/references/CLAUDE.md +3 -1
  100. package/skills/skill-builder/references/delegation-map.md +21 -12
  101. package/skills/skill-builder/references/description-field.md +9 -11
  102. package/skills/skill-builder/references/deterministic-elements.md +218 -0
  103. package/skills/skill-builder/references/self-audit-checklist.md +62 -45
  104. package/skills/skill-builder/references/skill-modularity.md +8 -9
  105. package/skills/skill-builder/templates/CLAUDE.md +2 -2
  106. package/skills/skill-builder/templates/gap-analysis.md +15 -0
  107. package/skills/skill-builder/workflows/CLAUDE.md +5 -5
  108. package/skills/skill-builder/workflows/improve-skill.md +18 -9
  109. package/skills/skill-builder/workflows/new-skill.md +23 -15
  110. package/skills/skill-builder/workflows/polish-skill.md +28 -21
@@ -7,6 +7,7 @@ Reference documentation for the `autoconverge` skill. The `converge.mjs` workflo
7
7
  | File | Role |
8
8
  |---|---|
9
9
  | `convergence.md` | Round shape: the static sweep, the three parallel internal lenses (code-review, bug-audit, self-review), deduplication, the fix commit step, the terminal Bugbot, Copilot, and Codex gates, and the definition of a clean convergence. |
10
+ | `copilot-findings.md` | The Copilot gate tiering, per-finding verification, and the `userReview` return contract. |
10
11
  | `stop-conditions.md` | Every condition that ends the run short of ready: budget cap, iteration cap, blocker exit, static-sweep stall, Bugbot, Copilot, and Codex bypass. |
11
12
  | `gotchas.md` | Hard-won lessons from failed runs: PR title validation, conflicting PRs, worktree branch lock, resumed sessions rerooting, and minter issues. |
12
13
  | `closing-report.md` | The closing HTML convergence report the teardown step builds and publishes: data source, build steps, publishing. |
@@ -66,11 +66,11 @@ confirmation gates that are expected to return zero.
66
66
  - **Code-review lens** — a correctness-focused review pass (`code-quality-agent`),
67
67
  report-only workflow agent — see runCodeReviewLens in workflow/converge.mjs for its configuration.
68
68
  The built-in `/code-review` command is a separate surface outside this
69
- workflow path. Autoconverge runs only where the Workflow tool exists (the
70
- skill hub Requirements sentence `autoconverge requires the Workflow tool`).
71
- Harness portability of that command is a pr-converge concern: pr-converge's
72
- CODE_REVIEW phase runs Claude Code's built-in `/code-review high --fix`
73
- ([`../../pr-converge/reference/per-tick.md`](../../pr-converge/reference/per-tick.md)).
69
+ workflow path. On `pacer=workflow`, this lens runs inside `converge.mjs`.
70
+ On `pacer=portable`, the continuous driver uses the pr-converge CODE_REVIEW
71
+ phase through `invoke_code_review.py` instead of `runCodeReviewLens`
72
+ ([`../../_shared/pr-loop/portable-driver.md`](../../_shared/pr-loop/portable-driver.md);
73
+ [`../../pr-converge/reference/per-tick.md`](../../pr-converge/reference/per-tick.md)).
74
74
  - **Bug-audit lens** — the bug-audit (`code-quality-agent`) applying the
75
75
  shared A–P rubric from `_shared/pr-loop/audit-contract.md`, then its
76
76
  adversarial second pass, and the doc-parity, test-assertion, and
@@ -0,0 +1,51 @@
1
+ # Copilot findings — tier, verify, then route
2
+
3
+ How the autoconverge Copilot gate handles each finding. The hub
4
+ ([`../SKILL.md`](../SKILL.md)) points here; the orchestrating session's
5
+ user-review hold runs the [`copilot-finding-triage`](../../copilot-finding-triage/SKILL.md)
6
+ skill.
7
+
8
+ The Copilot gate tiers each finding: a **self-healing** finding (style, type
9
+ hints, imports, formatting, magic-value extraction, test-only or doc-vs-code
10
+ fixes — nothing that changes observable runtime behavior) flows into the fix
11
+ round with no user notification. A **code-concern** finding (logic, security,
12
+ data handling, error-handling semantics, concurrency — the tier whenever in
13
+ doubt) goes to a verification stage before any routing.
14
+
15
+ Each code-concern finding gets its own verifier agent, all in parallel, inside
16
+ the workflow. A verdict is conclusive only when an actual check ran: the verifier
17
+ executes a command against the flagged HEAD — running the code path with crafted
18
+ inputs, forcing the claimed error condition, or running a purpose-built test —
19
+ and captures its output. The verdict carries
20
+ `{ verdict, checkCommand, checkOutput, evidence }`; a conclusive verdict with an
21
+ empty `checkCommand` or `checkOutput` downgrades to inconclusive.
22
+
23
+ - **confirmed** — the check reproduces the defect. The finding becomes
24
+ self-healing: it joins the fix round carrying its repro, and the fix re-runs
25
+ that same check, adds a regression test where the suite covers the surface,
26
+ lands in one commit, pushes, and replies on the thread with the fix SHA and the
27
+ before/after output. No page.
28
+ - **refuted** — the check shows the code already behaves correctly in the exact
29
+ scenario the finding claims is broken. The workflow replies on the thread with
30
+ the command and output, resolves it, and counts it clean. No page.
31
+ - **inconclusive** — everything else, and the verifier's default: no runnable
32
+ check exists, the check is infeasible here, the results are ambiguous, or the
33
+ fix needs a product decision. Any doubt sorts here. Only inconclusive findings
34
+ page the user.
35
+
36
+ A round whose code concerns all confirm or refute never returns
37
+ `blocker: "user-review"`. On one or more inconclusive findings, the workflow
38
+ stops with `converged: false`, `blocker: "user-review"`, and a `userReview`
39
+ field carrying
40
+ `{ reviewUrl, findings: [{ file, line, severity, tier, title, evidence }] }` —
41
+ `evidence` is the verifier's one-line note stating what check was attempted and
42
+ why it was not decisive.
43
+
44
+ A background workflow cannot hold for a human, so the wait belongs to the
45
+ orchestrating session. On a `blocker: "user-review"` return, run the
46
+ [`copilot-finding-triage`](../../copilot-finding-triage/SKILL.md) skill: send the
47
+ ntfy notification (the per-finding summary and evidence note plus the `reviewUrl`
48
+ Copilot review link), then hold with a 45-minute `ScheduleWakeup` for the user's
49
+ response. When the user answers within the window, follow their direction. When
50
+ the window closes with no response, run normal teardown and report the
51
+ inconclusive findings un-reviewed.
@@ -42,7 +42,27 @@ each PR its own checkout with `git worktree add`. For each PR the user named:
42
42
  non-zero exit sets `copilotDisabled: true` on every entry, so each child skips
43
43
  the Copilot gate with no agent spawned.
44
44
 
45
- ## Launch the multi-PR workflow
45
+ ## Launch
46
+
47
+ Select the pacer once for the multi-PR run (same helper as single-PR):
48
+
49
+ ```
50
+ python "$HOME/.claude/skills/_shared/pr-loop/scripts/select_converge_pacer.py" \
51
+ --skill autoconverge \
52
+ --has-workflow <0|1> \
53
+ --has-schedule-wakeup <0|1>
54
+ ```
55
+
56
+ ### `pacer=portable`
57
+
58
+ Do **not** call `Workflow`. For each PR worktree from pre-flight, run the
59
+ continuous portable driver in
60
+ [`../../_shared/pr-loop/portable-driver.md`](../../_shared/pr-loop/portable-driver.md)
61
+ (serial, or host fan-out when the host can isolate workers). One teardown per
62
+ PR after that PR reaches ready or a named blocker. Shared Copilot quota and
63
+ permission grants from multi-PR pre-flight still apply once.
64
+
65
+ ### `pacer=workflow`
46
66
 
47
67
  Call the `Workflow` tool against the fan-out script, passing the absolute path of
48
68
  `converge.mjs` and one entry per PR:
@@ -74,7 +94,18 @@ Each record's `deferredPrs` is that PR's own list of draft hardening PRs, and
74
94
  `allDeferredPrs` is every record's `deferredPrs` flattened into one list. The
75
95
  top-level `converged` is true only when every PR converged.
76
96
 
77
- ## Multi-PR teardown (on workflow completion)
97
+ ## Multi-PR teardown
98
+
99
+ ### `pacer=portable`
100
+
101
+ For each PR worktree, run the single-PR **Teardown → pacer=portable** path in
102
+ [`SKILL.md`](../SKILL.md) (`pr-loop-lifecycle` Close, no Workflow journal
103
+ report). Write one durable handoff per PR with that PR's `--pr-number` and
104
+ resume command `/autoconverge <PR URL>`. Revoke project permissions once per
105
+ repository after every PR's close. Print one summary line per PR as
106
+ `#<prNumber>: <converged | blocked> — rounds <N>, final <finalSha>[, blocker <blocker>]`.
107
+
108
+ ### `pacer=workflow` (on workflow completion)
78
109
 
79
110
  Run the single-PR Teardown in [`SKILL.md`](../SKILL.md) once per entry in
80
111
  `results`, using that PR's `owner`, `repo`, `prNumber`, and `finalSha`, and its
@@ -6,14 +6,17 @@ skill still runs teardown (revoke permissions, final report).
6
6
 
7
7
  ## Blockers (end the run short of ready)
8
8
 
9
- - **Budget stop** — the workflow's `budget` API is the pacing signal: when a
10
- usage target is set, `converge.mjs` checks `budget.remaining()` before each
11
- round and stops at the round boundary when one full round (three parallel
12
- lenses + one fix commit + re-verify) does not fit. The run returns
13
- `blocker: "budget"` with the run id; resume with
9
+ - **Budget stop** — on `pacer=workflow`, the workflow's `budget` API is the
10
+ pacing signal: when a usage target is set, `converge.mjs` checks
11
+ `budget.remaining()` before each round and stops at the round boundary when
12
+ one full round (three parallel lenses + one fix commit + re-verify) does not
13
+ fit. The run returns `blocker: "budget"` with the run id; resume with
14
14
  `Workflow({scriptPath, resumeFromRunId})` — completed rounds replay from the
15
15
  journal. The workflow never starts a round the budget cannot finish: a
16
- half-run round records nothing resumable and replays dirty.
16
+ half-run round records nothing resumable and replays dirty. On
17
+ `pacer=portable`, stop at a tick boundary when the session cannot cover a
18
+ full clean tick; write handoff and resume with `/autoconverge <PR URL>`
19
+ ([`../../_shared/pr-loop/portable-driver.md`](../../_shared/pr-loop/portable-driver.md)).
17
20
  - **Iteration cap** — 20 loop iterations pass without a full convergence-check
18
21
  pass. The iteration counter increments on every pass through any phase, so a
19
22
  convergence-check gate that no round can clear (for example a `mergeable_state`
@@ -0,0 +1,54 @@
1
+ """Contract checks: autoconverge selects a portable pacer when Workflow is absent."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from pathlib import Path
7
+
8
+ SKILL_ROOT = Path(__file__).resolve().parent
9
+ SKILL_MARKDOWN_PATH = SKILL_ROOT / "SKILL.md"
10
+ MULTI_PR_MARKDOWN_PATH = SKILL_ROOT / "reference" / "multi-pr.md"
11
+ PORTABLE_DRIVER_PATH = (
12
+ SKILL_ROOT.parent / "_shared" / "pr-loop" / "portable-driver.md"
13
+ )
14
+
15
+ SELECT_CONVERGE_PACER_SCRIPT = "select_converge_pacer.py"
16
+ PACER_PORTABLE_TOKEN = "pacer=portable"
17
+ PACER_WORKFLOW_TOKEN = "pacer=workflow"
18
+ ABORT_ONLY_PHRASE = "autoconverge requires the Workflow tool"
19
+ PORTABLE_DRIVER_DOC = "portable-driver.md"
20
+ PORTABLE_DRIVER_LINK_PATTERN = re.compile(
21
+ r"\((?P<relative>(?:\.\./)+_shared/pr-loop/portable-driver\.md)\)"
22
+ )
23
+
24
+
25
+ def test_skill_selects_pacer_and_forbids_abort_only_workflow() -> None:
26
+ skill_text = SKILL_MARKDOWN_PATH.read_text(encoding="utf-8")
27
+ assert SELECT_CONVERGE_PACER_SCRIPT in skill_text
28
+ assert PACER_PORTABLE_TOKEN in skill_text
29
+ assert PACER_WORKFLOW_TOKEN in skill_text
30
+ assert PORTABLE_DRIVER_DOC in skill_text
31
+ assert ABORT_ONLY_PHRASE not in skill_text
32
+
33
+
34
+ def test_skill_portable_driver_links_resolve_on_disk() -> None:
35
+ skill_text = SKILL_MARKDOWN_PATH.read_text(encoding="utf-8")
36
+ all_relative_links = PORTABLE_DRIVER_LINK_PATTERN.findall(skill_text)
37
+ assert all_relative_links, "SKILL.md must link portable-driver.md"
38
+ for each_relative_link in all_relative_links:
39
+ resolved_path = (SKILL_MARKDOWN_PATH.parent / each_relative_link).resolve()
40
+ assert resolved_path.is_file(), (
41
+ f"portable-driver link must resolve on disk: "
42
+ f"{each_relative_link} -> {resolved_path}"
43
+ )
44
+ assert resolved_path == PORTABLE_DRIVER_PATH.resolve()
45
+
46
+
47
+ def test_multi_pr_documents_portable_launch_and_teardown() -> None:
48
+ multi_pr_text = MULTI_PR_MARKDOWN_PATH.read_text(encoding="utf-8")
49
+ assert PACER_PORTABLE_TOKEN in multi_pr_text
50
+ assert "Do **not** call `Workflow`" in multi_pr_text or (
51
+ "Do not call `Workflow`" in multi_pr_text
52
+ )
53
+ assert "pacer=portable" in multi_pr_text
54
+ assert PORTABLE_DRIVER_DOC in multi_pr_text
@@ -1,15 +1,13 @@
1
1
  ---
2
2
  name: closeout
3
3
  description: >-
4
- Harvests session obstacles into GitHub issues backed by quoted evidence at
5
- session end, validates each draft with the user, dedupes against open and
6
- closed issues, routes each to its repo, files children then a parent
7
- checklist, and prints a computed cloud handoff prompt. Triggers: /closeout,
8
- close out this session, close out the session, file the session obstacles,
9
- session closeout, harvest session obstacles, end-of-session issue filing.
10
- Near-miss: not session-log, which journals the session to the vault; closeout
11
- files GitHub issues and prints a handoff prompt, and writes no session
12
- journal.
4
+ Harvests session obstacles into GitHub issues backed by quoted evidence:
5
+ validates each draft with the user, dedupes against open and closed issues,
6
+ files children under a parent checklist, and prints a cloud handoff prompt.
7
+ Triggers: /closeout, close out this session, close out the session, file the
8
+ session obstacles, session closeout, harvest session obstacles, end-of-session
9
+ issue filing. Not session-log (vault journal) closeout files issues and
10
+ writes no journal.
13
11
  ---
14
12
 
15
13
  # Closeout
@@ -1,13 +1,11 @@
1
1
  ---
2
2
  name: copilot-finding-triage
3
3
  description: >-
4
- Sorts each Copilot gate finding from a converge run into one of two tiers, then
5
- verifies each code concern with an executed check before routing it: a
6
- self-healing finding is auto-fixed, pushed, and counted toward convergence with
7
- no user page; a confirmed or refuted code concern is handled by the run with its
8
- check evidence; only a code concern the check leaves inconclusive pages the user
9
- over ntfy and holds the run behind a 45-minute gate. Triggers: 'copilot finding
10
- triage', 'user review gate', 'tier copilot findings', 'triage the copilot gate'.
4
+ Tiers each Copilot gate finding from a converge run, verifies each code concern
5
+ with an executed check, then routes it — auto-fix a self-healing finding,
6
+ resolve a refuted one, page the user only for an inconclusive one. Triggers:
7
+ 'copilot finding triage', 'user review gate', 'tier copilot findings', 'triage
8
+ the copilot gate'.
11
9
  ---
12
10
 
13
11
  # Copilot Finding Triage
@@ -117,9 +115,21 @@ counts as approval.
117
115
 
118
116
  ### Step 2 — Hold and wait
119
117
 
120
- Arm `ScheduleWakeup` for a 45-minute deadline. Where `ScheduleWakeup` is absent,
121
- use `send_later`. The clock starts when the page reaches the user, which is the
122
- moment the script exits zero.
118
+ Branch the hold on the caller's pacer (or on whether a durable wake surface is
119
+ present when the caller has not selected a pacer):
120
+
121
+ - **Native pacer** (`pacer=schedule_wakeup`, `pacer=workflow`, or
122
+ `ScheduleWakeup` present): arm `ScheduleWakeup` for a 45-minute deadline.
123
+ Where `ScheduleWakeup` is absent on that native path, use `send_later`.
124
+ - **Portable pacer** (`pacer=portable`, or no durable wake): do not call
125
+ `ScheduleWakeup` or `send_later`. Hold with an in-session deadline poll, or
126
+ write handoff and stop, per
127
+ [`../_shared/pr-loop/portable-driver.md`](../_shared/pr-loop/portable-driver.md).
128
+ Callers may override this step after the ntfy page and apply their own
129
+ portable hold.
130
+
131
+ The clock starts when the page reaches the user, which is the moment the script
132
+ exits zero.
123
133
 
124
134
  - If the user answers, follow their direction.
125
135
  - If the deadline passes with no answer, run the caller's normal teardown and
@@ -143,7 +153,7 @@ its fix re-runs that same check and posts the before/after output on the thread.
143
153
  evidence note.
144
154
  - [ ] The page carries the PR name, the per-finding summary, and the review URL.
145
155
  - [ ] `scripts/notify_ntfy.py` exited zero before the 45-minute clock started.
146
- - [ ] The wakeup is armed for 45 minutes from a delivered page.
156
+ - [ ] The hold is armed from a delivered page (45-minute `ScheduleWakeup` / `send_later` on native pacer; portable in-session poll or handoff on `pacer=portable`).
147
157
  - [ ] A failed page kept the gate open rather than approving the round.
148
158
 
149
159
  ## Files
@@ -6,11 +6,12 @@ Spawns a background subagent that polls the GitHub Copilot reviewer on the curre
6
6
 
7
7
  The main session gathers PR context (number, HEAD SHA, owner/repo, branch), spawns a self-terminating background subagent with a fully filled-in prompt, and returns control at once. The subagent loops on a 6-minute `ScheduleWakeup` cadence: fetch Copilot's latest review, TDD-fix any inline findings, push a commit, reply inline, resolve each addressed thread, re-request review. It stops on convergence, a persistent blocker, `TaskStop`, three consecutive ticks with no Copilot review at the current HEAD, or after 20 ticks.
8
8
 
9
- ## Key file
9
+ ## Key files
10
10
 
11
11
  | File | Purpose |
12
12
  |---|---|
13
- | `SKILL.md` | Four-step orchestration (opt-out check, gather PR context, spawn subagent, report to user), the verbatim subagent prompt template with all placeholders, fix protocol, stop conditions, and ground rules (one commit per tick, honor hooks, preserve draft state, use `copilot-pull-request-reviewer[bot]` with the `[bot]` suffix). |
13
+ | `SKILL.md` | Four-step orchestration (opt-out check, gather PR context, spawn subagent, report to user), fix protocol, stop conditions, and ground rules (one commit per tick, honor hooks, preserve draft state, use `copilot-pull-request-reviewer[bot]` with the `[bot]` suffix). |
14
+ | `templates/subagent-prompt.md` | The background watcher prompt Step 3 passes to the subagent word for word, with placeholders for PR number, owner, repo, branch, and HEAD SHA. |
14
15
 
15
16
  ## Environment opt-out
16
17
 
@@ -1,155 +1,119 @@
1
- ---
2
- name: copilot-review
3
- description: >-
4
- Spawns a background subagent that babysits the GitHub Copilot reviewer on the
5
- current PR: each tick it fixes unaddressed findings against HEAD, re-requests
6
- review, and exits on convergence. Triggers: '/copilot-review', 'watch
7
- copilot', 'babysit copilot review', 'keep re-requesting copilot'.
8
- ---
9
-
10
- # Copilot Review
11
-
12
- Delegates Copilot babysitting to a background subagent so the main session stays free. The subagent loops internally and closes itself on convergence.
13
-
14
- ## Transport check (before any GitHub step)
15
-
16
- Run `command -v gh`; when it succeeds, run `gh auth status`; once the PR scope is resolved, run `gh api repos/<owner>/<repo> --jq .permissions.push` and take `true` as the pass. When any check fails, run the `pr-loop-cloud-transport` skill first, route every `gh` operation in this skill through its substitution matrix, and carry the same routing into the subagent prompt so the spawned watcher inherits it.
17
-
18
- ## When this skill applies
19
-
20
- The user is on a PR branch, wants Copilot (the GitHub Copilot reviewer bot) to keep re-reviewing after each push, and wants findings auto-addressed between ticks — but does not want the main conversation consumed by polling.
21
-
22
- ## The Process
23
-
24
- ### Step 0: Opt-out check
25
-
26
- Before any other work, run:
27
-
28
- ```bash
29
- python "$HOME/.claude/_shared/pr-loop/scripts/reviews_disabled.py" --reviewer copilot
30
- ```
31
-
32
- Exit 0 — Copilot reviews are disabled: respond with the literal line
33
- `/copilot-review is disabled via CLAUDE_REVIEWS_DISABLED.` and stop — do not
34
- spawn the subagent, do not call the Copilot reviewer API, do not run any
35
- other step of this skill. Exit 1 — continue. Gate semantics live in the
36
- `reviewer-gates` skill ([../reviewer-gates/SKILL.md](../reviewer-gates/SKILL.md)).
37
-
38
- ### Step 1: Gather PR context
39
-
40
- From the current repo:
41
-
42
- ```bash
43
- # MCP: pull_request_read(method="get") returns {number, url, head.sha, base.ref, head.ref, isDraft}
44
- ```
45
-
46
- Capture `number`, `head.sha`, owner/repo (from `url`), and branch name. Pass these to the subagent so it does not rediscover them.
47
-
48
- ### Step 2: Spawn the background subagent
49
-
50
- Invoke the `Agent` tool with:
51
-
52
- - `subagent_type: "general-purpose"`
53
- - `run_in_background: true`
54
- - `description: "Copilot review loop for PR #<N>"`
55
- - `prompt`: the full instructions in **Step 3 (Subagent prompt template)**, with placeholders filled in from Step 1.
56
-
57
- Record the returned agent ID. Report to the user in one or two lines:
58
-
59
- - The subagent is running in the background.
60
- - It self-terminates on convergence.
61
- - To stop it early, the user says "stop the copilot loop" and you call `TaskStop <agent_id>`.
62
- - The main session stays free; completion arrives as a notification.
63
-
64
- Let the subagent own the cadence. The skill's job in the main session ends once the subagent is spawned and reported.
65
-
66
- ### Step 3: Subagent prompt template
67
-
68
- Pass this verbatim to the subagent (substituting the bracketed values):
69
-
70
- > You are babysitting the GitHub Copilot reviewer on PR **#[NUMBER]** at **[OWNER]/[REPO]** (branch `[BRANCH]`, current HEAD `[HEAD_SHA]`). Your job: keep the loop running until Copilot returns a clean review against the current HEAD, then stop.
71
- >
72
- > **Per-tick work** (do this now, then on each wakeup):
73
- >
74
- > 1. Resolve current HEAD: `pull_request_read(method="get", pullNumber=[NUMBER], owner="[OWNER]", repo="[REPO]")` and extract `.head.sha`.
75
- > 2. Fetch latest Copilot review via `pull_request_read(method="get_reviews", pullNumber=[NUMBER], owner="[OWNER]", repo="[REPO]")`.
76
- > Capture `commit_id`, `state`, `submitted_at`, `id`.
77
- > 3. Decide the branch:
78
- > - **No review exists:** increment `no_review_count` (see escalation rule below), re-request (step 4), schedule next wakeup, return.
79
- > - **Latest review's `commit_id` != current HEAD:** increment `no_review_count`, re-request (step 4), schedule next wakeup, return.
80
- > - **Latest review's `commit_id` == current HEAD with unresolved inline findings:** reset `no_review_count` to 0, TDD-fix them, push, reply inline on each thread, resolve each addressed thread, re-request (step 4), schedule next wakeup, return.
81
- > - **Latest review's `commit_id` == current HEAD and clean:** report convergence to the parent with a one-sentence summary and terminate. The loop is done; skip the ScheduleWakeup call.
82
- >
83
- > **Escalation rule:** `no_review_count` starts at 0, counts consecutive ticks where no Copilot review exists at the current HEAD, and resets to 0 on every push and every review sighted at HEAD. When it reaches 3, Copilot is not delivering reviews — report the stall to the parent with the count and the last request timestamp, then terminate without scheduling another wakeup.
84
- > 4. Re-request Copilot via `request_copilot_review(owner="[OWNER]", repo="[REPO]", pullNumber=[NUMBER])`.
85
- > The reviewer ID **must** be `copilot-pull-request-reviewer[bot]` with the `[bot]` suffix — empirically verified: `Copilot`, `copilot`, and `github-copilot` all return `requested_reviewers: []` with no error, silently no-op.
86
- > 5. Schedule the next wakeup with `ScheduleWakeup`:
87
- > - `delaySeconds: 360`
88
- > - `reason`: one short sentence on what you are waiting for.
89
- > - `prompt`: the literal sentinel `<<autonomous-loop-dynamic>>` so the next firing re-enters these instructions.
90
- >
91
- > **Fix protocol** (step 3, third branch):
92
- >
93
- > - Read `$HOME/.claude/skills/pr-fix-protocol/SKILL.md` and apply it — it carries the shared fix sequence, the reply-and-resolve unit, and the unresolved-thread sweep.
94
- > - Read each referenced file:line.
95
- > - Write a failing test first when the finding has behavior to test. For pure doc or comment nits that have no behavior, go straight to the fix.
96
- > - Stage the fix and create one new commit on the existing branch: `git add <files> && git commit -m "fix(review): ..."`.
97
- > - Push the new commit: `git push origin [BRANCH]`.
98
- > - Reply inline via `add_reply_to_pull_request_comment(owner="[OWNER]", repo="[REPO]", pullNumber=[NUMBER], body="...", commentId=<comment_id>)`, referencing the new commit SHA; then resolve each addressed thread via `pull_request_review_write(method="resolve_thread", pullNumber=[NUMBER], owner="[OWNER]", repo="[REPO]", threadId="<PRRT id>")`, harvesting the `PRRT_…` id from `pull_request_read(method="get_review_comments", ...)`. Reply first, then resolve — atomic per thread, per the protocol.
99
- >
100
- > When a pre-push, pre-commit, or other hook rejects the change, solve it. Read the hook's error message, diagnose the root cause in the code or test, and fix that. Then rerun the commit or push. Hooks exist to catch real problems; treat each rejection as new evidence to act on.
101
- >
102
- > **Stop conditions:**
103
- >
104
- > - Convergence (clean review against HEAD): report one-sentence summary to parent and terminate.
105
- > - Blocker you have exhausted fix attempts on (API auth failure persists, CI regression whose root cause falls outside this PR, a hook you have investigated and cannot resolve in one commit): report the specific blocker and its diagnosis to the parent, then terminate without scheduling another wakeup.
106
- > - Parent sends `TaskStop`: terminate immediately.
107
- > - `no_review_count` reaches 3 (escalation rule above): report the stall and terminate.
108
- >
109
- > **Safety cap:** after 20 ticks without convergence, stop and report. This is the total-tick runaway guard, distinct from the 3-consecutive-no-review escalation; that many rounds means something structural is wrong with the loop.
110
-
111
- ### Step 4: Report back to the user
112
-
113
- After spawning, tell the user in one or two lines: subagent ID, PR URL, that it will notify on convergence or blocker. Nothing else.
114
-
115
- ## Stopping the subagent
116
-
117
- - Convergence subagent stops itself.
118
- - Blocker subagent reports and stops.
119
- - User says stop → `TaskStop <agent_id>`.
120
- - User asks what loops are running → `TaskList`.
121
-
122
- ## Ground rules (for the subagent)
123
-
124
- - **Append commits.** Each tick adds one new commit on the existing branch with `git commit` and `git push origin [BRANCH]`.
125
- - **Honor pre-push and pre-commit hooks.** When a hook rejects the change, read its output, fix the underlying issue (the failing test, the missing constant, the broken import), and retry. Solve, do not punt.
126
- - **Respect the PR's current state.** Whatever draft-vs-ready state the PR has when the loop starts is the state the subagent preserves. The user decides when to flip it.
127
- - **One fix commit per tick.** Batch all of the current tick's findings into a single commit; the next tick handles the next review round.
128
- - **Use `copilot-pull-request-reviewer[bot]` with the `[bot]` suffix for the reviewer ID.** That exact spelling is load-bearing — it is the only form the API accepts.
129
-
130
- ## Examples
131
-
132
- <example>
133
- User: `/copilot-review`
134
- Claude: [reads PR context, spawns background subagent with the Step 3 template, reports "subagent X watching PR #123; will notify on convergence"]
135
- </example>
136
-
137
- <example>
138
- User: "babysit copilot on this PR until it's clean"
139
- Claude: [same as above]
140
- </example>
141
-
142
- <example>
143
- Subagent tick fires, latest Copilot review is against an older commit.
144
- Subagent: [re-requests review, schedules next wakeup, returns]
145
- </example>
146
-
147
- <example>
148
- Subagent tick fires, Copilot has 2 unaddressed inline findings on HEAD.
149
- Subagent: [TDD-fixes both, one commit, pushes, replies inline on both threads, resolves both threads, re-requests review, schedules next wakeup]
150
- </example>
151
-
152
- <example>
153
- Subagent tick fires, latest review is clean against HEAD.
154
- Subagent: [reports convergence to parent, terminates — no further wakeups]
155
- </example>
1
+ ---
2
+ name: copilot-review
3
+ description: >-
4
+ Spawns a background subagent that babysits the GitHub Copilot reviewer on the
5
+ current PR: each tick it fixes unaddressed findings against HEAD, re-requests
6
+ review, and exits on convergence. Triggers: '/copilot-review', 'watch
7
+ copilot', 'babysit copilot review', 'keep re-requesting copilot'.
8
+ ---
9
+
10
+ # Copilot Review
11
+
12
+ Delegates Copilot babysitting to a background subagent so the main session stays free. The subagent loops internally and closes itself on convergence.
13
+
14
+ ## Transport check (before any GitHub step)
15
+
16
+ Run `command -v gh`; when it succeeds, run `gh auth status`; once the PR scope is resolved, run `gh api repos/<owner>/<repo> --jq .permissions.push` and take `true` as the pass. When any check fails, run the `pr-loop-cloud-transport` skill first, route every `gh` operation in this skill through its substitution matrix, and carry the same routing into the subagent prompt so the spawned watcher inherits it.
17
+
18
+ ## When this skill applies
19
+
20
+ The user is on a PR branch, wants Copilot (the GitHub Copilot reviewer bot) to keep re-reviewing after each push, and wants findings auto-addressed between ticks — but does not want the main conversation consumed by polling.
21
+
22
+ ## The Process
23
+
24
+ ### Step 0: Opt-out check
25
+
26
+ Before any other work, run:
27
+
28
+ ```bash
29
+ python "$HOME/.claude/_shared/pr-loop/scripts/reviews_disabled.py" --reviewer copilot
30
+ ```
31
+
32
+ Exit 0 — Copilot reviews are disabled: respond with the literal line
33
+ `/copilot-review is disabled via CLAUDE_REVIEWS_DISABLED.` and stop — do not
34
+ spawn the subagent, do not call the Copilot reviewer API, do not run any
35
+ other step of this skill. Exit 1 — continue. Gate semantics live in the
36
+ `reviewer-gates` skill ([../reviewer-gates/SKILL.md](../reviewer-gates/SKILL.md)).
37
+
38
+ ### Step 1: Gather PR context
39
+
40
+ From the current repo:
41
+
42
+ ```bash
43
+ # MCP: pull_request_read(method="get") returns {number, url, head.sha, base.ref, head.ref, isDraft}
44
+ ```
45
+
46
+ Capture `number`, `head.sha`, owner/repo (from `url`), and branch name. Pass these to the subagent so it does not rediscover them.
47
+
48
+ ### Step 2: Spawn the background subagent
49
+
50
+ Invoke the `Agent` tool with:
51
+
52
+ - `subagent_type: "general-purpose"`
53
+ - `run_in_background: true`
54
+ - `description: "Copilot review loop for PR #<N>"`
55
+ - `prompt`: the full instructions in **Step 3 (Subagent prompt template)**, with placeholders filled in from Step 1.
56
+
57
+ Record the returned agent ID. Report to the user in one or two lines:
58
+
59
+ - The subagent is running in the background.
60
+ - It self-terminates on convergence.
61
+ - To stop it early, the user says "stop the copilot loop" and you call `TaskStop <agent_id>`.
62
+ - The main session stays free; completion arrives as a notification.
63
+
64
+ Let the subagent own the cadence. The skill's job in the main session ends once the subagent is spawned and reported.
65
+
66
+ ### Step 3: Subagent prompt template
67
+
68
+ Pass the prompt in
69
+ [`templates/subagent-prompt.md`](templates/subagent-prompt.md) to the subagent
70
+ word for word, filling in the bracketed values ([NUMBER], [OWNER], [REPO],
71
+ [BRANCH], [HEAD_SHA]) from Step 1. It carries the per-tick work, the escalation
72
+ rule (three consecutive no-review ticks), the fix protocol, the stop conditions,
73
+ and the 20-tick safety cap.
74
+
75
+ ### Step 4: Report back to the user
76
+
77
+ After spawning, tell the user in one or two lines: subagent ID, PR URL, that it will notify on convergence or blocker. Nothing else.
78
+
79
+ ## Stopping the subagent
80
+
81
+ - Convergence subagent stops itself.
82
+ - Blocker → subagent reports and stops.
83
+ - User says stop `TaskStop <agent_id>`.
84
+ - User asks what loops are running → `TaskList`.
85
+
86
+ ## Ground rules (for the subagent)
87
+
88
+ - **Append commits.** Each tick adds one new commit on the existing branch with `git commit` and `git push origin [BRANCH]`.
89
+ - **Honor pre-push and pre-commit hooks.** When a hook rejects the change, read its output, fix the underlying issue (the failing test, the missing constant, the broken import), and retry. Solve, do not punt.
90
+ - **Respect the PR's current state.** Whatever draft-vs-ready state the PR has when the loop starts is the state the subagent preserves. The user decides when to flip it.
91
+ - **One fix commit per tick.** Batch all of the current tick's findings into a single commit; the next tick handles the next review round.
92
+ - **Use `copilot-pull-request-reviewer[bot]` with the `[bot]` suffix for the reviewer ID.** That exact spelling is load-bearing — it is the only form the API accepts.
93
+
94
+ ## Examples
95
+
96
+ <example>
97
+ User: `/copilot-review`
98
+ Claude: [reads PR context, spawns background subagent with the Step 3 template, reports "subagent X watching PR #123; will notify on convergence"]
99
+ </example>
100
+
101
+ <example>
102
+ User: "babysit copilot on this PR until it's clean"
103
+ Claude: [same as above]
104
+ </example>
105
+
106
+ <example>
107
+ Subagent tick fires, latest Copilot review is against an older commit.
108
+ Subagent: [re-requests review, schedules next wakeup, returns]
109
+ </example>
110
+
111
+ <example>
112
+ Subagent tick fires, Copilot has 2 unaddressed inline findings on HEAD.
113
+ Subagent: [TDD-fixes both, one commit, pushes, replies inline on both threads, resolves both threads, re-requests review, schedules next wakeup]
114
+ </example>
115
+
116
+ <example>
117
+ Subagent tick fires, latest review is clean against HEAD.
118
+ Subagent: [reports convergence to parent, terminates — no further wakeups]
119
+ </example>