@bookedsolid/rea 0.13.2 → 0.13.3

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 (2) hide show
  1. package/MIGRATING.md +306 -0
  2. package/package.json +2 -1
package/MIGRATING.md ADDED
@@ -0,0 +1,306 @@
1
+ # Migrating to `@bookedsolid/rea` from a project with prior tooling
2
+
3
+ `rea` was originally written for greenfield projects. Real consumers
4
+ arrive with prior infrastructure already in place — commitlint,
5
+ lint-staged, gitleaks, act-CI, branch-policy linters, project-specific
6
+ gates wired into `.husky/`. This guide names the conflict patterns by
7
+ name and shows the supported migration path for each.
8
+
9
+ If you hit something this doc doesn't cover, file an issue at
10
+ https://github.com/bookedsolidtech/rea/issues with the offending hook
11
+ body and the prior tool name.
12
+
13
+ ## Prerequisite — husky must be installed and `core.hooksPath` configured
14
+
15
+ The `.husky/{commit-msg,pre-push}.d/` extension surface is sourced from
16
+ the rea-managed bodies under `.husky/<hookname>`. Those bodies only fire
17
+ when git is configured to use husky. Confirm one of the following:
18
+
19
+ - Husky 9 (recommended): `pnpm dlx husky init` (or `npx husky init`)
20
+ during onboarding. Husky 9 sets `core.hooksPath=.husky/_` automatically;
21
+ rea's bodies live at `.husky/<hookname>` and husky's auto-generated
22
+ stubs at `.husky/_/<hookname>` source them at hook-fire time. `rea
23
+ doctor` (0.13.1+) follows the husky 9 stub indirection correctly.
24
+ - Husky 4-8 (legacy): `core.hooksPath=.husky` set, husky's
25
+ `_/husky.sh` runner installed. Functional but unsupported by husky
26
+ upstream — migrate to husky 9.
27
+ - Vanilla git (no husky): rea installs the fallback at
28
+ `.git/hooks/pre-push`. **The fragment recipes below DO NOT run** in
29
+ this configuration — `.git/hooks/pre-push` is not the same body as
30
+ `.husky/pre-push`. Either install husky (recommended) or chain your
31
+ per-tool commands directly into the fallback (you'll lose the
32
+ upgrade-safe property — `rea upgrade` will refresh the fallback and
33
+ drop your chain).
34
+
35
+ `pnpm rea doctor` reports the active hook path. If it shows
36
+ `.git/hooks/pre-push` (rea-managed at .../.git/hooks/pre-push), you
37
+ are on the vanilla-git path — install husky first.
38
+
39
+ ## TL;DR
40
+
41
+ 1. Confirm husky is installed (see prereq above).
42
+ 2. Run `rea init` (fresh install) or `rea upgrade` (existing).
43
+ 3. **Do not lose your existing chain.** rea now refuses to silently
44
+ overwrite an executable `.husky/pre-push` or `.husky/commit-msg`
45
+ that is not rea-managed; you'll see a `[fail]` from `rea doctor`
46
+ pointing here.
47
+ 4. Move each chained command from your existing hook body to a
48
+ per-tool fragment under `.husky/pre-push.d/<NN>-<name>` or
49
+ `.husky/commit-msg.d/<NN>-<name>` (executable, lex-ordered).
50
+ 5. Re-run `rea init`. The fresh hook body delegates to
51
+ `rea hook push-gate` and then runs your fragments AFTER the
52
+ governance gate.
53
+ 6. `rea doctor` should now report all checks green.
54
+
55
+ ## What rea ships and what it doesn't
56
+
57
+ `rea init` / `rea upgrade` install:
58
+
59
+ - `.husky/pre-push` — package-managed; **do not edit**. Refreshed on every
60
+ `rea upgrade`.
61
+ - `.husky/commit-msg` — package-managed; **do not edit**. Same.
62
+ - `.git/hooks/pre-push` (fallback when `core.hooksPath` is unset).
63
+ - `.claude/hooks/*.sh` — protection + audit + advisory hooks.
64
+ - `.claude/agents/*.md`, `.claude/commands/*.md`.
65
+ - `.rea/policy.yaml`, `.rea/registry.yaml`.
66
+
67
+ `rea` does **not** install:
68
+
69
+ - `.husky/pre-commit` — completely yours. Out of scope for the rea
70
+ push-gate. If you have one, keep it.
71
+ - `.husky/post-commit`, `post-merge`, `post-checkout`, etc. — yours.
72
+ - Any tool's binary (`commitlint`, `gitleaks`, `husky`, etc.) — yours.
73
+
74
+ The only files rea touches are explicitly enumerated above. Everything
75
+ else is the consumer's surface.
76
+
77
+ ## Extension surface (added in 0.13.0)
78
+
79
+ `.husky/pre-push.d/*` and `.husky/commit-msg.d/*` are the
80
+ **upgrade-safe** place to layer your own gates. Files in those
81
+ directories must be executable; rea sources them in lex order AFTER
82
+ its own governance work succeeds. A non-zero exit from any fragment
83
+ fails the hook (matches husky's normal chaining).
84
+
85
+ - Fragment receives positional args from git (`<remote-name> <remote-url>`
86
+ for pre-push, `<commit-msg-file>` for commit-msg).
87
+ - Missing directory is a no-op (no fragments = no chained checks).
88
+ - Non-executable files are silently skipped (drop a `README` if you
89
+ want context next to the fragments — it won't run).
90
+ - Fragments run with the current shell's `set -eu`; an unset variable
91
+ or a non-zero exit anywhere in the fragment short-circuits.
92
+
93
+ `rea doctor` reports detected fragments at `[info]` level so you can
94
+ confirm the chain.
95
+
96
+ ## Conflict pattern: commitlint
97
+
98
+ You probably have something like this in `.husky/commit-msg`:
99
+
100
+ ```sh
101
+ #!/usr/bin/env sh
102
+ . "$(dirname -- "$0")/_/husky.sh" # husky 4-8
103
+ npx --no-install commitlint --edit "$1"
104
+ ```
105
+
106
+ Or, with husky 9, your own command interleaved with `husky 9`'s body.
107
+
108
+ **rea 0.11.0+ overwrites `.husky/commit-msg` on `rea upgrade --force`.**
109
+ Your commitlint invocation will be lost.
110
+
111
+ ### Migration
112
+
113
+ Move commitlint to a fragment:
114
+
115
+ ```bash
116
+ mkdir -p .husky/commit-msg.d
117
+ cat > .husky/commit-msg.d/01-commitlint <<'EOF'
118
+ #!/bin/sh
119
+ exec npx --no-install commitlint --edit "$1"
120
+ EOF
121
+ chmod +x .husky/commit-msg.d/01-commitlint
122
+ ```
123
+
124
+ Re-run `rea upgrade`. The package-managed `.husky/commit-msg` body now
125
+ runs first (HALT check, AI-attribution block when policy enables it),
126
+ then runs your fragment.
127
+
128
+ ## Conflict pattern: lint-staged on pre-push
129
+
130
+ You probably have:
131
+
132
+ ```sh
133
+ #!/usr/bin/env sh
134
+ . "$(dirname -- "$0")/_/husky.sh"
135
+ npx --no-install lint-staged
136
+ ```
137
+
138
+ ### Migration
139
+
140
+ ```bash
141
+ mkdir -p .husky/pre-push.d
142
+ cat > .husky/pre-push.d/02-lint-staged <<'EOF'
143
+ #!/bin/sh
144
+ exec npx --no-install lint-staged
145
+ EOF
146
+ chmod +x .husky/pre-push.d/02-lint-staged
147
+ ```
148
+
149
+ ## Conflict pattern: gitleaks (pre-commit)
150
+
151
+ `rea` does NOT install a pre-commit hook. Your existing
152
+ `.husky/pre-commit` keeps working unchanged. Just confirm:
153
+
154
+ - Shebang is `#!/usr/bin/env bash` (not `#!/bin/sh`) if the body uses
155
+ `set -o pipefail`. On Linux where `/bin/sh = dash`, `pipefail`
156
+ aborts immediately.
157
+ - gitleaks invocation includes `--redact` so detected secrets don't
158
+ hit terminal scrollback.
159
+ - gitleaks binary is vendored or installed via postinstall (e.g.
160
+ `gitleaks-secret-scanner` npm wrapper) so fresh clones work without
161
+ manual install.
162
+
163
+ If you want gitleaks to run on push instead of commit, add a fragment:
164
+
165
+ ```bash
166
+ cat > .husky/pre-push.d/03-gitleaks <<'EOF'
167
+ #!/bin/sh
168
+ exec gitleaks detect --redact --no-banner
169
+ EOF
170
+ chmod +x .husky/pre-push.d/03-gitleaks
171
+ ```
172
+
173
+ ## Conflict pattern: act-CI matrix
174
+
175
+ If you have a project-specific CI gate like `./scripts/act-ci.sh`
176
+ chained into `.husky/pre-push` (e.g. BST), it gets clobbered by
177
+ `rea upgrade --force`.
178
+
179
+ ### Migration
180
+
181
+ ```bash
182
+ cat > .husky/pre-push.d/00-act-ci <<'EOF'
183
+ #!/bin/sh
184
+ exec ./scripts/act-ci.sh
185
+ EOF
186
+ chmod +x .husky/pre-push.d/00-act-ci
187
+ ```
188
+
189
+ The `00-` prefix puts act-CI first in lex order so it runs before any
190
+ later fragments. Adjust ordering as needed.
191
+
192
+ ## Conflict pattern: branch-policy linter
193
+
194
+ A common pattern that reads `$1` (remote name) and `$2` (remote URL)
195
+ to allow/deny pushes to specific remotes:
196
+
197
+ ```sh
198
+ #!/bin/sh
199
+ remote="$1"
200
+ url="$2"
201
+ if [ "$remote" = "origin" ] && echo "$url" | grep -q "production"; then
202
+ echo "Direct push to production blocked. PR via main." >&2
203
+ exit 1
204
+ fi
205
+ ```
206
+
207
+ This requires the standard pre-push argv. **rea 0.13.2+ preserves
208
+ git's argv unchanged** for fragments — earlier versions (0.13.0 /
209
+ 0.13.1) had a known bug where `set --` mutation in the rea dispatch
210
+ clobbered `$@`. Upgrade to `^0.13.2` if branch-policy linters are part
211
+ of your chain.
212
+
213
+ ### Migration
214
+
215
+ Drop the body into a fragment as-is:
216
+
217
+ ```bash
218
+ cat > .husky/pre-push.d/05-branch-policy <<'EOF'
219
+ #!/bin/sh
220
+ remote="$1"
221
+ url="$2"
222
+ if [ "$remote" = "origin" ] && echo "$url" | grep -q "production"; then
223
+ echo "Direct push to production blocked. PR via main." >&2
224
+ exit 1
225
+ fi
226
+ EOF
227
+ chmod +x .husky/pre-push.d/05-branch-policy
228
+ ```
229
+
230
+ ## Conflict pattern: pre-existing rea-CLI invocation
231
+
232
+ Some consumers had `exec rea hook push-gate "$@"` chained inline in a
233
+ foreign hook body. `rea doctor` recognizes this pattern and reports
234
+ the hook as `external (delegates to rea hook push-gate)` — `pass`,
235
+ not `fail`. No migration required, but you cannot benefit from the
236
+ extension-fragment chain unless you let rea own the hook body.
237
+
238
+ If you want both — rea ownership AND your other commands — migrate the
239
+ other commands to fragments.
240
+
241
+ ## Conflict pattern: husky 9 layout (`core.hooksPath=.husky/_`)
242
+
243
+ This is the default husky 9 install. rea 0.13.1+ supports it
244
+ correctly: doctor follows the husky 9 stub indirection from
245
+ `.husky/_/<hookname>` through `.husky/_/h` to the canonical
246
+ `.husky/<hookname>`. No migration required.
247
+
248
+ If you're on rea 0.13.0 and seeing `[fail] pre-push hook` despite a
249
+ correctly-installed `.husky/pre-push`, upgrade to `^0.13.1`.
250
+
251
+ ## What `rea doctor` will tell you
252
+
253
+ After migration, run `pnpm rea doctor`. The relevant lines:
254
+
255
+ - `[ok] pre-push hook installed` — rea-managed body active, fragments
256
+ (if any) detected
257
+ - `[fail] pre-push hook installed` with **"Detected prior tooling: X,
258
+ Y, Z"** — your existing hook still chains tooling that should be in
259
+ fragments. Move each named tool to a `.d/` fragment, then re-run
260
+ `rea init`.
261
+ - `[info] extension-hook fragments detected: N pre-push.d, M
262
+ commit-msg.d` — your fragment chain is active
263
+
264
+ ## Policy knobs worth setting
265
+
266
+ For consumers with a long-running migration branch (>30 commits since
267
+ last push), the push-gate auto-narrows the codex review window unless
268
+ you opt out. Pin explicit values to avoid surprises:
269
+
270
+ ```yaml
271
+ # .rea/policy.yaml
272
+ review:
273
+ codex_required: true
274
+ timeout_ms: 1800000 # 30 min — explicit pin
275
+ auto_narrow_threshold: 30 # 0 to disable auto-narrow
276
+ last_n_commits: 10 # explicit scope window
277
+ ```
278
+
279
+ ## Bypass when you genuinely need to
280
+
281
+ ```bash
282
+ # Audited skip: codex flips on a known-ambivalent file
283
+ REA_SKIP_CODEX_REVIEW="cemPath-ambivalence" git push
284
+
285
+ # Whole-gate skip: codex CLI itself is broken
286
+ REA_SKIP_PUSH_GATE="codex-cli-crash-pinging-team" git push
287
+
288
+ # Concerns-only override (P2 findings) without skipping the gate
289
+ REA_ALLOW_CONCERNS=1 git push
290
+ ```
291
+
292
+ Every bypass is audit-logged with the reason in `.rea/audit.jsonl`.
293
+ Reasons should be specific — "skip" is not a reason; the file or
294
+ verdict that triggered it is.
295
+
296
+ ## When to file an issue vs handle in-tree
297
+
298
+ - **rea hook ate my chain on `rea upgrade`** → file an issue, that's
299
+ rea's fault. Workaround: migrate to `.d/` fragments.
300
+ - **rea doctor false-positives on my legitimate setup** → file an
301
+ issue.
302
+ - **codex flips verdicts on the same code** → upstream of rea (codex
303
+ CLI itself). Use `REA_SKIP_CODEX_REVIEW` with a specific reason and
304
+ document the ambivalence.
305
+ - **My pre-commit hook breaks on push** → not rea (rea ships no
306
+ pre-commit). Fix in your repo.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bookedsolid/rea",
3
- "version": "0.13.2",
3
+ "version": "0.13.3",
4
4
  "description": "Agentic governance layer for Claude Code — policy enforcement, hook-based safety gates, audit logging, and Codex-integrated adversarial review for AI-assisted projects",
5
5
  "license": "MIT",
6
6
  "author": "Booked Solid Technology <oss@bookedsolid.tech> (https://bookedsolid.tech)",
@@ -49,6 +49,7 @@
49
49
  ".husky/",
50
50
  "LICENSE",
51
51
  "README.md",
52
+ "MIGRATING.md",
52
53
  "SECURITY.md",
53
54
  "THREAT_MODEL.md"
54
55
  ],