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.
- package/CLAUDE.md +1 -1
- package/hooks/blocking/CLAUDE.md +1 -0
- package/hooks/blocking/code_rules_shared.py +126 -47
- package/hooks/blocking/config/CLAUDE.md +2 -0
- package/hooks/blocking/config/verified_commit_context_constants.py +21 -0
- package/hooks/blocking/config/verified_commit_gate_output_constants.py +14 -0
- package/hooks/blocking/conftest.py +36 -30
- package/hooks/blocking/convergence_gate_blocker.py +76 -8
- package/hooks/blocking/plain_language_blocker.py +8 -0
- package/hooks/blocking/state_description_blocker.py +4 -1
- package/hooks/blocking/test_code_rules_shared.py +120 -20
- package/hooks/blocking/test_convergence_gate_blocker.py +146 -0
- package/hooks/blocking/test_plain_language_blocker.py +15 -0
- package/hooks/blocking/test_state_description_blocker.py +15 -0
- package/hooks/blocking/test_verified_commit_config_bootstrap.py +18 -0
- package/hooks/blocking/test_verified_commit_gate_additional_context.py +134 -0
- package/hooks/blocking/tests/test_verified_commit_gate.py +41 -0
- package/hooks/blocking/verified_commit_config_bootstrap.py +22 -10
- package/hooks/blocking/verified_commit_gate.py +113 -568
- package/hooks/blocking/verified_commit_gate_parts/CLAUDE.md +28 -0
- package/hooks/blocking/verified_commit_gate_parts/__init__.py +1 -0
- package/hooks/blocking/verified_commit_gate_parts/command_tokenization.py +174 -0
- package/hooks/blocking/verified_commit_gate_parts/deny_payload.py +53 -0
- package/hooks/blocking/verified_commit_gate_parts/deny_reason.py +80 -0
- package/hooks/blocking/verified_commit_gate_parts/directory_resolution.py +170 -0
- package/hooks/blocking/verified_commit_gate_parts/gated_invocations.py +205 -0
- package/hooks/blocking/verified_commit_gate_parts/tests/conftest.py +10 -0
- package/hooks/blocking/verified_commit_gate_parts/tests/test_command_tokenization.py +94 -0
- package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_payload.py +17 -0
- package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_reason.py +38 -0
- package/hooks/blocking/verified_commit_gate_parts/tests/test_directory_resolution.py +71 -0
- package/hooks/blocking/verified_commit_gate_parts/tests/test_gated_invocations.py +61 -0
- package/hooks/hooks_constants/CLAUDE.md +4 -1
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -0
- package/hooks/hooks_constants/convergence_gate_blocker_constants.py +36 -0
- package/hooks/hooks_constants/harness_scratchpad_constants.py +10 -9
- package/hooks/hooks_constants/mypy_integration_constants.py +16 -0
- package/hooks/validators/mypy_integration.py +145 -24
- package/hooks/validators/python_antipattern_checks.py +16 -0
- package/hooks/validators/run_all_validators.py +9 -3
- package/hooks/validators/test_mypy_integration.py +154 -0
- package/hooks/validators/test_python_antipattern_checks.py +112 -1
- package/hooks/validators/test_run_all_validators_pretooluse.py +16 -0
- package/package.json +1 -1
- package/rules/CLAUDE.md +1 -0
- package/rules/verified-commit-gate-skip.md +28 -0
- package/skills/_shared/pr-loop/CLAUDE.md +18 -13
- package/skills/_shared/pr-loop/portable-driver.md +150 -0
- package/skills/_shared/pr-loop/scripts/CLAUDE.md +3 -0
- package/skills/_shared/pr-loop/scripts/build_converge_task_list.py +310 -0
- package/skills/_shared/pr-loop/scripts/portable_converge_driver.py +1637 -0
- package/skills/_shared/pr-loop/scripts/select_converge_pacer.py +215 -0
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/CLAUDE.md +3 -0
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/converge_task_list_constants.py +56 -0
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/pacer_constants.py +47 -0
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +181 -0
- package/skills/_shared/pr-loop/scripts/test_build_converge_task_list.py +140 -0
- package/skills/_shared/pr-loop/scripts/test_portable_converge_driver.py +1119 -0
- package/skills/_shared/pr-loop/scripts/test_select_converge_pacer.py +207 -0
- package/skills/auditing-claude-config/CLAUDE.md +2 -1
- package/skills/auditing-claude-config/SKILL.md +114 -176
- package/skills/auditing-claude-config/reference/probe-hook.md +74 -0
- package/skills/autoconverge/CLAUDE.md +6 -3
- package/skills/autoconverge/SKILL.md +421 -346
- package/skills/autoconverge/reference/CLAUDE.md +1 -0
- package/skills/autoconverge/reference/convergence.md +5 -5
- package/skills/autoconverge/reference/copilot-findings.md +51 -0
- package/skills/autoconverge/reference/multi-pr.md +33 -2
- package/skills/autoconverge/reference/stop-conditions.md +9 -6
- package/skills/autoconverge/test_portable_pacer_gate.py +54 -0
- package/skills/closeout/SKILL.md +7 -9
- package/skills/copilot-finding-triage/SKILL.md +21 -11
- package/skills/copilot-review/CLAUDE.md +3 -2
- package/skills/copilot-review/SKILL.md +119 -155
- package/skills/copilot-review/templates/subagent-prompt.md +49 -0
- package/skills/fresh-branch/CLAUDE.md +6 -9
- package/skills/fresh-branch/SKILL.md +84 -33
- package/skills/fresh-branch/scripts/create_fresh_branch.py +445 -0
- package/skills/fresh-branch/scripts/fresh_branch_scripts_constants/__init__.py +1 -0
- package/skills/fresh-branch/scripts/fresh_branch_scripts_constants/fresh_branch_cli_constants.py +74 -0
- package/skills/fresh-branch/scripts/test_create_fresh_branch.py +670 -0
- package/skills/pr-converge/CLAUDE.md +4 -3
- package/skills/pr-converge/SKILL.md +469 -422
- package/skills/pr-converge/reference/CLAUDE.md +1 -0
- package/skills/pr-converge/reference/multi-pr-orchestration.md +5 -1
- package/skills/pr-converge/reference/per-tick.md +51 -24
- package/skills/pr-converge/reference/progress-checklist.md +168 -0
- package/skills/pr-converge/test_portable_pacer_gate.py +67 -0
- package/skills/pr-converge/test_step5_host_branch.py +8 -6
- package/skills/pr-converge/workflows/schedule-wakeup-loop.md +5 -3
- package/skills/pr-loop-cloud-transport/SKILL.md +1 -1
- package/skills/privacy-hygiene/SKILL.md +68 -115
- package/skills/privacy-hygiene/reference/sweep-procedure.md +62 -0
- package/skills/session-log/CLAUDE.md +2 -1
- package/skills/session-log/SKILL.md +4 -26
- package/skills/session-log/templates/frontmatter.md +40 -0
- package/skills/skill-builder/CLAUDE.md +8 -7
- package/skills/skill-builder/SKILL.md +26 -11
- package/skills/skill-builder/references/CLAUDE.md +3 -1
- package/skills/skill-builder/references/delegation-map.md +21 -12
- package/skills/skill-builder/references/description-field.md +9 -11
- package/skills/skill-builder/references/deterministic-elements.md +218 -0
- package/skills/skill-builder/references/self-audit-checklist.md +62 -45
- package/skills/skill-builder/references/skill-modularity.md +8 -9
- package/skills/skill-builder/templates/CLAUDE.md +2 -2
- package/skills/skill-builder/templates/gap-analysis.md +15 -0
- package/skills/skill-builder/workflows/CLAUDE.md +5 -5
- package/skills/skill-builder/workflows/improve-skill.md +18 -9
- package/skills/skill-builder/workflows/new-skill.md +23 -15
- package/skills/skill-builder/workflows/polish-skill.md +28 -21
|
@@ -6,6 +6,7 @@ Reference documents for the `pr-converge` skill. These files define the per-tick
|
|
|
6
6
|
|
|
7
7
|
| File | Purpose |
|
|
8
8
|
|---|---|
|
|
9
|
+
| `progress-checklist.md` | The per-tick eleven-step checklist (Step 0 open → Step 11 report), each step pointing to its spoke file |
|
|
9
10
|
| `per-tick.md` | Step-by-step procedure for one tick (resolve HEAD, run the internal CODE_REVIEW and BUGTEAM phases, then the terminal BUGBOT and COPILOT_WAIT gates, schedule next wakeup) |
|
|
10
11
|
| `convergence-gates.md` | Six gates that must all pass before the PR is marked ready for review |
|
|
11
12
|
| `fix-protocol.md` | pr-converge deltas on the `pr-fix-protocol` skill: multi-PR teammate obligations and the same-tick bugbot re-trigger rule |
|
|
@@ -210,7 +210,11 @@ When bugfix (clean-coder) subagent completes after push:
|
|
|
210
210
|
3. Each PR with new subagent results → spawn next agent per rules, all
|
|
211
211
|
in one parallel message.
|
|
212
212
|
4. Re-read `state.json` if needed for scheduling.
|
|
213
|
-
5.
|
|
213
|
+
5. Advance the pacer: on `pacer=schedule_wakeup`, call `ScheduleWakeup`
|
|
214
|
+
with the appropriate delay; on `pacer=portable`, continue or poll
|
|
215
|
+
in-session per
|
|
216
|
+
[`../../_shared/pr-loop/portable-driver.md`](../../_shared/pr-loop/portable-driver.md)
|
|
217
|
+
(no `ScheduleWakeup`).
|
|
214
218
|
6. Nothing else.
|
|
215
219
|
|
|
216
220
|
## Memory
|
|
@@ -15,29 +15,38 @@ Use on **draft PR**. Cursor Bugbot and `/bugteam` re-run after each push. Fix
|
|
|
15
15
|
findings between rounds until back-to-back clean on same `HEAD`, then mark
|
|
16
16
|
PR ready for review.
|
|
17
17
|
|
|
18
|
-
Run every tick in parent harness session. Pacing
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
Run every tick in the parent harness session. Pacing depends on the selected
|
|
19
|
+
pacer from pre-flight (`select_converge_pacer.py`):
|
|
20
|
+
|
|
21
|
+
- `schedule_wakeup` — [`../workflows/schedule-wakeup-loop.md`](../workflows/schedule-wakeup-loop.md)
|
|
22
|
+
- `portable` — [`../../_shared/pr-loop/portable-driver.md`](../../_shared/pr-loop/portable-driver.md)
|
|
23
|
+
|
|
24
|
+
See [Pacing workflow](#pacing-workflow).
|
|
21
25
|
|
|
22
26
|
Every BUGTEAM tick runs **bugteam** — never hand-rolled substitute. Fix
|
|
23
|
-
protocol per [fix-protocol.md](fix-protocol.md). Pacing stays in main
|
|
24
|
-
`ScheduleWakeup`
|
|
27
|
+
protocol per [fix-protocol.md](fix-protocol.md). Pacing stays in the main
|
|
28
|
+
session (native `ScheduleWakeup` or the portable continuous driver).
|
|
25
29
|
|
|
26
30
|
## Invocation modes
|
|
27
31
|
|
|
28
|
-
- **`/pr-converge`** runs one tick, then Step 4
|
|
29
|
-
`ScheduleWakeup`. Omit the next wakeup only on
|
|
30
|
-
conditions**.
|
|
32
|
+
- **`/pr-converge` with `pacer=schedule_wakeup`** runs one tick, then Step 4
|
|
33
|
+
schedules the next via `ScheduleWakeup`. Omit the next wakeup only on
|
|
34
|
+
convergence or **Stop conditions**.
|
|
35
|
+
- **`/pr-converge` with `pacer=portable`** runs ticks continuously in-session
|
|
36
|
+
(poll waits at the same delays); write handoff at budget boundaries. See
|
|
37
|
+
the portable driver.
|
|
31
38
|
|
|
32
39
|
## Pacing workflow
|
|
33
40
|
|
|
34
|
-
|
|
41
|
+
When `pacer=schedule_wakeup`, read
|
|
42
|
+
[`../workflows/schedule-wakeup-loop.md`](../workflows/schedule-wakeup-loop.md)
|
|
35
43
|
(installed copy under `$HOME/.claude/skills/pr-converge/workflows/`) before
|
|
36
|
-
Step 4.
|
|
37
|
-
|
|
44
|
+
Step 4. When `pacer=portable`, read
|
|
45
|
+
[`../../_shared/pr-loop/portable-driver.md`](../../_shared/pr-loop/portable-driver.md)
|
|
46
|
+
and skip `ScheduleWakeup` calls.
|
|
38
47
|
|
|
39
48
|
- **`/pr-converge`** (default): loops until convergence. After each tick
|
|
40
|
-
(unless converged or stopped), run **Step 4
|
|
49
|
+
(unless converged or stopped), run **Step 4** using the selected pacer.
|
|
41
50
|
|
|
42
51
|
## Step 1: Resolve current HEAD and PR context
|
|
43
52
|
|
|
@@ -244,8 +253,8 @@ c. Decide (three branches; match first whose predicate holds):
|
|
|
244
253
|
|
|
245
254
|
- **Failed review (`returncode != 0`, or chain mode with null
|
|
246
255
|
`served_command`):** The review did not complete a successful serve. Do
|
|
247
|
-
not set `code_review_clean_at`. Stay `phase = CODE_REVIEW`,
|
|
248
|
-
|
|
256
|
+
not set `code_review_clean_at`. Stay `phase = CODE_REVIEW`, apply Step 4 pacer
|
|
257
|
+
(ScheduleWakeup or portable continue/poll), return. A failed chain often leaves `dirty_tree` false — that is
|
|
249
258
|
not a clean stamp.
|
|
250
259
|
- **Fixes applied (working tree dirty / `dirty_tree` true):** Commit the
|
|
251
260
|
applied fixes in one commit → push, following the shared fix protocol
|
|
@@ -253,7 +262,7 @@ c. Decide (three branches; match first whose predicate holds):
|
|
|
253
262
|
push-invalidated markers per [ground-rules.md](ground-rules.md) /
|
|
254
263
|
[state-schema.md](state-schema.md) (all `*_clean_at`, `merge_state_status`,
|
|
255
264
|
`bugbot_down`, `bugbot_acknowledged_at`, `codex_down`). Stay
|
|
256
|
-
`phase = CODE_REVIEW`,
|
|
265
|
+
`phase = CODE_REVIEW`, apply Step 4 pacer (ScheduleWakeup or portable continue/poll), return. Every fix push
|
|
257
266
|
re-enters the internal passes on the new HEAD.
|
|
258
267
|
- **Clean (successful serve: `returncode == 0`, chain `served_command`
|
|
259
268
|
non-null when `mode == chain`, and `dirty_tree` false):** Set
|
|
@@ -318,7 +327,7 @@ d. Decide based on post-bugteam state — order matters. Check
|
|
|
318
327
|
pushed-during-bugteam FIRST so a convergence report against a stale HEAD
|
|
319
328
|
never falsely terminates:
|
|
320
329
|
- **Audit pushed this tick (clean-at fields reset in step b):**
|
|
321
|
-
`phase = CODE_REVIEW`,
|
|
330
|
+
`phase = CODE_REVIEW`, apply Step 4 pacer (ScheduleWakeup or portable continue/poll), return. Every fix push
|
|
322
331
|
re-enters the internal passes on the new HEAD.
|
|
323
332
|
- **Convergence AND no push:** the internal passes are clean on
|
|
324
333
|
`current_head`. Stamp `bugteam_clean_at = current_head`, then
|
|
@@ -328,8 +337,8 @@ never falsely terminates:
|
|
|
328
337
|
([`../../../_shared/pr-loop/fix-protocol.md`](../../../_shared/pr-loop/fix-protocol.md); skill deltas in [fix-protocol.md](fix-protocol.md)). Reset push-invalidated markers
|
|
329
338
|
per [ground-rules.md](ground-rules.md) / [state-schema.md](state-schema.md)
|
|
330
339
|
(all `*_clean_at`, `merge_state_status`, `bugbot_down`,
|
|
331
|
-
`bugbot_acknowledged_at`, `codex_down`), `phase = CODE_REVIEW`,
|
|
332
|
-
|
|
340
|
+
`bugbot_acknowledged_at`, `codex_down`), `phase = CODE_REVIEW`, apply Step 4 pacer
|
|
341
|
+
(ScheduleWakeup or portable continue/poll), return.
|
|
333
342
|
|
|
334
343
|
### `phase == BUGBOT` (terminal gate)
|
|
335
344
|
|
|
@@ -386,7 +395,7 @@ pull_request_read(owner=OWNER, repo=REPO, pullNumber=NUMBER, method="get_review_
|
|
|
386
395
|
c. Decide (four branches; match first whose predicate holds):
|
|
387
396
|
- **No bugbot review yet, OR latest review's `commit_id` ≠
|
|
388
397
|
`current_head`:** Re-trigger bugbot (Step 3), set `bugbot_clean_at =
|
|
389
|
-
null`, reset `inline_lag_streak = 0`,
|
|
398
|
+
null`, reset `inline_lag_streak = 0`, apply Step 4 pacer (ScheduleWakeup or portable continue/poll), return to the
|
|
390
399
|
Bugbot gate next tick.
|
|
391
400
|
- **`commit_id == current_head` AND zero unaddressed inline AND review
|
|
392
401
|
body clean:** Set `bugbot_clean_at = current_head`, reset
|
|
@@ -402,7 +411,7 @@ c. Decide (four branches; match first whose predicate holds):
|
|
|
402
411
|
`state.json`: the clean-coder teammate executes the fix, writes
|
|
403
412
|
`state.json`, goes idle; the next tick re-enters CODE_REVIEW on the new
|
|
404
413
|
HEAD. No `state.json` (single-PR): the lead executes it, stays
|
|
405
|
-
`phase = CODE_REVIEW`.
|
|
414
|
+
`phase = CODE_REVIEW`. Apply Step 4 pacer (ScheduleWakeup or portable continue/poll), return.
|
|
406
415
|
|
|
407
416
|
### `phase == COPILOT_WAIT`
|
|
408
417
|
|
|
@@ -435,12 +444,12 @@ b. Decide (three branches; match first whose predicate holds):
|
|
|
435
444
|
(all `*_clean_at`, `merge_state_status`, `bugbot_down`,
|
|
436
445
|
`bugbot_acknowledged_at`, `codex_down`). **Set `phase = CODE_REVIEW`**
|
|
437
446
|
(NOT COPILOT_WAIT) — every fix push re-enters the internal passes on the
|
|
438
|
-
new HEAD.
|
|
447
|
+
new HEAD. Apply Step 4 pacer (ScheduleWakeup or portable continue/poll), return.
|
|
439
448
|
- **No Copilot review at `current_head` yet:** Increment
|
|
440
449
|
`copilot_wait_count` (init 0 on COPILOT_WAIT entry; reset to 0 on
|
|
441
450
|
every push and on every successful Copilot review). `>= 3` → hard
|
|
442
451
|
blocker per [stop-conditions.md](stop-conditions.md). Otherwise
|
|
443
|
-
|
|
452
|
+
apply Step 4 pacer (ScheduleWakeup or portable continue/poll; 360s wait), return.
|
|
444
453
|
|
|
445
454
|
**Non-negotiable:** After any Copilot fix push, `phase` MUST route to
|
|
446
455
|
`CODE_REVIEW`. Never cycle COPILOT_WAIT → fix → COPILOT_WAIT. The
|
|
@@ -508,8 +517,11 @@ base), never an invented commit range.
|
|
|
508
517
|
|
|
509
518
|
## Step 4: Loop pacing
|
|
510
519
|
|
|
511
|
-
|
|
512
|
-
|
|
520
|
+
Apply the pacer selected at pre-flight ([Pacing workflow](#pacing-workflow)).
|
|
521
|
+
|
|
522
|
+
### `pacer=schedule_wakeup`
|
|
523
|
+
|
|
524
|
+
**`ScheduleWakeup` field hints:**
|
|
513
525
|
|
|
514
526
|
- `delaySeconds: 360` after bugbot re-trigger. Exception:
|
|
515
527
|
BUGBOT inline-lag branch uses `delaySeconds: 90` (no re-trigger;
|
|
@@ -521,6 +533,21 @@ workflow](#pacing-workflow)):
|
|
|
521
533
|
**On convergence:** apply **Convergence** section of
|
|
522
534
|
`../workflows/schedule-wakeup-loop.md` (omit wakeups).
|
|
523
535
|
|
|
536
|
+
### `pacer=portable`
|
|
537
|
+
|
|
538
|
+
Do **not** call `ScheduleWakeup`. After writing state and handoff:
|
|
539
|
+
|
|
540
|
+
- **Immediate work next** → continue the next tick in the same turn.
|
|
541
|
+
- **Wait next** (Bugbot queued, COPILOT_WAIT with no review yet) → poll
|
|
542
|
+
in-session for the same delay (`360` default, `90` on Bugbot inline-lag),
|
|
543
|
+
then resume the same step. Honor the same hard caps as the ScheduleWakeup
|
|
544
|
+
path.
|
|
545
|
+
- **Budget too low for a full tick** → stop at the tick boundary; print
|
|
546
|
+
`/pr-converge <PR URL>` and the persisted phase.
|
|
547
|
+
|
|
548
|
+
Full portable rules:
|
|
549
|
+
[`../../_shared/pr-loop/portable-driver.md`](../../_shared/pr-loop/portable-driver.md).
|
|
550
|
+
|
|
524
551
|
## Bugteam execution
|
|
525
552
|
|
|
526
553
|
**Second audit** (BUGTEAM phase) is **always** **bugteam** skill: preflight,
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# pr-converge progress checklist
|
|
2
|
+
|
|
3
|
+
The per-tick step sequence for the pr-converge loop. The hub
|
|
4
|
+
([`../SKILL.md`](../SKILL.md)) points here; run these steps in order, one pass
|
|
5
|
+
per tick.
|
|
6
|
+
|
|
7
|
+
State variables (`phase`, `bugbot_clean_at`, `code_review_clean_at`,
|
|
8
|
+
`bugteam_clean_at`, `copilot_clean_at`, `merge_state_status`, counters) are
|
|
9
|
+
defined in [`state-schema.md`](state-schema.md). Ground rules
|
|
10
|
+
in [`ground-rules.md`](ground-rules.md).
|
|
11
|
+
|
|
12
|
+
Each step references its spoke file for full procedural detail — MCP calls,
|
|
13
|
+
script invocations, decision criteria. Every "return to Step N" means the next
|
|
14
|
+
tick starts fresh from that step.
|
|
15
|
+
|
|
16
|
+
**Hard gate: do not advance from any step while ANY unresolved review
|
|
17
|
+
thread exists on the PR.** Sweep semantics and per-thread handling live in
|
|
18
|
+
the shared fix protocol
|
|
19
|
+
([`../../../_shared/pr-loop/fix-protocol.md`](../../../_shared/pr-loop/fix-protocol.md)
|
|
20
|
+
step 12; skill deltas in [`fix-protocol.md`](fix-protocol.md)).
|
|
21
|
+
|
|
22
|
+
**Full-PR-diff rule: every CODE-REVIEW round (Step 5) and every BUGTEAM
|
|
23
|
+
round (Step 6) covers the FULL `origin/main...HEAD` diff — every file
|
|
24
|
+
the PR touches.** A round that scopes to a subset — only the last commit,
|
|
25
|
+
only files touched since the prior clean SHA, only bugbot-flagged paths,
|
|
26
|
+
or any other delta cut — does not satisfy the gate, and a "clean" verdict
|
|
27
|
+
against a partial diff is not a valid clean. Re-run the round against the
|
|
28
|
+
full diff before recording `code_review_clean_at` or treating the bugteam
|
|
29
|
+
round as converged. This rule holds every tick, every loop, every PR.
|
|
30
|
+
|
|
31
|
+
- [ ] **Step 0: Open the run**
|
|
32
|
+
Apply the `pr-loop-lifecycle` skill's Open section
|
|
33
|
+
(`../../pr-loop-lifecycle/SKILL.md`): permission grant + worktree preflight.
|
|
34
|
+
|
|
35
|
+
- [ ] **Step 1: Resolve PR scope + PR worktree**
|
|
36
|
+
Apply Step 1.5 scope resolution in
|
|
37
|
+
[`per-tick.md`](per-tick.md). Resolve the **PR
|
|
38
|
+
worktree** — the local checkout every local step this tick targets:
|
|
39
|
+
the `EnterWorktree` checkout when the PR shares the session's repo,
|
|
40
|
+
else a checkout of the PR's repo that the working directory routes
|
|
41
|
+
into via `cd`. Cross-repo routing is automatic, not a fork.
|
|
42
|
+
See: [`per-tick.md` § Step 1](per-tick.md)
|
|
43
|
+
and [§ Step 1.5](per-tick.md)
|
|
44
|
+
|
|
45
|
+
- [ ] **Step 2: Initialize loop state**
|
|
46
|
+
`phase = CODE_REVIEW`; all counters at zero; `run_name` resolved.
|
|
47
|
+
|
|
48
|
+
- [ ] **Step 3: Mergeability check**
|
|
49
|
+
See: [`convergence-gates.md` § (c)](convergence-gates.md)
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
pull_request_read(method="get") → .mergeable_state, .mergeable
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- [ ] mergeable → advance to Step 4
|
|
56
|
+
- [ ] not mergeable → rebase → force-push → return to Step 1
|
|
57
|
+
|
|
58
|
+
- [ ] **Step 4: BUGBOT — terminal Bugbot confirmation gate**
|
|
59
|
+
Step 6 routes here after BUGTEAM converges. Bugbot confirms code the
|
|
60
|
+
internal passes already drove to clean.
|
|
61
|
+
See: [`per-tick.md` § BUGBOT terminal gate + Step 3](per-tick.md);
|
|
62
|
+
availability and trigger via `reviewer-gates`
|
|
63
|
+
(`../../reviewer-gates/SKILL.md` § Gate 1 / Gate 3).
|
|
64
|
+
|
|
65
|
+
- [ ] **disabled / down** → `bugbot_down = true` → Step 7
|
|
66
|
+
- [ ] **dirty on `current_head`** → apply shared fix protocol
|
|
67
|
+
([`../../../_shared/pr-loop/fix-protocol.md`](../../../_shared/pr-loop/fix-protocol.md);
|
|
68
|
+
skill deltas in [`fix-protocol.md`](fix-protocol.md))
|
|
69
|
+
→ push → reset push-invalidated markers → `phase = CODE_REVIEW` → Step 5
|
|
70
|
+
- [ ] **clean on `current_head`** → zero unresolved threads (else fix + resolve first)
|
|
71
|
+
→ `bugbot_clean_at = current_head` → Step 7
|
|
72
|
+
- [ ] **no review / commit_id mismatch** → `reviewer-gates` Bugbot flow (Gate 3):
|
|
73
|
+
silent pass → stamp + Step 7; queued/triggered → 360s wakeup → Step 4;
|
|
74
|
+
down → `bugbot_down = true` → Step 7
|
|
75
|
+
|
|
76
|
+
- [ ] **Step 5: CODE-REVIEW — static sweep, review, fix, advance**
|
|
77
|
+
Entry phase every tick; re-entered after any fix push.
|
|
78
|
+
See: [`per-tick.md` § CODE_REVIEW entry](per-tick.md).
|
|
79
|
+
Pre-condition: cwd is the Step 1.5 PR worktree on `current_head`.
|
|
80
|
+
Scope: FULL `origin/main...HEAD` diff every tick (no path args, no delta cut).
|
|
81
|
+
Review always runs at effort high on model opus through
|
|
82
|
+
`invoke_code_review.py`. Mode decision inputs: host profile + session
|
|
83
|
+
model. Call:
|
|
84
|
+
`python "$HOME/.claude/scripts/invoke_code_review.py" --cwd <PR-worktree>
|
|
85
|
+
--session-model <alias>`. Chain mode uses that cwd and empty stdin; the
|
|
86
|
+
chain process never commits and never pushes. JSON stdout carries
|
|
87
|
+
`mode` (`in_session` | `chain`), `served_command`, `returncode`, and
|
|
88
|
+
`dirty_tree`. Config/host errors still emit that JSON with non-zero
|
|
89
|
+
`returncode` (no traceback-only failure).
|
|
90
|
+
|
|
91
|
+
- [ ] **Static sweep fails** → apply shared fix protocol → push → reset markers
|
|
92
|
+
→ stay CODE_REVIEW → Step 5
|
|
93
|
+
- [ ] **`mode == in_session`** (Claude host, session model opus) → run
|
|
94
|
+
`/code-review high --fix` in-session (no path args)
|
|
95
|
+
- [ ] **`mode == chain`** (any other host or non-opus session) → helper
|
|
96
|
+
already ran the headless review; read `returncode`,
|
|
97
|
+
`served_command`, and `dirty_tree` from JSON
|
|
98
|
+
- [ ] **failed review** (`returncode != 0`, or chain with null
|
|
99
|
+
`served_command`) → do not set `code_review_clean_at` → stay
|
|
100
|
+
CODE_REVIEW → Step 5
|
|
101
|
+
- [ ] **fixes applied** (`dirty_tree` true / working tree dirty) →
|
|
102
|
+
commit + push via shared fix protocol → reset markers → stay
|
|
103
|
+
CODE_REVIEW → Step 5
|
|
104
|
+
- [ ] **clean** (successful serve: `returncode == 0`, chain
|
|
105
|
+
`served_command` non-null when chain, and `dirty_tree` false) →
|
|
106
|
+
zero unresolved threads (else fix + resolve) →
|
|
107
|
+
`code_review_clean_at = current_head` → `phase = BUGTEAM` → Step 6
|
|
108
|
+
|
|
109
|
+
- [ ] **Step 6: BUGTEAM — run, decide, fix, reply, resolve**
|
|
110
|
+
See: [`per-tick.md` § Step 2 BUGTEAM](per-tick.md);
|
|
111
|
+
[`../../bugteam/SKILL.md`](../../bugteam/SKILL.md).
|
|
112
|
+
Pre-condition: `code_review_clean_at == current_head`.
|
|
113
|
+
Mandatory: `Skill({skill: "bugteam", args: "<PR URL>"})` this tick
|
|
114
|
+
(enforcer-blocked otherwise; `qbug` is not a substitute). Scope: FULL
|
|
115
|
+
`origin/main...HEAD` diff. Re-resolve HEAD after bugteam.
|
|
116
|
+
|
|
117
|
+
- [ ] **bugteam pushed** → verify threads replied + resolved → reset markers
|
|
118
|
+
→ `phase = CODE_REVIEW` → 360s wakeup → Step 5
|
|
119
|
+
- [ ] **converged, no push** → zero unresolved threads →
|
|
120
|
+
`bugteam_clean_at = current_head` → `phase = BUGBOT` → Step 4
|
|
121
|
+
- [ ] **findings without committed fixes** → apply shared fix protocol → push →
|
|
122
|
+
reset markers → `phase = CODE_REVIEW` → Step 5
|
|
123
|
+
|
|
124
|
+
- [ ] **Step 7: Convergence gates**
|
|
125
|
+
Full procedure: [`convergence-gates.md`](convergence-gates.md).
|
|
126
|
+
|
|
127
|
+
Pre-condition: Step 6 converged AND (`bugbot_clean_at == current_head` OR
|
|
128
|
+
`bugbot_down`). The terminal Bugbot gate (Step 4) sets that state just
|
|
129
|
+
before these gates run. Count unresolved threads before each gate.
|
|
130
|
+
Every gate records evidence; gate (f) cites evidence from (a)–(e).
|
|
131
|
+
|
|
132
|
+
- [ ] **(a) Copilot findings** — fetch Copilot on `current_head`; dirty → fix + return to Step 5; clean → stamp `copilot_clean_at`; absent → continue; when `copilot_down`, skip
|
|
133
|
+
- [ ] **(b) Claude reviewer** — fetch Claude on `current_head`; dirty → fix + return to Step 5; clean or absent → continue
|
|
134
|
+
- [ ] **(c) Mergeability** — `mergeable_state == "clean"` and `mergeable == true`; dirty → rebase + return to Step 1; blocked/behind/unknown/unstable → hard blocker
|
|
135
|
+
- [ ] **(d) Post-convergence Copilot request** — request Copilot when not pending and not `copilot_down`; enter `COPILOT_WAIT` (Step 7a); when `copilot_down`, skip to (e)
|
|
136
|
+
- [ ] **(e) Thread-resolution** — zero unresolved threads across the PR; else sweep + fix/resolve
|
|
137
|
+
- [ ] **(f) Mark ready** — run `check_convergence.py`; exit 0 → `update_pull_request(draft=false)` → Step 8; exit 1 → fix path; exit 2 → retry/stop
|
|
138
|
+
|
|
139
|
+
- [ ] **Step 7a: COPILOT_WAIT — fetch Copilot, decide**
|
|
140
|
+
See: [`per-tick.md` § Step 2 COPILOT_WAIT](per-tick.md).
|
|
141
|
+
Skipped entirely when `copilot_down` (gate (d) never enters this phase).
|
|
142
|
+
|
|
143
|
+
- [ ] **clean** → `copilot_clean_at = current_head` → Step 7 (re-validate (b), (c), (e), (f))
|
|
144
|
+
- [ ] **dirty** → apply shared fix protocol
|
|
145
|
+
([`../../../_shared/pr-loop/fix-protocol.md`](../../../_shared/pr-loop/fix-protocol.md);
|
|
146
|
+
skill deltas in [`fix-protocol.md`](fix-protocol.md))
|
|
147
|
+
→ push → reset markers → `phase = CODE_REVIEW` → Step 5
|
|
148
|
+
- [ ] **no review yet** → increment `copilot_wait_count` → ≥ 3 hard-blocks;
|
|
149
|
+
else 360s wakeup → Step 7a next tick
|
|
150
|
+
|
|
151
|
+
- [ ] **Step 8: Clean working tree**
|
|
152
|
+
`pr-loop-lifecycle` Close (`../../pr-loop-lifecycle/SKILL.md`).
|
|
153
|
+
See: [`../../pr-loop-lifecycle/reference/teardown-publish-permissions.md` § Clean working tree](../../pr-loop-lifecycle/reference/teardown-publish-permissions.md)
|
|
154
|
+
|
|
155
|
+
- [ ] **Step 9: Rewrite PR description**
|
|
156
|
+
`pr-loop-lifecycle` Close.
|
|
157
|
+
See: [`../../pr-loop-lifecycle/reference/teardown-publish-permissions.md` § Publish the final PR description](../../pr-loop-lifecycle/reference/teardown-publish-permissions.md)
|
|
158
|
+
|
|
159
|
+
- [ ] **Step 10: Revoke project permissions (always)**
|
|
160
|
+
`pr-loop-lifecycle` Close (`../../pr-loop-lifecycle/SKILL.md` § Close).
|
|
161
|
+
|
|
162
|
+
- [ ] **Step 11: Print final report**
|
|
163
|
+
Print this block verbatim — no paraphrase, no extra commentary:
|
|
164
|
+
```
|
|
165
|
+
/pr-converge exit: converged
|
|
166
|
+
Loops: <N>
|
|
167
|
+
Final commit: <SHA>
|
|
168
|
+
```
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Contract checks: pr-converge selects a portable pacer when ScheduleWakeup 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
|
+
PER_TICK_MARKDOWN_PATH = SKILL_ROOT / "reference" / "per-tick.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_SCHEDULE_WAKEUP_TOKEN = "pacer=schedule_wakeup"
|
|
18
|
+
ABORT_ONLY_PHRASE = "pr-converge requires ScheduleWakeup; aborting"
|
|
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_schedule_wakeup() -> 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_SCHEDULE_WAKEUP_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_per_tick_documents_both_pacers() -> None:
|
|
48
|
+
per_tick_text = PER_TICK_MARKDOWN_PATH.read_text(encoding="utf-8")
|
|
49
|
+
assert "portable" in per_tick_text
|
|
50
|
+
assert "schedule_wakeup" in per_tick_text or "ScheduleWakeup" in per_tick_text
|
|
51
|
+
assert PORTABLE_DRIVER_DOC in per_tick_text
|
|
52
|
+
assert "pacer=portable" in per_tick_text
|
|
53
|
+
assert "Do **not** call `ScheduleWakeup`" in per_tick_text or (
|
|
54
|
+
"Do not call `ScheduleWakeup`" in per_tick_text
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_portable_driver_protocol_exists() -> None:
|
|
59
|
+
assert PORTABLE_DRIVER_PATH.is_file()
|
|
60
|
+
portable_text = PORTABLE_DRIVER_PATH.read_text(encoding="utf-8")
|
|
61
|
+
assert "check_convergence.py" in portable_text
|
|
62
|
+
assert "invoke_code_review.py" in portable_text
|
|
63
|
+
assert "resolve_worker_spawn.py" in portable_text
|
|
64
|
+
assert "build_converge_task_list.py" in portable_text
|
|
65
|
+
assert "all_runnable_reviews_clean_same_head" in portable_text
|
|
66
|
+
assert "portable_converge_driver.py" in portable_text
|
|
67
|
+
assert "Never abort solely because" in portable_text
|
|
@@ -11,7 +11,7 @@ from pathlib import Path
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
SKILL_ROOT = Path(__file__).resolve().parent
|
|
14
|
-
|
|
14
|
+
PROGRESS_CHECKLIST_MARKDOWN_PATH = SKILL_ROOT / "reference" / "progress-checklist.md"
|
|
15
15
|
PER_TICK_MARKDOWN_PATH = SKILL_ROOT / "reference" / "per-tick.md"
|
|
16
16
|
|
|
17
17
|
HELPER_SCRIPT_NAME = "invoke_code_review.py"
|
|
@@ -45,10 +45,10 @@ def _code_review_section(per_tick_text: str) -> str:
|
|
|
45
45
|
return per_tick_text[section_start:section_end]
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
def _step_five_checklist(
|
|
49
|
-
step_start =
|
|
50
|
-
step_end =
|
|
51
|
-
return
|
|
48
|
+
def _step_five_checklist(progress_checklist_text: str) -> str:
|
|
49
|
+
step_start = progress_checklist_text.index("**Step 5: CODE-REVIEW")
|
|
50
|
+
step_end = progress_checklist_text.index("**Step 6: BUGTEAM", step_start)
|
|
51
|
+
return progress_checklist_text[step_start:step_end]
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
def test_per_tick_code_review_names_helper_and_mode_inputs() -> None:
|
|
@@ -75,7 +75,9 @@ def test_per_tick_code_review_names_helper_and_mode_inputs() -> None:
|
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
def test_skill_step_five_checklist_names_helper_and_mode_inputs() -> None:
|
|
78
|
-
step_five_checklist = _step_five_checklist(
|
|
78
|
+
step_five_checklist = _step_five_checklist(
|
|
79
|
+
_read_markdown(PROGRESS_CHECKLIST_MARKDOWN_PATH)
|
|
80
|
+
)
|
|
79
81
|
assert HELPER_SCRIPT_NAME in step_five_checklist
|
|
80
82
|
assert HOST_PROFILE_PHRASE in step_five_checklist
|
|
81
83
|
assert SESSION_MODEL_PHRASE in step_five_checklist
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# ScheduleWakeup loop pacing (pr-converge)
|
|
2
2
|
|
|
3
|
-
Load this document
|
|
4
|
-
|
|
5
|
-
/
|
|
3
|
+
Load this document when pre-flight selects `pacer=schedule_wakeup`. When
|
|
4
|
+
`pacer=portable`, use
|
|
5
|
+
[`../../_shared/pr-loop/portable-driver.md`](../../_shared/pr-loop/portable-driver.md)
|
|
6
|
+
instead. Shared bugbot / bugteam / Fix protocol steps stay in the main
|
|
7
|
+
`SKILL.md`.
|
|
6
8
|
|
|
7
9
|
## Calling ScheduleWakeup
|
|
8
10
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
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
|
|
3
|
+
description: Runs any PR-loop skill in a Claude Code session whose gh CLI cannot act on the PR. 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
4
|
---
|
|
5
5
|
|
|
6
6
|
# PR-loop cloud transport
|