claude-dev-env 1.84.0 → 1.85.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 (29) hide show
  1. package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -1
  2. package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +8 -0
  3. package/_shared/pr-loop/scripts/terminology_sweep.py +85 -39
  4. package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +94 -4
  5. package/hooks/blocking/claude_md_orphan_file_blocker.py +1 -1
  6. package/hooks/blocking/code_rules_docstrings.py +17 -13
  7. package/hooks/blocking/code_rules_imports_logging.py +10 -6
  8. package/hooks/blocking/code_rules_naming_collection.py +5 -3
  9. package/hooks/blocking/code_rules_paired_test.py +3 -2
  10. package/hooks/blocking/code_rules_string_magic.py +1 -1
  11. package/hooks/blocking/code_rules_unused_imports.py +2 -2
  12. package/hooks/blocking/test_code_rules_enforcer_docstring_args_span_scope.py +29 -0
  13. package/hooks/blocking/test_code_rules_enforcer_naive_datetime.py +24 -0
  14. package/hooks/hooks_constants/blocking_check_limits.py +1 -0
  15. package/package.json +1 -1
  16. package/rules/docstring-prose-matches-implementation.md +1 -1
  17. package/rules/env-var-table-code-drift.md +1 -1
  18. package/rules/package-inventory-stale-entry.md +1 -1
  19. package/rules/paired-test-coverage.md +1 -1
  20. package/skills/_shared/pr-loop/CLAUDE.md +1 -0
  21. package/skills/_shared/pr-loop/scripts/CLAUDE.md +1 -0
  22. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/CLAUDE.md +1 -0
  23. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/handoff_constants.py +45 -0
  24. package/skills/_shared/pr-loop/scripts/test_write_handoff.py +201 -0
  25. package/skills/_shared/pr-loop/scripts/write_handoff.py +309 -0
  26. package/skills/autoconverge/SKILL.md +46 -3
  27. package/skills/autoconverge/workflow/converge.mjs +1 -1
  28. package/skills/pr-converge/SKILL.md +27 -1
  29. package/skills/pr-converge/reference/state-schema.md +12 -0
@@ -115,6 +115,19 @@ and `false` when it exits 0; on `true` the workflow skips the Copilot gate with
115
115
  no agent spawned. The workflow runs in the background and notifies this session
116
116
  on completion. Watch live progress with `/workflows`.
117
117
 
118
+ The moment the `Workflow` call returns its run id, write the durable handoff so a
119
+ fresh session can resume the same run:
120
+
121
+ ```
122
+ python "$HOME/.claude/skills/_shared/pr-loop/scripts/write_handoff.py" \
123
+ --pr-number <N> --head-ref <branch> --phase workflow \
124
+ --resume-command "Workflow({scriptPath, resumeFromRunId: '<runId>'})" \
125
+ --run-id <runId>
126
+ ```
127
+
128
+ Write it again when the result lands, so the handoff carries the final run id and
129
+ names the teardown phase the fresh session picks up from.
130
+
118
131
  The workflow returns
119
132
  `{ converged, rounds, finalSha, blocker, standardsNote, copilotNote, reuseNote, deferredPrs }`.
120
133
  `deferredPrs` is the list of draft environment-hardening PRs the standards-deferral
@@ -134,8 +147,32 @@ returns `blocker: "budget"` with the run id; resume with
134
147
  the journal. Never start a round the budget cannot finish: a half-run
135
148
  round records nothing resumable and replays dirty.
136
149
 
150
+ On a `blocker: "budget"` return, write the durable handoff with the run id and the
151
+ `Workflow({scriptPath, resumeFromRunId})` resume command before stopping, so a
152
+ fresh session resumes the paced run without the stopped session's transcript.
153
+
137
154
  ## Teardown (on workflow completion)
138
155
 
156
+ Teardown runs as an ordered checkpoint list. After each checkpoint finishes,
157
+ re-write the durable handoff with `--phase teardown`, the run id, and the
158
+ checkpoints done so far, so a fresh session that resumes reads `handoff.json`
159
+ `completed_steps` and skips the checkpoints already done:
160
+
161
+ ```
162
+ python "$HOME/.claude/skills/_shared/pr-loop/scripts/write_handoff.py" \
163
+ --pr-number <N> --head-ref <branch> --phase teardown \
164
+ --resume-command "/autoconverge <PR URL>" \
165
+ --run-id <runId> \
166
+ --completed-steps "<checkpoints done so far>"
167
+ ```
168
+
169
+ The checkpoints run in this order: `report`, `close`. Write the
170
+ handoff after each one finishes.
171
+
172
+ On teardown entry, when `~/.claude/runtime/pr-loop/bugteam-pr-<N>/handoff.json`
173
+ exists, read its `completed_steps` and skip any checkpoint the list already
174
+ names, so a resumed run performs only the checkpoints left.
175
+
139
176
  1. **When `converged` is true — build and publish the closing report.**
140
177
  Skip this entire step (report, artifact publish, comment, Chrome open) when the
141
178
  workflow returned a non-null `blocker`. Per-round live-dashboard refresh is out of
@@ -204,14 +241,18 @@ round records nothing resumable and replays dirty.
204
241
  ```
205
242
  Start-Process chrome -ArgumentList '--new-window', '<artifact URL>'
206
243
  ```
207
- Tolerate a missing Chrome without aborting the rest of teardown.
244
+ Skip a missing Chrome without aborting the rest of teardown.
245
+
246
+ After the report is published, write the handoff with
247
+ `--completed-steps "report"`.
208
248
 
209
249
  2. **Close the run.** Apply the `pr-loop-lifecycle` skill's Close section
210
250
  (`../pr-loop-lifecycle/SKILL.md`): when `converged` is true, rewrite the PR
211
251
  description and clean the working tree — see
212
252
  [`pr-loop-lifecycle/reference/teardown-publish-permissions.md` § Clean working tree and § Publish the final PR description](../pr-loop-lifecycle/reference/teardown-publish-permissions.md);
213
253
  the workflow already marked the PR ready. The permission revoke always runs,
214
- including on a blocker exit.
254
+ including on a blocker exit. Write the handoff with
255
+ `--completed-steps "report,close"`.
215
256
 
216
257
  3. **Print the final report:**
217
258
 
@@ -385,7 +426,9 @@ top-level `converged` is true only when every PR converged.
385
426
 
386
427
  Run the single-PR [Teardown](#teardown-on-workflow-completion) once per entry in
387
428
  `results`, using that PR's `owner`, `repo`, `prNumber`, and `finalSha`, and its
388
- own worktree as the working directory. Build and publish a PR's closing report
429
+ own worktree as the working directory. Write one durable handoff per PR entry
430
+ each with that PR's own `--pr-number` — so a fresh session can resume any PR on
431
+ its own. Build and publish a PR's closing report
389
432
  only for a PR whose `converged` is true; for a PR that returned a blocker, skip
390
433
  its report and carry the blocker into the final summary. Revoke project
391
434
  permissions once per repository after every PR's teardown. Then print one summary
@@ -1359,7 +1359,7 @@ function runCopilotGate(head) {
1359
1359
  `Copilot can run out of usage. When the newest Copilot review on HEAD carries an out-of-usage notice — a body stating Copilot was unable to review because the user who requested the review has reached their quota limit, or any equivalent quota / premium-request / usage-limit exhaustion message rather than an actual code review — Copilot is down for this run: return {sha:${'`'}${head}${'`'}, clean:true, down:true, findings:[]} and stop. Do NOT re-request a review, do NOT keep polling, and do NOT treat the notice as a finding.\n\n` +
1360
1360
  `1. Read any existing Copilot review on HEAD first: python "${CONFIG.sharedScripts}/fetch_copilot_reviews.py" --owner ${input.owner} --repo ${input.repo} --pr-number ${input.prNumber}. This lists every Copilot review across all commits newest-first; only count entries whose commit_id starts with ${head}. If the newest such HEAD-scoped Copilot review is the out-of-usage notice above -> return the down result and stop. A notice on any earlier commit is NOT down: ignore it and continue. With no Copilot review on HEAD, skip a duplicate request: python "${CONFIG.sharedScripts}/check_pending_reviews.py" --owner ${input.owner} --repo ${input.repo} --pr-number ${input.prNumber} --user copilot. Exit 0 means a request is already pending; otherwise request one:\n` +
1361
1361
  ` ${REVIEWER_GATE_SENTINEL}gh api --method POST repos/${input.owner}/${input.repo}/pulls/${input.prNumber}/requested_reviewers -f 'reviewers[]=copilot-pull-request-reviewer[bot]'\n` +
1362
- `2. Poll for Copilot's review on HEAD ${head}: up to ${CONFIG.copilotMaxPolls} attempts, 360 seconds apart (wait each 360-second interval inside this turn with the Monitor tool, per the WAITS AND POLLS rule above; if the attempt budget is spent with no review on HEAD, return down: true). Each attempt: python "${CONFIG.sharedScripts}/fetch_copilot_reviews.py" --owner ${input.owner} --repo ${input.repo} --pr-number ${input.prNumber} for the top-level review state, plus gh api "repos/${input.owner}/${input.repo}/pulls/${input.prNumber}/comments" --paginate --slurp for inline comment ids (Copilot's login contains "copilot", case-insensitive). Only count entries whose commit_id starts with ${head}.\n` +
1362
+ `2. Poll for Copilot's review on HEAD ${head}: up to ${CONFIG.copilotMaxPolls} attempts, 360 seconds apart (wait each 360-second interval inside this turn with the Monitor tool, per the WAITS AND POLLS rule above; if the attempt budget is spent with no review on HEAD, return the full down result {sha:${'`'}${head}${'`'}, clean:false, down:true, findings:[]}). Each attempt: python "${CONFIG.sharedScripts}/fetch_copilot_reviews.py" --owner ${input.owner} --repo ${input.repo} --pr-number ${input.prNumber} for the top-level review state, plus gh api "repos/${input.owner}/${input.repo}/pulls/${input.prNumber}/comments" --paginate --slurp for inline comment ids (Copilot's login contains "copilot", case-insensitive). Only count entries whose commit_id starts with ${head}.\n` +
1363
1363
  ` - Out-of-usage notice on HEAD -> return the down result above (clean:true, down:true) and stop.\n` +
1364
1364
  ` - Copilot review present on HEAD whose state is APPROVED, or COMMENTED with no inline findings -> a clean pass: return {sha:${'`'}${head}${'`'}, clean:true, down:false, findings:[]}.\n` +
1365
1365
  ` - Copilot findings on HEAD -> return them (each with its inline comment id in replyToCommentId; category 'code-standard' for pure CODE_RULES/style violations with no behavioral impact, 'bug' otherwise), clean:false, down:false.\n` +
@@ -43,6 +43,15 @@ working directory routes into the PR's repo for local work and returns to
43
43
  the session worktree before teardown. See
44
44
  [`reference/per-tick.md` § Step 1.5](reference/per-tick.md).
45
45
 
46
+ ## Resume from a prior run
47
+
48
+ Before Step 0, check
49
+ `~/.claude/runtime/pr-loop/bugteam-pr-<PR number>/handoff.json`. When
50
+ `$CLAUDE_JOB_DIR/pr-converge-state.json` is absent but that handoff exists, seed
51
+ `phase`, `tick_count`, and the clean-at SHAs from the run's `state-copy.json`, so
52
+ a fresh session continues where the last one stopped rather than restarting at
53
+ BUGBOT.
54
+
46
55
  ## Copilot quota pre-check (start of run)
47
56
 
48
57
  On the first tick, apply the `reviewer-gates` skill's Copilot quota gate
@@ -61,7 +70,8 @@ Before starting any tick, estimate whether the remaining session/usage
61
70
  budget covers one full clean tick (worst case: a BUGBOT fetch + a
62
71
  full-diff CODE_REVIEW + a fix commit + replies). If it does not, do not
63
72
  start the tick. Stop at the current tick boundary: write updated state to
64
- `$CLAUDE_JOB_DIR/pr-converge-state.json`, then report the exact resume
73
+ `$CLAUDE_JOB_DIR/pr-converge-state.json`, write the durable handoff (see
74
+ [State persistence](#state-persistence)), then report the exact resume
65
75
  command (`/pr-converge <PR URL>`) and the persisted `phase`/`tick_count`.
66
76
  A tick cut off mid-flight poisons the resume state — clean SHAs recorded
67
77
  against work that never landed — so an unstarted tick is always cheaper
@@ -83,6 +93,22 @@ On tick entry, read this file if it exists to restore phase, tick_count, and
83
93
  clean-at SHAs. On tick exit, write updated state before calling ScheduleWakeup
84
94
  so the next tick resumes with accurate state.
85
95
 
96
+ After the state write and before ScheduleWakeup, write the durable handoff so a
97
+ fresh session in a new job can resume this run:
98
+
99
+ ```
100
+ python "$HOME/.claude/skills/_shared/pr-loop/scripts/write_handoff.py" \
101
+ --pr-number <N> --head-ref <branch> --phase <phase> \
102
+ --resume-command "/pr-converge <PR URL>" \
103
+ --state-file "$CLAUDE_JOB_DIR/pr-converge-state.json" \
104
+ --completed-steps "<clean phases this run>"
105
+ ```
106
+
107
+ It writes `handoff.json`, `HANDOFF.md`, and `state-copy.json` under
108
+ `~/.claude/runtime/pr-loop/<run-name>/`. The job-dir state stays the source of
109
+ truth for a resumed tick; the handoff copy is the pointer a fresh session reads
110
+ when `$CLAUDE_JOB_DIR` is gone.
111
+
86
112
  Fields: `phase`, `tick_count`, `bugbot_clean_at`, `code_review_clean_at`,
87
113
  `bugteam_clean_at`, `copilot_clean_at`, `current_head`,
88
114
  `bugbot_acknowledged_at`, `bugbot_down`, `copilot_down`,
@@ -100,3 +100,15 @@ if it exists and ends by writing the updated state back to that same file
100
100
  before scheduling the next wakeup. Multi-PR mode additionally coordinates
101
101
  across PRs via `<TMPDIR>/pr-converge-<session_id>/state.json` per
102
102
  `multi-pr-orchestration.md` §What orchestrator does per tick.
103
+
104
+ ## Handoff files
105
+
106
+ Each tick also writes a durable handoff under
107
+ `~/.claude/runtime/pr-loop/<run-name>/` via
108
+ `skills/_shared/pr-loop/scripts/write_handoff.py`: `handoff.json` (resume
109
+ command, phase, tick, and state path), `HANDOFF.md` (a prompt a fresh session
110
+ reads), and `state-copy.json` (a copy of the job-dir state). `$CLAUDE_JOB_DIR`
111
+ can be cleaned between sessions; this directory under the user home is not, so it
112
+ holds the pointer a new job needs. Live job-dir state wins for a resumed tick. A
113
+ fresh session reads the handoff copy only when `$CLAUDE_JOB_DIR` state is gone,
114
+ seeding phase, tick, and SHAs from `state-copy.json`.