Skip to content

Commit 47ff391

Browse files
lapc506claude
andauthored
feat(hooks): Cure 4b cross-cutting hooks + 1.22.0 (DOJ-4571) (#32)
* feat(hooks): Cure 4b cross-cutting hooks + 1.20.0 (DOJ-4571) Three generalized PreToolUse hooks distributed via the toolkit so every consumer repo inherits cross-cutting defenses without duplicating bash scripts. Per-repo opt-in via `.claude/config/cross-cutting-hooks.json`. File absence -> all three hooks no-op (full backward compatibility). Hooks (under `hooks/cross-cutting/`): - pre-write-no-cleartext-secret-in-config.sh Blocks Write/Edit/MultiEdit of JSON/YAML/TOML/env config files that introduce ${...KEY|SECRET|TOKEN|PASSWORD|...} placeholders without the cure-shape _FILE/_PATH suffix. Generalized from DOJ-4554's openclaw.json-specific version (PR #266 in dojo-agent-openclaw-plugin). - pre-write-cross-repo-schema-ownership.sh Blocks new SQL migrations for tables not owned by this repo, per a config-driven owned_tables allowlist + migration_paths glob. Empty allowlist blocks every migration in the configured paths (the gateway pattern). Prevents the DOJ-4524 15-day persistence freeze failure mode. - pre-write-version-bump-discipline.sh Blocks multi-step version bumps by delegating to a per-repo validator script. Each entry in version_bumps[] names a file pattern, version- extraction regex, and validator. Old version read from git HEAD blob; new from proposed content. Bash native =~ matching (avoids sed-delim clashes with URL-shape regexes containing /). Per-surface defer_to_local_hook flag implements the belt-and-braces coexistence with existing Cure 4a hooks: dojo-agent-openclaw-plugin's 4a hooks stay in place AND the repo opts in with defer_to_local_hook= true on all three surfaces; dojo-os has no 4a coverage and opts in with 4b owning enforcement. Three bypass-marker comment leaders (#, //, --) so the marker fits whichever syntax the target file uses. Trailing terminator class includes backslash so JSON-serialized embedded newlines (marker\\n) don't break detection. Files: - hooks/cross-cutting/{*.sh,README.md,lib/,tests/} - schemas/cross-cutting-hooks.schema.json — JSON Schema for the config - hooks/hooks.json — register 3 new scripts under PreToolUse Write|Edit|MultiEdit AFTER pre-edit.sh - hooks/test-hooks.sh — wire in test-cross-cutting.sh runner - package.json, .claude-plugin/plugin.json, README.md — bump 1.19.0 -> 1.20.0 - package.json files[] — add schemas/ so the JSON Schema ships in the npm package - CHANGELOG.md — [1.20.0] entry - openspec/changes/2026-05-28-doj-4571-cure-4b-cross-repo-hooks/ — approved proposal/design/tasks spec Tests: 23/23 cross-cutting + 225/225 manifest rules = 248/248 total. Consumer-repo opt-in PRs (dojo-os + dojo-agent-openclaw-plugin) land as sibling PRs after 1.20.0 publishes. Per the DOJ-4571 belt-and-braces decision the gateway's existing 4a hooks are NOT deleted. Refs: DOJ-4571 (this work), DOJ-4554 (Cure 4a foundation), DOJ-4064 (4-cure thesis), DOJ-4524, DOJ-4208, DOJ-4061. Created by Claude Code on behalf of @lapc506 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(hooks): cross-platform sed + cleaner regex quoting (DOJ-4571 review) Address dojo-code-reviewer findings on PR #32 (2 P2 + 1 P3, 0 blockers). P2 — schema_ownership hook crashes on BSD sed (macOS): pre-write-cross-repo-schema-ownership.sh used `sed -E '...//I'` to match the TABLE keyword case-insensitively while stripping the IF NOT EXISTS clause. The `/I` flag is a GNU extension; BSD sed on macOS errors out with "unknown option to \`s'". Replaced with explicit bracket-class spelling (`[Tt][Aa][Bb][Ll][Ee]` etc.) which is ERE-portable across both implementations. `grep -i` upstream stays as-is — it's portable. P2 + P3 — cleartext-secret hook regex quoting: HIGH_IMPACT_RE + CURE_RE used quad-backslash escaping inside double quotes ("\\\\\\$\\\\{..."), which is fragile across shell versions and hard to maintain. Switched to single-quote-plus-interpolation convention: the static literal part stays single-quoted (no bash re-interpretation), the dynamic ${TAILS}/${CURE_SUFFIXES} group is interpolated via a separate double-quoted segment. Significantly more readable, matches the rest of the toolkit's bash codebase. Tests: 248/248 still passing (23 cross-cutting + 225 manifest). No behavior change — these are pure portability and readability fixes. Refs: DOJ-4571, PR #32 review by dojo-code-reviewer. Created by Claude Code on behalf of @lapc506 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 5609503 commit 47ff391

17 files changed

Lines changed: 2230 additions & 6 deletions

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "make-no-mistakes",
3-
"version": "1.21.0",
3+
"version": "1.22.0",
44
"description": "The disciplined dev lifecycle — implement issues, review PRs, sync releases, test E2E, manage sessions, stash secrets, and enforce manifest-driven tool-call hooks. One plugin to make no mistakes.",
55
"author": {
66
"name": "Luis Andres Pena Castillo",

CHANGELOG.md

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,99 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1818
1919
## [Unreleased]
2020

21+
## [1.22.0] - 2026-05-29
22+
23+
### Added
24+
25+
- **Cure 4b cross-cutting PreToolUse hooks (DOJ-4571).** Three generalized
26+
hooks distributed via the toolkit so every consumer repo inherits
27+
cross-cutting defenses, parametrized via a per-repo opt-in config file at
28+
`.claude/config/cross-cutting-hooks.json`. File absence → all three hooks
29+
no-op (full backward compatibility). Hooks live in
30+
`hooks/cross-cutting/` alongside the existing manifest-driven rules:
31+
32+
- `pre-write-no-cleartext-secret-in-config.sh` — blocks Write/Edit/
33+
MultiEdit of JSON/YAML/TOML/env config files that introduce
34+
`${...KEY|SECRET|TOKEN|PASSWORD|...}` placeholders without the
35+
cure-shape `_FILE` / `_PATH` suffix. Generalized from DOJ-4554's
36+
openclaw.json-specific version (PR #266 in
37+
`dojo-agent-openclaw-plugin`).
38+
- `pre-write-cross-repo-schema-ownership.sh` — blocks new SQL
39+
migrations for tables not owned by this repo, per a config-driven
40+
`owned_tables` allowlist + `migration_paths` glob. Empty allowlist
41+
blocks every migration in the configured paths (the gateway pattern,
42+
where the repo has no migration pipeline). Generalized from
43+
DOJ-4554's `pre-write-plugin-side-migration.sh`.
44+
- `pre-write-version-bump-discipline.sh` — blocks multi-step version
45+
bumps on any pinned dependency by delegating to a per-repo validator
46+
script. Each entry in the `version_bumps` array names a file
47+
pattern, version-extraction regex, and validator script. Old version
48+
is read from the git HEAD blob; new version from the proposed
49+
content; both via bash native `=~` matching (avoids sed-delimiter
50+
clashes with regexes containing `/`).
51+
52+
- **Per-surface `defer_to_local_hook` flag (belt-and-braces).** Repos
53+
that already have a tighter Cure 4a hook for one of these surfaces
54+
(currently only `dojo-agent-openclaw-plugin`) set
55+
`defer_to_local_hook: true` on the matching config block. The 4b hook
56+
emits an info-stderr and fail-opens; the 4a hook owns enforcement.
57+
Lets the config block stay live (visible, documented, ready for the
58+
day 4a is retired) without firing the looser 4b version.
59+
60+
- **Schema:** `schemas/cross-cutting-hooks.schema.json` (JSON Schema for
61+
editor autocomplete + CI validation).
62+
63+
- **Bypass markers:** three comment leaders accepted (`#`, `//`, `--`)
64+
so the marker fits whichever syntax the target file uses. Trailing
65+
terminator class extended to include backslash so JSON-serialized
66+
embedded newlines (`marker\n...`) don't break detection.
67+
68+
- **Tests:** `hooks/cross-cutting/tests/test-cross-cutting.sh` — 23
69+
hermetic fixtures (≥7 per hook) spinning up isolated git repos per
70+
case; wired into `npm run test-hooks` after the manifest-rules block.
71+
Total runner now reports 248/248 passing.
72+
73+
- **Docs:** `hooks/cross-cutting/README.md` — opt-in walkthrough,
74+
surface semantics, bypass markers, belt-and-braces with local 4a
75+
hooks, three-layer rollback (per-surface disable /
76+
`CLAUDE_DISABLE_PLUGIN_HOOKS` / plugin pin), fail-open invariants.
77+
78+
### Changed
79+
80+
- `hooks/hooks.json` description updated to surface the new
81+
`hooks/cross-cutting/` directory alongside `hooks/rules/` and
82+
`hooks/atomic/`.
83+
- `hooks/hooks.json` PreToolUse `Write|Edit|MultiEdit|NotebookEdit`
84+
block now registers the 3 cross-cutting scripts AFTER `pre-edit.sh`
85+
and alongside `hooks/atomic/pre-atomic.sh` (manifest-driven rules run
86+
first; atomic-design and cross-cutting hooks layer on as siblings).
87+
- `package.json` `files[]` adds `schemas/` and `references/` so the
88+
JSON Schemas and example configs ship in the npm package (also
89+
benefits `schemas/atomic-design-rules.schema.json` and
90+
`references/atomic-design-rules.example.json` from 1.21.0).
91+
92+
### Notes
93+
94+
- Originally targeted `1.20.0` (per the parallel-version note in 1.21.0);
95+
PR #28 landed first as 1.21.0, so this rebases onto 1.22.0 to preserve
96+
monotonic ordering. No semantic content change vs. the originally
97+
proposed 1.20.0.
98+
- Two review fixes from PR #32 (dojo-code-reviewer): replaced GNU-only
99+
`sed ... //I` with explicit bracket-class spelling (BSD sed
100+
compatibility on macOS); switched HIGH_IMPACT_RE / CURE_RE from
101+
quad-backslash escaping to single-quote-plus-interpolation convention.
102+
- Consumer-repo opt-in (config files in `dojo-os` and
103+
`dojo-agent-openclaw-plugin`) lands in sibling PRs after `1.22.0`
104+
publishes. Per DOJ-4571 belt-and-braces decision,
105+
`dojo-agent-openclaw-plugin` keeps its existing 4a hooks AND opts in
106+
with `defer_to_local_hook: true` on all three surfaces; `dojo-os`
107+
opts in with the 4b hooks owning enforcement.
108+
- Refs: DOJ-4571 (this work), DOJ-4554 (Cure 4a foundation), DOJ-4064
109+
(4-cure thesis), DOJ-4524 (the persistence-freeze incident the
110+
schema-ownership hook prevents), DOJ-4208 (the cleartext-key incident
111+
the cleartext-secret hook prevents), DOJ-4061 (the gateway-version-bump
112+
chain the version-bump hook prevents).
113+
21114
## [1.21.0] - 2026-05-29
22115

23116
### Added
@@ -53,8 +146,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
53146
2026-05-14 audit (pathways, launchpad, community, projects, marketplace,
54147
hackathons, events, agent, dojo-score, plus platform as the shared pillar).
55148
The example only enumerates a subset; consumers configure their own list.
56-
- **Parallel-version coordination:** version `1.20.0` is claimed by the
57-
DOJ-4571 Cure 4b cross-repo hooks PR. This release follows as `1.21.0`.
149+
- **Parallel-version coordination:** version `1.20.0` was originally
150+
reserved for the DOJ-4571 Cure 4b cross-repo hooks PR. PR #28
151+
(this release) landed first as `1.21.0`; DOJ-4571 followed as
152+
`1.22.0` to preserve monotonic ordering. See `[1.22.0]` above.
58153

59154
## [1.19.0] - 2026-05-26
60155

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# make-no-mistakes
22

3-
**Version: 1.21.0** · [CHANGELOG](./CHANGELOG.md) · [Marketplace](https://github.com/DojoCodingLabs/make-no-mistakes-toolkit)
3+
**Version: 1.22.0** · [CHANGELOG](./CHANGELOG.md) · [Marketplace](https://github.com/DojoCodingLabs/make-no-mistakes-toolkit)
44

55
The disciplined dev lifecycle — implement issues, review PRs, sync releases, test E2E, and manage sessions. One plugin to make no mistakes.
66

hooks/cross-cutting/README.md

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
# Cross-cutting PreToolUse hooks — Cure 4b (DOJ-4571)
2+
3+
These hooks ship as part of the `make-no-mistakes` toolkit and apply to
4+
every repo that installs the plugin. They are **opt-in per repo** via a
5+
single config file at the consumer-repo root.
6+
7+
| File | Purpose | Surface | Source |
8+
|------|---------|---------|--------|
9+
| `pre-write-no-cleartext-secret-in-config.sh` | Block `${...KEY/SECRET/TOKEN/PASSWORD/...}` placeholders in any JSON/YAML/TOML/env config file that the runtime would substitute to disk in cleartext. | `cleartext_secrets` | Generalized from DOJ-4554 hook of the same family in `dojo-agent-openclaw-plugin`. |
10+
| `pre-write-cross-repo-schema-ownership.sh` | Block new SQL migrations for tables this repo does not own. | `schema_ownership` | Generalized from DOJ-4554 `pre-write-plugin-side-migration.sh`. |
11+
| `pre-write-version-bump-discipline.sh` | Block multi-step version bumps in pinned dependencies by delegating to a per-repo validator script. | `version_bumps` | Generalized from DOJ-4554 `pre-write-openclaw-version-bump-discipline.sh`. |
12+
13+
## Opt-in
14+
15+
Create `.claude/config/cross-cutting-hooks.json` at your repo root. File
16+
absence → all three hooks no-op. Minimal opt-in:
17+
18+
```json
19+
{
20+
"$schema": "https://raw.githubusercontent.com/DojoCodingLabs/make-no-mistakes-toolkit/main/schemas/cross-cutting-hooks.schema.json",
21+
"version": 1,
22+
"cleartext_secrets": { "enabled": true }
23+
}
24+
```
25+
26+
Full example (every surface enabled, every override demonstrated):
27+
28+
```json
29+
{
30+
"$schema": "https://raw.githubusercontent.com/DojoCodingLabs/make-no-mistakes-toolkit/main/schemas/cross-cutting-hooks.schema.json",
31+
"version": 1,
32+
"cleartext_secrets": {
33+
"enabled": true,
34+
"defer_to_local_hook": false,
35+
"extra_block_patterns": ["MY_CUSTOM_TOKEN"],
36+
"extra_cure_suffixes": ["_REF", "_VOLUME"]
37+
},
38+
"schema_ownership": {
39+
"enabled": true,
40+
"defer_to_local_hook": false,
41+
"owned_tables": ["chat_sessions", "chat_messages"],
42+
"migration_paths": ["supabase/migrations"]
43+
},
44+
"version_bumps": [
45+
{
46+
"file_pattern": "Dockerfile",
47+
"version_regex": "openclaw/releases/download/(v[0-9]+\\.[0-9]+\\.[0-9]+)/",
48+
"validator_script": "scripts/check-openclaw-version-bump.sh",
49+
"validator_args": [],
50+
"defer_to_local_hook": false
51+
}
52+
]
53+
}
54+
```
55+
56+
JSON Schema for editor autocomplete + CI validation:
57+
[`schemas/cross-cutting-hooks.schema.json`](../../schemas/cross-cutting-hooks.schema.json).
58+
59+
## How each hook works
60+
61+
### `cleartext_secrets`
62+
63+
Triggers on `Write|Edit|MultiEdit` of any file ending in `.json`,
64+
`.jsonc`, `.yaml`, `.yml`, `.toml`, `.env`, or starting with `.env.`.
65+
66+
Built-in blocked tails (case-sensitive, after optional `[A-Z_]*` prefix
67+
and before optional `[A-Z0-9_]*` suffix):
68+
69+
- `SERVICE_ROLE`
70+
- `JWT_SECRET`
71+
- `PRIVATE_KEY`
72+
- `CLIENT_SECRET`
73+
- `ADMIN_TOKEN`
74+
- `PASSWORD`
75+
- `ENCRYPTION_KEY`
76+
- `SIGNING_SECRET`
77+
78+
Built-in cure-shape suffixes (placeholders ending in these PASS):
79+
80+
- `_FILE`
81+
- `_PATH`
82+
83+
Use `extra_block_patterns` and `extra_cure_suffixes` to extend both
84+
sets. The hook only ADDS to built-ins — there is no removal API; use
85+
the bypass marker for one-off overrides.
86+
87+
### `schema_ownership`
88+
89+
Triggers on `Write` only (`Edit`/`MultiEdit` on existing migrations is
90+
allowed — typical for cleanup/annotation of historical artifacts).
91+
Fires only when `FILE_PATH` lives under one of `migration_paths`
92+
(default `["supabase/migrations"]`) AND ends in `.sql`.
93+
94+
Behavior depends on `owned_tables`:
95+
96+
- `[]` → blocks every match (the gateway pattern: no migrations belong
97+
in this repo at all)
98+
- `["table_a", ...]` → extracts `CREATE/ALTER/DROP/RENAME TABLE <name>`
99+
identifiers from the proposed content and blocks if any referenced
100+
name is not in the allowlist
101+
102+
Conservative SQL parsing: only the four statement types above. Migrations
103+
that only define functions, views, policies, or data are allowed (the
104+
ownership check has no signal to act on).
105+
106+
### `version_bumps`
107+
108+
Triggers on `Write|Edit|MultiEdit` of any file whose basename matches a
109+
configured `file_pattern`. For each match:
110+
111+
1. Extract `OLD_VERSION` from the git HEAD blob via the configured
112+
`version_regex` (single capture group).
113+
2. Extract `NEW_VERSION` from the proposed content via the same regex.
114+
3. If both extract, differ, and the `validator_script` is executable,
115+
invoke `<validator_script> <OLD_VERSION> <NEW_VERSION> [extra_args]`.
116+
4. Validator exit codes:
117+
- `0` → pass
118+
- `2` → block (the validator's stderr is shown above the hook's block message)
119+
- any other → warn + fail-open (defense in depth, never block on validator infrastructure)
120+
121+
If `OLD_VERSION` cannot be extracted (e.g. file is new), the hook passes
122+
silently — the rule applies to BUMPS, not introductions.
123+
124+
## Bypass markers
125+
126+
Each hook honors a kebab-case bypass marker matching its surface. Add
127+
the marker as a comment near the offending content:
128+
129+
- `# hook-bypass: cross-cutting-cleartext-secret`
130+
- `# hook-bypass: cross-cutting-schema-ownership`
131+
- `# hook-bypass: cross-cutting-version-bump`
132+
133+
Three comment leaders are accepted so the marker fits whichever syntax
134+
the target file uses:
135+
136+
| Leader | Used in |
137+
|--------|---------|
138+
| `#` | Bash / YAML / TOML / Python (SQL also accepts this) |
139+
| `//` | JSON-with-comments / JS / TS / C-family |
140+
| `--` | SQL / Haskell / Lua |
141+
142+
## Belt-and-braces with local 4a hooks
143+
144+
If your repo already has a tighter `.claude/hooks/`-level 4a hook for
145+
one of these surfaces (the canonical case is
146+
`dojo-agent-openclaw-plugin`), set `defer_to_local_hook: true` on the
147+
matching config block. The 4b hook logs an info-stderr and fail-opens;
148+
the 4a hook owns enforcement. This lets the config block stay live
149+
(visible, documented, ready for the day 4a is retired) without firing
150+
the looser 4b version.
151+
152+
Default `false` → both hooks fire. They produce the same verdict by
153+
construction (4b generalizes 4a) so double-blocks are harmless; the
154+
only user-visible artifact is two stderr blocks instead of one.
155+
156+
## Disabling
157+
158+
Three layers, least to most invasive:
159+
160+
1. **Per surface.** Set `enabled: false` (or omit the key) in the
161+
per-repo config.
162+
2. **All toolkit hooks for the current shell.** Set
163+
`CLAUDE_DISABLE_PLUGIN_HOOKS=1` in your environment.
164+
3. **Plugin pin.** Pin the consumer repo to the pre-Cure-4b toolkit
165+
version (`1.19.0`) in your plugin install command.
166+
167+
A full rollback (delete the config file) is also valid — the hooks
168+
no-op without their config.
169+
170+
## Fail-open invariants
171+
172+
Every hook exits 0 (pass) silently when any of these are true:
173+
174+
- `CLAUDE_DISABLE_PLUGIN_HOOKS=1`
175+
- `jq` not on PATH
176+
- Hook input JSON malformed
177+
- Config file missing
178+
- Config file present but unsupported `version`
179+
- Per-surface `enabled` is false
180+
- Per-surface `defer_to_local_hook` is true (with an info-stderr line)
181+
- `version_bumps`: validator script missing/non-executable
182+
183+
This matches the existing toolkit hook posture (defense in depth, never
184+
a single point of failure).
185+
186+
## Tests
187+
188+
See `hooks/cross-cutting/tests/test-cross-cutting.sh` — invoked by
189+
`npm run test-hooks` alongside the manifest-driven `rules.json` tests.
190+
Coverage: ≥5 fixtures per hook (block on positive, pass on negative,
191+
no-op when disabled, no-op when config missing, bypass marker honored).
192+
193+
## Reference
194+
195+
- DOJ-4571 — this work (Cure 4b)
196+
- DOJ-4554 — Cure 4a foundation in `dojo-agent-openclaw-plugin`
197+
- DOJ-4524 — 15-day persistence-freeze incident that motivated the
198+
schema-ownership hook
199+
- DOJ-4208 — service-role key cleartext-leak incident that motivated
200+
the cleartext-secret hook
201+
- DOJ-4061 — gateway version-bump fix-forward chain that motivated
202+
the version-bump hook
203+
- DOJ-4064 — 4-cure thesis

0 commit comments

Comments
 (0)