claude-dev-env 8.36.3 → 8.37.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 (30) hide show
  1. package/docs/codex-compatibility.md +1 -1
  2. package/docs/rule-guides/bdd.md +28 -0
  3. package/docs/rule-guides/code-standards.md +38 -0
  4. package/docs/rule-guides/correction-lens-excerpt.md +35 -0
  5. package/docs/rule-guides/doc-inventory-integrity.md +39 -0
  6. package/docs/rule-guides/docstring-prose-matches-implementation.md +49 -0
  7. package/docs/rule-guides/failure-blast-radius.md +123 -0
  8. package/docs/rule-guides/orphan-css-class.md +25 -0
  9. package/docs/rule-guides/paired-test-coverage.md +39 -0
  10. package/docs/rule-guides/plain-illustrative-docstrings.md +88 -0
  11. package/docs/rule-guides/windows-filesystem-safe.md +11 -0
  12. package/hooks/hooks.json +10 -0
  13. package/hooks/hooks_constants/spawn_readiness_hook_constants.py +111 -0
  14. package/hooks/routing/spawn_readiness_hook.py +195 -0
  15. package/hooks/routing/spawn_readiness_steps.py +266 -0
  16. package/hooks/routing/test_spawn_readiness_hook.py +278 -0
  17. package/hooks/routing/test_spawn_readiness_steps.py +87 -0
  18. package/package.json +1 -1
  19. package/rules/bdd.md +2 -23
  20. package/rules/code-standards.md +3 -32
  21. package/rules/correction-lens.md +2 -30
  22. package/rules/doc-inventory-integrity.md +6 -32
  23. package/rules/docstring-prose-matches-implementation.md +4 -42
  24. package/rules/failure-blast-radius.md +4 -114
  25. package/rules/orphan-css-class.md +4 -18
  26. package/rules/paired-test-coverage.md +4 -32
  27. package/rules/plain-illustrative-docstrings.md +4 -81
  28. package/rules/windows-filesystem-safe.md +3 -5
  29. package/scripts/codex_compat_materializer.py +4 -1
  30. package/scripts/tests/test_codex_compat_materializer.py +18 -18
@@ -6,7 +6,7 @@
6
6
 
7
7
  Run `codex-compat materialize --source-root <claude-root> --target-root <codex-root>`. The command defaults to a dry run; add `--apply` to publish files. Use `--python <command>` or `CODEX_COMPAT_PYTHON` to select Python. If no usable interpreter is found, the command reports that condition. The launcher passes an argv array, never a shell command.
8
8
 
9
- The Python materializer maps Claude `_shared/`, `agents/`, `hooks/`, `rules/`, and `scripts/` into the target according to the package's compatibility materialization rules. Claude agent frontmatter is converted to Codex TOML metadata. The canonical failure blast-radius rule projects its repository-instruction excerpt into a managed `AGENTS.md` file. Claude metadata reports its supported-field shape.
9
+ The Python materializer maps Claude `_shared/`, `agents/`, `hooks/`, `rules/`, and `scripts/` into the target according to the package's compatibility materialization rules. Claude agent frontmatter is converted to Codex TOML metadata. The repository-instruction excerpts in `docs/rule-guides/failure-blast-radius.md` and `docs/rule-guides/correction-lens-excerpt.md` project into a managed `AGENTS.md` file. Claude metadata reports its supported-field shape.
10
10
 
11
11
  The Codex hook projection merges a managed `apply_patch` entry for `code_rules_enforcer.py` into the target `hooks.json`. Existing Codex hook entries keep their order, repeated enforcer entries collapse to one deterministic record, and the command resolves under the target root. The enforcer reads the patch command, reconstructs every file's pre-edit and projected post-edit content, and returns a blocking diagnostic for patch shapes requiring correction or code-rule violations. The existing Claude `Write`, `Edit`, and `MultiEdit` dispatcher keeps its current order and behavior.
12
12
 
@@ -0,0 +1,28 @@
1
+ # BDD (discovery-driven development)
2
+
3
+ Full text behind [`rules/bdd.md`](../../rules/bdd.md), which loads in sessions as the short form.
4
+
5
+ **Canonical detail:** `~/.claude/system-prompts/software-engineer.xml` → `<behavior_protocol>`.
6
+
7
+ **Optional long-form references (load when needed):**
8
+
9
+ - `@~/.claude/docs/BDD_SCENARIO_QUALITY.md` — seven scenario quality patterns (§7.6-style)
10
+ - `@~/.claude/docs/BDD_DISCOVERY_PROTOCOL.md` — Example Mapping algorithm for chat
11
+ - `@~/.claude/docs/BDD_TEST_LAYOUT.md` — describe/when/should layout and soap-opera personas
12
+
13
+ ## What you do for every non-trivial feature
14
+
15
+ 1. **Deliberate Discovery** — Reduce uncertainty before code; surface what you do not know (Smart & Molak §5.4).
16
+ 2. **Illustrate** — Explore goals, constraints, and concrete examples in chat; "given … when … then …" style outcomes.
17
+ 3. **Formulate** — Express behavior as narrow **"should …"** specifications the user can approve.
18
+ 4. **Automate** — Build each formulated behavior with tests. Red-green-refactor is the default loop, and the TDD skill (`pstack:tdd`) carries it: CODE_RULES §8, as stated in [`code-standards.md`](../../rules/code-standards.md). A prototype may run ahead of its tests and adds them before the pull request goes ready.
19
+
20
+ Conversation is the essential practice: if discovery is skipped, structured formats do not rescue the workflow (Minimal BDD).
21
+
22
+ ## Solo developer
23
+
24
+ You are often the stakeholder. Use **Example Mapping** in chat ("the one where …", probes, parking lot). See the optional long-form references above for the full algorithm and anti-pattern list.
25
+
26
+ ## Naming
27
+
28
+ Developer-facing specs and tests use **should** sentences so intent stays visible (Dan North, "Introducing BDD", 2006).
@@ -0,0 +1,38 @@
1
+ # Code Standards
2
+
3
+ Full text behind [`rules/code-standards.md`](../../rules/code-standards.md), which loads in sessions as the short form.
4
+
5
+ > **Canonical review contract:** [`CODE_RULES.md`](../CODE_RULES.md) — the human and AI review contract for code quality, loaded on demand.
6
+ > **Checked-in pointer:** [`.cursor/BUGBOT.md`](../../../../.cursor/BUGBOT.md) — the file Cursor BugBot reads; it points at `CODE_RULES.md`.
7
+ > **Production enforcement.** The staged policy lint runs `hooks/blocking/code_rules_enforcer.py` over each changed file. No write-time hook runs it. CI runs that lint against the merge base. Each mechanical rule carries a synchronization test.
8
+
9
+ ## Policy surface map
10
+
11
+ | Layer | Path | Role |
12
+ |---|---|---|
13
+ | Contract | `docs/CODE_RULES.md` | Full review criteria for PR agents, loaded on demand |
14
+ | Pointer | `.cursor/BUGBOT.md` | Checked-in file Cursor BugBot reads; points at `CODE_RULES.md` |
15
+ | Enforcer | `hooks/blocking/code_rules_enforcer.py` | Hand-maintained checks the staged policy lint runs; not generated from the docs |
16
+ | Lint | `scripts/cde_lint.py` | Runs the enforcer and the other policy rules over staged or changed files, grading each against the file's prior text; see [`ci-owns-the-gate.md`](../../rules/ci-owns-the-gate.md) for what each selection flag reports |
17
+ | Session rules | `rules/*.md` | Runtime session policy (questions, tasks, shell) |
18
+
19
+ Load `CODE_RULES.md` when reviewing a PR, resolving a policy conflict, or generating code. Prefer linking this ref over restating rules.
20
+
21
+ Two standards live in `CODE_RULES.md` in full:
22
+
23
+ - **TDD** — CODE_RULES §8: red, green, refactor is the default loop for a bug fix and for new behavior, and the TDD skill (`pstack:tdd`) carries the procedure. A prototype may run ahead of its tests and adds them before the pull request goes ready. A bug fix ships with a test that reproduces the bug.
24
+ - **Right-sized engineering** — CODE_RULES §7 / AGENTS Design: functions over classes; concrete over abstract; add an abstraction at the commit that introduces its second concrete implementation. That count is a house call, one occurrence earlier than the rule of three Fowler credits to Don Roberts. The direction comes from the literature; the number does not, so read it as this package's setting.
25
+
26
+ BDD is the outer process and TDD is the inner loop: [`bdd.md`](../../rules/bdd.md) discovers and formulates the behavior a feature needs, then each formulated behavior is built through the TDD cycle.
27
+
28
+ ## Session policies (ref docs)
29
+
30
+ | Concern | Rule file |
31
+ |---|---|
32
+ | Handling a correction from the user | [`correction-lens.md`](../../rules/correction-lens.md) |
33
+ | Task tracking / worker completion | [`workers-done-before-complete.md`](../../rules/workers-done-before-complete.md) |
34
+ | Multi-step task list | skill `task-build` (see agents catalog) |
35
+
36
+ ## Validation
37
+
38
+ Mechanical enforcer coverage is checked by the existing `hooks/blocking/test_code_rules_enforcer*.py` suite.
@@ -0,0 +1,35 @@
1
+ # Correction lens excerpt
2
+
3
+ This file carries the Codex copy of [`rules/correction-lens.md`](../../rules/correction-lens.md). The Codex compatibility materializer projects the fenced block below into a managed `AGENTS.md`.
4
+
5
+ ## Excerpt for repository-instruction sessions
6
+
7
+ Codex reads its repository `AGENTS.md`; this excerpt supplies the standalone
8
+ contract.
9
+
10
+ ```
11
+ Correction handling for this run, from rules/correction-lens.md.
12
+
13
+ Every correction the user gives becomes a control. Run it through five layers
14
+ and land it at the highest one that can hold it:
15
+
16
+ 1. Codebase. The mistake is impossible by how the code is written: a type, a
17
+ signature, a data structure, an API shape.
18
+ 2. Static analysis. A program reads the tree and decides: a lint rule, an
19
+ enforcer check, a paired test, a CI gate.
20
+ 3. Review tooling. A reviewer or a review bot reads the criterion: a code-rules
21
+ row, a BUGBOT pointer, a Graphite rule.
22
+ 4. Skill. An agent follows a procedure.
23
+ 5. Style guide. Word choice and prose shape.
24
+
25
+ Close the correction with three sentences: the layer chosen, why each higher
26
+ layer cannot hold the lesson, and the change opened at that layer in this run.
27
+ Effort rules out no layer; a missing capability does, and you name which one.
28
+
29
+ The same correction arriving twice means the layer was too low. Move it up one
30
+ layer and say so.
31
+
32
+ Land the control in the repository whose code, CI, or pipeline it guards. The
33
+ shared environment package takes only repo-agnostic controls that any
34
+ repository could use. A memory file records the decision and encodes nothing.
35
+ ```
@@ -0,0 +1,39 @@
1
+ # Documentation Inventory Integrity
2
+
3
+ Full text behind [`rules/doc-inventory-integrity.md`](../../rules/doc-inventory-integrity.md), which loads in sessions as the short form.
4
+
5
+ A doc that inventories code is a contract: a reader trusts the listing to map the directory, trusts a shown command to run, and trusts a table row to name the file that reads the variable. Three repository checks hold the three inventory shapes in step with the code. No write-time hook runs them. Run `python packages/claude-dev-env/scripts/repository_policy.py` before you commit, and CI runs the same command.
6
+
7
+ ## 1. A per-directory `CLAUDE.md` names files that exist
8
+
9
+ Every bare filename a per-directory `CLAUDE.md` names points at a file in the subtree that `CLAUDE.md` describes — both the filenames its table cells list and the scripts its fenced run commands invoke (`python script.py`). Add the row and the run command in the change that adds the file; drop both in the change that removes it.
10
+
11
+ `repository_checks/claude_md.py` scans every tracked `CLAUDE.md` in the committed tree and reports each orphan it holds, loading its detection logic from `claude_md_orphan_file_blocker.py`. It reads committed files, so it reports a pre-existing orphan alongside a new one.
12
+
13
+ It collects two kinds of reference:
14
+
15
+ - **Table cells** — the first column of each markdown table row **outside** a fenced code block, keeping cells that name a bare filename in backticks that holds no path separator, is no slash-command, and ends in a known extension (`.py`, `.md`, `.json`, `.mjs`, `.js`, `.ts`, `.ps1`, `.cmd`, `.ahk`, `.yml`, `.yaml`, `.sh`, `.txt`, `.cfg`, `.toml`, `.ini`).
16
+ - **Run commands** — each line **inside** a fenced code block that invokes an interpreter (`python`, `python.exe`, `python3`, `node`, `pwsh`, `powershell`, `bash`, `sh`, `ruby`, `perl`) on a script, taking that script's basename when it ends in `.py`, `.mjs`, `.js`, `.ts`, `.ps1`, `.sh`, `.rb`, or `.pl`.
17
+
18
+ A fenced *table row* is an example and contributes nothing; a fenced *run command* is a contract the reader runs and is checked. The write is blocked when a collected filename exists nowhere under the scan root — the `CLAUDE.md` directory's parent, covering the directory, its subdirectories, and its siblings. A filesystem error that halts the subtree walk fails open.
19
+
20
+ The check stays quiet for a target that is not a `CLAUDE.md`, for a cell holding a path, a subdirectory ending in `/`, or a slash-command, for a table row inside a fence, for an inline `python x.py` mention outside a fence, and for a table naming an explicit relative-path source (a `../` token), which documents files outside the subtree by design.
21
+
22
+ ## 2. A package inventory names each new production file
23
+
24
+ A package directory that documents its own files in a `README.md` Layout table, a `CLAUDE.md` "Key files" list, or a skill `SKILL.md` Layout table keeps that inventory in step with the directory. A new production file in such a directory gets its entry — a table row or a list bullet naming the file in backticks and saying what it does — in the same change.
25
+
26
+ `repository_checks/package_inventory.py` scans the committed tree and reports a production file whose basename appears in no present inventory, loading its detection logic from `package_inventory_stale_blocker.py`. It names the fix. A skill `SKILL.md` Layout table that maps `scripts/` counts as the inventory for files in that subdirectory.
27
+
28
+ Two free-prose slices stay with judgment and belong in the same change:
29
+
30
+ 1. **Purpose / scope sentence.** When the new module adds a responsibility the package `## Purpose` (or the parent inventory's one-line summary of the subdirectory) omits, broaden that sentence to name it. A hook cannot derive a module's responsibility from its filename.
31
+ 2. **Per-file description clause.** When a file gains a responsibility the inventory's em-dash description omits — a new public function, a new module-level constant — broaden the clause to name it. The gate checks only that the basename appears once and never reads the description. Constants modules (`*_constants.py`, or any `.py` directly inside `config/`) are the common shape: the clause that lands in the module docstring lands in the inventory description in the same change. The gate fires on Write of a new file and skips files directly inside `config/`, so an Edit adding a constant to an existing config module matches neither path.
32
+
33
+ This is the `category-o-docstring-vs-impl-drift` (O8) orphaned-doc-claim shape applied to a package inventory.
34
+
35
+ ## 3. An env-var table row names a file that reads the variable
36
+
37
+ Every row in an env-var summary table pairs an UPPER_SNAKE variable with a code-file path that reads it — written as `` | `GOOGLE_APPLICATION_CREDENTIALS` | `auth/google_auth.py` | … | ``. When a code change removes the last read of a variable from a file, the same change drops or corrects the row naming that file.
38
+
39
+ `repository_checks/env_var_documentation.py` scans tracked `.md` files and reports a row whose named code file exists yet never references the variable, loading its detection logic from `env_var_table_code_drift_blocker.py`. It names the fix. A row whose code file resolves nowhere stays quiet, since the check cannot prove the drift.
@@ -0,0 +1,49 @@
1
+ # Docstring Prose Matches Implementation
2
+
3
+ Full text behind [`rules/docstring-prose-matches-implementation.md`](../../rules/docstring-prose-matches-implementation.md), which loads in sessions as the short form.
4
+
5
+ **When this applies:** Any Write or Edit to a public function, method, class, or module whose docstring prose makes an enumerable claim about behavior — a list of inputs the code handles, the conditions it treats as a match, the cases it skips, or the order of its steps. It applies equally to a skill's companion `SKILL.md` (or any sibling `.md`) that describes a producer the skill's `scripts/` carry out: a doc sentence that claims a produced artifact's ordering or content is the prose this rule governs, and it tracks the producer function's own docstring and body.
6
+
7
+ ## Rule
8
+
9
+ When a docstring enumerates the behaviors a body applies, the enumeration covers every behavior the body applies. A reader trusts the list to be complete: an item the code applies but the prose omits is a silent gap that misleads every future reader and reviewer.
10
+
11
+ When the body changes the set of behaviors it applies, the same edit updates the prose enumeration. The two move together in one commit.
12
+
13
+ ## Write-time checks
14
+
15
+ Read the body and the docstring side by side. Apply each check that matches the prose:
16
+
17
+ - **Unions / match sources** — every member of a "what counts" union appears in the prose.
18
+ - **Suppressors / skip lists** — every early-return suppressor appears in the prose.
19
+ - **Step order** — named order matches call order; branch-guarded corrective steps are named too.
20
+ - **Shared fallbacks** — every condition that reaches a fallback call is named.
21
+ - **Predicate breadth** — the body accepts only the inputs the prose names.
22
+ - **Exclusion axis** — an exclusion clause keys on the same axis the body classifies on.
23
+ - **Companion docs** — a `SKILL.md` (or sibling) order/content claim matches the producer body.
24
+ - **Gate-outcome status flags** — an outcome routed to a blocker (`blocker = ...; break`) reads as blocked everywhere, never as a bypass.
25
+ - **Returns / Raises / Note claims** — each free-form claim matches the body.
26
+
27
+ Many deterministic shapes of this drift are checked in `code_rules_docstrings.py` (and the JS and `.mjs` slices in `code_rules_imports_logging.py`). The staged policy lint reaches both through `code_rules_enforcer.py`, and CI runs it against the merge base. Free-form rest is judgment.
28
+
29
+ ## Hook prose matches its detector
30
+
31
+ A hook module is the sharpest case of the same rule: its docstring lead narrative and its `CORRECTIVE_MESSAGE` describe exactly the shapes the detector flags, and claim no broader trigger surface than the regex enforces.
32
+
33
+ The staged policy lint carries this as its `hook-prose-consistency` rule, covering hook modules and their `*_constants.py` companions. It reports prose that claims a trigger the detector never fires on, and names the fix. No write-time hook runs it, so CI is where it reports.
34
+
35
+ After writing a hook, ask: would a token matching every word of this message trip the detector? When the message names a shape the regex skips, rewrite the message to name only what the regex catches. The path-shape case is the common overstatement — a detector that keys off a path separator must not claim it blocks an "output-key segment". The corrective message spells the rewrite.
36
+
37
+ ## Full standard
38
+
39
+ The full Category O judgment standard — sub-buckets O1–O9, the complete write-time gate inventory, free-form checklists, and worked examples — lives in:
40
+
41
+ `~/.claude/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md`
42
+
43
+ ## Division of labor
44
+
45
+ | Surface | Role |
46
+ |---|---|
47
+ | **This rule** | Always-on write-time policy and the compact checklist above. |
48
+ | Category O rubric | Single thick source for the full standard (on demand). |
49
+ | Category O prompt | Audit template; points at the rubric for judgment. |
@@ -0,0 +1,123 @@
1
+ # Failure Blast Radius
2
+
3
+ Full text behind [`rules/failure-blast-radius.md`](../../rules/failure-blast-radius.md), which loads in sessions as the short form.
4
+
5
+ **When this applies:** Batch code that processes assets, rows, accounts, messages, or files where one member can fail while the others are fine.
6
+
7
+ ## Rule
8
+
9
+ A check that raises decides two things at once: that a condition requires action, and what stops because of it. Name the second one.
10
+
11
+ An exception type ending in `RunFatal` says the whole run stops. An exception type ending in `ItemBlocked` says this one member stops and the batch carries on. Every raise reached through per-member work uses one or the other.
12
+
13
+ Define `RunFatal` outside the `ItemBlocked` inheritance branch. The per-member boundary routes `ItemBlocked` to parking, while a `RunFatal` escalation passes straight through to the run-level branch.
14
+
15
+ ## What ends a run
16
+
17
+ Four failures end a run, and they share one property: continuing compromises delivery integrity.
18
+
19
+ Three of them are declared, and carry a `RunFatal` type:
20
+
21
+ - The source bytes changed under the run.
22
+ - A provenance or digest comparison failed.
23
+ - Authentication is required.
24
+
25
+ The fourth is a runtime crash, such as `TypeError` or `AttributeError`. Its runtime type reaches run-level handling because the per-member boundary recognizes the contract's named types.
26
+
27
+ All remaining failures are member failures. A size mismatch, an optional input error, a path resolution error, or a manifest-field error each stops one member while the batch continues with its other members.
28
+
29
+ ## The boundary
30
+
31
+ Put the `try`/`except` inside the loop body, around the per-member work:
32
+
33
+ ```python
34
+ for each_member in all_members:
35
+ try:
36
+ remaster(each_member)
37
+ except AssetRunFatal:
38
+ raise
39
+ except AssetItemBlocked as failure:
40
+ park(each_member, failure)
41
+ ```
42
+
43
+ The re-raise comes first, sending an escalation directly through the boundary.
44
+
45
+ **The boundary recognizes declared types.** A runtime crash inside member work — a `TypeError`, an `AttributeError` — ends the run because its type identifies a code defect. `except Exception` triggers the rule (`CODE_RULES.md` §31).
46
+
47
+ ## Repair, park, and the deliverable
48
+
49
+ An agent that hits a member failure keeps working the problem. Three things bound how:
50
+
51
+ - **Repair in place.** The current run preserves every completed member.
52
+ - **Three attempts, then park.** An attempt is a theory of the cause, acted on. Re-running the same code on the same input counts as one attempt. Three theories cover the obvious cause, the second guess, and the cause revealed by the first two attempts. Judgment selects each theory; the third attempt sets the stopping point. After the third theory fails, park the member with its reason and move to the next. Parked members return after the batch.
53
+ - **The batch always reaches a deliverable.** Complete every member that can complete, produce the packaged artifact, then work the parked list. A run with 34 of 37 members complete and 3 parked records progress and continues to delivery.
54
+
55
+ ## Three alike means one cause
56
+
57
+ When three or more members park with the same failure signature — same exception type, same `file:line` — one shared defect affects all three members. Route repair to the shared cause.
58
+
59
+ The run report groups parked members by that signature and names every group of three or more as a suspected shared cause. The raise site provides a reliable grouping key and removes message-text normalization.
60
+
61
+ ## Close the run with every outcome
62
+
63
+ Every issue the run hit gets one line in the closing report: what failed, and how it ended — repaired, worked around, or parked. Members that finished after a repair belong in that list beside the parked ones. A workaround patched past mid-run is the likeliest defect in the batch, because the closing report becomes its durable record.
64
+
65
+ The report then presents each candidate to the owner for a durable-fix decision and names the fix the run recommends for each one. The report is a statement. The current run completes its deliverable and ends. A later run builds whichever fixes the owner selects, and the recommendation stands as the default for any the owner does not rule on.
66
+
67
+ ## Enforcement
68
+
69
+ `code_rules_blast_radius.py` runs inside `code_rules_enforcer.py`, which the staged policy lint applies to each changed file under its `code-rules` rule. No write-time hook runs it, so CI is where it reports. CI runs the same lint against the merge base.
70
+
71
+ The check requires each raised type written directly inside a loop body to end in `RunFatal` or `ItemBlocked`. The lexical check covers raises written directly in loop bodies. Shared helpers carry multiple caller contexts, so their callers classify the boundary.
72
+
73
+ Findings compare each changed file against its baseline. A raise already present in the baseline stays accepted, so the check reports only newly written raises.
74
+
75
+ ## Excerpt for repository-instruction sessions
76
+
77
+ Codex reads its repository `AGENTS.md`; this excerpt supplies the standalone failure-handling contract.
78
+
79
+ ```
80
+ Failure handling for this run — from rules/failure-blast-radius.md.
81
+
82
+ Keep solving problems. You own the fix. Each blast radius sets the repair
83
+ scope, and the deliverable remains the run priority.
84
+
85
+ Repair in place and preserve every completed asset in the current run.
86
+
87
+ Three attempts, then park. An attempt is a theory of the cause, acted
88
+ on. Repeated execution of one theory remains one attempt.
89
+ Three theories cover the obvious cause, the second guess, and the cause
90
+ revealed by the first two attempts. Use your judgment to select each theory;
91
+ the third attempt sets the stopping point. After the third theory fails, park
92
+ the asset with its reason and continue. Parked assets return after the batch.
93
+
94
+ The batch always reaches a deliverable. Finish every asset you can, produce
95
+ the packaged artifact, then work the parked list.
96
+
97
+ When three or more assets fail the same way, one shared defect affects all
98
+ three assets. Route repair to the shared cause.
99
+
100
+ Four things end a run outright: the source bytes changed, a provenance or
101
+ digest mismatch, authentication is required, or the code crashed with a
102
+ runtime failure that requires run-level handling. Work every other failure. The
103
+ named-type boundary defines the accepted exception handling; broad `except`
104
+ handling triggers the rule.
105
+
106
+ When you add a check that raises, name what it stops. End the type in
107
+ RunFatal when the whole run stops, or ItemBlocked when a single asset
108
+ stops. For an asset-level stop, put the handling inside the loop body.
109
+
110
+ Close the run by reporting what broke and what you did about it. Every issue
111
+ gets one line: what failed, and how it ended — repaired, worked around, or
112
+ parked. Include the ones you solved; a workaround you patched past in attempt
113
+ two is the likeliest defect in the list, because the closing report becomes
114
+ its durable record.
115
+ Present each candidate for the owner's durable-fix decision and state which
116
+ fix you recommend for each and why. Do not wait on the answer. Complete this
117
+ run's deliverable and end. A later run builds whichever fixes the owner
118
+ selects, and your recommendation stands as the default for the rest.
119
+
120
+ Report as: N of M complete, K parked, and what you are working now.
121
+ Close with: what broke, how each one ended, and which of them deserve a
122
+ durable fix.
123
+ ```
@@ -0,0 +1,25 @@
1
+ # Orphan CSS Class in Generated Markup
2
+
3
+ Full text behind [`rules/orphan-css-class.md`](../../rules/orphan-css-class.md), which loads in sessions as the short form.
4
+
5
+ **When this applies:** Any Write or Edit to a production `.py` file that builds HTML by emitting `class="..."` attributes inside string literals and pairs them with a `<style>` block — in the same file or in a companion module beside it.
6
+
7
+ ## Rule
8
+
9
+ Every class name a markup string references has a matching `.<class>` selector in the `<style>` block. A class that appears in the markup but carries no selector anywhere is a dead attribute (or a missing rule): the markup names a style that the stylesheet never defines, so a reader who trusts the class to be styled is misled, and the attribute adds noise without effect.
10
+
11
+ When you add a `class="..."` attribute, add its `.<class>` selector to the `<style>` block in the same change. When you drop a selector, drop the class attribute it styled.
12
+
13
+ ## What the check covers
14
+
15
+ The `check_orphan_css_classes` check in `code_rules_orphan_css_class.py` reaches production Python through the staged policy lint, whose `code-rules` rule loads `code_rules_enforcer.py` and applies it to each changed file. CI runs that lint against the merge base. It:
16
+
17
+ 1. Collects each class name referenced in a `class="..."` attribute across the file's string literals.
18
+ 2. Collects each class selector defined in a `<style>` block — both in the file under edit and in every Python module beside it (its own directory and immediate child directories), since a markup module commonly imports its style constant from a companion package directory.
19
+ 3. Flags each referenced class with no matching selector in that whole set.
20
+
21
+ The check stays quiet for a file that emits no `class="..."` markup, and for a file whose markup has no `<style>` source nearby (its stylesheet lives outside the scan, so the check cannot judge it). Test files are exempt, since a fixture may carry intentional orphan markup.
22
+
23
+ ## Why this check is mechanical
24
+
25
+ A class attribute with no matching selector reads as styled but renders unstyled. Native elements such as `<details>` stay functional without CSS, so the gap survives review as a cosmetic defect. That is the class of issue a manual pass slips past, and it lands later as a deferred code-standard finding. Running the lint on every staged change keeps the markup and the stylesheet in step.
@@ -0,0 +1,39 @@
1
+ # Public-Function Paired-Test Coverage
2
+
3
+ Full text behind [`rules/paired-test-coverage.md`](../../rules/paired-test-coverage.md), which loads in sessions as the short form.
4
+
5
+ **When this applies:** Either side of a paired module/test pair, so the check fires whichever file the write touches:
6
+
7
+ - A Write or Edit to a production Python module that already has a dedicated stem-matched test file — `test_<stem>.py` beside the module or under an ancestor `tests/` directory — whose paired suite already exercises the module, either by covering at least one of its public functions or by referencing one of its private helpers by name.
8
+ - A Write or Edit to a stem-matched test file (`test_<stem>.py` or `<stem>_test.py`) whose paired production module exists on disk and whose post-edit suite already exercises at least one of that module's public functions.
9
+
10
+ ## Rule
11
+
12
+ Every public function a production module defines is exercised by a test in the module's paired test suite. The suite proves the module is unit-tested function by function, so a public entry point the suite omits is a forgotten test: a reader who trusts the suite to cover the module's public surface misses that gap.
13
+
14
+ When you add a public function to a module whose test suite already exercises that module — covering a sibling public function, or testing one of its private helpers — add a behavioral test that calls the new function and asserts on its return value or side effect — in the same change that adds the function. A suite that exercises only a private helper (such as a color-conversion helper) while leaving the module's public renderers untested is the exact gap this rule closes. This is the function-level half of the project rule "Every new production code path gets a paired behavioral test ... call the path and assert on what it does."
15
+
16
+ ## What the check covers
17
+
18
+ Two complementary checks in `code_rules_paired_test.py` reach changed files through `code_rules_enforcer.py`, which the staged policy lint runs under its `code-rules` rule. No write-time hook runs them, so CI is where they report, against the merge base. The two checks cover the two write orders.
19
+
20
+ Both checks record smells, per [`flag-non-breaking-findings.md`](../../rules/flag-non-breaking-findings.md). `SEVERITY_BY_CHECK_ID` in `scripts/policy_lint/config/check_catalog_constants.py` declares `code-rules/paired-test-missing-function` and `code-rules/paired-test-omitted-function` as smells. The lint prints each finding as a warning, records it in `.claude/followups/smells.jsonl`, and exits zero when every finding is a warning. A later pull request adds the missing tests.
21
+
22
+ `check_public_function_missing_paired_test` runs on a production Python write or edit and flags a public function when all of these hold:
23
+
24
+ 1. The target is production code — not a test module, hook infrastructure, config module, migration, workflow registry, or `__init__.py`.
25
+ 2. A stem-matched test file exists for the module — `test_<stem>.py` or `<stem>_test.py` beside the module, or `test_<stem>.py` under an ancestor `tests/` directory.
26
+ 3. That suite already exercises the module — referencing at least one public function the module defines, or referencing one of its private (underscore-prefixed) helper functions by name — the signature of a maintained per-module suite.
27
+ 4. The public function is referenced by no test file in the directory that holds the stem-matched test.
28
+
29
+ `check_test_file_omits_module_public_function` runs on a stem-matched test-file write or edit and closes the reverse order, in which the production module is written before its test file exists. It resolves the production module the written `test_<stem>.py` or `<stem>_test.py` file pairs with — beside the test file, or in the parent of the `tests/` directory that holds it — reads that module from disk, and flags every public function the post-edit suite references nowhere, subject to the same established-suite precondition (the suite already covers at least one of the module's public functions). A production module that is itself exempt — a test module, hook infrastructure, config module, migration, workflow registry, or `__init__.py` — is skipped.
30
+
31
+ A public function counts as covered when its name appears — imported, called, or named — in any `test_*.py` or `*_test.py` file in the suite directory, so a function exercised by a differently-named sibling test still counts. `main` and underscore-prefixed functions are never required to carry a test.
32
+
33
+ ## Relationship to the file-level TDD order
34
+
35
+ No hook or lint checks the order in which a test and its module are written. Red-green-refactor is the default loop, and the TDD skill (`pstack:tdd`) carries it; a prototype may run ahead of its tests and adds them before the pull request goes ready. This check judges coverage one function at a time for a module that already carries a stem-matched test file, and reports any public function that file leaves uncovered.
36
+
37
+ ## Why this check is mechanical
38
+
39
+ A public function with no test reads as covered when the module's test file sits right beside it and exercises its siblings. The gap survives review because the suite looks complete. Running the lint on every staged change records each gap in the follow-up ledger, where `cde followup list` names it until a pull request closes it.
@@ -0,0 +1,88 @@
1
+ # Plain, Illustrative Docstrings
2
+
3
+ Full text behind [`rules/plain-illustrative-docstrings.md`](../../rules/plain-illustrative-docstrings.md), which loads in sessions as the short form.
4
+
5
+ **When this applies:** Any Write or Edit to a public function, method, class, or module docstring whose narrative prose — the summary and description before the first `Args:` / `Returns:` / `Raises:` / `Yields:` section — says what the code is for or how it behaves. The standard governs the prose a reader meets first. The structured `Args:` / `Returns:` entries below it sit outside it.
6
+
7
+ ## Rule
8
+
9
+ A docstring's narrative reads plainly enough that a general developer follows it on the first read. Two things make that true:
10
+
11
+ - **Illustrative.** The prose paints a concrete scene — the reader pictures the moment the code matters, the input it sees, the outcome it produces. A reader who finishes the narrative can say what breaks without it.
12
+ - **Brief.** The narrative makes its point in few words. Short sentences, each carrying one idea.
13
+
14
+ Two shapes break the standard. Hold the prose clear of both:
15
+
16
+ - **No machinery nouns stacked into a wall.** A sentence that chains abstract machinery terms (`the SIGINT install/restore/installability check, the atexit terminal-record registration, and the interrupted-run finalizer`) names parts without painting a scene. Name what the reader sees and why it matters.
17
+ - **No defining by negation.** Prose that explains a thing by what it is not (`the non-promoter-specific machinery`) leaves the reader without a picture. Say what the thing is.
18
+
19
+ ## Shape: a summary line, then a diagram
20
+
21
+ The clearest way to be illustrative is to show a worked example. A docstring that lands well reads in four parts:
22
+
23
+ 1. **One summary line** that says what the code does.
24
+ 2. **A diagram block** that carries the explanation by sight — a reStructuredText literal block (a line ending in `::`, then an indented example) or a doctest (`>>>`). Give the concrete input, mark the outcome, and add `ok:` / `flag:` contrast lines where a pass-and-fail pair makes the point. Keep the diagram clear of a bare number or an ALL-CAPS `NAME = value` line, which read as a magic value or a stray constant to a line-based lint pass.
25
+ 3. **A couple of short narrative lines** after the block — two or three at most.
26
+ 4. **The Google `Args:` / `Returns:` sections.**
27
+
28
+ Keep the wording neutral in the diagram and the prose: two names "contradict" or "clash", two names "agree". Skip words that pass judgment.
29
+
30
+ Canonical example:
31
+
32
+ ```
33
+ Flag a boolean assignment whose target and callee assert opposite polarity.
34
+
35
+ ::
36
+
37
+ is_inside_allowed = _point_hits_any_forbidden(...)
38
+ ^^^^^^^ ^^^^^^^^^
39
+ allowed vs. forbidden ⚠ the two names clash
40
+ ok: is_inside_allowed = _point_inside_allowed_region(...)
41
+ flag: is_inside_allowed = _point_hits_any_forbidden(...)
42
+
43
+ The target token and the callee token contradict each other, so the reader
44
+ cannot tell which name states the truth. Rename the callee to a neutral form
45
+ the two names agree on at every call site.
46
+ ```
47
+
48
+ The live version of this docstring sits on `check_polarity_name_contradiction` in `~/.claude/hooks/blocking/code_rules_naming_collection.py`.
49
+
50
+ A short narrative with no diagram is fine when a couple of plain sentences carry the whole picture. The diagram earns its place once the explanation grows past what two or three lines hold — the moment a wall of prose starts to form.
51
+
52
+ ## What to check before you write the docstring
53
+
54
+ Read the narrative back as a stranger would:
55
+
56
+ - Does one sentence run long while joining clauses with an em-dash or a semicolon? That is the wall mark — break it into short sentences.
57
+ - Does the prose name a concrete moment, input, and outcome, or only abstract parts?
58
+ - Does any sentence define the thing by what it is not? Rewrite it to say what the thing is.
59
+
60
+ ## Worked example
61
+
62
+ A dense wall — one long sentence, machinery nouns, a term defined by negation:
63
+
64
+ ```
65
+ Owns the SIGINT install/restore/installability check, the atexit terminal-record
66
+ registration, and the interrupted-run finalizer — the non-promoter-specific
67
+ machinery that brackets a run so the JSONL artifact always carries a terminal
68
+ record and an in-flight theme record on interrupt.
69
+ ```
70
+
71
+ The same contract, plain and illustrative:
72
+
73
+ ```
74
+ Make sure a run's log always records how it ended.
75
+
76
+ So when you reopen the report, the last line tells you the truth: the run
77
+ finished cleanly, or you hit Ctrl-C while theme 42 was processing, or it died
78
+ on an unexpected error. Without this, a killed run looks identical to a clean
79
+ one — and you're debugging blind.
80
+ ```
81
+
82
+ ## Enforcement
83
+
84
+ Two surfaces carry this standard:
85
+
86
+ - **Lint (the run-on backstop).** `check_docstring_runon_sentence` in `code_rules_docstrings.py` flags the one mechanical mark of a wall, a single narrative sentence that is both over the word limit and joined by an em-dash or a semicolon. The staged policy lint reaches it through `code_rules_enforcer.py`, and no write-time hook runs it. A mechanical check cannot judge whether prose paints a picture, so it catches only this structural mark. It reads the narrative through a shared partition that sets aside any `::` literal block and any doctest, so a diagram's own arrows and dashes never count against the sentence.
87
+ - **Lint (the prose-wall backstop).** `check_docstring_prose_wall_without_illustration` in the same module flags a narrative that runs more than six prose lines with no diagram block. It marks the wall so the writer shows the behavior with a `::` example or a doctest and trims the prose to a few short lines. It cannot judge whether the diagram illustrates well, and that stays with the audit lane.
88
+ - **Audit (the judgment lane).** Category O sub-bucket O9 in `~/.claude/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md` carries the illustrative-and-brief judgment the lint cannot. The audit teammate reads each changed docstring's narrative and asks whether a general developer follows it on the first read.
@@ -0,0 +1,11 @@
1
+ # Windows Filesystem Safety
2
+
3
+ Full text behind [`rules/windows-filesystem-safe.md`](../../rules/windows-filesystem-safe.md), which loads in sessions as the short form.
4
+
5
+ Never call `shutil.rmtree` with `ignore_errors=True` — Windows `ReadOnly` files (e.g. `.git/objects/pack/`) raise `PermissionError`, the flag swallows it, and the tree silently stays on disk. Use an `onexc` (Python >= 3.12) / `onerror` handler that runs `os.chmod(target_path, stat.S_IWRITE)` then retries the removal function the failure interrupted.
6
+
7
+ In Node, call `mkdirSync(targetPath, { recursive: true })` on possibly-existing paths — `ReadOnly` directories break the non-recursive form. When the call must be non-recursive, strip the attribute first (`(Get-Item $path -Force).Attributes = "Directory"` / `os.chmod(path, stat.S_IWRITE)`).
8
+
9
+ The staged policy lint carries this check as its `rmtree-safety` rule. It reports the unsafe rmtree pattern and returns the full `force_rmtree` safe-pattern code. CI runs that lint against the merge base.
10
+
11
+ Define the safe handler trio (`_strip_read_only_and_retry`, `_force_remove_tree` / `force_rmtree`, and the `inspect.signature` onexc/onerror guard) once in a shared Windows-filesystem utility module, and import it from every call site. A second local copy drifts from the first, so a fix lands in one and the other keeps the bug (CODE_RULES.md, CORE PRINCIPLES, "Reuse before create"). The same `rmtree-safety` lint rule reports a local re-definition of any trio member outside the shared home and points the writer at the import. This complements the same-directory `check_duplicate_function_body_across_files` check, which a copy between two distant packages slips past.
package/hooks/hooks.json CHANGED
@@ -9,6 +9,11 @@
9
9
  "type": "command",
10
10
  "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/session/skill_loaded_reminder.py",
11
11
  "timeout": 10
12
+ },
13
+ {
14
+ "type": "command",
15
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/routing/spawn_readiness_hook.py",
16
+ "timeout": 10
12
17
  }
13
18
  ]
14
19
  },
@@ -79,6 +84,11 @@
79
84
  "type": "command",
80
85
  "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/routing/thread_spawn_pace_hook.py",
81
86
  "timeout": 30
87
+ },
88
+ {
89
+ "type": "command",
90
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/routing/spawn_readiness_hook.py",
91
+ "timeout": 10
82
92
  }
83
93
  ]
84
94
  },