@bookedsolid/rea 0.28.1 → 0.29.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.
@@ -114,6 +114,24 @@ Consumer projects may extend the roster via `.rea/agents/` and profile YAMLs, bu
114
114
  4. Delegate with full context — include file paths, constraints from policy.yaml, acceptance criteria, and the commit-discipline note above
115
115
  5. Verify outputs before reporting completion — do not trust agent summaries at face value. Read the files, check git status, confirm the build.
116
116
 
117
+ ## Self-review when the orchestrator implements directly (0.29.0+)
118
+
119
+ There are sessions where the orchestrator must implement work itself instead of dispatching:
120
+
121
+ - Subagent dispatch is unavailable (no Task tool in the current harness, exempt-subagent scenario).
122
+ - The task is narrowly scoped to a single small surface where the dispatch overhead exceeds the implementation cost.
123
+ - A codex round between specialist hand-offs is being used as the de facto specialist tier (the "Option C" iteration pattern from the 0.29.0 marathon).
124
+
125
+ In every such case, you MUST still apply the specialist discipline that delegation would have enforced. This is not optional — the structural risk of "one Opus turn implements five surfaces" is exactly the failure mode that principal-engineer review caught in the 0.28.0 cycle (manifest glob-injection P1 + cache-staleness P2, both pre-commit). Reach the same closure shape by:
126
+
127
+ 1. **Name the specialists you are channeling.** Before each surface, state which specialist's discipline applies (e.g. "shell-scripting-specialist + adversarial-test-specialist for the bash gate corpus; typescript-specialist for the CLI; platform-architect for the workflow"). State it out loud so the user can spot a mis-cast role.
128
+ 2. **Codex round between surfaces, not just at the end.** A single end-of-build codex round across 5 surfaces buries P1s in noise. One round per surface keeps the signal sharp. The 0.27.0 direct-Bash codex CLI is cheap enough at one Opus turn per round to make this routine.
129
+ 3. **Explicit threat-model framing for security-tier changes.** When patching a hook, name the bypass class, the conservative-vs-narrow reading, and the sibling shapes the class implies. Refuse to commit until the corpus enumerates every shape the class includes.
130
+ 4. **Single-commit-per-PR discipline still applies.** Squash local work before push. The pre-push gate's stateless codex review runs once against the squashed diff; granular commits multiply the review burden without surfacing new findings.
131
+ 5. **Defer ruthlessly.** Trimmed-scope greenlights from the user are a maximum, not a minimum. The marathon's 0.28.0 lesson was "principal-engineer trimmed the 11-item plate to 6 with crisp deferral reasons." Apply the same lens during direct-implementation: if surface 6 needs structural rework, defer it to the next minor with the reason in the changeset rather than ship a half-baked closure.
132
+
133
+ A self-review checkpoint after each surface (read the diff back, run the targeted tests, fire codex against the working tree) IS the specialist tier when no subagent is in the path. Skip the checkpoint and the structural lesson resets.
134
+
117
135
  ## The Plan / Build / Review Loop (default workflow)
118
136
 
119
137
  REA's default engineering workflow is three-legged, with Review performed by a different model than Build:
@@ -83,3 +83,4 @@ export type { AuditRecord, EmissionSource } from '../gateway/middleware/audit-ty
83
83
  export { Tier, InvocationStatus } from '../policy/types.js';
84
84
  export { CODEX_REVIEW_TOOL_NAME, CODEX_REVIEW_SERVER_NAME, type CodexVerdict, type CodexReviewMetadata, } from './codex-event.js';
85
85
  export { LOCAL_REVIEW_TOOL_NAME, LOCAL_REVIEW_SKIPPED_OVERRIDE_TOOL_NAME, LOCAL_REVIEW_SKIPPED_UNAVAILABLE_TOOL_NAME, LOCAL_REVIEW_PREFLIGHT_SKIPPED_TOOL_NAME, LOCAL_REVIEW_SERVER_NAME, type LocalReviewVerdict, type LocalReviewMetadata, type LocalReviewSkippedOverrideMetadata, type LocalReviewSkippedUnavailableMetadata, } from './local-review-event.js';
86
+ export { DELEGATION_SIGNAL_TOOL_NAME, DELEGATION_SIGNAL_SERVER_NAME, DELEGATION_SIGNAL_SCHEMA_VERSION, DelegationSignalMetadataSchema, type DelegationTool, type DelegationSignalMetadata, type DelegationSignalMetadataParsed, } from './delegation-event.js';
@@ -204,3 +204,4 @@ export async function appendAuditRecord(baseDir, input) {
204
204
  export { Tier, InvocationStatus } from '../policy/types.js';
205
205
  export { CODEX_REVIEW_TOOL_NAME, CODEX_REVIEW_SERVER_NAME, } from './codex-event.js';
206
206
  export { LOCAL_REVIEW_TOOL_NAME, LOCAL_REVIEW_SKIPPED_OVERRIDE_TOOL_NAME, LOCAL_REVIEW_SKIPPED_UNAVAILABLE_TOOL_NAME, LOCAL_REVIEW_PREFLIGHT_SKIPPED_TOOL_NAME, LOCAL_REVIEW_SERVER_NAME, } from './local-review-event.js';
207
+ export { DELEGATION_SIGNAL_TOOL_NAME, DELEGATION_SIGNAL_SERVER_NAME, DELEGATION_SIGNAL_SCHEMA_VERSION, DelegationSignalMetadataSchema, } from './delegation-event.js';
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Single source of truth for the `rea.delegation_signal` audit event shape
3
+ * (0.29.0+).
4
+ *
5
+ * 0.29.0 — delegation-telemetry MVP. Claude Code's PreToolUse hook tree
6
+ * gains a new matcher (`Agent|Skill`) that pipes a redacted, hashed
7
+ * record of every subagent dispatch and skill invocation into
8
+ * `.rea/audit.jsonl`. The signal is observational, not gating — it
9
+ * answers "which specialists is this session actually delegating to,
10
+ * and how often" without altering the autonomy tree.
11
+ *
12
+ * # The two delegation tools
13
+ *
14
+ * Current Claude Code exposes exactly two delegation surfaces:
15
+ *
16
+ * - `Agent` — dispatches a curated subagent (rea-orchestrator,
17
+ * code-reviewer, …). The agent name is at
18
+ * `tool_input.subagent_type`.
19
+ * - `Skill` — invokes a named skill (deep-dive, /loop, …). The skill
20
+ * name is at `tool_input.skill`.
21
+ *
22
+ * mcp-protocol-specialist verified BOTH payload paths against current
23
+ * Claude Code. A Skill that internally forks an Agent fires PreToolUse
24
+ * TWICE (Skill then Agent) for the same logical action; v1 records
25
+ * both — deduplication lives in the reader, not the writer.
26
+ *
27
+ * # Not `Task`
28
+ *
29
+ * In current Claude Code the tools are `Agent` and `Skill`. The names
30
+ * `TaskCreate`/`TaskList`/`TaskUpdate` belong to the unrelated todo-list
31
+ * tool surface and MUST NOT match. The settings.json matcher is
32
+ * `Agent|Skill` everywhere — anchored on a `^…$` boundary by the hook
33
+ * runtime, so `Agent` doesn't accidentally collide with hypothetical
34
+ * future tools named `Agentic…`.
35
+ *
36
+ * # Privacy invariant
37
+ *
38
+ * The raw `description` / `prompt` payload NEVER touches `.rea/audit.jsonl`
39
+ * — only its SHA-256 hash. The hash is collision-resistant identification
40
+ * (two identical prompts produce identical hashes, enabling
41
+ * delegation-pattern discovery) without persisting prompt content.
42
+ *
43
+ * The agent / skill name field DOES land in the audit log, but is run
44
+ * through `redactSecrets` first. A subagent_type that contains a
45
+ * planted credential string (synthetic AWS key, GitHub token, …) is
46
+ * replaced with `[REDACTED]` and the matching pattern names are
47
+ * appended to the record's `redacted_fields` envelope.
48
+ *
49
+ * # Provider seam (kept tiny)
50
+ *
51
+ * Unlike `rea.local_review`, this event does NOT have a `provider`
52
+ * field. The producer is always Claude Code's hook runtime and the
53
+ * `emission_source: 'rea-cli'` envelope is sufficient. If a future
54
+ * runtime (e.g. another agent host) wants to emit signals through the
55
+ * same channel, it writes the same shape with the same tool_name and
56
+ * relies on `session_id_observed` / `delegation_tool` for
57
+ * disambiguation.
58
+ *
59
+ * # Schema version
60
+ *
61
+ * The literal `schema_version: 1` is part of the metadata payload. Zod
62
+ * strict-mode rejects unknown fields, so a future v2 producer writing
63
+ * v2-only fields against a v1 consumer fails-loud rather than silently
64
+ * dropping data. Readers filter by `tool_name === 'rea.delegation_signal'`
65
+ * AND `metadata.schema_version === 1`.
66
+ */
67
+ import { z } from 'zod';
68
+ /**
69
+ * Canonical `tool_name` on the audit record envelope. Readers filter on
70
+ * this exact literal — anything else is a different event class.
71
+ */
72
+ export declare const DELEGATION_SIGNAL_TOOL_NAME: "rea.delegation_signal";
73
+ /**
74
+ * `server_name` envelope value. The signal originates from Claude Code's
75
+ * hook runtime, captured by `rea hook delegation-signal` and appended
76
+ * via the public audit-record API. Naming it `claude-code-hooks` makes
77
+ * the producer surface unambiguous in forensic queries (vs.
78
+ * `'rea'` which is used for first-party rea CLI events like
79
+ * `rea.local_review`).
80
+ */
81
+ export declare const DELEGATION_SIGNAL_SERVER_NAME: "claude-code-hooks";
82
+ /**
83
+ * Schema version literal. Bumped only when the metadata shape gains a
84
+ * non-backwards-compatible change. Adding optional fields does NOT bump
85
+ * the version — zod's strict mode rejects them, so any new field MUST
86
+ * either ship with a major-version bump OR have its zod parser updated
87
+ * in lockstep.
88
+ */
89
+ export declare const DELEGATION_SIGNAL_SCHEMA_VERSION: 1;
90
+ /**
91
+ * The two valid delegation-tool values. `Agent` and `Skill` are the
92
+ * exact tool names emitted by Claude Code's PreToolUse hook payload —
93
+ * anything else is a misclassification at the hook layer.
94
+ */
95
+ export type DelegationTool = 'Agent' | 'Skill';
96
+ /**
97
+ * Canonical metadata payload for `rea.delegation_signal`. Embedded
98
+ * under `metadata` on the audit record. The audit-record envelope
99
+ * itself supplies `tool_name`, `server_name`, `session_id`, `timestamp`,
100
+ * `prev_hash`, `hash`, `redacted_fields`, etc. — keep those out of
101
+ * metadata.
102
+ */
103
+ export interface DelegationSignalMetadata {
104
+ /**
105
+ * Always `1` for the 0.29.0 shape. Carried as a literal so future
106
+ * v2-aware readers can distinguish records they understand from
107
+ * records they don't.
108
+ */
109
+ schema_version: typeof DELEGATION_SIGNAL_SCHEMA_VERSION;
110
+ /**
111
+ * Which Claude Code surface fired the hook — `'Agent'` for the
112
+ * subagent dispatch tool, `'Skill'` for the skill invocation tool.
113
+ * The reader CLI groups records on `subagent_type` regardless of
114
+ * `delegation_tool` (a `deep-dive` skill and a `deep-dive` agent
115
+ * roll into the same bucket), but the field is retained for forensic
116
+ * queries that want to distinguish the two.
117
+ */
118
+ delegation_tool: DelegationTool;
119
+ /**
120
+ * For `Agent`: the value of `tool_input.subagent_type` at the hook
121
+ * (e.g. `'rea-orchestrator'`).
122
+ *
123
+ * For `Skill`: the value of `tool_input.skill` (e.g. `'deep-dive'`).
124
+ *
125
+ * Always passed through `redactSecrets` before landing here. If a
126
+ * planted secret pattern fires, this field is `'[REDACTED]'` and
127
+ * the matching pattern name appears in the record's
128
+ * `redacted_fields` envelope.
129
+ *
130
+ * Reader CLI groups records on this field.
131
+ */
132
+ subagent_type: string;
133
+ /**
134
+ * The session id Claude Code attached to the hook payload — the same
135
+ * value the harness uses for its own correlation. Captured verbatim
136
+ * so a future per-session breakdown (deferred to 0.29.1) can group
137
+ * records without scanning the entire chain.
138
+ *
139
+ * Distinct from the audit envelope's `session_id`, which uses the
140
+ * caller's session ("external" for the CLI subcommand). The
141
+ * envelope's `session_id` says WHO wrote the record; this field says
142
+ * WHO Claude Code thinks is delegating.
143
+ */
144
+ session_id_observed: string;
145
+ /**
146
+ * When the dispatching agent is itself a subagent, this is the
147
+ * parent's subagent_type at hook-fire time. Drawn from
148
+ * `CLAUDE_PARENT_SUBAGENT` / `tool_input.parent_subagent_type` when
149
+ * present; `null` for top-level dispatches.
150
+ *
151
+ * Like `subagent_type`, redacted before landing here.
152
+ */
153
+ parent_subagent_type: string | null;
154
+ /**
155
+ * SHA-256 hex digest of `tool_input.description` (Agent) or
156
+ * `tool_input.prompt` (Skill). When neither is present an empty
157
+ * string is hashed — the resulting digest is the well-known
158
+ * `e3b0c4...` constant, which readers can recognize as "no prompt".
159
+ *
160
+ * # Why hash, not redact
161
+ *
162
+ * The prompt is the actionable content of the delegation — it
163
+ * routinely names files, customers, internal URLs, half-finished
164
+ * thoughts. Redacting it via pattern-matching is best-effort; hashing
165
+ * it is total. The collision-resistance of SHA-256 still lets two
166
+ * identical prompts produce identical hashes, which is enough for
167
+ * the delegation-pattern queries this telemetry exists to support.
168
+ */
169
+ invocation_description_sha256: string;
170
+ /**
171
+ * ISO-8601 timestamp Claude Code attached to the hook event, when
172
+ * present. Distinct from the audit-record envelope's `timestamp`
173
+ * (which is the moment the CLI subcommand wrote the line). Both
174
+ * fields are useful: the envelope timestamp orders the chain,
175
+ * `hook_event_timestamp` orders the underlying events.
176
+ */
177
+ hook_event_timestamp?: string;
178
+ }
179
+ /**
180
+ * Strict-mode zod schema for the metadata payload. Unknown fields are
181
+ * rejected — a future v2 producer must bump `DELEGATION_SIGNAL_SCHEMA_VERSION`
182
+ * AND update this schema in the same commit, otherwise v1 readers fail
183
+ * loud rather than silently dropping new fields.
184
+ *
185
+ * The schema is exported so the CLI subcommand validates its OWN
186
+ * emitted metadata before passing it to `appendAuditRecord` — defense
187
+ * in depth against a future refactor that wires the field set
188
+ * incorrectly. (Same posture as `loadPolicy` self-validation.)
189
+ */
190
+ export declare const DelegationSignalMetadataSchema: z.ZodObject<{
191
+ schema_version: z.ZodLiteral<1>;
192
+ delegation_tool: z.ZodUnion<[z.ZodLiteral<"Agent">, z.ZodLiteral<"Skill">]>;
193
+ subagent_type: z.ZodString;
194
+ session_id_observed: z.ZodString;
195
+ parent_subagent_type: z.ZodUnion<[z.ZodString, z.ZodNull]>;
196
+ invocation_description_sha256: z.ZodString;
197
+ hook_event_timestamp: z.ZodOptional<z.ZodString>;
198
+ }, "strict", z.ZodTypeAny, {
199
+ schema_version: 1;
200
+ delegation_tool: "Agent" | "Skill";
201
+ subagent_type: string;
202
+ session_id_observed: string;
203
+ parent_subagent_type: string | null;
204
+ invocation_description_sha256: string;
205
+ hook_event_timestamp?: string | undefined;
206
+ }, {
207
+ schema_version: 1;
208
+ delegation_tool: "Agent" | "Skill";
209
+ subagent_type: string;
210
+ session_id_observed: string;
211
+ parent_subagent_type: string | null;
212
+ invocation_description_sha256: string;
213
+ hook_event_timestamp?: string | undefined;
214
+ }>;
215
+ export type DelegationSignalMetadataParsed = z.infer<typeof DelegationSignalMetadataSchema>;
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Single source of truth for the `rea.delegation_signal` audit event shape
3
+ * (0.29.0+).
4
+ *
5
+ * 0.29.0 — delegation-telemetry MVP. Claude Code's PreToolUse hook tree
6
+ * gains a new matcher (`Agent|Skill`) that pipes a redacted, hashed
7
+ * record of every subagent dispatch and skill invocation into
8
+ * `.rea/audit.jsonl`. The signal is observational, not gating — it
9
+ * answers "which specialists is this session actually delegating to,
10
+ * and how often" without altering the autonomy tree.
11
+ *
12
+ * # The two delegation tools
13
+ *
14
+ * Current Claude Code exposes exactly two delegation surfaces:
15
+ *
16
+ * - `Agent` — dispatches a curated subagent (rea-orchestrator,
17
+ * code-reviewer, …). The agent name is at
18
+ * `tool_input.subagent_type`.
19
+ * - `Skill` — invokes a named skill (deep-dive, /loop, …). The skill
20
+ * name is at `tool_input.skill`.
21
+ *
22
+ * mcp-protocol-specialist verified BOTH payload paths against current
23
+ * Claude Code. A Skill that internally forks an Agent fires PreToolUse
24
+ * TWICE (Skill then Agent) for the same logical action; v1 records
25
+ * both — deduplication lives in the reader, not the writer.
26
+ *
27
+ * # Not `Task`
28
+ *
29
+ * In current Claude Code the tools are `Agent` and `Skill`. The names
30
+ * `TaskCreate`/`TaskList`/`TaskUpdate` belong to the unrelated todo-list
31
+ * tool surface and MUST NOT match. The settings.json matcher is
32
+ * `Agent|Skill` everywhere — anchored on a `^…$` boundary by the hook
33
+ * runtime, so `Agent` doesn't accidentally collide with hypothetical
34
+ * future tools named `Agentic…`.
35
+ *
36
+ * # Privacy invariant
37
+ *
38
+ * The raw `description` / `prompt` payload NEVER touches `.rea/audit.jsonl`
39
+ * — only its SHA-256 hash. The hash is collision-resistant identification
40
+ * (two identical prompts produce identical hashes, enabling
41
+ * delegation-pattern discovery) without persisting prompt content.
42
+ *
43
+ * The agent / skill name field DOES land in the audit log, but is run
44
+ * through `redactSecrets` first. A subagent_type that contains a
45
+ * planted credential string (synthetic AWS key, GitHub token, …) is
46
+ * replaced with `[REDACTED]` and the matching pattern names are
47
+ * appended to the record's `redacted_fields` envelope.
48
+ *
49
+ * # Provider seam (kept tiny)
50
+ *
51
+ * Unlike `rea.local_review`, this event does NOT have a `provider`
52
+ * field. The producer is always Claude Code's hook runtime and the
53
+ * `emission_source: 'rea-cli'` envelope is sufficient. If a future
54
+ * runtime (e.g. another agent host) wants to emit signals through the
55
+ * same channel, it writes the same shape with the same tool_name and
56
+ * relies on `session_id_observed` / `delegation_tool` for
57
+ * disambiguation.
58
+ *
59
+ * # Schema version
60
+ *
61
+ * The literal `schema_version: 1` is part of the metadata payload. Zod
62
+ * strict-mode rejects unknown fields, so a future v2 producer writing
63
+ * v2-only fields against a v1 consumer fails-loud rather than silently
64
+ * dropping data. Readers filter by `tool_name === 'rea.delegation_signal'`
65
+ * AND `metadata.schema_version === 1`.
66
+ */
67
+ import { z } from 'zod';
68
+ /**
69
+ * Canonical `tool_name` on the audit record envelope. Readers filter on
70
+ * this exact literal — anything else is a different event class.
71
+ */
72
+ export const DELEGATION_SIGNAL_TOOL_NAME = 'rea.delegation_signal';
73
+ /**
74
+ * `server_name` envelope value. The signal originates from Claude Code's
75
+ * hook runtime, captured by `rea hook delegation-signal` and appended
76
+ * via the public audit-record API. Naming it `claude-code-hooks` makes
77
+ * the producer surface unambiguous in forensic queries (vs.
78
+ * `'rea'` which is used for first-party rea CLI events like
79
+ * `rea.local_review`).
80
+ */
81
+ export const DELEGATION_SIGNAL_SERVER_NAME = 'claude-code-hooks';
82
+ /**
83
+ * Schema version literal. Bumped only when the metadata shape gains a
84
+ * non-backwards-compatible change. Adding optional fields does NOT bump
85
+ * the version — zod's strict mode rejects them, so any new field MUST
86
+ * either ship with a major-version bump OR have its zod parser updated
87
+ * in lockstep.
88
+ */
89
+ export const DELEGATION_SIGNAL_SCHEMA_VERSION = 1;
90
+ /**
91
+ * Strict-mode zod schema for the metadata payload. Unknown fields are
92
+ * rejected — a future v2 producer must bump `DELEGATION_SIGNAL_SCHEMA_VERSION`
93
+ * AND update this schema in the same commit, otherwise v1 readers fail
94
+ * loud rather than silently dropping new fields.
95
+ *
96
+ * The schema is exported so the CLI subcommand validates its OWN
97
+ * emitted metadata before passing it to `appendAuditRecord` — defense
98
+ * in depth against a future refactor that wires the field set
99
+ * incorrectly. (Same posture as `loadPolicy` self-validation.)
100
+ */
101
+ export const DelegationSignalMetadataSchema = z
102
+ .object({
103
+ schema_version: z.literal(DELEGATION_SIGNAL_SCHEMA_VERSION),
104
+ delegation_tool: z.union([z.literal('Agent'), z.literal('Skill')]),
105
+ subagent_type: z.string(),
106
+ session_id_observed: z.string(),
107
+ parent_subagent_type: z.union([z.string(), z.null()]),
108
+ invocation_description_sha256: z
109
+ .string()
110
+ .regex(/^[0-9a-f]{64}$/, 'invocation_description_sha256 must be a lowercase 64-char hex SHA-256 digest'),
111
+ hook_event_timestamp: z.string().optional(),
112
+ })
113
+ .strict();
@@ -0,0 +1,113 @@
1
+ /**
2
+ * `rea audit specialists` — 0.29.0 reader CLI for delegation-telemetry.
3
+ *
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`).
8
+ *
9
+ * # Current-session-only in v1
10
+ *
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:
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.
18
+ *
19
+ * # Output shape
20
+ *
21
+ * subagent_type count last_seen (UTC)
22
+ * rea-orchestrator 12 2026-05-12T21:30:00Z
23
+ * code-reviewer 5 2026-05-12T21:28:00Z
24
+ * deep-dive 2 2026-05-12T21:14:00Z
25
+ *
26
+ * JSON mode prints `{ session_filter, records, groups }` where
27
+ * `records` is the raw filtered subset (for piping into jq) and
28
+ * `groups` is the per-subagent rollup.
29
+ */
30
+ import type { Command } from 'commander';
31
+ import { type DelegationTool } from '../audit/delegation-event.js';
32
+ export interface AuditSpecialistsOptions {
33
+ /** Emit a single JSON document on stdout instead of the table. */
34
+ json?: boolean;
35
+ /**
36
+ * 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`.
39
+ *
40
+ * - `string` → filter to records whose `session_id_observed` matches.
41
+ * - `null` → no filter (show all records).
42
+ * - `undefined` → derive from env.
43
+ */
44
+ sessionFilter?: string | null;
45
+ /** Override CWD. Tests set this; production uses `process.cwd()`. */
46
+ baseDir?: string;
47
+ }
48
+ interface DelegationGroup {
49
+ subagent_type: string;
50
+ count: number;
51
+ /** Latest envelope timestamp seen in the group. */
52
+ last_seen: string;
53
+ /** Breakdown of which delegation_tool fired (Agent vs. Skill). */
54
+ by_tool: Record<DelegationTool, number>;
55
+ }
56
+ interface DelegationRecord {
57
+ timestamp: string;
58
+ session_id_observed: string;
59
+ delegation_tool: DelegationTool;
60
+ subagent_type: string;
61
+ parent_subagent_type: string | null;
62
+ invocation_description_sha256: string;
63
+ hook_event_timestamp?: string;
64
+ }
65
+ interface AuditSpecialistsResult {
66
+ /** Which session filter was applied. `null` means "no filter". */
67
+ session_filter: string | null;
68
+ /** Was the filter derived from `CLAUDE_SESSION_ID` env? Informational. */
69
+ session_filter_source: 'env' | 'option' | 'none';
70
+ /** Raw matched records, in chain order. */
71
+ records: DelegationRecord[];
72
+ /** Per-subagent rollups, sorted by descending count then by name. */
73
+ groups: DelegationGroup[];
74
+ /**
75
+ * Files actually walked. v1 only walks `.rea/audit.jsonl`; future
76
+ * `--since` rotated-file support extends this.
77
+ */
78
+ files_scanned: string[];
79
+ }
80
+ /**
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.
84
+ */
85
+ export declare function loadDelegationRecords(baseDir: string, sessionFilter: string | null): Promise<{
86
+ records: DelegationRecord[];
87
+ filesScanned: string[];
88
+ }>;
89
+ /**
90
+ * Group records by `subagent_type`. Sorts by descending count, then
91
+ * alphabetical on tie. `last_seen` is the latest envelope timestamp in
92
+ * the group.
93
+ */
94
+ export declare function groupBySubagent(records: DelegationRecord[]): DelegationGroup[];
95
+ /**
96
+ * Computation-only entrypoint. Returns the full result so callers
97
+ * (CLI, tests) can render or assert. `runAuditSpecialists` is the thin
98
+ * commander wrapper that prints + exits.
99
+ */
100
+ export declare function computeAuditSpecialists(options?: AuditSpecialistsOptions): Promise<AuditSpecialistsResult>;
101
+ /**
102
+ * Commander entrypoint. Reads, renders, exits 0. The CLI is read-only
103
+ * — no audit-chain writes, no exit-code-as-verdict semantics.
104
+ */
105
+ export declare function runAuditSpecialists(options: AuditSpecialistsOptions): Promise<void>;
106
+ /**
107
+ * Attach the `specialists` subcommand to the `rea audit` command group.
108
+ * Exported as a registrar so `src/cli/index.ts` can wire it next to the
109
+ * existing `rotate` and `verify` subcommands without leaking commander
110
+ * knowledge into this module.
111
+ */
112
+ export declare function registerAuditSpecialistsSubcommand(auditCommand: Command): void;
113
+ export {};