@bookedsolid/rea 0.30.1 → 0.32.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 (51) hide show
  1. package/.husky/prepare-commit-msg +80 -6
  2. package/MIGRATING.md +24 -15
  3. package/dist/cli/audit-specialists.d.ts +106 -24
  4. package/dist/cli/audit-specialists.js +239 -64
  5. package/dist/cli/delegation-advisory.d.ts +161 -0
  6. package/dist/cli/delegation-advisory.js +433 -0
  7. package/dist/cli/doctor.d.ts +110 -39
  8. package/dist/cli/doctor.js +302 -90
  9. package/dist/cli/hook.d.ts +6 -0
  10. package/dist/cli/hook.js +45 -22
  11. package/dist/cli/index.js +1 -1
  12. package/dist/cli/install/settings-merge.js +25 -0
  13. package/dist/cli/roster.d.ts +119 -0
  14. package/dist/cli/roster.js +141 -0
  15. package/dist/hooks/_lib/halt-check.d.ts +78 -0
  16. package/dist/hooks/_lib/halt-check.js +106 -0
  17. package/dist/hooks/_lib/payload.d.ts +86 -0
  18. package/dist/hooks/_lib/payload.js +166 -0
  19. package/dist/hooks/_lib/segments.d.ts +100 -0
  20. package/dist/hooks/_lib/segments.js +444 -0
  21. package/dist/hooks/attribution-advisory/index.d.ts +72 -0
  22. package/dist/hooks/attribution-advisory/index.js +233 -0
  23. package/dist/hooks/bash-scanner/protected-scan.js +14 -2
  24. package/dist/hooks/pr-issue-link-gate/index.d.ts +91 -0
  25. package/dist/hooks/pr-issue-link-gate/index.js +127 -0
  26. package/dist/hooks/security-disclosure-gate/index.d.ts +91 -0
  27. package/dist/hooks/security-disclosure-gate/index.js +502 -0
  28. package/dist/policy/loader.d.ts +23 -0
  29. package/dist/policy/loader.js +46 -0
  30. package/dist/policy/profiles.d.ts +23 -0
  31. package/dist/policy/profiles.js +16 -0
  32. package/dist/policy/types.d.ts +61 -0
  33. package/hooks/_lib/protected-paths.sh +10 -3
  34. package/hooks/attribution-advisory.sh +139 -131
  35. package/hooks/delegation-advisory.sh +162 -0
  36. package/hooks/pr-issue-link-gate.sh +114 -45
  37. package/hooks/security-disclosure-gate.sh +148 -316
  38. package/hooks/settings-protection.sh +13 -9
  39. package/package.json +1 -1
  40. package/profiles/bst-internal-no-codex.yaml +12 -0
  41. package/profiles/bst-internal.yaml +13 -0
  42. package/profiles/client-engagement.yaml +11 -0
  43. package/profiles/lit-wc.yaml +10 -0
  44. package/profiles/minimal.yaml +11 -0
  45. package/profiles/open-source-no-codex.yaml +11 -0
  46. package/profiles/open-source.yaml +11 -0
  47. package/templates/attribution-advisory.dogfood-staged.sh +170 -0
  48. package/templates/pr-issue-link-gate.dogfood-staged.sh +134 -0
  49. package/templates/prepare-commit-msg.husky.sh +80 -6
  50. package/templates/security-disclosure-gate.dogfood-staged.sh +171 -0
  51. package/templates/settings-protection.dogfood.patch +58 -0
@@ -36,28 +36,65 @@ set -u
36
36
  COMMIT_MSG_FILE="${1:-}"
37
37
  COMMIT_SOURCE="${2:-}"
38
38
 
39
+ REA_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
40
+
41
+ # Forward declaration — the extension-chain runner is defined further
42
+ # down (after $REA_ROOT is set so the dir lookup is anchored). We call
43
+ # it from every "augmenter skipped" exit point so consumer fragments
44
+ # under .husky/prepare-commit-msg.d/* run regardless of whether rea's
45
+ # own augmenter ran. The function fires fragments in lex order,
46
+ # logs-and-continues on non-zero exits, and is a no-op if the dir is
47
+ # absent or empty.
48
+ #
49
+ # 0.32.0 Phase 3: the pre-0.32.0 layout exited early at every
50
+ # precondition gate, which made the extension surface unreachable
51
+ # when (a) attribution was disabled, (b) HALT was active, or (c)
52
+ # REA_SKIP_ATTRIBUTION was set. The new layout runs the chain at the
53
+ # end of every exit path EXCEPT when the message file itself is
54
+ # missing/unparseable (no point running fragments against a path that
55
+ # doesn't exist).
56
+ run_extension_chain() {
57
+ ext_dir="${REA_ROOT}/.husky/prepare-commit-msg.d"
58
+ if [ -d "$ext_dir" ]; then
59
+ for frag in "$ext_dir"/*; do
60
+ [ -e "$frag" ] || continue
61
+ [ -f "$frag" ] || continue
62
+ [ -x "$frag" ] || continue
63
+ if ! "$frag" "$COMMIT_MSG_FILE" "$COMMIT_SOURCE"; then
64
+ printf 'rea: prepare-commit-msg.d fragment exited non-zero: %s (continuing)\n' \
65
+ "$(basename "$frag")" >&2
66
+ fi
67
+ done
68
+ fi
69
+ }
70
+
39
71
  # Skip conditions: any missing precondition exits 0 silently. The hook
40
72
  # is purely additive; refusing here would break commits with no upside.
41
73
 
42
- # Missing message file → nothing to augment.
74
+ # Missing message file → nothing to augment AND nothing for fragments
75
+ # to act on either. Exit immediately without running the chain.
43
76
  if [ -z "$COMMIT_MSG_FILE" ] || [ ! -f "$COMMIT_MSG_FILE" ]; then
44
77
  exit 0
45
78
  fi
46
79
 
47
- # Per-invocation override.
80
+ # Per-invocation override — skip the augmenter, but still run consumer
81
+ # fragments. The flag is named REA_SKIP_ATTRIBUTION, not REA_SKIP_HOOK,
82
+ # precisely so the rest of the chain runs.
48
83
  if [ -n "${REA_SKIP_ATTRIBUTION:-}" ]; then
84
+ run_extension_chain
49
85
  exit 0
50
86
  fi
51
87
 
52
- REA_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
53
-
54
- # HALT kill switch refuse to mutate anything while frozen.
88
+ # HALT kill switch refuse to mutate anything while frozen. The
89
+ # extension chain is also skipped under HALT: a frozen system means
90
+ # "no agent-side actions" and consumer fragments are agent-side too.
55
91
  if [ -f "${REA_ROOT}/.rea/HALT" ]; then
56
92
  exit 0
57
93
  fi
58
94
 
59
95
  POLICY_FILE="${REA_ROOT}/.rea/policy.yaml"
60
96
  if [ ! -f "$POLICY_FILE" ]; then
97
+ run_extension_chain
61
98
  exit 0
62
99
  fi
63
100
 
@@ -172,6 +209,7 @@ print(enabled); print(name); print(email); print(skip_merge)
172
209
  PY
173
210
  )
174
211
  if [ -z "$CO_AUTHOR_PARSE" ]; then
212
+ run_extension_chain
175
213
  exit 0
176
214
  fi
177
215
  ENABLED=$(printf '%s\n' "$CO_AUTHOR_PARSE" | sed -n '1p')
@@ -179,11 +217,15 @@ PY
179
217
  CO_EMAIL=$(printf '%s\n' "$CO_AUTHOR_PARSE" | sed -n '3p')
180
218
  SKIP_MERGE=$(printf '%s\n' "$CO_AUTHOR_PARSE" | sed -n '4p')
181
219
  else
182
- # Neither rea CLI nor python3 reachable — silent no-op.
220
+ # Neither rea CLI nor python3 reachable — silent no-op for the
221
+ # augmenter, but still run consumer fragments. The chain doesn't
222
+ # need policy values; it just runs `.husky/prepare-commit-msg.d/*`.
223
+ run_extension_chain
183
224
  exit 0
184
225
  fi
185
226
 
186
227
  if [ "$ENABLED" != "true" ]; then
228
+ run_extension_chain
187
229
  exit 0
188
230
  fi
189
231
 
@@ -204,11 +246,13 @@ if [ -z "$CO_NAME" ] || [ -z "$CO_EMAIL" ]; then
204
246
  "$([ -z "$CO_NAME" ] && [ -z "$CO_EMAIL" ] && printf '+')" \
205
247
  "$([ -z "$CO_EMAIL" ] && printf email)" >&2
206
248
  printf 'rea: edit .rea/policy.yaml — set name + email, OR set enabled: false.\n' >&2
249
+ run_extension_chain
207
250
  exit 0
208
251
  fi
209
252
 
210
253
  # skip_merge: true → skip when commit source is 'merge'.
211
254
  if [ "$SKIP_MERGE" = "true" ] && [ "$COMMIT_SOURCE" = "merge" ]; then
255
+ run_extension_chain
212
256
  exit 0
213
257
  fi
214
258
 
@@ -226,6 +270,7 @@ LOWER_EMAIL=$(printf '%s' "$CO_EMAIL" | tr '[:upper:]' '[:lower:]')
226
270
  ESCAPED_EMAIL=$(printf '%s' "$LOWER_EMAIL" | sed 's/[.[\*^$(){}+?|]/\\&/g')
227
271
  if grep -iE "^co-authored-by:[[:space:]]*[^<]*<${ESCAPED_EMAIL}>[[:space:]]*$" \
228
272
  "$COMMIT_MSG_FILE" >/dev/null 2>&1; then
273
+ run_extension_chain
229
274
  exit 0
230
275
  fi
231
276
 
@@ -311,4 +356,33 @@ awk '
311
356
  } > "${COMMIT_MSG_FILE}.rea-tmp" && mv "${COMMIT_MSG_FILE}.rea-tmp" "$COMMIT_MSG_FILE"
312
357
 
313
358
  rm -f "$TMP_BODY_TRIMMED"
359
+
360
+ # ── Extension-hook chaining ───────────────────────────────────────────────────
361
+ # 0.32.0 — `.husky/prepare-commit-msg.d/*` extension surface mirrors
362
+ # the `.husky/commit-msg.d/*` and `.husky/pre-push.d/*` patterns from
363
+ # 0.13.0. Source every executable file under
364
+ # `.husky/prepare-commit-msg.d/` in lexical order. Missing directory
365
+ # is a no-op (backward compatible). Each fragment receives the same
366
+ # `$1` (commit message file path) and `$2` (commit source) that git
367
+ # delivered to this hook so consumers can layer on their own
368
+ # augmenters (lint-staged --on-prepare, branch-name-injection,
369
+ # ticket-reference-prepend, …) without losing rea coverage.
370
+ #
371
+ # Fragments run AFTER rea's attribution augmenter so the
372
+ # `Co-Authored-By` trailer is already in the file before any consumer
373
+ # fragment reads it; that lets a fragment reorder trailers, dedupe,
374
+ # or run its own template substitution against the augmented body.
375
+ #
376
+ # A non-zero exit from a fragment does NOT fail the commit — this
377
+ # hook is purely additive (its bash counterpart `commit-msg` is the
378
+ # blocking gate). We log the failure to stderr and continue so a
379
+ # broken consumer fragment can't take down `git commit`.
380
+ #
381
+ # The actual chain body lives in `run_extension_chain` (defined near
382
+ # the top of the file). The reason for the early definition: several
383
+ # augmenter-skip exit paths (enabled: false, missing identity, idempo-
384
+ # tency hit, skip_merge match) need to run the chain too, so consumer
385
+ # fragments fire regardless of whether rea's own augmenter activated.
386
+ run_extension_chain
387
+
314
388
  exit 0
package/MIGRATING.md CHANGED
@@ -78,13 +78,16 @@ are on the vanilla-git path — install husky first.
78
78
  The only files rea touches are explicitly enumerated above. Everything
79
79
  else is the consumer's surface.
80
80
 
81
- ## Extension surface (added in 0.13.0)
81
+ ## Extension surface (added in 0.13.0; expanded in 0.32.0)
82
82
 
83
- `.husky/pre-push.d/*` and `.husky/commit-msg.d/*` are the
84
- **upgrade-safe** place to layer your own gates. Files in those
85
- directories must be executable; rea sources them in lex order AFTER
86
- its own governance work succeeds. A non-zero exit from any fragment
87
- fails the hook (matches husky's normal chaining).
83
+ `.husky/pre-push.d/*`, `.husky/commit-msg.d/*`, and (as of 0.32.0)
84
+ `.husky/prepare-commit-msg.d/*` are the **upgrade-safe** place to
85
+ layer your own gates. Files in those directories must be executable;
86
+ rea sources them in lex order AFTER its own governance work succeeds.
87
+ A non-zero exit from any fragment fails the hook (matches husky's
88
+ normal chaining) — EXCEPT for the `prepare-commit-msg.d/*` lane,
89
+ which logs and continues so a broken fragment can't take down `git
90
+ commit`.
88
91
 
89
92
  - Fragment receives positional args from git (`<remote-name> <remote-url>`
90
93
  for pre-push, `<commit-msg-file>` for commit-msg).
@@ -158,26 +161,32 @@ Two paths, depending on whether you intend to use the rea augmenter.
158
161
 
159
162
  **Path A — you want the augmenter (Co-Authored-By trailer)**
160
163
 
161
- Move your branch-prefix logic into rea's chained body. As of 0.30.0
162
- rea's prepare-commit-msg body does NOT support `.husky/prepare-commit-msg.d/*`
163
- fragments yet (it's on the 0.31.0 roadmap). For now, port the logic
164
- into a wrapper invoked by `commit-msg.d` instead:
164
+ Move your branch-prefix logic into a `.husky/prepare-commit-msg.d/*`
165
+ fragment. As of **0.32.0** rea's prepare-commit-msg body sources every
166
+ executable file in `.husky/prepare-commit-msg.d/` in lexical order
167
+ AFTER its own attribution augmenter runs (mirrors the
168
+ `commit-msg.d/*` and `pre-push.d/*` extension surfaces from 0.13.0).
169
+ Each fragment receives the same `$1` (commit-message file path) and
170
+ `$2` (commit source) git delivered to the hook:
165
171
 
166
172
  ```bash
167
- mkdir -p .husky/commit-msg.d
168
- cat > .husky/commit-msg.d/00-branch-prefix <<'EOF'
173
+ mkdir -p .husky/prepare-commit-msg.d
174
+ cat > .husky/prepare-commit-msg.d/00-branch-prefix <<'EOF'
169
175
  #!/bin/sh
170
- # Branch-prefix logic moved from prepare-commit-msg to commit-msg.d
171
- # (runs AFTER rea's augmenter, before the commit is finalized).
176
+ # Runs AFTER rea's Co-Authored-By augmenter. $1 = commit-msg file.
172
177
  BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown)
173
178
  case $(head -1 "$1") in
174
179
  "[$BRANCH]"*) ;; # already prefixed
175
180
  *) printf '[%s] %s' "$BRANCH" "$(cat "$1")" > "$1" ;;
176
181
  esac
177
182
  EOF
178
- chmod +x .husky/commit-msg.d/00-branch-prefix
183
+ chmod +x .husky/prepare-commit-msg.d/00-branch-prefix
179
184
  ```
180
185
 
186
+ A non-zero exit from a fragment does NOT fail the commit (the augmenter
187
+ hook is purely additive; the blocking gate is `commit-msg`). Broken
188
+ fragments log to stderr and the hook continues.
189
+
181
190
  Then remove the old `.husky/prepare-commit-msg`:
182
191
 
183
192
  ```bash
@@ -1,20 +1,33 @@
1
1
  /**
2
- * `rea audit specialists` — 0.29.0 reader CLI for delegation-telemetry.
2
+ * `rea audit specialists` — reader CLI for delegation-telemetry
3
+ * (0.29.0; `--since` / `--session` added 0.31.0).
3
4
  *
4
- * Walks `.rea/audit.jsonl`, filters records by
5
- * `tool_name === 'rea.delegation_signal'`, groups by
6
- * `metadata.subagent_type`, and prints a table (default) or JSON
7
- * document (`--json`).
5
+ * Walks `.rea/audit.jsonl` (plus any rotated files when `--since` is
6
+ * given), filters records by `tool_name === 'rea.delegation_signal'`,
7
+ * groups by `metadata.subagent_type`, and prints a table (default) or
8
+ * JSON document (`--json`).
8
9
  *
9
- * # Current-session-only in v1
10
+ * # Session filtering
10
11
  *
11
- * v1 has NO `--since` flag and NO `--session=ID` flag. The principal-
12
- * engineer scope-cut deferred both to 0.29.1. The filter is:
12
+ * Three ways to scope, in precedence order:
13
13
  *
14
- * - If `CLAUDE_SESSION_ID` is set, include only records whose
15
- * `metadata.session_id_observed` matches.
16
- * - Otherwise, include all records in the chain and print a note so
17
- * the operator knows what they're seeing.
14
+ * 1. `--session <id>` — explicit. Filters to records whose
15
+ * `metadata.session_id_observed` matches `<id>`. The literal
16
+ * `--session all` disables filtering entirely (show every
17
+ * session). Wins over the env var.
18
+ * 2. `$CLAUDE_SESSION_ID` — when set and `--session` is omitted,
19
+ * filters to the current Claude Code session.
20
+ * 3. neither — no filter; every record in the walked files is shown,
21
+ * and a note tells the operator what they're seeing.
22
+ *
23
+ * # `--since <rotated-file>`
24
+ *
25
+ * By default the reader walks only the current `.rea/audit.jsonl`.
26
+ * `--since audit-YYYYMMDD-HHMMSS.jsonl` extends the walk backward: the
27
+ * named rotated file and every rotated file after it (timestamp-
28
+ * ascending) are scanned, then the current `audit.jsonl` as the tail.
29
+ * Mirrors `rea audit verify --since`. The filename must match the
30
+ * canonical rotated-audit shape or the CLI exits 1.
18
31
  *
19
32
  * # Output shape
20
33
  *
@@ -23,8 +36,8 @@
23
36
  * code-reviewer 5 2026-05-12T21:28:00Z
24
37
  * deep-dive 2 2026-05-12T21:14:00Z
25
38
  *
26
- * JSON mode prints `{ session_filter, records, groups }` where
27
- * `records` is the raw filtered subset (for piping into jq) and
39
+ * JSON mode prints `{ session_filter, records, groups, files_scanned }`
40
+ * where `records` is the raw filtered subset (for piping into jq) and
28
41
  * `groups` is the per-subagent rollup.
29
42
  */
30
43
  import type { Command } from 'commander';
@@ -34,17 +47,48 @@ export interface AuditSpecialistsOptions {
34
47
  json?: boolean;
35
48
  /**
36
49
  * Override session filtering. Production callers omit (the CLI reads
37
- * `CLAUDE_SESSION_ID` from the env). Tests set this so they don't
38
- * mutate `process.env`.
50
+ * `--session` then `CLAUDE_SESSION_ID` from the env). Tests set this
51
+ * so they don't mutate `process.env`.
39
52
  *
40
53
  * - `string` → filter to records whose `session_id_observed` matches.
41
54
  * - `null` → no filter (show all records).
42
- * - `undefined` → derive from env.
55
+ * - `undefined` → derive from `--session` flag then env.
43
56
  */
44
57
  sessionFilter?: string | null;
58
+ /**
59
+ * 0.31.0 — explicit `--session <id>` flag value. Resolution rules:
60
+ *
61
+ * - `'all'` (case-insensitive) → no filter (equivalent to
62
+ * `sessionFilter: null`), `session_filter_source: 'option'`.
63
+ * - any other non-empty string → filter to that session,
64
+ * `session_filter_source: 'option'`.
65
+ * - `undefined` → fall through to `sessionFilter`, then env.
66
+ *
67
+ * `sessionFilter` (the test seam) still wins over this when both are
68
+ * set — tests inject `sessionFilter` directly and don't pass
69
+ * `sessionOption`.
70
+ */
71
+ sessionOption?: string;
72
+ /**
73
+ * 0.31.0 — `--since <rotated-file>` flag value. When set, the named
74
+ * rotated audit file plus every later rotated file (timestamp-
75
+ * ascending) are walked before the current `.rea/audit.jsonl`.
76
+ * Must match `audit-YYYYMMDD-HHMMSS(-N).jsonl`. Validation failure
77
+ * throws `AuditSpecialistsSinceError` so the commander wrapper can
78
+ * exit 1 with a clear message.
79
+ */
80
+ since?: string;
45
81
  /** Override CWD. Tests set this; production uses `process.cwd()`. */
46
82
  baseDir?: string;
47
83
  }
84
+ /**
85
+ * Thrown by `computeAuditSpecialists` when `--since` names something
86
+ * that is not a valid rotated-audit basename, or names a rotated file
87
+ * that does not exist. The commander wrapper catches it and exits 1.
88
+ */
89
+ export declare class AuditSpecialistsSinceError extends Error {
90
+ constructor(message: string);
91
+ }
48
92
  interface DelegationGroup {
49
93
  subagent_type: string;
50
94
  count: number;
@@ -53,7 +97,7 @@ interface DelegationGroup {
53
97
  /** Breakdown of which delegation_tool fired (Agent vs. Skill). */
54
98
  by_tool: Record<DelegationTool, number>;
55
99
  }
56
- interface DelegationRecord {
100
+ export interface DelegationRecord {
57
101
  timestamp: string;
58
102
  session_id_observed: string;
59
103
  delegation_tool: DelegationTool;
@@ -78,11 +122,48 @@ interface AuditSpecialistsResult {
78
122
  files_scanned: string[];
79
123
  }
80
124
  /**
81
- * Read the audit file and return delegation records (filtered + parsed
82
- * into a reader-friendly shape). Malformed lines are skipped silently
83
- * `rea audit verify` is the right tool for chain integrity.
125
+ * List rotated audit files in `.rea/`, timestamp-ascending. Filenames
126
+ * follow `audit-YYYYMMDD-HHMMSS(-N).jsonl`. Sorted via
127
+ * `rotatedAuditSortKey` the `YYYYMMDD-HHMMSS` block lexically (it is
128
+ * fixed-width zero-padded, so lexical == chronological) then the `-N`
129
+ * intra-second suffix NUMERICALLY (a plain lexical sort of the whole
130
+ * basename misorders two-digit suffixes — see `rotatedAuditSortKey`).
131
+ * Mirrors the private helper in `audit.ts` — kept local so this reader
132
+ * doesn't import the verify command's internals.
133
+ *
134
+ * Exported (0.31.0 round-2 P3) so `delegation-advisory.ts` can resolve
135
+ * the rotated-file set without duplicating the `ROTATED_AUDIT_RE` glob:
136
+ * the advisory's "did this session delegate" predicate must scan rotated
137
+ * segments too, or a delegation recorded before an audit rotation is
138
+ * invisible to the nudge and the session gets a false-positive advisory.
139
+ */
140
+ export declare function listRotatedAuditFiles(reaDir: string): Promise<string[]>;
141
+ /**
142
+ * Resolve the ordered list of absolute audit-file paths to walk.
143
+ *
144
+ * - `since === undefined` → just the current `.rea/audit.jsonl` (when
145
+ * it exists). Pre-0.31.0 behavior.
146
+ * - `since` set → validate it names a real rotated file, then walk
147
+ * that file + every later rotated file (timestamp-ascending), with
148
+ * the current `audit.jsonl` as the tail.
149
+ *
150
+ * The current `audit.jsonl` is included at the END of the walk
151
+ * whenever it exists — it is always the newest segment of the chain.
152
+ * A `--since` that names a non-rotated string, or a rotated file that
153
+ * isn't present on disk, throws `AuditSpecialistsSinceError`.
154
+ */
155
+ export declare function resolveAuditFileWalk(baseDir: string, since: string | undefined): Promise<string[]>;
156
+ /**
157
+ * Read the audit file(s) and return delegation records (filtered +
158
+ * parsed into a reader-friendly shape). Malformed lines are skipped
159
+ * silently — `rea audit verify` is the right tool for chain integrity.
160
+ *
161
+ * `since` defaults to `undefined` (current `.rea/audit.jsonl` only) so
162
+ * existing callers — including `computeDelegationAdvisory` in
163
+ * `delegation-advisory.ts` — keep their pre-0.31.0 single-file
164
+ * behavior without passing the argument.
84
165
  */
85
- export declare function loadDelegationRecords(baseDir: string, sessionFilter: string | null): Promise<{
166
+ export declare function loadDelegationRecords(baseDir: string, sessionFilter: string | null, since?: string): Promise<{
86
167
  records: DelegationRecord[];
87
168
  filesScanned: string[];
88
169
  }>;
@@ -99,8 +180,9 @@ export declare function groupBySubagent(records: DelegationRecord[]): Delegation
99
180
  */
100
181
  export declare function computeAuditSpecialists(options?: AuditSpecialistsOptions): Promise<AuditSpecialistsResult>;
101
182
  /**
102
- * Commander entrypoint. Reads, renders, exits 0. The CLI is read-only
103
- * no audit-chain writes, no exit-code-as-verdict semantics.
183
+ * Commander entrypoint. Reads, renders, exits 0 on success / 1 when
184
+ * `--since` is malformed or names a missing rotated file. The CLI is
185
+ * read-only — no audit-chain writes.
104
186
  */
105
187
  export declare function runAuditSpecialists(options: AuditSpecialistsOptions): Promise<void>;
106
188
  /**