claude-dev-env 2.18.0 → 2.20.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 (133) hide show
  1. package/.agents/skills/AGENTS.md +4 -4
  2. package/.agents/skills/_shared/AGENTS.md +1 -0
  3. package/.agents/skills/_shared/pr-loop/AGENTS.md +1 -0
  4. package/.agents/skills/_shared/pr-loop/preflight-proposal.contract.test.mjs +119 -0
  5. package/.agents/skills/_shared/pr-loop/preflight-proposal.md +5 -0
  6. package/.agents/skills/e-code-review/SKILL.md +12 -1
  7. package/.agents/skills/e-code-review/reference/fix.md +5 -1
  8. package/.agents/skills/e-code-review/reference/loop.md +4 -0
  9. package/.agents/skills/e-code-review/reference/mode-contract.test.mjs +66 -0
  10. package/.agents/skills/e-code-review/reference/preflight-proposal.md +40 -0
  11. package/.agents/skills/e-code-review/reference/runner-selection.md +1 -0
  12. package/.agents/skills/pr-cleanup/SKILL.md +105 -75
  13. package/.agents/skills/pr-cleanup/reference/process-inventory.md +15 -0
  14. package/.agents/skills/pr-cleanup/reference/task-seeds.md +20 -0
  15. package/.agents/skills/{name-by-capability-audit → pr-name-by-capability}/SKILL.md +18 -12
  16. package/.agents/skills/{name-by-capability-audit → pr-name-by-capability}/reference/offense-examples.md +1 -1
  17. package/.agents/skills/{name-by-capability-audit → pr-name-by-capability}/reference/task-seeds.md +3 -2
  18. package/.agents/skills/pr-refinement/SKILL.md +31 -0
  19. package/.agents/skills/{shared-extraction-audit → pr-shared-extraction}/SKILL.md +24 -7
  20. package/.agents/skills/{small-cl → pr-small-cl}/SKILL.md +1 -1
  21. package/.agents/skills/prototype/scripts/test_probe_sandbox_safety.py +33 -5
  22. package/.agents/skills/skill-builder/SKILL.md +1 -0
  23. package/.agents/skills-archived/bugteam/SKILL.md +1 -1
  24. package/.agents/skills-archived/bugteam/test_skill_additions.py +1 -1
  25. package/.agents/skills-archived/comments/SKILL.md +1 -1
  26. package/.agents/skills-archived/descriptions/SKILL.md +1 -1
  27. package/.agents/skills-archived/reviews/SKILL.md +1 -1
  28. package/AGENTS.md +1 -1
  29. package/_shared/pr-loop/AGENTS.md +2 -1
  30. package/_shared/pr-loop/preflight-proposal.md +56 -0
  31. package/_shared/pr-loop/scripts/code_rules_gate.py +29 -6
  32. package/_shared/pr-loop/scripts/code_rules_gate_parts/gate_arguments.py +15 -3
  33. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_gate_running.py +47 -4
  34. package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +4 -0
  35. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +47 -0
  36. package/bin/ever-shipped-skills.mjs +14 -0
  37. package/bin/install-constants.mjs +5 -0
  38. package/bin/install.agents-home.test.mjs +23 -0
  39. package/bin/install.codex-rules.test.mjs +61 -6
  40. package/bin/install.mjs +57 -9
  41. package/bin/install.prune.test.mjs +65 -3
  42. package/bin/install.test.mjs +202 -2
  43. package/docs/CODE_RULES.md +6 -0
  44. package/hooks/advisory/conftest.py +10 -0
  45. package/hooks/advisory/migration_safety_advisor.py +14 -14
  46. package/hooks/advisory/refactor_guard.py +250 -144
  47. package/hooks/advisory/refactor_guard_test_support.py +46 -0
  48. package/hooks/advisory/test_migration_safety_advisor.py +120 -0
  49. package/hooks/advisory/test_refactor_guard_advisory.py +171 -0
  50. package/hooks/advisory/test_refactor_guard_eligibility.py +166 -0
  51. package/hooks/atomic_file_writer.py +81 -0
  52. package/hooks/blocking/AGENTS.md +2 -1
  53. package/hooks/blocking/block_main_commit.py +66 -33
  54. package/hooks/blocking/code_rules_banned_identifiers.py +55 -4
  55. package/hooks/blocking/code_rules_blast_radius.py +194 -0
  56. package/hooks/blocking/code_rules_enforcer.py +12 -0
  57. package/hooks/blocking/code_rules_enforcer_test_support.py +92 -0
  58. package/hooks/blocking/destructive_command_blocker.py +8 -7
  59. package/hooks/blocking/pre_tool_use_dispatcher.py +11 -7
  60. package/hooks/blocking/precommit_code_rules_gate.py +14 -139
  61. package/hooks/blocking/sensitive_file_protector.py +8 -6
  62. package/hooks/blocking/session_edit_stage_gate.py +31 -1
  63. package/hooks/blocking/test_block_main_commit.py +145 -0
  64. package/hooks/blocking/test_claude_md_orphan_file_blocker.py +171 -1
  65. package/hooks/blocking/test_code_rules_blast_radius.py +161 -0
  66. package/hooks/blocking/test_code_rules_enforcer_agent_home_tooling.py +37 -12
  67. package/hooks/blocking/test_code_rules_enforcer_banned_noun_word.py +54 -0
  68. package/hooks/blocking/test_code_rules_enforcer_duplicate_body_hook_routing.py +15 -53
  69. package/hooks/blocking/test_code_rules_enforcer_ephemeral.py +13 -62
  70. package/hooks/blocking/test_code_rules_enforcer_narrow_edit.py +309 -0
  71. package/hooks/blocking/test_code_rules_enforcer_precheck_forecast.py +14 -71
  72. package/hooks/blocking/test_code_rules_enforcer_scratchpad.py +14 -16
  73. package/hooks/blocking/test_code_rules_enforcer_split_entry_2.py +31 -46
  74. package/hooks/blocking/test_code_rules_enforcer_stage.py +192 -0
  75. package/hooks/blocking/test_code_rules_enforcer_zero_payload_alias_hook_routing.py +13 -59
  76. package/hooks/blocking/test_destructive_command_blocker.py +154 -138
  77. package/hooks/blocking/test_destructive_command_blocker_deny_mode.py +85 -25
  78. package/hooks/blocking/test_destructive_command_blocker_no_verify.py +58 -38
  79. package/hooks/blocking/test_destructive_command_blocker_patterns.py +133 -0
  80. package/hooks/blocking/test_docstring_rule_gate_count_blocker.py +113 -1
  81. package/hooks/blocking/test_env_var_table_code_drift_blocker.py +70 -3
  82. package/hooks/blocking/test_hook_subprocess_support.py +296 -0
  83. package/hooks/blocking/test_package_inventory_stale_blocker.py +1 -1
  84. package/hooks/blocking/test_pii_scanner.py +81 -0
  85. package/hooks/blocking/test_pre_tool_use_dispatcher.py +32 -3
  86. package/hooks/blocking/test_precommit_code_rules_gate.py +11 -16
  87. package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +234 -0
  88. package/hooks/blocking/test_pytest_testpaths_orphan_blocker.py +25 -18
  89. package/hooks/blocking/test_sensitive_file_protector.py +145 -5
  90. package/hooks/blocking/test_session_edit_stage_gate_staging.py +64 -0
  91. package/hooks/blocking/test_session_edit_stage_gate_support.py +204 -0
  92. package/hooks/blocking/test_session_edit_stage_gate_tracker.py +116 -0
  93. package/hooks/blocking/test_shared_stdin_adoption.py +31 -20
  94. package/hooks/blocking/test_test_preflight_check.py +80 -0
  95. package/hooks/git-hooks/AGENTS.md +1 -1
  96. package/hooks/git-hooks/git_hooks_constants/__init__.py +1 -0
  97. package/hooks/git-hooks/post_commit.py +160 -51
  98. package/hooks/git-hooks/pre_commit.py +9 -5
  99. package/hooks/git-hooks/test_post_commit.py +203 -0
  100. package/hooks/git-hooks/test_pre_commit.py +2 -2
  101. package/hooks/git-hooks/test_pre_push.py +57 -0
  102. package/hooks/hooks_constants/AGENTS.md +3 -1
  103. package/hooks/hooks_constants/atomic_file_writer_constants.py +4 -0
  104. package/hooks/hooks_constants/banned_identifiers_constants.py +1 -0
  105. package/hooks/hooks_constants/blast_radius_constants.py +14 -0
  106. package/hooks/hooks_constants/destructive_command_environment_constants.py +18 -0
  107. package/hooks/hooks_constants/destructive_command_segment_constants.py +1 -11
  108. package/hooks/hooks_constants/hardcoded_user_path_constants.py +9 -3
  109. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +1 -1
  110. package/hooks/hooks_constants/refactor_guard_constants.py +75 -0
  111. package/hooks/hooks_constants/sensitive_file_protector_constants.py +2 -4
  112. package/hooks/hooks_constants/test_refactor_guard_constants.py +21 -0
  113. package/hooks/json_file_reader.py +22 -0
  114. package/hooks/observability/test_instructions_loaded_logger.py +54 -0
  115. package/hooks/pending_sidecars.py +33 -0
  116. package/hooks/session/test_plugin_data_dir_cleanup.py +70 -0
  117. package/hooks/session/test_session_edit_tracker_cleanup.py +16 -3
  118. package/hooks/test_atomic_file_writer.py +73 -0
  119. package/hooks/test_json_file_reader.py +24 -0
  120. package/hooks/test_pending_sidecars.py +25 -0
  121. package/hooks/validation/mypy_validator.py +213 -80
  122. package/hooks/validation/test_mypy_validator.py +288 -13
  123. package/hooks/workflow/auto_formatter.py +225 -93
  124. package/hooks/workflow/investigation_tracker_reset.py +2 -0
  125. package/hooks/workflow/test_auto_formatter.py +261 -12
  126. package/hooks/workflow/test_investigation_tracker_reset.py +90 -0
  127. package/package.json +1 -1
  128. package/rules/failure-blast-radius.md +126 -0
  129. /package/.agents/skills/{name-by-capability-audit → pr-name-by-capability}/reference/fetch-commands.md +0 -0
  130. /package/.agents/skills/{name-by-capability-audit → pr-name-by-capability}/reference/report-template.md +0 -0
  131. /package/.agents/skills/{name-by-capability-audit → pr-name-by-capability}/reference/rule-checklist.md +0 -0
  132. /package/.agents/skills/{shared-extraction-audit → pr-shared-extraction}/reference/examples.md +0 -0
  133. /package/.agents/skills/{shared-extraction-audit → pr-shared-extraction}/reference/offense-taxonomy.md +0 -0
@@ -1,17 +1,26 @@
1
1
  ---
2
- name: name-by-capability-audit
2
+ name: pr-name-by-capability
3
3
  description: >-
4
4
  Audit a GitHub PR for name-by-capability violations: driver/motive words on
5
5
  reusable capability modules (queues and report routers may keep the driver
6
6
  word), then apply capability-oriented renames by default. Triggers:
7
- /name-by-capability-audit, name-by-capability audit, audit PR for naming, name
7
+ /pr-name-by-capability, name-by-capability audit, audit PR for naming, name
8
8
  by capability, capability naming review, cert_fix rename check, driver word in
9
9
  package name.
10
10
  ---
11
11
 
12
12
  # Name-by-Capability Audit
13
13
 
14
- Audit a GitHub PR against the **name-by-capability** rule. Inspect paths and title wording for driver/motive words on reusable capability code, report findings with a rename direction, and apply the suggested renames by default. An explicit audit-only request ends after the report.
14
+ Audit a GitHub PR against the **name-by-capability** rule. Inspect paths and title wording for driver or motive words on reusable capability code, and report violations with capability-oriented rename directions.
15
+
16
+ ## Mode routing
17
+
18
+ First-match routing selects the mode before the audit steps:
19
+
20
+ 1. `preflight-proposal` runs `pr-name-by-capability preflight-proposal <pr_number> --base-sha <base_sha> --head-sha <head_sha> --worktree <isolated_worktree>` under the [shared preflight proposal contract](../_shared/pr-loop/preflight-proposal.md). Record each finding with violation or OK-driver classification.
21
+ 2. `audit-only` is report-only and ends after the findings report.
22
+ 3. A user-supplied rename or fix direction follows the existing normal fix workflow.
23
+ 4. Normal mode applies the suggested rename direction by default.
15
24
 
16
25
  ## Gotchas
17
26
 
@@ -21,15 +30,10 @@ Audit a GitHub PR against the **name-by-capability** rule. Inspect paths and tit
21
30
 
22
31
  ## When this applies
23
32
 
24
- - User invokes `/name-by-capability-audit <PR>` or asks to audit a PR for name-by-capability / capability naming.
33
+ - User invokes `/pr-name-by-capability <PR>` or asks to audit a PR for name-by-capability / capability naming.
25
34
  - PR adds or renames packages/modules, or frames a general shared operation with a driver/motive word (`cert_fix`, `cert_closeout`, `portal`, `export`, …).
26
35
 
27
- **First match wins:**
28
-
29
- - Missing PR number or URL → respond exactly: `Give a GitHub PR number or URL to audit for name-by-capability.`
30
- - User requests audit-only → finish the audit report and stop.
31
- - User gives a rename or fix direction → finish the audit report, then apply the direction they gave.
32
- - A violation with no user-supplied direction → finish the audit report, then apply the suggested rename direction by default.
36
+ A missing PR number or URL returns exactly: `Give a GitHub PR number or URL to audit for name-by-capability.` Resolved targets follow **Mode routing**.
33
37
 
34
38
  ## Constraints
35
39
 
@@ -39,7 +43,7 @@ Audit a GitHub PR against the **name-by-capability** rule. Inspect paths and tit
39
43
 
40
44
  ## Process
41
45
 
42
- Register every bullet from `reference/task-seeds.md` on the host task tool (`TodoWrite` / `TaskCreate`). Mark each complete with evidence. Follow those seeds in order — do not restate them here.
46
+ Register every bullet from `reference/task-seeds.md` with `update_plan`. Mark each complete with evidence. Follow those seeds in order — do not restate them here.
43
47
 
44
48
  Load order for the rule: if `docs/agents/name-by-capability.md` exists, read it first; always keep `reference/rule-checklist.md` as the fallback when the doc is missing. On disagreement after the docs PR merges, prefer the repo doc and update the skill checklist in a follow-up.
45
49
 
@@ -47,7 +51,7 @@ Load order for the rule: if `docs/agents/name-by-capability.md` exists, read it
47
51
 
48
52
  | Peer | Relationship |
49
53
  |------|----------------|
50
- | `shared-extraction-audit` | Layering and extraction (where code lives). Invoke when a path’s *role* is unclear; this skill only scores the *name*. |
54
+ | `pr-shared-extraction` | Layering and extraction (where code lives). Invoke when a path’s *role* is unclear; this skill only scores the *name*. |
51
55
  | `reviews` / PR review skills | May invoke this skill by name when naming is in scope |
52
56
 
53
57
  ## File index
@@ -60,6 +64,8 @@ Load order for the rule: if `docs/agents/name-by-capability.md` exists, read it
60
64
  | `reference/fetch-commands.md` | Minimal `gh` fetch for PR naming surface |
61
65
  | `reference/report-template.md` | Compact report shape |
62
66
  | `reference/task-seeds.md` | Ordered task seeds for the audit run |
67
+ | `../_shared/pr-loop/preflight-proposal.md` | Shared isolated proposal mode and evidence record |
68
+ | `../_shared/pr-loop/preflight-proposal.contract.test.mjs` | Cross-skill mode and contract test |
63
69
 
64
70
  ## Folder map
65
71
 
@@ -1,6 +1,6 @@
1
1
  # Offense examples (this repo)
2
2
 
3
- Classify by **role** — the reusable work the surface performs — not by matching a string list. For whether code belongs in `shared_utils` vs a workflow package, invoke `shared-extraction-audit`; this map only scores names.
3
+ Classify by **role** — the reusable work the surface performs — not by matching a string list. For whether code belongs in `shared_utils` vs a workflow package, invoke `pr-shared-extraction`; this map only scores names.
4
4
 
5
5
  ## Quick classifier
6
6
 
@@ -1,9 +1,10 @@
1
1
  # Task seeds
2
2
 
3
- Register each item on `TodoWrite` / `TaskCreate` at audit start. Complete with evidence.
3
+ Register each item with `update_plan` at audit start. Complete with evidence.
4
4
 
5
5
  1. Load the rule: `docs/agents/name-by-capability.md` when present, else `reference/rule-checklist.md` (always keep the skill checklist as fallback).
6
6
  2. Fetch PR title, body, and changed/renamed paths via `reference/fetch-commands.md`.
7
7
  3. Classify each naming signal as violation, OK driver, or skip (`reference/offense-examples.md` classifier first).
8
8
  4. Emit report from `reference/report-template.md`.
9
- 5. Apply the requested or suggested rename direction by default; an explicit audit-only request ends after the report.
9
+ 5. After the report, continue with `SKILL.md` **Mode routing**.
10
+ 6. For `preflight-proposal`, complete the shared contract referenced by `SKILL.md` **Mode routing**.
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: pr-refinement
3
+ description: Coordinate shared-extraction and capability-naming audits, then update an existing pull request or publish a required focused GitHub pull-request stack. Use when a user asks to turn a PR architecture audit into extracted shared code and small pull requests.
4
+ ---
5
+
6
+ # PR Refinement
7
+
8
+ Turn audit findings into a focused existing pull-request update or a required dependency-ordered GitHub pull-request stack.
9
+
10
+ ## Peer skills
11
+
12
+ 1. Run `pr-shared-extraction` for reusable-code placement, canonical shared homes, and extraction findings.
13
+ 2. Run `pr-name-by-capability` for module, symbol, path, branch, and pull-request naming findings.
14
+ 3. Use `pr-small-cl` to divide approved fixes into coherent, independently reviewable pull requests.
15
+
16
+ The peer skills own their audit rules and finding priorities.
17
+
18
+ ## Workflow
19
+
20
+ 1. Run both audits in parallel against the same pull request. Keep their findings, locations, priorities, destinations, API directions, and naming directions.
21
+ 2. Build one change map. Group findings by shared capability and dependency.
22
+ 3. Use `pr-small-cl` to choose the delivery shape. Update the existing pull request when it remains one coherent, reviewable outcome.
23
+ 4. Create a replacement pull-request stack when the focused-change review identifies independent increments. Give each pull request one coherent outcome, related tests, a clear verification boundary, and a capability-oriented branch and title. Close the original pull request as superseded and link the replacement stack.
24
+ 5. Implement the existing pull request or the replacement stack in dependency order. Earlier pull requests create stable shared foundations. Later pull requests migrate consumers and remove replaced code.
25
+ 6. Run scoped production-path tests for each pull request. Record commands and results.
26
+ 7. Commit and push each branch. Update the existing pull request, or open draft replacement pull requests in stack order and set every child pull request base to its parent.
27
+ 8. Include scope, verification, risks, and stack dependencies in every pull-request body.
28
+
29
+ ## Completion
30
+
31
+ Deliver a focused, tested, pushed existing pull request, or a replacement stack with explicit parent-child links. Keep merge authority with the user.
@@ -1,11 +1,11 @@
1
1
  ---
2
- name: shared-extraction-audit
2
+ name: pr-shared-extraction
3
3
  description: Audits pull requests and packages for general helpers living in workflow-specific code instead of shared_utils, then applies extraction fixes by default in small tested CLs. Use when reviewing PRs for architectural layering, migrating utilities out of cert_fix_queue/theme_dialer_pipeline/skills, auditing "similar offenses", or when the user mentions extraction audit, shared_utils migration, orchestration-only packages, thin wrappers, or layer inversions.
4
4
  ---
5
5
 
6
6
  # Shared Extraction Audit
7
7
 
8
- Audit PRs and packages, report findings, and apply extraction fixes by default to keep **orchestration packages thin** and **shared_utils canonical**.
8
+ Audit PRs and packages for reusable behavior placed in workflow code. Keep **orchestration packages thin** and **shared_utils canonical**.
9
9
 
10
10
  ## When to use
11
11
 
@@ -13,6 +13,14 @@ Audit PRs and packages, report findings, and apply extraction fixes by default t
13
13
  - A package grew `fix_one`, `_default_*`, backup upload, rembg session, or residual gates alongside orchestration
14
14
  - Before merge: confirm consumers are adapters, not second implementations
15
15
 
16
+ ## Mode routing
17
+
18
+ Resolve the first matching mode before the audit steps:
19
+
20
+ 1. `preflight-proposal` runs `pr-shared-extraction preflight-proposal <pr_number> --base-sha <base_sha> --head-sha <head_sha> --worktree <isolated_worktree>` under the [shared preflight proposal contract](../_shared/pr-loop/preflight-proposal.md). Record each finding with priority and target.
21
+ 2. `audit-only` is report-only and ends after the findings report.
22
+ 3. Normal mode follows the existing audit workflow and applies the prioritized fix band by default.
23
+
16
24
  ## Target architecture
17
25
 
18
26
  | Layer | Holds | Examples |
@@ -25,7 +33,7 @@ Audit PRs and packages, report findings, and apply extraction fixes by default t
25
33
 
26
34
  ## Audit workflow
27
35
 
28
- Copy to TodoWrite:
36
+ Register this checklist with `update_plan` and mark each step complete with evidence:
29
37
 
30
38
  ```
31
39
  Shared extraction audit:
@@ -33,9 +41,10 @@ Shared extraction audit:
33
41
  - [ ] 2. Map canonical homes already in shared_utils
34
42
  - [ ] 3. Grep for offense patterns (see reference/offense-taxonomy.md)
35
43
  - [ ] 4. Write prioritized findings (P0–P3)
36
- - [ ] 5. Apply the prioritized fix band by default; an explicit audit-only request ends after the report
44
+ - [ ] 5. Apply **Mode routing** after the report
37
45
  - [ ] 6. Extract in small CLs (~100 lines) + move/adjust tests
38
- - [ ] 7. Run scoped pytest, commit, push, update PR
46
+ - [ ] 7. Run scoped pytest, then follow the selected mode's mutation boundary
47
+ - [ ] 8. When the mode is `preflight-proposal`, record proposal evidence from the [shared preflight proposal contract](../_shared/pr-loop/preflight-proposal.md) and require downstream owner selection before reapplication
39
48
  ```
40
49
 
41
50
  ### Step 1 — Scope
@@ -97,8 +106,6 @@ Use [reference/offense-taxonomy.md](reference/offense-taxonomy.md). Assign prior
97
106
  1. …
98
107
  ```
99
108
 
100
- Apply the prioritized fixes after the report by default. An explicit audit-only request ends after the report.
101
-
102
109
  ## Fix workflow
103
110
 
104
111
  ### Extraction rules
@@ -158,3 +165,13 @@ Provide evidence: pytest output counts, not "should work".
158
165
  ## Examples
159
166
 
160
167
  See [reference/examples.md](reference/examples.md) for cert_fix_queue (#1965) and background_removal (#1954) audits.
168
+
169
+ ## File index
170
+
171
+ | File | Purpose |
172
+ |------|---------|
173
+ | `SKILL.md` | Hub — architecture, routing, audit checklist, and fix workflow |
174
+ | `reference/offense-taxonomy.md` | Offense signals and priority guide |
175
+ | `reference/examples.md` | Audit examples and fix order |
176
+ | `../_shared/pr-loop/preflight-proposal.md` | Shared isolated proposal mode and evidence record |
177
+ | `../_shared/pr-loop/preflight-proposal.contract.test.mjs` | Cross-skill mode and contract test |
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: small-cl
2
+ name: pr-small-cl
3
3
  description: >-
4
4
  Scope or split a pull request into a self-contained reviewable increment. Use
5
5
  when a user asks to "split this PR", "make this change smaller", or "small CL".
@@ -33,6 +33,7 @@ from prototype_scripts_constants.config.probe_sandbox_safety_constants import (
33
33
  )
34
34
 
35
35
  SCRIPTS_DIRECTORY = Path(__file__).resolve().parent
36
+ BUILDER_PATH = SCRIPTS_DIRECTORY / "build_sandbox_settings.py"
36
37
  PROBE_PATH = SCRIPTS_DIRECTORY / "probe_sandbox_safety.py"
37
38
  HOOKS_BLOCKING_DIRECTORY = SCRIPTS_DIRECTORY.parents[3] / "hooks" / "blocking"
38
39
 
@@ -52,13 +53,21 @@ ASK_HOOK_SOURCE = (
52
53
  ALLOW_HOOK_SOURCE = "import sys\nsys.exit(0)\n"
53
54
 
54
55
 
55
- def load_probe_module() -> ModuleType:
56
- spec = importlib.util.spec_from_file_location("probe_sandbox_safety", PROBE_PATH)
56
+ def load_module_from_path(module_name: str, module_path: Path) -> ModuleType:
57
+ spec = importlib.util.spec_from_file_location(module_name, module_path)
57
58
  assert spec is not None
58
59
  assert spec.loader is not None
59
- probe_module = importlib.util.module_from_spec(spec)
60
- spec.loader.exec_module(probe_module)
61
- return probe_module
60
+ loaded_module = importlib.util.module_from_spec(spec)
61
+ spec.loader.exec_module(loaded_module)
62
+ return loaded_module
63
+
64
+
65
+ def load_probe_module() -> ModuleType:
66
+ return load_module_from_path("probe_sandbox_safety", PROBE_PATH)
67
+
68
+
69
+ def load_builder_module() -> ModuleType:
70
+ return load_module_from_path("build_sandbox_settings", BUILDER_PATH)
62
71
 
63
72
 
64
73
  def quoted_command(script_path: Path) -> str:
@@ -123,6 +132,25 @@ def test_main_exits_zero_when_the_real_destructive_hook_denies_in_deny_mode(
123
132
  assert exit_code == PROBE_SUCCESS_EXIT_CODE
124
133
 
125
134
 
135
+ def test_built_deny_environment_denies_real_destructive_hook() -> None:
136
+ builder_module = load_builder_module()
137
+ settings_document = builder_module.build_minimal_settings(
138
+ builder_module.resolve_safety_hook_entries(real_hook_settings_document())
139
+ )
140
+ probe_module = load_probe_module()
141
+ probe_payload = probe_module.build_probe_payload_for_basename(DESTRUCTIVE_HOOK_BASENAME)
142
+ destructive_hook_path = HOOKS_BLOCKING_DIRECTORY / DESTRUCTIVE_HOOK_BASENAME
143
+ destructive_hook_tokens = probe_module.parse_command_argv(
144
+ quoted_command(destructive_hook_path)
145
+ )
146
+ is_hook_blocking = probe_module.hook_blocks_probe(
147
+ destructive_hook_tokens,
148
+ probe_payload,
149
+ settings_document["env"],
150
+ )
151
+ assert is_hook_blocking is True
152
+
153
+
126
154
  def test_pii_basename_probe_payload_carries_the_secret() -> None:
127
155
  probe = load_probe_module()
128
156
  probe_payload = probe.build_probe_payload_for_basename(PII_HOOK_BASENAME)
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: skill-builder
3
+ disable-model-invocation: true
3
4
  description: >-
4
5
  Skill lifecycle: classify, scaffold, write via the skill-writer-agent, self-audit, compose
5
6
  sub-skills, polish description triggers, enforce deterministic scripts. Triggers:
@@ -35,7 +35,7 @@ Use the [review guide](../reviews/SKILL.md#review-workflow) to decide whether a
35
35
  changed behavior is a finding. Use the [comment guide](../comments/SKILL.md#writing-useful-review-comments)
36
36
  for posted findings and replies, the [description guide](../descriptions/SKILL.md#reviewing-a-description)
37
37
  for final PR context, the [emergency guide](../emergencies/SKILL.md#while-the-incident-is-active)
38
- for active incidents, and the [small-change guide](../small-cl/SKILL.md#reviewable-scope)
38
+ for active incidents, and the [small-change guide](../../skills/pr-small-cl/SKILL.md#reviewable-scope)
39
39
  when the diff needs a narrower review boundary.
40
40
 
41
41
  ## Pre-flight
@@ -26,4 +26,4 @@ def test_skill_routes_judgment_to_canonical_guides() -> None:
26
26
  assert "../comments/SKILL.md#writing-useful-review-comments" in skill_text
27
27
  assert "../descriptions/SKILL.md#reviewing-a-description" in skill_text
28
28
  assert "../emergencies/SKILL.md#while-the-incident-is-active" in skill_text
29
- assert "../small-cl/SKILL.md#reviewable-scope" in skill_text
29
+ assert "../../skills/pr-small-cl/SKILL.md#reviewable-scope" in skill_text
@@ -59,7 +59,7 @@ needed. Record the resulting decision in the pull request thread.
59
59
 
60
60
  - Use [reviews](../reviews/SKILL.md#review-workflow) for substantive code
61
61
  assessment.
62
- - Use [small-cl](../small-cl/SKILL.md#responding-to-review) for author-side
62
+ - Use [pr-small-cl](../../skills/pr-small-cl/SKILL.md#responding-to-review) for author-side
63
63
  review responses and focused follow-up work.
64
64
  - Use [emergencies](../emergencies/SKILL.md#while-the-incident-is-active) for
65
65
  urgent production-change communication.
@@ -47,5 +47,5 @@ summary, scope, verification, and risk information aligned with the pull
47
47
  request's current contents.
48
48
 
49
49
  Use [reviews](../reviews/SKILL.md#review-workflow) for substantive code
50
- assessment and [small-cl](../small-cl/SKILL.md#what-a-focused-pull-request-contains)
50
+ assessment and [pr-small-cl](../../skills/pr-small-cl/SKILL.md#what-a-focused-pull-request-contains)
51
51
  to establish a focused scope.
@@ -81,5 +81,5 @@ the pull request record.
81
81
  or assess pull request context.
82
82
  - Use [emergencies](../emergencies/SKILL.md#emergency-classification) to classify
83
83
  an urgent production change.
84
- - Use [small-cl](../small-cl/SKILL.md#when-to-use-this-guide) to focus or split
84
+ - Use [pr-small-cl](../../skills/pr-small-cl/SKILL.md#when-to-use-this-guide) to focus or split
85
85
  a change that exceeds a clear review boundary.
package/AGENTS.md CHANGED
@@ -76,7 +76,7 @@ Use full capability names for files, modules, functions, variables, branches, an
76
76
 
77
77
  When planning work or opening a pull request, size the change first: one self-contained change, around 100 lines, with its tests. Read the small-changelists guide for the numbers, the allowed exceptions, and how to split.
78
78
 
79
- Use `~/.claude/skills/small-cl/SKILL.md` for change-size guidance and review-sized boundaries.
79
+ Use `~/.claude/skills/pr-small-cl/SKILL.md` for change-size guidance and review-sized boundaries.
80
80
 
81
81
  ## Execution and delegation
82
82
 
@@ -11,6 +11,7 @@ Runtime documents and scripts shared by every PR-loop skill. Changes here affect
11
11
  | `post-audit-thread-contract.md` | Single source of truth for the `post_audit_thread.py` invocation string, exit-code table, and per-caller policy (bugteam exit 2 = hard blocker; autoconverge clean-audit failed post = recorded bypass) |
12
12
  | `fix-protocol.md` | Ordered sequence a fix lens follows: read, capture SHA, TDD, apply, validate, self-audit, commit, push, reply + resolve |
13
13
  | `gh-payloads.md` | How to build GitHub review and reply payloads via MCP tools; describes the one-review-per-loop pattern |
14
+ | `preflight-proposal.md` | Shared immutable proposal evidence and mutation-boundary contract for audit preflight worktrees |
14
15
  | `state-schema.md` | Fields each PR-loop workflow tracks across iterations; documents common fields and per-skill extensions |
15
16
  | `code-rules-gate.md` | Reference for the CODE_RULES pre-commit gate check; describes what the gate blocks and when it runs |
16
17
  | `precatch-rubric.md` | Shared pre-catch lane checklist — deterministic sweep, doc-vs-code parity, test-assertion completeness, PR-description parity, adversarial audit — that autoconverge's lenses and pr-converge's CODE_REVIEW step read on demand |
@@ -24,4 +25,4 @@ Runtime documents and scripts shared by every PR-loop skill. Changes here affect
24
25
 
25
26
  ## Breaking-change rule
26
27
 
27
- Any shape change in `audit-contract.md` or `audit-reply-template.md` requires updating every consuming skill in the same commit.
28
+ Any shape change in a canonical contract listed under **Key documents** requires updating every consuming skill in the same commit.
@@ -0,0 +1,56 @@
1
+ # Preflight proposal contract
2
+
3
+ Audit skills use this contract when the caller selects `preflight-proposal`. The mode prepares a local candidate and returns evidence for downstream selection.
4
+
5
+ Each skill hub defines its command name and finding classification. Commands use this shape:
6
+
7
+ ```text
8
+ <audit-skill> preflight-proposal <pr_number> --base-sha <base_sha> --head-sha <head_sha> --worktree <isolated_worktree>
9
+ ```
10
+
11
+ ## Required inputs
12
+
13
+ | Input | Contract |
14
+ |---|---|
15
+ | `pr_number` | Resolved open pull-request number |
16
+ | `--base-sha` | Full immutable base commit SHA |
17
+ | `--head-sha` | Full immutable head commit SHA |
18
+ | `--worktree` | Caller-supplied isolated worktree path |
19
+
20
+ ## Contract
21
+
22
+ 1. Validate every required input before the first audit command.
23
+ 2. Run `git rev-parse HEAD` in the supplied worktree and require exact equality with the head SHA.
24
+ 3. Gather changed paths and audit scope from the exact `base_sha...head_sha` range. Use the resolved PR number for PR metadata.
25
+ 4. Run the complete audit and findings against the immutable range and worktree.
26
+ 5. Keep every audit edit and test inside the caller-supplied isolated worktree.
27
+ 6. Create an immutable proposal identity. Use the existing commit SHA for committed proposals. For working-tree edits, use a deterministic diff hash over the baseline `HEAD` SHA, base SHA, head SHA, sorted normalized changed paths, status records, and file bytes.
28
+ 7. Record every changed path, including tracked, untracked, renamed, and deleted paths.
29
+ 8. Record exact tests and outcomes: each test command, exit code, and outcome.
30
+ 9. Return selected-candidate-ready proposal evidence with the proposal ID, isolated worktree path, changed paths, findings, tests, and outcomes.
31
+ 10. Keep commit, push, pull-request body, pull-request comment, pull-request review, pull-request update, merge, rebase, and Ready-state mutations disabled.
32
+ 11. Return the proposal for downstream disposition. The downstream owner adds the proposal ID to its selected or dispositioned proposal collection. Reapplication uses exactly the selected records and their changed paths. Give each later finding a new proposal ID and evidence record.
33
+
34
+ ## Evidence record
35
+
36
+ Use this record shape:
37
+
38
+ ```yaml
39
+ mode: preflight-proposal
40
+ pr_number: <resolved PR number>
41
+ base_sha: <immutable base SHA>
42
+ head_sha: <immutable head SHA>
43
+ proposal_id:
44
+ kind: commit-sha | diff-sha256
45
+ value: <immutable identity>
46
+ isolated_worktree: <caller-supplied path>
47
+ changed_paths:
48
+ - <normalized path>
49
+ findings:
50
+ - <audit-classified finding>
51
+ tests:
52
+ - command: <exact command>
53
+ exit_code: <integer>
54
+ outcome: <exact outcome>
55
+ downstream_selection: required
56
+ ```
@@ -4,6 +4,7 @@
4
4
 
5
5
  default mode: git diff since merge-base, joined with untracked files
6
6
  --staged: validate the staged index; --paths: validate explicit files
7
+ --immediate: validate staged rules and terminology at commit time
7
8
  every mode ends by naming how many files it inspected
8
9
 
9
10
  This entry module wires the ``code_rules_gate_parts`` submodules into one CLI
@@ -43,6 +44,7 @@ try:
43
44
  ALL_WINDOWS_VENV_PYTHON_RELATIVE_PATH_SEGMENTS,
44
45
  EMPTY_FILE_SET_EXIT_CODE,
45
46
  EMPTY_FILE_SET_MESSAGE,
47
+ IMMEDIATE_SCOPE_ARGUMENT,
46
48
  INSPECTED_COUNT_MESSAGE,
47
49
  MAXIMUM_STAGED_PYTEST_COMMAND_LINE_CHARACTERS,
48
50
  MINIMUM_STAGED_PYTEST_PYTHON_MAJOR,
@@ -263,31 +265,50 @@ def _run_explicit_paths_mode(
263
265
  )
264
266
 
265
267
 
266
- def _run_staged_mode(
268
+ def _run_staged_validation(
267
269
  validate_content: enforcer_loading.ValidateContentCallable,
268
270
  arguments: argparse.Namespace,
269
271
  repository_root: Path,
270
272
  ) -> int:
271
- """Validate the staged changes, run staged tests, and sweep terminology."""
272
- _report_terminology_findings(staged_terminology_findings(repository_root))
273
- staged_test_exit_code = _staged_pytest_exit_code_for_current_python(repository_root)
273
+ """Validate staged file content and report scoped rule findings."""
274
274
  staged_file_paths = filter_paths_under_prefixes(
275
275
  paths_from_git_staged(repository_root), repository_root, arguments.only_under
276
276
  )
277
277
  if not staged_file_paths:
278
278
  sys.stderr.write(INSPECTED_COUNT_MESSAGE.format(inspected_count=0) + "\n")
279
- return staged_test_exit_code
279
+ return 0
280
280
  staged_added_lines = added_lines_by_file_staged(repository_root, staged_file_paths)
281
- gate_exit_code = run_gate(
281
+ return run_gate(
282
282
  validate_content,
283
283
  staged_file_paths,
284
284
  repository_root,
285
285
  all_added_lines_by_path=staged_added_lines,
286
286
  should_read_staged_content=True,
287
287
  )
288
+
289
+
290
+ def _run_staged_mode(
291
+ validate_content: enforcer_loading.ValidateContentCallable,
292
+ arguments: argparse.Namespace,
293
+ repository_root: Path,
294
+ ) -> int:
295
+ """Validate staged rules, terminology, and staged-test behavior."""
296
+ _report_terminology_findings(staged_terminology_findings(repository_root))
297
+ staged_test_exit_code = _staged_pytest_exit_code_for_current_python(repository_root)
298
+ gate_exit_code = _run_staged_validation(validate_content, arguments, repository_root)
288
299
  return gate_exit_code or staged_test_exit_code
289
300
 
290
301
 
302
+ def _run_immediate_mode(
303
+ validate_content: enforcer_loading.ValidateContentCallable,
304
+ arguments: argparse.Namespace,
305
+ repository_root: Path,
306
+ ) -> int:
307
+ """Validate staged rules and terminology at the native commit boundary."""
308
+ _report_terminology_findings(staged_terminology_findings(repository_root))
309
+ return _run_staged_validation(validate_content, arguments, repository_root)
310
+
311
+
291
312
  def _run_diff_mode(
292
313
  validate_content: enforcer_loading.ValidateContentCallable,
293
314
  arguments: argparse.Namespace,
@@ -339,6 +360,8 @@ def main(all_arguments: list[str]) -> int:
339
360
  validate_content = load_validate_content()
340
361
  if arguments.paths:
341
362
  return _run_explicit_paths_mode(validate_content, arguments, repository_root)
363
+ if arguments.immediate:
364
+ return _run_immediate_mode(validate_content, arguments, repository_root)
342
365
  if arguments.staged:
343
366
  return _run_staged_mode(validate_content, arguments, repository_root)
344
367
  return _run_diff_mode(validate_content, arguments, repository_root)
@@ -3,9 +3,14 @@
3
3
  import argparse
4
4
  from pathlib import Path
5
5
 
6
+ from pr_loop_shared_constants.code_rules_gate_constants import (
7
+ IMMEDIATE_SCOPE_ARGUMENT,
8
+ IMMEDIATE_SCOPE_HELP,
9
+ )
10
+
6
11
 
7
12
  def _add_source_arguments(parser: argparse.ArgumentParser) -> None:
8
- """Add the repo-root, base-ref, and staged-mode arguments to *parser*."""
13
+ """Add the repo-root, base-ref, and staged-scope arguments to *parser*."""
9
14
  parser.add_argument(
10
15
  "--repo-root",
11
16
  type=Path,
@@ -17,12 +22,19 @@ def _add_source_arguments(parser: argparse.ArgumentParser) -> None:
17
22
  default="origin/main",
18
23
  help="Merge-base ref for git diff (default: origin/main).",
19
24
  )
20
- parser.add_argument(
25
+ staged_scope_group = parser.add_mutually_exclusive_group()
26
+ staged_scope_group.add_argument(
21
27
  "--staged",
22
28
  action="store_true",
23
29
  default=False,
24
30
  help="Scope to staged changes only (git diff --cached).",
25
31
  )
32
+ staged_scope_group.add_argument(
33
+ IMMEDIATE_SCOPE_ARGUMENT,
34
+ action="store_true",
35
+ default=False,
36
+ help=IMMEDIATE_SCOPE_HELP,
37
+ )
26
38
 
27
39
 
28
40
  def _add_filter_arguments(parser: argparse.ArgumentParser) -> None:
@@ -65,6 +77,6 @@ def parse_arguments(all_arguments: list[str]) -> argparse.Namespace:
65
77
 
66
78
  Returns:
67
79
  The parsed namespace with ``repo_root``, ``base``, ``staged``,
68
- ``only_under``, and ``paths`` attributes.
80
+ ``immediate``, ``only_under``, and ``paths`` attributes.
69
81
  """
70
82
  return _build_argument_parser().parse_args(all_arguments)
@@ -3,14 +3,23 @@
3
3
  from pathlib import Path
4
4
 
5
5
  import pytest
6
- from code_rules_gate_parts import gate_running
7
6
 
7
+ from code_rules_gate_parts import enforcer_loading, gate_running
8
+ from code_rules_gate_parts.tests._repo_test_helpers import (
9
+ init_repository,
10
+ write_commit_and_stage_change,
11
+ )
8
12
 
9
- def _clean_validate(_content: str, _path: str, _prior: str = "", **_kwargs: object) -> list[str]:
13
+
14
+ def _clean_validate(
15
+ _content: str, _path: str, _prior: str = "", **_kwargs: object
16
+ ) -> list[str]:
10
17
  return []
11
18
 
12
19
 
13
- def _dirty_validate(_content: str, _path: str, _prior: str = "", **_kwargs: object) -> list[str]:
20
+ def _dirty_validate(
21
+ _content: str, _path: str, _prior: str = "", **_kwargs: object
22
+ ) -> list[str]:
14
23
  return ["Line 1: bad"]
15
24
 
16
25
 
@@ -92,8 +101,42 @@ def test_print_violation_section_groups_by_relative_path(
92
101
  ) -> None:
93
102
  module_path = (tmp_path / "module.py").resolve()
94
103
 
95
- gate_running.print_violation_section("HEADER", {module_path: ["Line 1: issue"]}, tmp_path)
104
+ gate_running.print_violation_section(
105
+ "HEADER", {module_path: ["Line 1: issue"]}, tmp_path
106
+ )
96
107
 
97
108
  captured = capsys.readouterr()
98
109
  assert "HEADER" in captured.err
99
110
  assert "Line 1: issue" in captured.err
111
+
112
+
113
+ def test_gate_caller_scans_staged_content_with_commit_stage_scope(
114
+ tmp_path: Path,
115
+ capsys: pytest.CaptureFixture[str],
116
+ ) -> None:
117
+ repository_root = tmp_path / "repository"
118
+ repository_root.mkdir()
119
+ init_repository(repository_root)
120
+ committed_source = "def calculate_total() -> int:\n return 0\n"
121
+ staged_source = (
122
+ "def calculate_total() -> int:\n total = 9999\n return total\n"
123
+ )
124
+ file_path = write_commit_and_stage_change(
125
+ repository_root,
126
+ "service.py",
127
+ committed_source,
128
+ staged_source,
129
+ )
130
+
131
+ resolved_file_path = file_path.resolve()
132
+ exit_code = gate_running.run_gate(
133
+ validate_content=enforcer_loading.load_validate_content(),
134
+ all_file_paths=[resolved_file_path],
135
+ repository_root=repository_root,
136
+ all_added_lines_by_path={resolved_file_path: {2, 3}},
137
+ should_read_staged_content=True,
138
+ )
139
+
140
+ captured = capsys.readouterr()
141
+ assert exit_code == 1
142
+ assert "9999" in captured.err
@@ -97,6 +97,10 @@ CODE_RULES_GATE_PYTHON_ENV_VAR: str = "CODE_RULES_GATE_PYTHON"
97
97
 
98
98
  CODE_RULES_GATE_PYTHONPATH_ENV_VAR: str = "CODE_RULES_GATE_PYTHONPATH"
99
99
 
100
+ IMMEDIATE_SCOPE_ARGUMENT: str = "--immediate"
101
+
102
+ IMMEDIATE_SCOPE_HELP: str = "Validate staged rules without running staged tests."
103
+
100
104
  PYTHONPATH_ENV_VAR: str = "PYTHONPATH"
101
105
 
102
106
  ALL_VENV_DIRECTORY_NAMES: tuple[str, ...] = (".venv", "venv")