claude-dev-env 2.7.1 → 2.8.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/_shared/CLAUDE.md +1 -0
- package/_shared/advisor/advisor-protocol.md +19 -9
- package/_shared/pr-loop/audit-contract.md +4 -4
- package/_shared/pr-loop/precatch-rubric.md +2 -2
- package/_shared/process-tree/CLAUDE.md +41 -0
- package/_shared/process-tree/scripts/config/process_tree_scripts_constants/__init__.py +1 -0
- package/_shared/process-tree/scripts/config/process_tree_scripts_constants/process_tree_kill_constants.py +27 -0
- package/_shared/process-tree/scripts/process_tree_kill.py +141 -0
- package/_shared/process-tree/scripts/pyproject.toml +16 -0
- package/_shared/process-tree/scripts/test_process_tree_kill.py +278 -0
- package/agents/code-quality-agent.md +6 -5
- package/agents/deep-research.md +7 -24
- package/agents/docs-agent.md +1 -27
- package/agents/issue-tracker.md +1 -7
- package/agents/skill-writer-agent.md +1 -2
- package/agents/test_agent_frontmatter.py +309 -12
- package/hooks/blocking/CLAUDE.md +2 -0
- package/hooks/blocking/fable_spawn_gate.py +187 -0
- package/hooks/blocking/piped_pytest_blocker.py +1223 -0
- package/hooks/blocking/plain_language_blocker.py +287 -15
- package/hooks/blocking/test_fable_spawn_gate.py +374 -0
- package/hooks/blocking/test_piped_pytest_blocker.py +587 -0
- package/hooks/blocking/test_plain_language_blocker.py +277 -2
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +34 -2
- package/hooks/git-hooks/CLAUDE.md +2 -2
- package/hooks/git-hooks/git_hooks_constants/__init__.py +28 -0
- package/hooks/git-hooks/pre_push.py +343 -54
- package/hooks/git-hooks/test_pre_push.py +852 -6
- package/hooks/hooks.json +9 -19
- package/hooks/hooks_constants/CLAUDE.md +2 -0
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +1 -0
- package/hooks/hooks_constants/fable_spawn_gate_constants.py +62 -0
- package/hooks/hooks_constants/piped_pytest_blocker_constants.py +360 -0
- package/hooks/hooks_constants/plain_language_blocker_constants.py +64 -1
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +10 -0
- package/hooks/hooks_constants/shell_command_segments.py +1 -1
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +1 -0
- package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +27 -0
- package/hooks/hooks_constants/test_prose_metrics_parity.py +124 -0
- package/package.json +1 -1
- package/rules/CLAUDE.md +1 -0
- package/rules/ask-user-question-required.md +26 -0
- package/rules/claims-as-quotes.md +65 -0
- package/scripts/CLAUDE.md +1 -1
- package/scripts/_code_review_test_support.py +6 -0
- package/scripts/check.ps1 +18 -5
- package/scripts/claude_chain_runner.py +203 -31
- package/scripts/codec_forwarding_test_support.py +2 -0
- package/scripts/dev_env_scripts_constants/CLAUDE.md +4 -4
- package/scripts/dev_env_scripts_constants/claude_chain_constants.py +38 -0
- package/scripts/dev_env_scripts_constants/code_review_constants.py +403 -2
- package/scripts/dev_env_scripts_constants/grok_worker_constants.py +28 -24
- package/scripts/grok_headless_runner.py +16 -83
- package/scripts/spawn_grok_batch.py +38 -1
- package/scripts/test_claude_chain_runner.py +358 -0
- package/scripts/test_grok_headless_runner.py +18 -63
- package/scripts/test_invoke_code_review.py +298 -0
- package/scripts/test_resolve_worker_spawn.py +6 -0
- package/scripts/test_spawn_grok_batch.py +101 -0
- package/scripts/tests/CLAUDE.md +1 -0
- package/scripts/tests/test_grok_worker_constants.py +59 -0
- package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +46 -0
- package/skills/autoconverge/workflow/converge.contract.test.mjs +105 -2
- package/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +34 -1
- package/skills/autoconverge/workflow/converge.mjs +66 -33
- package/skills/codex-review/scripts/codex_review_scripts_constants/codex_usage_probe_constants.py +0 -4
- package/skills/codex-review/scripts/codex_usage_probe.py +20 -33
- package/skills/codex-review/scripts/run_codex_review.py +16 -64
- package/skills/codex-review/scripts/test_codex_usage_probe.py +46 -41
- package/skills/codex-review/scripts/test_run_codex_review.py +1 -33
- package/skills/e-code-review/SKILL.md +9 -8
- package/skills/e-code-review/reference/fix.md +29 -7
- package/skills/e-code-review/reference/loop.md +230 -14
- package/skills/e-code-review/reference/low.md +33 -15
- package/skills/e-code-review/reference/medium.md +55 -21
- package/skills/e-code-review/reference/xhigh.md +30 -12
- package/skills/fresh-branch/CLAUDE.md +5 -5
- package/skills/fresh-branch/SKILL.md +14 -6
- package/skills/fresh-branch/scripts/create_fresh_branch.py +122 -39
- package/skills/fresh-branch/scripts/fresh_branch_scripts_constants/fresh_branch_cli_constants.py +10 -3
- package/skills/fresh-branch/scripts/test_create_fresh_branch.py +251 -0
- package/skills/grok-spawn/SKILL.md +5 -0
- package/skills/orchestrator/SKILL.md +4 -1
- package/skills/orchestrator-refresh/SKILL.md +5 -1
- package/skills/team-advisor/SKILL.md +4 -1
|
@@ -34,9 +34,6 @@ from codex_review_scripts_constants.codex_usage_probe_constants import (
|
|
|
34
34
|
USAGE_REPORT_KEY_WINDOW_RESET,
|
|
35
35
|
WEEKLY_USAGE_GATE_THRESHOLD_PERCENT,
|
|
36
36
|
WEEKLY_WINDOW_DURATION_MINUTES,
|
|
37
|
-
WINDOWS_OS_NAME,
|
|
38
|
-
WINDOWS_TASKKILL_COMMAND,
|
|
39
|
-
WINDOWS_TASKKILL_TREE_FLAG,
|
|
40
37
|
)
|
|
41
38
|
|
|
42
39
|
PROBE_PATH = SCRIPTS_DIRECTORY / "codex_usage_probe.py"
|
|
@@ -300,58 +297,66 @@ class TestParseTextStatus:
|
|
|
300
297
|
|
|
301
298
|
|
|
302
299
|
class TestProcessTreeTeardown:
|
|
303
|
-
def
|
|
304
|
-
|
|
305
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
306
|
-
) -> None:
|
|
300
|
+
def should_end_a_running_app_server_and_reap_it(self) -> None:
|
|
301
|
+
"""A real sleeping child is gone once teardown returns, on any platform."""
|
|
307
302
|
probe = load_probe_module()
|
|
308
|
-
|
|
309
|
-
fake_process = subprocess.Popen(
|
|
303
|
+
sleeping_process = subprocess.Popen(
|
|
310
304
|
[sys.executable, "-c", "import time; time.sleep(30)"],
|
|
311
305
|
stdout=subprocess.DEVNULL,
|
|
312
306
|
stderr=subprocess.DEVNULL,
|
|
307
|
+
start_new_session=probe.should_start_new_session(),
|
|
313
308
|
)
|
|
314
309
|
try:
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
def capture_run(
|
|
318
|
-
command: list[str],
|
|
319
|
-
**_kwargs: object,
|
|
320
|
-
) -> subprocess.CompletedProcess[str]:
|
|
321
|
-
all_taskkill_commands.append(list(command))
|
|
322
|
-
fake_process.kill()
|
|
323
|
-
fake_process.wait(timeout=PROCESS_TREE_WAIT_SECONDS)
|
|
324
|
-
return subprocess.CompletedProcess(command, 0)
|
|
325
|
-
|
|
326
|
-
monkeypatch.setattr(probe.subprocess, "run", capture_run)
|
|
327
|
-
probe._terminate_process_tree(fake_process)
|
|
310
|
+
probe._terminate_process_tree(sleeping_process)
|
|
311
|
+
assert sleeping_process.poll() is not None
|
|
328
312
|
finally:
|
|
329
|
-
if
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
assert WINDOWS_TASKKILL_TREE_FLAG in all_taskkill_commands[0]
|
|
335
|
-
assert str(fake_process.pid) in all_taskkill_commands[0]
|
|
336
|
-
|
|
337
|
-
def should_kill_process_directly_when_os_name_is_not_windows(
|
|
313
|
+
if sleeping_process.poll() is None:
|
|
314
|
+
sleeping_process.kill()
|
|
315
|
+
sleeping_process.wait(timeout=PROCESS_TREE_WAIT_SECONDS)
|
|
316
|
+
|
|
317
|
+
def should_hand_the_whole_tree_to_the_shared_kill(
|
|
338
318
|
self,
|
|
339
319
|
monkeypatch: pytest.MonkeyPatch,
|
|
340
320
|
) -> None:
|
|
321
|
+
"""Teardown routes the server process through the shared tree kill.
|
|
322
|
+
|
|
323
|
+
::
|
|
324
|
+
|
|
325
|
+
shared tree kill ok: the cmd.exe shim's app-server child dies too
|
|
326
|
+
Popen.kill() flag: the app-server grandchild holds stdout open
|
|
327
|
+
"""
|
|
341
328
|
probe = load_probe_module()
|
|
342
|
-
|
|
343
|
-
|
|
329
|
+
all_killed_processes: list[object] = []
|
|
330
|
+
finished_process = subprocess.Popen(
|
|
331
|
+
[sys.executable, "-c", ""],
|
|
344
332
|
stdout=subprocess.DEVNULL,
|
|
345
333
|
stderr=subprocess.DEVNULL,
|
|
346
334
|
)
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
probe
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
335
|
+
finished_process.wait(timeout=PROCESS_TREE_WAIT_SECONDS)
|
|
336
|
+
monkeypatch.setattr(
|
|
337
|
+
probe,
|
|
338
|
+
"terminate_process_tree",
|
|
339
|
+
lambda target_process: all_killed_processes.append(target_process),
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
probe._terminate_process_tree(finished_process)
|
|
343
|
+
|
|
344
|
+
assert all_killed_processes == [finished_process]
|
|
345
|
+
|
|
346
|
+
def should_return_when_a_killed_child_never_reaps(
|
|
347
|
+
self,
|
|
348
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
349
|
+
) -> None:
|
|
350
|
+
"""A child that outlives its grace window ends teardown, never hangs it."""
|
|
351
|
+
probe = load_probe_module()
|
|
352
|
+
|
|
353
|
+
class _UnreapableProcess:
|
|
354
|
+
def wait(self, timeout: float | None = None) -> int:
|
|
355
|
+
raise subprocess.TimeoutExpired(cmd="codex", timeout=timeout or 0)
|
|
356
|
+
|
|
357
|
+
monkeypatch.setattr(probe, "terminate_process_tree", lambda _process: None)
|
|
358
|
+
|
|
359
|
+
probe._terminate_process_tree(_UnreapableProcess())
|
|
355
360
|
|
|
356
361
|
|
|
357
362
|
class TestReaderThreadLifecycle:
|
|
@@ -890,38 +890,6 @@ def test_run_command_kills_grandchild_tree_on_timeout_without_hanging(
|
|
|
890
890
|
assert _wait_until_process_stops(grandchild_identifier, wall_clock_ceiling_seconds)
|
|
891
891
|
|
|
892
892
|
|
|
893
|
-
def test_windows_process_tree_kill_builds_taskkill_argv(
|
|
894
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
895
|
-
) -> None:
|
|
896
|
-
"""The Windows kill path issues taskkill /T /F /PID for the given process id."""
|
|
897
|
-
target_process_identifier = 4242
|
|
898
|
-
recorded_argv: list[list[str]] = []
|
|
899
|
-
|
|
900
|
-
def record_argv(all_arguments: list[str], **_keywords: object) -> None:
|
|
901
|
-
recorded_argv.append(all_arguments)
|
|
902
|
-
|
|
903
|
-
monkeypatch.setattr(wrapper.subprocess, "run", record_argv)
|
|
904
|
-
wrapper._kill_windows_process_tree(target_process_identifier)
|
|
905
|
-
|
|
906
|
-
assert recorded_argv == [
|
|
907
|
-
["taskkill", "/T", "/F", "/PID", str(target_process_identifier)]
|
|
908
|
-
]
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
def test_windows_process_tree_kill_swallows_taskkill_timeout(
|
|
912
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
913
|
-
) -> None:
|
|
914
|
-
"""A hung taskkill must not raise — caller falls back to Popen.kill + drain."""
|
|
915
|
-
|
|
916
|
-
def raise_taskkill_timeout(
|
|
917
|
-
_all_arguments: list[str], **_keywords: object
|
|
918
|
-
) -> None:
|
|
919
|
-
raise subprocess.TimeoutExpired(cmd="taskkill", timeout=1)
|
|
920
|
-
|
|
921
|
-
monkeypatch.setattr(wrapper.subprocess, "run", raise_taskkill_timeout)
|
|
922
|
-
wrapper._kill_windows_process_tree(4242)
|
|
923
|
-
|
|
924
|
-
|
|
925
893
|
def test_drain_joins_pipes_when_direct_kill_raises_process_lookup_error() -> None:
|
|
926
894
|
"""Even when the process is already gone, drain still joins pipe readers."""
|
|
927
895
|
all_communicate_timeouts: list[float | None] = []
|
|
@@ -990,7 +958,7 @@ def test_run_command_surfaces_timeout_when_tree_kill_is_noop(
|
|
|
990
958
|
def leave_process_alive(_review_process: object) -> None:
|
|
991
959
|
return None
|
|
992
960
|
|
|
993
|
-
monkeypatch.setattr(wrapper, "
|
|
961
|
+
monkeypatch.setattr(wrapper, "terminate_process_tree", leave_process_alive)
|
|
994
962
|
start_time = time.monotonic()
|
|
995
963
|
with pytest.raises(subprocess.TimeoutExpired):
|
|
996
964
|
wrapper._run_command(
|
|
@@ -13,11 +13,12 @@ description: >-
|
|
|
13
13
|
|
|
14
14
|
## Gotchas
|
|
15
15
|
|
|
16
|
-
- **`low` stays single-pass.**
|
|
16
|
+
- **`low` stays single-pass.** No subagents, no full-file reads: one read pass per target item, one findings pass.
|
|
17
17
|
- **`medium` favors precision, `xhigh` favors recall.** At `medium` (8 angles) surface only findings a maintainer would act on. At `xhigh` (10 angles plus a gap sweep) a single non-REFUTED vote carries the finding; do not drop on uncertainty.
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
18
|
+
- **Every retained finding carries `severity` and `verdict`.** Severity is one of `blocker`, `high`, `medium`, `low`, `nit`. Verdict is `CONFIRMED` or `PLAUSIBLE`. Drop REFUTED candidates; never emit an unclassified retained finding.
|
|
19
|
+
- **`--fix` applies findings once.** Load `reference/fix.md` and follow it — it owns the fix agent, the code-rules gate, skip logging, and outcome reporting. Commits are lead-owned; fix agents never commit or push.
|
|
20
|
+
- **`loop` never asks.** A round with bug findings validates them with an advisor, fixes, and re-reviews. Terminals are exactly `clean`, `nits_fixed`, and `advisor_blocked`. There is no reviewed-head count limit; a new head increments the count once, a re-review of the same head does not. Load `reference/loop.md` and follow it.
|
|
21
|
+
- **`--fix` and `loop` combine.** With both, each loop round runs the level file, and the round's fixing happens inside `reference/loop.md`'s gate sequence, which loads `reference/fix.md` for the mechanics. There is no separate fix pass around the round.
|
|
21
22
|
|
|
22
23
|
## When this skill applies
|
|
23
24
|
|
|
@@ -31,8 +32,8 @@ Triggers: `/e-code-review <level> [--fix] [loop]`. `<level>` is `low`, `medium`,
|
|
|
31
32
|
|
|
32
33
|
1. Read `<level>` and the optional `--fix` and `loop` flags. Apply the refusal first.
|
|
33
34
|
2. Load `reference/low.md`, `reference/medium.md`, or `reference/xhigh.md`. Run that file as one review cycle, ending in its structured findings report.
|
|
34
|
-
3. If `--fix` is set, load `reference/fix.md` and apply it to that cycle's findings.
|
|
35
|
-
4. If `loop` is set, load `reference/loop.md` and follow it.
|
|
35
|
+
3. If `--fix` is set and `loop` is not set, load `reference/fix.md` and apply it to that cycle's findings. This path has no commit step: the fixes stay uncommitted in the working tree for the user to review and commit.
|
|
36
|
+
4. If `loop` is set, load `reference/loop.md` and follow it with that cycle's findings still unfixed. Round 1's findings are the ones step 2 already produced; from the second round on, the round re-runs that same level file end to end. End to end means the level file's review phases, up to and including its findings report — not its *Looping* section, which hands control to `loop.md` and would re-enter the loop the round is already inside. `loop.md`'s gate sequence owns when the round fixes.
|
|
36
37
|
5. Without `--fix` or `loop`, return the cycle findings and stop.
|
|
37
38
|
|
|
38
39
|
## File index
|
|
@@ -40,10 +41,10 @@ Triggers: `/e-code-review <level> [--fix] [loop]`. `<level>` is `low`, `medium`,
|
|
|
40
41
|
| File | Purpose |
|
|
41
42
|
|---|---|
|
|
42
43
|
| `SKILL.md` | Route by level; dispatch `--fix` and `loop` |
|
|
43
|
-
| `reference/low.md` | low review procedure — 1 diff pass, no verify |
|
|
44
|
+
| `reference/low.md` | low review procedure — 1 diff pass per target item, no verify |
|
|
44
45
|
| `reference/medium.md` | medium review procedure — 8 angles, 1-vote verify |
|
|
45
46
|
| `reference/xhigh.md` | xhigh review procedure — 10 angles, 1-vote verify, gap sweep |
|
|
46
|
-
| `reference/fix.md` | Fix application,
|
|
47
|
+
| `reference/fix.md` | Fix application, code-rules gate, skip logging, outcome reporting |
|
|
47
48
|
| `reference/loop.md` | Repeat review/fix rounds until clean |
|
|
48
49
|
|
|
49
50
|
## Folder map
|
|
@@ -12,16 +12,38 @@ At `low` — and any time the Agent tool is unavailable — no finder agent exis
|
|
|
12
12
|
to resume. Apply the fixes yourself, sequentially, in this context, holding to
|
|
13
13
|
every rule below.
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Code-rules gate
|
|
16
16
|
|
|
17
|
-
Before returning, the resumed agent
|
|
17
|
+
Before returning, the resumed agent runs the code-rules gate with the same bare
|
|
18
|
+
call every other surface in this skill uses:
|
|
18
19
|
|
|
19
20
|
1. Run `~/.claude/_shared/pr-loop/scripts/code_rules_gate.py --repo-root <repo
|
|
20
|
-
root
|
|
21
|
-
2. If the gate reports violations
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
root>` with no file paths and no `--only-under` prefix.
|
|
22
|
+
2. If the gate reports violations **on lines this fix already owns** (the files
|
|
23
|
+
and added lines the fix changed), fix them and re-run the exact same command.
|
|
24
|
+
3. A violation on a path or added line outside this fix's own work is reported
|
|
25
|
+
or skipped — it is not force-fixed into unrelated files. Log each skip with
|
|
26
|
+
the path and reason, then continue.
|
|
27
|
+
4. Repeat until every violation on this fix's own work is clean (or skipped with
|
|
28
|
+
a logged reason for out-of-scope hits).
|
|
29
|
+
5. Only after that result does the agent return control and report its outcome.
|
|
30
|
+
|
|
31
|
+
A file path named on the command line puts the gate in whole-file scope and can
|
|
32
|
+
churn on untouched lines; keep the call bare.
|
|
33
|
+
|
|
34
|
+
This gate commits nothing. The fix lands in the working tree and stays
|
|
35
|
+
uncommitted. **Commit ownership is lead-only:** a fix agent, patch worker, or
|
|
36
|
+
resumed finding agent never runs `git commit` or `git push`. The lead that owns
|
|
37
|
+
the branch creates the commit after gates pass. The bare code-rules call covers
|
|
38
|
+
the merge-base surface and staged added lines only — working-tree-only lines
|
|
39
|
+
enter that scope after the lead stages or commits them.
|
|
40
|
+
|
|
41
|
+
Under a bare `--fix` — this document invoked without `loop` — nothing
|
|
42
|
+
downstream commits either, and that is the intended outcome: a one-shot fix
|
|
43
|
+
pass leaves its fixes uncommitted in the working tree for the lead to review
|
|
44
|
+
and commit (stage first if you re-run the gate on those lines). Under `loop`,
|
|
45
|
+
the round tail in `reference\loop.md` stages and runs required checks; the lead
|
|
46
|
+
still owns the commit and push.
|
|
25
47
|
|
|
26
48
|
## Skip candidates
|
|
27
49
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Act
|
|
4
4
|
|
|
5
|
-
`loop` on the hub command authorizes the full cycle. After the effort level procedure returns findings,
|
|
5
|
+
`loop` on the hub command authorizes the full cycle. After the effort level procedure returns findings, run the gate sequence below immediately.
|
|
6
6
|
|
|
7
7
|
Do not ask whether to fix, which nits to keep, whether to commit or push, or whether to re-review. Do not open a plan fork. Do not end the turn on a recommendation.
|
|
8
8
|
|
|
@@ -10,11 +10,18 @@ Report progress while you work. Stop for the user only on a terminal outcome bel
|
|
|
10
10
|
|
|
11
11
|
## Where fixes come from
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
A round fixes whether or not `--fix` is set. Gate 2 below applies the round's
|
|
14
|
+
fixes on every run, and its three cases read the same way with the flag and
|
|
15
|
+
without it. What `--fix` adds is `fix.md`'s mechanics, not permission to fix:
|
|
16
|
+
the flag decides where the mechanics come from, never whether a round fixes at
|
|
17
|
+
all.
|
|
18
|
+
|
|
19
|
+
When `--fix` is also set, the round's fixing happens inside the gate sequence
|
|
20
|
+
below. Gate 2 is the only place a round applies a fix, and there is no separate
|
|
21
|
+
fix pass sitting around the round. Gate 2 loads `reference\fix.md` (relative to
|
|
22
|
+
this skill's folder) for the mechanics — which agent applies each fix, agent
|
|
23
|
+
resume, the code-rules gate, skip logging, and outcome reporting — while the
|
|
24
|
+
gate sequence decides whether a round fixes, commits, pushes, and re-reviews.
|
|
18
25
|
|
|
19
26
|
## Scope stays narrow
|
|
20
27
|
|
|
@@ -22,22 +29,231 @@ Auto-fix only verified findings on the review target. Leave deferred PR-body fol
|
|
|
22
29
|
|
|
23
30
|
## How to class each finding
|
|
24
31
|
|
|
25
|
-
|
|
32
|
+
Every retained finding carries a verified `severity` and a verification
|
|
33
|
+
`verdict` (`CONFIRMED` or `PLAUSIBLE`). The frozen severity set is exactly
|
|
34
|
+
`blocker`, `high`, `medium`, `low`, `nit`.
|
|
35
|
+
|
|
36
|
+
A finding is a `nit` only when that severity is `nit`. Runtime-correctness,
|
|
37
|
+
security, data-loss, compatibility, and every other non-nit finding is a
|
|
38
|
+
`bug`.
|
|
26
39
|
|
|
27
|
-
A finding
|
|
40
|
+
A finding that lacks severity is **unclassified**. Consult the assigned
|
|
41
|
+
advisor to classify it before Gate 2. When that advisor is unreachable,
|
|
42
|
+
terminate with `advisor_blocked` and preserve draft state.
|
|
28
43
|
|
|
29
|
-
|
|
44
|
+
A finding this document has already classed keeps that class at every level. An
|
|
45
|
+
on-target shape-reader break is `bug` under *A shape change names its readers*;
|
|
46
|
+
no advisor call reopens that.
|
|
30
47
|
|
|
31
48
|
## Required checks
|
|
32
49
|
|
|
33
|
-
"Run required checks" means: run
|
|
50
|
+
"Run required checks" means: run
|
|
51
|
+
`~/.claude/_shared/pr-loop/scripts/code_rules_gate.py --repo-root <repo root>`
|
|
52
|
+
with no file paths and no `--only-under` prefix. On a violation on lines this
|
|
53
|
+
round already owns, fix it and re-run the exact same command until clean. A
|
|
54
|
+
violation outside the review target is reported or skipped — not force-fixed into
|
|
55
|
+
unrelated files — and the skip is logged with path and reason.
|
|
56
|
+
|
|
57
|
+
That bare call gates the git diff since the merge-base (and any staged added
|
|
58
|
+
lines the gate already includes). Working-tree lines that are neither staged
|
|
59
|
+
nor committed sit outside that scope until they are staged or committed. Gate 2
|
|
60
|
+
leaves its edits uncommitted, so the round tail stages those edits before
|
|
61
|
+
required checks when this round produced edits.
|
|
62
|
+
|
|
63
|
+
Two failure scenarios keep the call bare. A file path named on the command line
|
|
64
|
+
puts the gate in whole-file scope on that file: a CODE_RULES violation on a line
|
|
65
|
+
this round never touched fails the round and churns the loop. An `--only-under`
|
|
66
|
+
prefix that matches nothing in the diff leaves the gate zero files to inspect and
|
|
67
|
+
exits clean, so a run that inspected nothing reads as a pass.
|
|
68
|
+
|
|
69
|
+
## Each round reviews new code
|
|
70
|
+
|
|
71
|
+
A repair diff is new code. From the second round on, the round runs the level
|
|
72
|
+
file end to end at the new head. End to end means that file's review phases, up
|
|
73
|
+
to and including its findings report; the round stops there and brings those
|
|
74
|
+
findings back to the gate sequence below. It does not run the level file's
|
|
75
|
+
*Looping* section — that section hands control to this document, and the round
|
|
76
|
+
is already inside it. The round's scope is the level's own review
|
|
77
|
+
target — the diff or path the level gathers up front, called Phase 0 in
|
|
78
|
+
`medium.md` and `xhigh.md` — taken against that target's base. A repair edit
|
|
79
|
+
landing outside that target widens the next round's scope to cover it: the next
|
|
80
|
+
round's review target is the original target **plus** that path, and the round
|
|
81
|
+
reviews both. A later widening adds to that target the same way.
|
|
82
|
+
|
|
83
|
+
The widened target is what the round hands the level file. When a round runs the
|
|
84
|
+
level file end to end, it passes the current review target — the original target
|
|
85
|
+
plus every path a widening has since added — as that run's target argument, in
|
|
86
|
+
place of the argument the first round was given. When the first round was given
|
|
87
|
+
no target argument, the original target is the item `default-range` — the level
|
|
88
|
+
file's own default gather — so the widened target is `default-range` plus every
|
|
89
|
+
added path, and the range the first round reviewed stays in scope. The level
|
|
90
|
+
file gathers what the round hands it, so a widened path is gathered and reviewed
|
|
91
|
+
like any other part of the target.
|
|
92
|
+
|
|
93
|
+
## Dangerous diffs take two full rounds
|
|
94
|
+
|
|
95
|
+
A diff is dangerous when it touches deletion paths, locks or other concurrency
|
|
96
|
+
control, or shared mutable state. A deletion path is a runtime path that removes
|
|
97
|
+
data or files; a dead-code cleanup is not one. Each round names whether the diff
|
|
98
|
+
it reviewed is dangerous. A dangerous diff holds the loop open until two full
|
|
99
|
+
rounds have reviewed it. A repair that rewrites the dangerous surface restarts
|
|
100
|
+
the two-round count at the first round that reviews the rewritten surface.
|
|
101
|
+
|
|
102
|
+
The round's progress report is where both facts are recorded: the dangerous
|
|
103
|
+
classification, and the dangerous-round count written as `N of M`.
|
|
104
|
+
|
|
105
|
+
## A shape change names its readers
|
|
106
|
+
|
|
107
|
+
When a repair changes a key, an identifier format, or a data shape, list every
|
|
108
|
+
reader of the shape it changed and state how each one reads the new shape. The
|
|
109
|
+
list goes in the round's progress report.
|
|
110
|
+
|
|
111
|
+
The round that follows a posted list checks each reader on that list against the
|
|
112
|
+
new shape and names each reader with its result in that round's progress report.
|
|
113
|
+
|
|
114
|
+
Discharge then turns on where the reader sits.
|
|
115
|
+
|
|
116
|
+
- An **off-target** reader discharges once it is named with a result. This
|
|
117
|
+
branch cannot repair it.
|
|
118
|
+
- An **on-target** reader that reads the new shape correctly discharges on that
|
|
119
|
+
result.
|
|
120
|
+
- An **on-target** reader that reads the new shape wrong is a bug-severity
|
|
121
|
+
finding of the round that checked it. It joins that round's findings, and it
|
|
122
|
+
discharges on exactly one of three results, each of them an outcome gate 2
|
|
123
|
+
already produces: it is repaired, it is recorded as a skipped finding, or the
|
|
124
|
+
advisor refutes it. Naming the break discharges nothing on its own, and
|
|
125
|
+
neither does a further round merely happening.
|
|
126
|
+
|
|
127
|
+
The list discharges once every reader on it has discharged.
|
|
128
|
+
|
|
129
|
+
A broken reader outside the review target does not block the loop and does not
|
|
130
|
+
widen scope. Hand it off as a reported finding. Two separate things are owed for
|
|
131
|
+
that hand-off, and they land at different times:
|
|
132
|
+
|
|
133
|
+
- **The round-scoped record** — what a round produces. Check the reader, record
|
|
134
|
+
its result, and name it in that round's progress report. All three are done
|
|
135
|
+
inside the round that checks the reader, and nothing outside that round is
|
|
136
|
+
needed to complete them. This is the record gate 2 reads.
|
|
137
|
+
- **The termination-time disclosure** — what terminating requires. The
|
|
138
|
+
ready-for-review message names every broken off-target reader and every
|
|
139
|
+
skipped finding that still exists, and the pull request body carries the same
|
|
140
|
+
names when the target is a pull request. A target with no pull request owes
|
|
141
|
+
the ready message alone. Gate 3 enforces this at the moment the loop
|
|
142
|
+
terminates; no earlier round owes it.
|
|
34
143
|
|
|
35
144
|
## Terminal outcomes
|
|
36
145
|
|
|
37
|
-
|
|
146
|
+
The loop emits exactly one of these terminals when it stops:
|
|
147
|
+
|
|
148
|
+
| Terminal | When |
|
|
149
|
+
|---|---|
|
|
150
|
+
| `clean` | Zero retained findings on the current head, and required checks pass |
|
|
151
|
+
| `nits_fixed` | Every retained finding is a nit with severity and a retained verdict, every nit is fixed, and required checks pass |
|
|
152
|
+
| `advisor_blocked` | Classification needs the assigned advisor and that advisor is unreachable |
|
|
153
|
+
|
|
154
|
+
Every round does this round's own work first, then runs the three gates below,
|
|
155
|
+
in order: gate 1, then gate 2, then gate 3.
|
|
156
|
+
|
|
157
|
+
**This round's work — before the gates.** Record this round's dangerous
|
|
158
|
+
classification and the dangerous-round count as `N of M`; and when a shape-change
|
|
159
|
+
list is open, check each reader on that list and name each reader with its
|
|
160
|
+
result, adding any on-target reader that reads the new shape wrong to this
|
|
161
|
+
round's findings as *A shape change names its readers* directs. Then run the
|
|
162
|
+
gates.
|
|
163
|
+
|
|
164
|
+
**Gate 1 — obligations.** Gate 1 is evaluated first in the sequence, and its
|
|
165
|
+
answer turns on the round's open obligations alone — the findings are in hand by
|
|
166
|
+
now, and no content they carry changes it. Ask only: does any obligation remain
|
|
167
|
+
open? Two kinds exist.
|
|
168
|
+
|
|
169
|
+
- A dangerous diff that has had fewer than two full rounds.
|
|
170
|
+
- A posted shape-change list that no round has discharged.
|
|
171
|
+
|
|
172
|
+
Gate 1 states its answer and stops there: an obligation remains open, or none
|
|
173
|
+
does. It states no re-entry, no continuation, and no routing. Gate 3 is the sole
|
|
174
|
+
router — every path out of a round passes through it.
|
|
175
|
+
|
|
176
|
+
**Gate 2 — findings.** When `--fix` is set, load `reference\fix.md` here and
|
|
177
|
+
follow it for the mechanics of every fix this gate applies — the fix agent,
|
|
178
|
+
agent resume, the code-rules gate, skip logging, and outcome reporting. When `--fix` is
|
|
179
|
+
absent, apply each fix in this session yourself, and log each skip in this
|
|
180
|
+
round's progress report. Then take the one case that matches the round's
|
|
181
|
+
findings.
|
|
182
|
+
|
|
183
|
+
- Any bug-severity finding: validate each bug with an advisor before touching
|
|
184
|
+
code — confirm it's real and confirm the intended fix — then fix every
|
|
185
|
+
validated bug and every nit on the review target. A refuted bug removes only
|
|
186
|
+
itself from the round's work; the nits are fixed either way.
|
|
187
|
+
- Nits only, with at least one nit present: fix all of them on the review
|
|
188
|
+
target.
|
|
189
|
+
- No findings at all: make no edits.
|
|
190
|
+
|
|
191
|
+
Gate 2 then ends by stating one of exactly two outcomes: unresolved findings
|
|
192
|
+
remain, or none remain. A refuted bug is resolved. A fixed nit is resolved. A
|
|
193
|
+
fixed validated bug is resolved. A handed-off off-target finding is resolved once
|
|
194
|
+
this round has made its round-scoped record as *A shape change names its readers*
|
|
195
|
+
defines that record — checked, result recorded, named in this round's progress
|
|
196
|
+
report — whether or not the problem behind it is solved. Gate 2 reads the record
|
|
197
|
+
and nothing else; the termination-time disclosure belongs to gate 3.
|
|
198
|
+
|
|
199
|
+
A skipped finding — a finding deliberately not applied, because fixing it would
|
|
200
|
+
change intended behavior, would reach beyond the review target, or the finding
|
|
201
|
+
itself is judged a false positive — is resolved once its skip is logged in this
|
|
202
|
+
round's progress report, naming the finding and the reason it was skipped. That
|
|
203
|
+
report is the sink every run has, with or without `--fix`. When `--fix` is set,
|
|
204
|
+
the skip handling `fix.md` carries runs inside this gate and adds to this log
|
|
205
|
+
rather than replacing it.
|
|
206
|
+
|
|
207
|
+
Gate 3 reads that stated outcome, never a case label.
|
|
208
|
+
|
|
209
|
+
**Gate 3 — exit test.** Resolve the terminal from the table above, then stop or
|
|
210
|
+
continue:
|
|
211
|
+
|
|
212
|
+
- zero retained findings and required checks pass → `clean`;
|
|
213
|
+
- nits only (each with severity and a retained verdict), all fixed, required checks pass → `nits_fixed`;
|
|
214
|
+
- advisor needed for classification and unreachable → `advisor_blocked`;
|
|
215
|
+
- otherwise continue.
|
|
216
|
+
|
|
217
|
+
Also require, for `clean` and `nits_fixed`:
|
|
218
|
+
|
|
219
|
+
- gate 1 shows no open obligation;
|
|
220
|
+
- gate 2 states no unresolved findings remain;
|
|
221
|
+
- this round produced no edits after the gates settled.
|
|
222
|
+
|
|
223
|
+
Any other combination runs the round tail and re-enters the loop.
|
|
224
|
+
|
|
225
|
+
Terminating with `clean` or `nits_fixed` carries one further condition — the
|
|
226
|
+
termination-time disclosure: the ready-for-review message names every broken
|
|
227
|
+
off-target reader and every skipped finding that still exists. When the target
|
|
228
|
+
is a pull request, the pull request body carries the same names; a target with
|
|
229
|
+
no pull request owes the ready message alone. Every surface this condition
|
|
230
|
+
names is written at termination — the ready message always, the pull request
|
|
231
|
+
body too when the target is a pull request — so each one is available to the
|
|
232
|
+
terminating round. A round that cannot name them does not terminate; it runs
|
|
233
|
+
the round tail and re-enters the loop, the same as any other non-terminating
|
|
234
|
+
round. With that condition met, post the proof-of-work PR comment when the
|
|
235
|
+
target is a PR, then run `gh pr ready` for a draft PR, or state ready
|
|
236
|
+
otherwise.
|
|
237
|
+
|
|
238
|
+
`advisor_blocked` keeps the pull request draft. It does
|
|
239
|
+
not run `gh pr ready`. It reports every surviving structured finding.
|
|
240
|
+
|
|
241
|
+
Gate 3 points at gate 1 for the obligation answer. It does not restate the
|
|
242
|
+
two-round rule or the shape-reader rule; each of those keeps its one home in its
|
|
243
|
+
own section above.
|
|
244
|
+
|
|
245
|
+
**The round tail.** Every round runs required checks here, in the one form
|
|
246
|
+
*Required checks* gives: the bare command, no file paths. When this round
|
|
247
|
+
produced edits, stage those edits first so they enter the bare gate's scope,
|
|
248
|
+
then run the checks. When this round produced no edits, run the same checks.
|
|
249
|
+
Should those checks produce repairs, this round has produced edits — stage
|
|
250
|
+
them and re-run the checks.
|
|
38
251
|
|
|
39
|
-
|
|
40
|
-
-
|
|
41
|
-
|
|
252
|
+
**Commit and push are lead-owned.** Gate 2 and the round tail leave every fix
|
|
253
|
+
unstaged-or-staged in the working tree for the lead that owns the branch. A
|
|
254
|
+
fix agent, patch worker, or resumed finding agent never creates the commit and
|
|
255
|
+
never pushes. The lead stages, commits once per review round, and pushes after
|
|
256
|
+
the gates pass. Start the next round under *Each round reviews new code* only
|
|
257
|
+
after that lead commit lands a new head.
|
|
42
258
|
|
|
43
259
|
Do not drop findings to force ready. Without `loop`, run one review at the selected level, fix, and return every validated finding.
|
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
`low effort → 1 diff pass → no verify → ≥min(files,4) findings`
|
|
1
|
+
`low effort → 1 diff pass per target item → no verify → ≥min(files,4) findings`
|
|
2
2
|
|
|
3
3
|
## Turn 1 — read
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Read the unified diff (`git diff @{upstream}...HEAD; git diff HEAD`
|
|
6
6
|
to cover both committed and uncommitted changes, or `git diff main...HEAD` /
|
|
7
|
-
the target passed as an argument).
|
|
8
|
-
|
|
7
|
+
the target passed as an argument). A target names one or more items, each a PR
|
|
8
|
+
number, a branch name, a file path, or `default-range` — the default `git diff`
|
|
9
|
+
read named above, which is what this step reads when no target is passed — and
|
|
10
|
+
it may mix those forms. A loop round widens a target by adding a path to whatever it
|
|
11
|
+
started as, and it names `default-range` as an item whenever the round it
|
|
12
|
+
widened was given no target argument, so the original scope stays under review.
|
|
13
|
+
When a target names more than one item, read each item's diff and review their
|
|
14
|
+
union — a shared hunk counted once, an empty one adding nothing. Skip
|
|
15
|
+
test/fixture hunks (`test/`, `spec/`, `__tests__/`, `*_test.*`, `*.test.*`,
|
|
9
16
|
`fixtures/`, `testdata/`) — test-file changes are not reviewed at this level.
|
|
10
|
-
|
|
17
|
+
One read pass per target item, and no more: no subagents, no full-file reads.
|
|
11
18
|
|
|
12
19
|
## Turn 2 — findings
|
|
13
20
|
|
|
@@ -32,11 +39,13 @@ also print the findings as text.
|
|
|
32
39
|
|
|
33
40
|
## Applying fixes (--fix)
|
|
34
41
|
|
|
35
|
-
The `--fix` flag was passed. Follow `reference\fix.md` (relative
|
|
36
|
-
skill's folder) for the exact fix,
|
|
37
|
-
it governs which agent applies each fix, how
|
|
38
|
-
is logged, and how outcomes get reported. Do not repeat the
|
|
39
|
-
follow that document's reporting rules once fixes land.
|
|
42
|
+
The `--fix` flag was passed. Follow `reference\fix.md` (relative
|
|
43
|
+
to this skill's folder) for the exact fix, code-rules-gate, and skip-handling
|
|
44
|
+
behavior — it governs which agent applies each fix, how the code-rules gate
|
|
45
|
+
runs, how a skip is logged, and how outcomes get reported. Do not repeat the
|
|
46
|
+
findings as text; follow that document's reporting rules once fixes land.
|
|
47
|
+
|
|
48
|
+
When `loop` is also set, skip this section.
|
|
40
49
|
|
|
41
50
|
## If findings are fixed later
|
|
42
51
|
|
|
@@ -50,8 +59,17 @@ summary; the host UI's per-finding status updates only from that call.
|
|
|
50
59
|
## Looping (`loop`)
|
|
51
60
|
|
|
52
61
|
The `loop` arg was passed. Follow `reference\loop.md` (relative to this
|
|
53
|
-
skill's folder) for how to re-run Turn 1 (read)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
skill's folder) for how to re-run Turn 1 (read) and Turn 2 (findings)
|
|
63
|
+
repeatedly — including its exit condition and re-invocation rules. Schedule no
|
|
64
|
+
fix pass of your own here: when `--fix` is also present, `reference\loop.md`'s
|
|
65
|
+
gate sequence owns the round's fixing and loads `reference\fix.md` for the
|
|
66
|
+
mechanics. Do not treat a single pass through this document as complete while
|
|
67
|
+
`loop` is active; hand control to that document, and do not stop at Turn 2.
|
|
68
|
+
|
|
69
|
+
That hand-off applies when this document is entered directly. When a loop round
|
|
70
|
+
is already running and has handed this document its target, the round owns the
|
|
71
|
+
loop: end at Turn 2 with the findings report and return those findings to
|
|
72
|
+
`reference\loop.md`'s gate sequence, rather than handing control to that
|
|
73
|
+
document again from here.
|
|
74
|
+
|
|
75
|
+
When `loop` was not passed, skip this section.
|