@bookedsolid/rea 0.11.0 → 0.13.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.
@@ -34,14 +34,29 @@ declare const PolicySchema: z.ZodObject<{
34
34
  codex_required: z.ZodOptional<z.ZodBoolean>;
35
35
  concerns_blocks: z.ZodOptional<z.ZodBoolean>;
36
36
  timeout_ms: z.ZodOptional<z.ZodNumber>;
37
+ last_n_commits: z.ZodOptional<z.ZodNumber>;
38
+ /**
39
+ * Auto-narrow threshold (J / 0.13.0). When the resolved diff base is more
40
+ * than N commits away from HEAD, the gate auto-scopes to
41
+ * `last_n_commits` (or the 0.13 fallback default of 10) and emits a
42
+ * stderr warning. Default 30 when unset; explicit 0 disables auto-narrow
43
+ * entirely. Suppressed when the operator pinned `--last-n-commits`,
44
+ * `--base`, or `policy.review.last_n_commits` (those are explicit
45
+ * intent and auto-narrow stays out of the way).
46
+ */
47
+ auto_narrow_threshold: z.ZodOptional<z.ZodNumber>;
37
48
  }, "strict", z.ZodTypeAny, {
38
49
  codex_required?: boolean | undefined;
39
50
  concerns_blocks?: boolean | undefined;
40
51
  timeout_ms?: number | undefined;
52
+ last_n_commits?: number | undefined;
53
+ auto_narrow_threshold?: number | undefined;
41
54
  }, {
42
55
  codex_required?: boolean | undefined;
43
56
  concerns_blocks?: boolean | undefined;
44
57
  timeout_ms?: number | undefined;
58
+ last_n_commits?: number | undefined;
59
+ auto_narrow_threshold?: number | undefined;
45
60
  }>>;
46
61
  redact: z.ZodOptional<z.ZodObject<{
47
62
  match_timeout_ms: z.ZodOptional<z.ZodNumber>;
@@ -135,6 +150,8 @@ declare const PolicySchema: z.ZodObject<{
135
150
  codex_required?: boolean | undefined;
136
151
  concerns_blocks?: boolean | undefined;
137
152
  timeout_ms?: number | undefined;
153
+ last_n_commits?: number | undefined;
154
+ auto_narrow_threshold?: number | undefined;
138
155
  } | undefined;
139
156
  redact?: {
140
157
  match_timeout_ms?: number | undefined;
@@ -178,6 +195,8 @@ declare const PolicySchema: z.ZodObject<{
178
195
  codex_required?: boolean | undefined;
179
196
  concerns_blocks?: boolean | undefined;
180
197
  timeout_ms?: number | undefined;
198
+ last_n_commits?: number | undefined;
199
+ auto_narrow_threshold?: number | undefined;
181
200
  } | undefined;
182
201
  redact?: {
183
202
  match_timeout_ms?: number | undefined;
@@ -27,6 +27,17 @@ const ReviewPolicySchema = z
27
27
  codex_required: z.boolean().optional(),
28
28
  concerns_blocks: z.boolean().optional(),
29
29
  timeout_ms: z.number().int().positive().optional(),
30
+ last_n_commits: z.number().int().positive().optional(),
31
+ /**
32
+ * Auto-narrow threshold (J / 0.13.0). When the resolved diff base is more
33
+ * than N commits away from HEAD, the gate auto-scopes to
34
+ * `last_n_commits` (or the 0.13 fallback default of 10) and emits a
35
+ * stderr warning. Default 30 when unset; explicit 0 disables auto-narrow
36
+ * entirely. Suppressed when the operator pinned `--last-n-commits`,
37
+ * `--base`, or `policy.review.last_n_commits` (those are explicit
38
+ * intent and auto-narrow stays out of the way).
39
+ */
40
+ auto_narrow_threshold: z.number().int().nonnegative().optional(),
30
41
  })
31
42
  .strict();
32
43
  /**
@@ -54,12 +54,82 @@ export interface ReviewPolicy {
54
54
  /**
55
55
  * Hard cap on the `codex exec review` subprocess in milliseconds. Exceeding
56
56
  * this kills the subprocess and the gate returns exit 2 with a timeout
57
- * error (audited). Default when unset is 600_000 (10 minutes) matches
58
- * the upper bound we observe for a 500-line diff review on a slow link.
57
+ * error (audited). Default when unset is 1_800_000 (30 minutes) as of
58
+ * 0.12.0 raised from 10 minutes after the helixir migration session
59
+ * 2026-04-26 showed realistic feature-branch diffs routinely exceeded
60
+ * the previous default. Operators with explicit `timeout_ms:` in
61
+ * `.rea/policy.yaml` are unaffected.
59
62
  *
60
63
  * Positive integer only. The loader rejects zero/negative values.
61
64
  */
62
65
  timeout_ms?: number;
66
+ /**
67
+ * When set, `rea hook push-gate` resolves the diff base to `HEAD~N`
68
+ * instead of the upstream → origin/HEAD ladder. Useful when a feature
69
+ * branch accumulates many commits and the full origin/main diff
70
+ * overwhelms the reviewer (the helixir 2026-04-26 case: 50+ commits
71
+ * relative to origin/main produced non-deterministic Codex verdicts and
72
+ * 10-minute timeouts).
73
+ *
74
+ * Precedence: explicit `--base <ref>` flag wins; then `--last-n-commits N`
75
+ * flag; then this policy key; then refspec-aware base resolution; then
76
+ * the upstream-ladder fallback. When `--base` AND
77
+ * `--last-n-commits`/`policy.last_n_commits` are both set, `--base`
78
+ * wins and a stderr warning is emitted.
79
+ *
80
+ * Resolution: `git rev-parse HEAD~N`. When `HEAD~N` is unreachable
81
+ * the resolver consults `git rev-parse --is-shallow-repository` to
82
+ * pick the right clamp:
83
+ *
84
+ * - FULL clone, branch shorter than N: clamps to the empty-tree
85
+ * sentinel so the root commit's changes are included
86
+ * (`git diff base..HEAD` excludes `base`, so diffing against
87
+ * `HEAD~K` would silently drop the root commit). Reports
88
+ * `last_n_commits: K+1` — every commit on the branch reviewed.
89
+ *
90
+ * - SHALLOW clone: clamps to `HEAD~K` (the deepest LOCALLY
91
+ * resolvable ancestor) since older history exists on the remote
92
+ * but isn't fetched. Using empty-tree here would balloon the
93
+ * review to every tracked file in the checkout. Reports
94
+ * `last_n_commits: K`. The K-th commit's content is excluded —
95
+ * accepted as the cost of the shallow clone.
96
+ *
97
+ * A stderr warning surfaces the requested-vs-clamped numbers in
98
+ * both cases. Audit metadata records `base_source: 'last-n-commits'`,
99
+ * `last_n_commits: <count actually reviewed>`, and
100
+ * `last_n_commits_requested: N` (only present when clamped).
101
+ *
102
+ * Positive integer. The loader rejects zero/negative values.
103
+ */
104
+ last_n_commits?: number;
105
+ /**
106
+ * Auto-narrow threshold (J / 0.13.0). When the resolved diff base is more
107
+ * than N commits behind HEAD, the gate automatically scopes the review to
108
+ * the last 10 commits (or `last_n_commits` if pinned) and emits a stderr
109
+ * warning explaining the auto-narrow + how to override.
110
+ *
111
+ * Default `30` when unset. Explicit `0` disables auto-narrow entirely.
112
+ *
113
+ * Auto-narrow is SUPPRESSED when the operator already expressed explicit
114
+ * intent — any of these prevents auto-narrow from firing:
115
+ *
116
+ * - `--last-n-commits N` flag (the operator picked an exact window)
117
+ * - `--base <ref>` flag (the operator picked an exact base)
118
+ * - `policy.review.last_n_commits` (persistent narrow-window config)
119
+ *
120
+ * Audit metadata records `auto_narrowed: true|false` and
121
+ * `original_commit_count: N` on every reviewed event so operators can
122
+ * grep their audit log for narrowed reviews.
123
+ *
124
+ * Background: large feature branches (50+ commits relative to origin/main)
125
+ * routinely produced non-deterministic Codex verdicts, 10-minute timeouts,
126
+ * and the "thrashing" reported in helixir migration 2026-04-26. The 0.12.0
127
+ * `last_n_commits` knob fixed it for operators who knew to set it; J makes
128
+ * the protective default automatic.
129
+ *
130
+ * Non-negative integer. The loader rejects negative values.
131
+ */
132
+ auto_narrow_threshold?: number;
63
133
  }
64
134
  /**
65
135
  * User-supplied redaction pattern entry. Each pattern has a stable `name` used
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bookedsolid/rea",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
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)",
@@ -74,8 +74,13 @@ echo "[smoke] → $VERSION_OUT"
74
74
  echo "[smoke] rea --help"
75
75
  ./node_modules/.bin/rea --help >/dev/null
76
76
 
77
- echo "[smoke] rea init --yes --profile open-source"
78
- ./node_modules/.bin/rea init --yes --profile open-source
77
+ echo "[smoke] rea init --yes --profile open-source-no-codex"
78
+ # 0.12.0+: doctor hard-fails when policy.review.codex_required: true and codex
79
+ # is not on PATH (fix C of the helixir migration unblocker — see PR #85). CI
80
+ # does not provision the codex CLI, so the smoke uses the -no-codex profile
81
+ # variant which defaults codex_required: false. The new doctor probe is
82
+ # covered by unit tests in src/cli/doctor.test.ts.
83
+ ./node_modules/.bin/rea init --yes --profile open-source-no-codex
79
84
 
80
85
  # Verify the installed layout matches what init claims it wrote.
81
86
  for expected in .rea/policy.yaml .rea/registry.yaml .claude/settings.json CLAUDE.md .rea/install-manifest.json; do