@bli-cockpit/telemetry-core 0.1.13 → 0.1.15
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.
- package/dist/decision-event-id.d.ts +52 -0
- package/dist/decision-event-id.js +73 -0
- package/dist/decision-event.d.ts +610 -0
- package/dist/decision-event.js +385 -0
- package/dist/evidence-upload.d.ts +3 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fold a label to its comparison form.
|
|
3
|
+
*
|
|
4
|
+
* Case and surrounding whitespace are not identity — `Brandon`, `brandon` and
|
|
5
|
+
* ` brandon ` are one actor, and treating them as three would defeat the whole
|
|
6
|
+
* point of clustering. Nothing else is touched. In particular no punctuation is
|
|
7
|
+
* stripped and no prefix is trimmed, because every such rule is a guess about
|
|
8
|
+
* what two labels have in common, and guessing is exactly what the person
|
|
9
|
+
* resolver refuses to do. This is the same case-fold-then-compare the resolver
|
|
10
|
+
* itself uses, kept deliberately as dumb as that one.
|
|
11
|
+
*/
|
|
12
|
+
export declare function normalizeActorLabel(rawLabel: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* The stable identity of an actor nobody can name yet.
|
|
15
|
+
*
|
|
16
|
+
* Derived from the label alone, so every event bearing that label — across
|
|
17
|
+
* files, across sources, across backfill runs, months apart — clusters under
|
|
18
|
+
* one id. That is what lets a person later say "this one is Brandon" once and
|
|
19
|
+
* have it mean something for all of them.
|
|
20
|
+
*
|
|
21
|
+
* The label kind is deliberately NOT part of the input. The same human appears
|
|
22
|
+
* as a queue field in one artifact and a brief variable in another; folding the
|
|
23
|
+
* kind in would split one stranger into several and put the corpus right back
|
|
24
|
+
* where it started.
|
|
25
|
+
*/
|
|
26
|
+
export declare function unresolvedActorId(rawLabel: string): string;
|
|
27
|
+
export interface DecisionEventIdInput {
|
|
28
|
+
captureSource: string;
|
|
29
|
+
/** Hash of the file this was read out of; null for a live hook firing. */
|
|
30
|
+
sourceArtifactHashSha256: string | null;
|
|
31
|
+
/** Line within that file; null when the artifact is not line-oriented. */
|
|
32
|
+
sourceLineNumber: number | null;
|
|
33
|
+
kind: string;
|
|
34
|
+
/**
|
|
35
|
+
* Only for a live firing, which has no artifact to hash and no line to point
|
|
36
|
+
* at. Two hook firings a second apart are two events, and without this they
|
|
37
|
+
* would collapse into one.
|
|
38
|
+
*/
|
|
39
|
+
occurredAt?: string | null;
|
|
40
|
+
/** Also live-firing only: same hook, same instant, different subject. */
|
|
41
|
+
subjectDiscriminator?: string | null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The id of one decision event.
|
|
45
|
+
*
|
|
46
|
+
* Two consequences worth stating. Re-running the backfill is idempotent, because
|
|
47
|
+
* the same line of the same file always yields the same id. And an *edited*
|
|
48
|
+
* artifact produces a new id rather than mutating an old one, which keeps the
|
|
49
|
+
* store append-only in the same way jarvis_corrections is — history is added to,
|
|
50
|
+
* never rewritten.
|
|
51
|
+
*/
|
|
52
|
+
export declare function decisionEventId(input: DecisionEventIdInput): string;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// BLI-2425: the two ids a decision event depends on, derived the same way by
|
|
2
|
+
// every writer.
|
|
3
|
+
//
|
|
4
|
+
// Both are deterministic on purpose, and for the same reason: the historical
|
|
5
|
+
// backfill will be re-run. A hook that fires twice, a backfill script that dies
|
|
6
|
+
// halfway and starts over, a corrected parser replayed against the same files —
|
|
7
|
+
// all of those must land on the same rows rather than a second copy. Determinism
|
|
8
|
+
// is what makes "ingest is idempotent" a property of the data instead of a
|
|
9
|
+
// promise about the code.
|
|
10
|
+
//
|
|
11
|
+
// This lives beside the schema rather than in each writer because two writers
|
|
12
|
+
// deriving an id two ways is the same bug as two resolvers deciding who someone
|
|
13
|
+
// is: it produces two answers to a question that has one.
|
|
14
|
+
import { createHash } from "node:crypto";
|
|
15
|
+
/** How much of the digest we keep. Collision risk at 32 hex is not a concern at
|
|
16
|
+
* a corpus of thousands, and short ids stay readable in a URL and a log line. */
|
|
17
|
+
const ID_HEX_LENGTH = 32;
|
|
18
|
+
function sha256Hex(value) {
|
|
19
|
+
return createHash("sha256").update(value, "utf8").digest("hex");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Fold a label to its comparison form.
|
|
23
|
+
*
|
|
24
|
+
* Case and surrounding whitespace are not identity — `Brandon`, `brandon` and
|
|
25
|
+
* ` brandon ` are one actor, and treating them as three would defeat the whole
|
|
26
|
+
* point of clustering. Nothing else is touched. In particular no punctuation is
|
|
27
|
+
* stripped and no prefix is trimmed, because every such rule is a guess about
|
|
28
|
+
* what two labels have in common, and guessing is exactly what the person
|
|
29
|
+
* resolver refuses to do. This is the same case-fold-then-compare the resolver
|
|
30
|
+
* itself uses, kept deliberately as dumb as that one.
|
|
31
|
+
*/
|
|
32
|
+
export function normalizeActorLabel(rawLabel) {
|
|
33
|
+
return rawLabel.trim().toLowerCase();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The stable identity of an actor nobody can name yet.
|
|
37
|
+
*
|
|
38
|
+
* Derived from the label alone, so every event bearing that label — across
|
|
39
|
+
* files, across sources, across backfill runs, months apart — clusters under
|
|
40
|
+
* one id. That is what lets a person later say "this one is Brandon" once and
|
|
41
|
+
* have it mean something for all of them.
|
|
42
|
+
*
|
|
43
|
+
* The label kind is deliberately NOT part of the input. The same human appears
|
|
44
|
+
* as a queue field in one artifact and a brief variable in another; folding the
|
|
45
|
+
* kind in would split one stranger into several and put the corpus right back
|
|
46
|
+
* where it started.
|
|
47
|
+
*/
|
|
48
|
+
export function unresolvedActorId(rawLabel) {
|
|
49
|
+
const normalized = normalizeActorLabel(rawLabel);
|
|
50
|
+
return `ua_${sha256Hex(`decision-actor:${normalized}`).slice(0, ID_HEX_LENGTH)}`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The id of one decision event.
|
|
54
|
+
*
|
|
55
|
+
* Two consequences worth stating. Re-running the backfill is idempotent, because
|
|
56
|
+
* the same line of the same file always yields the same id. And an *edited*
|
|
57
|
+
* artifact produces a new id rather than mutating an old one, which keeps the
|
|
58
|
+
* store append-only in the same way jarvis_corrections is — history is added to,
|
|
59
|
+
* never rewritten.
|
|
60
|
+
*/
|
|
61
|
+
export function decisionEventId(input) {
|
|
62
|
+
const parts = [
|
|
63
|
+
input.captureSource,
|
|
64
|
+
input.sourceArtifactHashSha256 ?? "",
|
|
65
|
+
input.sourceLineNumber === null || input.sourceLineNumber === undefined
|
|
66
|
+
? ""
|
|
67
|
+
: String(input.sourceLineNumber),
|
|
68
|
+
input.kind,
|
|
69
|
+
input.occurredAt ?? "",
|
|
70
|
+
input.subjectDiscriminator ?? "",
|
|
71
|
+
];
|
|
72
|
+
return `de_${sha256Hex(parts.join("|")).slice(0, ID_HEX_LENGTH)}`;
|
|
73
|
+
}
|
|
@@ -0,0 +1,610 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const DECISION_EVENT_ENVELOPE_VERSION = "decision-event.v1";
|
|
3
|
+
/** A git commit sha. Sha256Schema is 64 hex and does not fit a 40-hex commit. */
|
|
4
|
+
export declare const CommitShaSchema: z.ZodString;
|
|
5
|
+
export declare const DecisionEventKindSchema: z.ZodEnum<{
|
|
6
|
+
delegation_issued: "delegation_issued";
|
|
7
|
+
review_verdict: "review_verdict";
|
|
8
|
+
human_approval: "human_approval";
|
|
9
|
+
intervention: "intervention";
|
|
10
|
+
takeover: "takeover";
|
|
11
|
+
rule_fired: "rule_fired";
|
|
12
|
+
}>;
|
|
13
|
+
export type DecisionEventKind = z.infer<typeof DecisionEventKindSchema>;
|
|
14
|
+
/**
|
|
15
|
+
* Which artifact this event was read out of.
|
|
16
|
+
*
|
|
17
|
+
* Deliberately its own enum rather than a member added to CaptureSourceSchema.
|
|
18
|
+
* That enum describes what a collector watched on a coding machine; these are
|
|
19
|
+
* files a manager's harness wrote as a side effect of managing. Sharing the
|
|
20
|
+
* enum would imply a shared envelope, and the whole reason this path exists is
|
|
21
|
+
* that decision records have no session, no work context and no capture
|
|
22
|
+
* provenance to fill.
|
|
23
|
+
*/
|
|
24
|
+
export declare const DecisionCaptureSourceSchema: z.ZodEnum<{
|
|
25
|
+
claude_state_verdict_json: "claude_state_verdict_json";
|
|
26
|
+
claude_state_queue_jsonl: "claude_state_queue_jsonl";
|
|
27
|
+
claude_state_worker_brief: "claude_state_worker_brief";
|
|
28
|
+
claude_state_retro_jsonl: "claude_state_retro_jsonl";
|
|
29
|
+
claude_state_bypass_log: "claude_state_bypass_log";
|
|
30
|
+
claude_state_merge_log: "claude_state_merge_log";
|
|
31
|
+
hook_runtime: "hook_runtime";
|
|
32
|
+
}>;
|
|
33
|
+
export type DecisionCaptureSource = z.infer<typeof DecisionCaptureSourceSchema>;
|
|
34
|
+
/** Which field of the source artifact produced this label. */
|
|
35
|
+
export declare const ActorLabelKindSchema: z.ZodEnum<{
|
|
36
|
+
email: "email";
|
|
37
|
+
github_login: "github_login";
|
|
38
|
+
linear_email: "linear_email";
|
|
39
|
+
linear_user_id: "linear_user_id";
|
|
40
|
+
reviewer_session: "reviewer_session";
|
|
41
|
+
hook_operator: "hook_operator";
|
|
42
|
+
queue_field: "queue_field";
|
|
43
|
+
brief_variable: "brief_variable";
|
|
44
|
+
}>;
|
|
45
|
+
export type ActorLabelKind = z.infer<typeof ActorLabelKindSchema>;
|
|
46
|
+
/** Whether the thing that acted was a person or something a person ran. */
|
|
47
|
+
export declare const ActorClassSchema: z.ZodEnum<{
|
|
48
|
+
human: "human";
|
|
49
|
+
agent: "agent";
|
|
50
|
+
}>;
|
|
51
|
+
export type ActorClass = z.infer<typeof ActorClassSchema>;
|
|
52
|
+
/**
|
|
53
|
+
* What the resolver concluded. These are the resolver's own four outcomes, kept
|
|
54
|
+
* apart rather than collapsed to null, because "nobody was named" and "somebody
|
|
55
|
+
* was named and we do not know them" call for different follow-up.
|
|
56
|
+
*/
|
|
57
|
+
export declare const ActorResolutionSchema: z.ZodEnum<{
|
|
58
|
+
resolved: "resolved";
|
|
59
|
+
unknown_person: "unknown_person";
|
|
60
|
+
ambiguous: "ambiguous";
|
|
61
|
+
no_person_named: "no_person_named";
|
|
62
|
+
}>;
|
|
63
|
+
export type ActorResolution = z.infer<typeof ActorResolutionSchema>;
|
|
64
|
+
/**
|
|
65
|
+
* Who acted — resolved to a person when we know them, and stably identified
|
|
66
|
+
* when we do not.
|
|
67
|
+
*
|
|
68
|
+
* The second half is the part worth reading. A backfill of ~2,400 historical
|
|
69
|
+
* decisions predates the person spine, so a large share of them name somebody
|
|
70
|
+
* the roster has never heard of. Collapsing all of those to one null makes the
|
|
71
|
+
* corpus useless: unresolved actor #1, #2 and #3 become indistinguishable and
|
|
72
|
+
* "who did what, and when" is unanswerable even in principle.
|
|
73
|
+
*
|
|
74
|
+
* So every distinct unresolved label gets `unresolved_actor_id`, derived
|
|
75
|
+
* deterministically from the normalized label (see `unresolvedActorId`). Events
|
|
76
|
+
* from the same label cluster under the same id forever. Later a human asserts
|
|
77
|
+
* "unresolved actor X is Brandon" and every event under that id resolves at
|
|
78
|
+
* once, retroactively, without a single event row being rewritten — the
|
|
79
|
+
* assertion lives in its own append-only alias table.
|
|
80
|
+
*
|
|
81
|
+
* That is not fuzzy matching sneaking back in. The ban is on the machine
|
|
82
|
+
* guessing; a human asserting an identity is the sanctioned escape hatch, and
|
|
83
|
+
* it is the same shape as jarvis_corrections, where the human outranks the
|
|
84
|
+
* pipeline and nothing is edited in place.
|
|
85
|
+
*/
|
|
86
|
+
export declare const ActorRefSchema: z.ZodObject<{
|
|
87
|
+
raw_label: z.ZodNullable<z.ZodString>;
|
|
88
|
+
label_kind: z.ZodNullable<z.ZodEnum<{
|
|
89
|
+
email: "email";
|
|
90
|
+
github_login: "github_login";
|
|
91
|
+
linear_email: "linear_email";
|
|
92
|
+
linear_user_id: "linear_user_id";
|
|
93
|
+
reviewer_session: "reviewer_session";
|
|
94
|
+
hook_operator: "hook_operator";
|
|
95
|
+
queue_field: "queue_field";
|
|
96
|
+
brief_variable: "brief_variable";
|
|
97
|
+
}>>;
|
|
98
|
+
actor_class: z.ZodEnum<{
|
|
99
|
+
human: "human";
|
|
100
|
+
agent: "agent";
|
|
101
|
+
}>;
|
|
102
|
+
resolution: z.ZodEnum<{
|
|
103
|
+
resolved: "resolved";
|
|
104
|
+
unknown_person: "unknown_person";
|
|
105
|
+
ambiguous: "ambiguous";
|
|
106
|
+
no_person_named: "no_person_named";
|
|
107
|
+
}>;
|
|
108
|
+
person_id: z.ZodNullable<z.ZodString>;
|
|
109
|
+
unresolved_actor_id: z.ZodNullable<z.ZodString>;
|
|
110
|
+
}, z.core.$strict>;
|
|
111
|
+
export type ActorRef = z.infer<typeof ActorRefSchema>;
|
|
112
|
+
/**
|
|
113
|
+
* The thing decided upon. `repo` is stored canonically as `owner/name` because
|
|
114
|
+
* the source artifacts disagree — the same pull request appears as
|
|
115
|
+
* `bli-cockpit` in the queue and `veetesh-glitch/bli-cockpit` in the verdict.
|
|
116
|
+
* Normalizing is an exact rule, never a prefix guess.
|
|
117
|
+
*/
|
|
118
|
+
export declare const DecisionSubjectSchema: z.ZodObject<{
|
|
119
|
+
repo: z.ZodNullable<z.ZodString>;
|
|
120
|
+
branch: z.ZodNullable<z.ZodString>;
|
|
121
|
+
ticket_id: z.ZodNullable<z.ZodString>;
|
|
122
|
+
pull_request_number: z.ZodNullable<z.ZodNumber>;
|
|
123
|
+
head_sha: z.ZodNullable<z.ZodString>;
|
|
124
|
+
}, z.core.$strict>;
|
|
125
|
+
export type DecisionSubject = z.infer<typeof DecisionSubjectSchema>;
|
|
126
|
+
/**
|
|
127
|
+
* Where to go to see the thing itself. Pointers, never content — the prose
|
|
128
|
+
* these point at is exactly what v1 refuses to carry.
|
|
129
|
+
*/
|
|
130
|
+
export declare const DecisionEvidencePointerSchema: z.ZodObject<{
|
|
131
|
+
kind: z.ZodEnum<{
|
|
132
|
+
claude_state_path: "claude_state_path";
|
|
133
|
+
github_pull_request: "github_pull_request";
|
|
134
|
+
linear_issue: "linear_issue";
|
|
135
|
+
commit: "commit";
|
|
136
|
+
reviewer_session_id: "reviewer_session_id";
|
|
137
|
+
}>;
|
|
138
|
+
locator: z.ZodString;
|
|
139
|
+
}, z.core.$strict>;
|
|
140
|
+
export type DecisionEvidencePointer = z.infer<typeof DecisionEvidencePointerSchema>;
|
|
141
|
+
export declare const DecisionOutcomeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
142
|
+
kind: z.ZodLiteral<"delegation_issued">;
|
|
143
|
+
eta_minutes: z.ZodNullable<z.ZodNumber>;
|
|
144
|
+
model: z.ZodNullable<z.ZodString>;
|
|
145
|
+
auto_merge_authorized: z.ZodBoolean;
|
|
146
|
+
blast_radius_tags: z.ZodArray<z.ZodString>;
|
|
147
|
+
depends_on: z.ZodArray<z.ZodString>;
|
|
148
|
+
acceptance_criteria_count: z.ZodNumber;
|
|
149
|
+
scope_opens_with_why: z.ZodBoolean;
|
|
150
|
+
plan_step: z.ZodEnum<{
|
|
151
|
+
skipped: "skipped";
|
|
152
|
+
required: "required";
|
|
153
|
+
}>;
|
|
154
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
155
|
+
kind: z.ZodLiteral<"review_verdict">;
|
|
156
|
+
verdict: z.ZodEnum<{
|
|
157
|
+
approve: "approve";
|
|
158
|
+
request_changes: "request_changes";
|
|
159
|
+
comment: "comment";
|
|
160
|
+
}>;
|
|
161
|
+
score: z.ZodNullable<z.ZodNumber>;
|
|
162
|
+
must_fix_count: z.ZodNumber;
|
|
163
|
+
should_fix_count: z.ZodNumber;
|
|
164
|
+
gate: z.ZodNullable<z.ZodEnum<{
|
|
165
|
+
CONTINUE: "CONTINUE";
|
|
166
|
+
ITERATE: "ITERATE";
|
|
167
|
+
BLOCK: "BLOCK";
|
|
168
|
+
}>>;
|
|
169
|
+
brief_contract_checked: z.ZodBoolean;
|
|
170
|
+
brief_missed_high_count: z.ZodNumber;
|
|
171
|
+
review_pass: z.ZodNumber;
|
|
172
|
+
reviewer_model: z.ZodNullable<z.ZodString>;
|
|
173
|
+
blast_radius: z.ZodNullable<z.ZodString>;
|
|
174
|
+
auto_merge_eligible: z.ZodNullable<z.ZodBoolean>;
|
|
175
|
+
needs_preview_signoff: z.ZodNullable<z.ZodBoolean>;
|
|
176
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
177
|
+
kind: z.ZodLiteral<"human_approval">;
|
|
178
|
+
approval_kind: z.ZodEnum<{
|
|
179
|
+
explicit_tap: "explicit_tap";
|
|
180
|
+
standing_tap: "standing_tap";
|
|
181
|
+
preview_confirmed: "preview_confirmed";
|
|
182
|
+
merge_authorized: "merge_authorized";
|
|
183
|
+
ruling: "ruling";
|
|
184
|
+
}>;
|
|
185
|
+
granted: z.ZodBoolean;
|
|
186
|
+
scope: z.ZodEnum<{
|
|
187
|
+
this_pr: "this_pr";
|
|
188
|
+
wave: "wave";
|
|
189
|
+
standing: "standing";
|
|
190
|
+
}>;
|
|
191
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
192
|
+
kind: z.ZodLiteral<"intervention">;
|
|
193
|
+
intervention_kind: z.ZodEnum<{
|
|
194
|
+
ruling: "ruling";
|
|
195
|
+
hook_bypass: "hook_bypass";
|
|
196
|
+
send_back: "send_back";
|
|
197
|
+
scope_correction: "scope_correction";
|
|
198
|
+
decision_block: "decision_block";
|
|
199
|
+
re_review_requested: "re_review_requested";
|
|
200
|
+
}>;
|
|
201
|
+
self_reported: z.ZodBoolean;
|
|
202
|
+
hook_name: z.ZodNullable<z.ZodString>;
|
|
203
|
+
blocked_action: z.ZodNullable<z.ZodString>;
|
|
204
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
205
|
+
kind: z.ZodLiteral<"takeover">;
|
|
206
|
+
takeover_kind: z.ZodEnum<{
|
|
207
|
+
human_finished_work: "human_finished_work";
|
|
208
|
+
work_abandoned: "work_abandoned";
|
|
209
|
+
reassigned: "reassigned";
|
|
210
|
+
output_discarded: "output_discarded";
|
|
211
|
+
}>;
|
|
212
|
+
prior_actor: z.ZodObject<{
|
|
213
|
+
raw_label: z.ZodNullable<z.ZodString>;
|
|
214
|
+
label_kind: z.ZodNullable<z.ZodEnum<{
|
|
215
|
+
email: "email";
|
|
216
|
+
github_login: "github_login";
|
|
217
|
+
linear_email: "linear_email";
|
|
218
|
+
linear_user_id: "linear_user_id";
|
|
219
|
+
reviewer_session: "reviewer_session";
|
|
220
|
+
hook_operator: "hook_operator";
|
|
221
|
+
queue_field: "queue_field";
|
|
222
|
+
brief_variable: "brief_variable";
|
|
223
|
+
}>>;
|
|
224
|
+
actor_class: z.ZodEnum<{
|
|
225
|
+
human: "human";
|
|
226
|
+
agent: "agent";
|
|
227
|
+
}>;
|
|
228
|
+
resolution: z.ZodEnum<{
|
|
229
|
+
resolved: "resolved";
|
|
230
|
+
unknown_person: "unknown_person";
|
|
231
|
+
ambiguous: "ambiguous";
|
|
232
|
+
no_person_named: "no_person_named";
|
|
233
|
+
}>;
|
|
234
|
+
person_id: z.ZodNullable<z.ZodString>;
|
|
235
|
+
unresolved_actor_id: z.ZodNullable<z.ZodString>;
|
|
236
|
+
}, z.core.$strict>;
|
|
237
|
+
turns_before_takeover: z.ZodNullable<z.ZodNumber>;
|
|
238
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
239
|
+
kind: z.ZodLiteral<"rule_fired">;
|
|
240
|
+
hook_name: z.ZodString;
|
|
241
|
+
decision: z.ZodEnum<{
|
|
242
|
+
blocked: "blocked";
|
|
243
|
+
allowed: "allowed";
|
|
244
|
+
allowed_bypass: "allowed_bypass";
|
|
245
|
+
warned: "warned";
|
|
246
|
+
}>;
|
|
247
|
+
bypass_token_present: z.ZodBoolean;
|
|
248
|
+
}, z.core.$strict>], "kind">;
|
|
249
|
+
export type DecisionOutcome = z.infer<typeof DecisionOutcomeSchema>;
|
|
250
|
+
export declare const DecisionEventSchema: z.ZodObject<{
|
|
251
|
+
decision_event_id: z.ZodString;
|
|
252
|
+
kind: z.ZodEnum<{
|
|
253
|
+
delegation_issued: "delegation_issued";
|
|
254
|
+
review_verdict: "review_verdict";
|
|
255
|
+
human_approval: "human_approval";
|
|
256
|
+
intervention: "intervention";
|
|
257
|
+
takeover: "takeover";
|
|
258
|
+
rule_fired: "rule_fired";
|
|
259
|
+
}>;
|
|
260
|
+
occurred_at: z.ZodString;
|
|
261
|
+
actor: z.ZodObject<{
|
|
262
|
+
raw_label: z.ZodNullable<z.ZodString>;
|
|
263
|
+
label_kind: z.ZodNullable<z.ZodEnum<{
|
|
264
|
+
email: "email";
|
|
265
|
+
github_login: "github_login";
|
|
266
|
+
linear_email: "linear_email";
|
|
267
|
+
linear_user_id: "linear_user_id";
|
|
268
|
+
reviewer_session: "reviewer_session";
|
|
269
|
+
hook_operator: "hook_operator";
|
|
270
|
+
queue_field: "queue_field";
|
|
271
|
+
brief_variable: "brief_variable";
|
|
272
|
+
}>>;
|
|
273
|
+
actor_class: z.ZodEnum<{
|
|
274
|
+
human: "human";
|
|
275
|
+
agent: "agent";
|
|
276
|
+
}>;
|
|
277
|
+
resolution: z.ZodEnum<{
|
|
278
|
+
resolved: "resolved";
|
|
279
|
+
unknown_person: "unknown_person";
|
|
280
|
+
ambiguous: "ambiguous";
|
|
281
|
+
no_person_named: "no_person_named";
|
|
282
|
+
}>;
|
|
283
|
+
person_id: z.ZodNullable<z.ZodString>;
|
|
284
|
+
unresolved_actor_id: z.ZodNullable<z.ZodString>;
|
|
285
|
+
}, z.core.$strict>;
|
|
286
|
+
subject: z.ZodObject<{
|
|
287
|
+
repo: z.ZodNullable<z.ZodString>;
|
|
288
|
+
branch: z.ZodNullable<z.ZodString>;
|
|
289
|
+
ticket_id: z.ZodNullable<z.ZodString>;
|
|
290
|
+
pull_request_number: z.ZodNullable<z.ZodNumber>;
|
|
291
|
+
head_sha: z.ZodNullable<z.ZodString>;
|
|
292
|
+
}, z.core.$strict>;
|
|
293
|
+
outcome: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
294
|
+
kind: z.ZodLiteral<"delegation_issued">;
|
|
295
|
+
eta_minutes: z.ZodNullable<z.ZodNumber>;
|
|
296
|
+
model: z.ZodNullable<z.ZodString>;
|
|
297
|
+
auto_merge_authorized: z.ZodBoolean;
|
|
298
|
+
blast_radius_tags: z.ZodArray<z.ZodString>;
|
|
299
|
+
depends_on: z.ZodArray<z.ZodString>;
|
|
300
|
+
acceptance_criteria_count: z.ZodNumber;
|
|
301
|
+
scope_opens_with_why: z.ZodBoolean;
|
|
302
|
+
plan_step: z.ZodEnum<{
|
|
303
|
+
skipped: "skipped";
|
|
304
|
+
required: "required";
|
|
305
|
+
}>;
|
|
306
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
307
|
+
kind: z.ZodLiteral<"review_verdict">;
|
|
308
|
+
verdict: z.ZodEnum<{
|
|
309
|
+
approve: "approve";
|
|
310
|
+
request_changes: "request_changes";
|
|
311
|
+
comment: "comment";
|
|
312
|
+
}>;
|
|
313
|
+
score: z.ZodNullable<z.ZodNumber>;
|
|
314
|
+
must_fix_count: z.ZodNumber;
|
|
315
|
+
should_fix_count: z.ZodNumber;
|
|
316
|
+
gate: z.ZodNullable<z.ZodEnum<{
|
|
317
|
+
CONTINUE: "CONTINUE";
|
|
318
|
+
ITERATE: "ITERATE";
|
|
319
|
+
BLOCK: "BLOCK";
|
|
320
|
+
}>>;
|
|
321
|
+
brief_contract_checked: z.ZodBoolean;
|
|
322
|
+
brief_missed_high_count: z.ZodNumber;
|
|
323
|
+
review_pass: z.ZodNumber;
|
|
324
|
+
reviewer_model: z.ZodNullable<z.ZodString>;
|
|
325
|
+
blast_radius: z.ZodNullable<z.ZodString>;
|
|
326
|
+
auto_merge_eligible: z.ZodNullable<z.ZodBoolean>;
|
|
327
|
+
needs_preview_signoff: z.ZodNullable<z.ZodBoolean>;
|
|
328
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
329
|
+
kind: z.ZodLiteral<"human_approval">;
|
|
330
|
+
approval_kind: z.ZodEnum<{
|
|
331
|
+
explicit_tap: "explicit_tap";
|
|
332
|
+
standing_tap: "standing_tap";
|
|
333
|
+
preview_confirmed: "preview_confirmed";
|
|
334
|
+
merge_authorized: "merge_authorized";
|
|
335
|
+
ruling: "ruling";
|
|
336
|
+
}>;
|
|
337
|
+
granted: z.ZodBoolean;
|
|
338
|
+
scope: z.ZodEnum<{
|
|
339
|
+
this_pr: "this_pr";
|
|
340
|
+
wave: "wave";
|
|
341
|
+
standing: "standing";
|
|
342
|
+
}>;
|
|
343
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
344
|
+
kind: z.ZodLiteral<"intervention">;
|
|
345
|
+
intervention_kind: z.ZodEnum<{
|
|
346
|
+
ruling: "ruling";
|
|
347
|
+
hook_bypass: "hook_bypass";
|
|
348
|
+
send_back: "send_back";
|
|
349
|
+
scope_correction: "scope_correction";
|
|
350
|
+
decision_block: "decision_block";
|
|
351
|
+
re_review_requested: "re_review_requested";
|
|
352
|
+
}>;
|
|
353
|
+
self_reported: z.ZodBoolean;
|
|
354
|
+
hook_name: z.ZodNullable<z.ZodString>;
|
|
355
|
+
blocked_action: z.ZodNullable<z.ZodString>;
|
|
356
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
357
|
+
kind: z.ZodLiteral<"takeover">;
|
|
358
|
+
takeover_kind: z.ZodEnum<{
|
|
359
|
+
human_finished_work: "human_finished_work";
|
|
360
|
+
work_abandoned: "work_abandoned";
|
|
361
|
+
reassigned: "reassigned";
|
|
362
|
+
output_discarded: "output_discarded";
|
|
363
|
+
}>;
|
|
364
|
+
prior_actor: z.ZodObject<{
|
|
365
|
+
raw_label: z.ZodNullable<z.ZodString>;
|
|
366
|
+
label_kind: z.ZodNullable<z.ZodEnum<{
|
|
367
|
+
email: "email";
|
|
368
|
+
github_login: "github_login";
|
|
369
|
+
linear_email: "linear_email";
|
|
370
|
+
linear_user_id: "linear_user_id";
|
|
371
|
+
reviewer_session: "reviewer_session";
|
|
372
|
+
hook_operator: "hook_operator";
|
|
373
|
+
queue_field: "queue_field";
|
|
374
|
+
brief_variable: "brief_variable";
|
|
375
|
+
}>>;
|
|
376
|
+
actor_class: z.ZodEnum<{
|
|
377
|
+
human: "human";
|
|
378
|
+
agent: "agent";
|
|
379
|
+
}>;
|
|
380
|
+
resolution: z.ZodEnum<{
|
|
381
|
+
resolved: "resolved";
|
|
382
|
+
unknown_person: "unknown_person";
|
|
383
|
+
ambiguous: "ambiguous";
|
|
384
|
+
no_person_named: "no_person_named";
|
|
385
|
+
}>;
|
|
386
|
+
person_id: z.ZodNullable<z.ZodString>;
|
|
387
|
+
unresolved_actor_id: z.ZodNullable<z.ZodString>;
|
|
388
|
+
}, z.core.$strict>;
|
|
389
|
+
turns_before_takeover: z.ZodNullable<z.ZodNumber>;
|
|
390
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
391
|
+
kind: z.ZodLiteral<"rule_fired">;
|
|
392
|
+
hook_name: z.ZodString;
|
|
393
|
+
decision: z.ZodEnum<{
|
|
394
|
+
blocked: "blocked";
|
|
395
|
+
allowed: "allowed";
|
|
396
|
+
allowed_bypass: "allowed_bypass";
|
|
397
|
+
warned: "warned";
|
|
398
|
+
}>;
|
|
399
|
+
bypass_token_present: z.ZodBoolean;
|
|
400
|
+
}, z.core.$strict>], "kind">;
|
|
401
|
+
capture_source: z.ZodEnum<{
|
|
402
|
+
claude_state_verdict_json: "claude_state_verdict_json";
|
|
403
|
+
claude_state_queue_jsonl: "claude_state_queue_jsonl";
|
|
404
|
+
claude_state_worker_brief: "claude_state_worker_brief";
|
|
405
|
+
claude_state_retro_jsonl: "claude_state_retro_jsonl";
|
|
406
|
+
claude_state_bypass_log: "claude_state_bypass_log";
|
|
407
|
+
claude_state_merge_log: "claude_state_merge_log";
|
|
408
|
+
hook_runtime: "hook_runtime";
|
|
409
|
+
}>;
|
|
410
|
+
source_artifact_hash_sha256: z.ZodNullable<z.ZodString>;
|
|
411
|
+
source_line_number: z.ZodNullable<z.ZodNumber>;
|
|
412
|
+
rule_id: z.ZodNullable<z.ZodString>;
|
|
413
|
+
rulebook_version: z.ZodNullable<z.ZodString>;
|
|
414
|
+
evidence_pointers: z.ZodArray<z.ZodObject<{
|
|
415
|
+
kind: z.ZodEnum<{
|
|
416
|
+
claude_state_path: "claude_state_path";
|
|
417
|
+
github_pull_request: "github_pull_request";
|
|
418
|
+
linear_issue: "linear_issue";
|
|
419
|
+
commit: "commit";
|
|
420
|
+
reviewer_session_id: "reviewer_session_id";
|
|
421
|
+
}>;
|
|
422
|
+
locator: z.ZodString;
|
|
423
|
+
}, z.core.$strict>>;
|
|
424
|
+
attributes: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>]>>;
|
|
425
|
+
privacy_classification: z.ZodLiteral<"metadata">;
|
|
426
|
+
}, z.core.$strict>;
|
|
427
|
+
export type DecisionEvent = z.infer<typeof DecisionEventSchema>;
|
|
428
|
+
export declare const DecisionEventEnvelopeSchema: z.ZodObject<{
|
|
429
|
+
envelope_version: z.ZodLiteral<"decision-event.v1">;
|
|
430
|
+
writer: z.ZodString;
|
|
431
|
+
writer_version: z.ZodString;
|
|
432
|
+
events: z.ZodArray<z.ZodObject<{
|
|
433
|
+
decision_event_id: z.ZodString;
|
|
434
|
+
kind: z.ZodEnum<{
|
|
435
|
+
delegation_issued: "delegation_issued";
|
|
436
|
+
review_verdict: "review_verdict";
|
|
437
|
+
human_approval: "human_approval";
|
|
438
|
+
intervention: "intervention";
|
|
439
|
+
takeover: "takeover";
|
|
440
|
+
rule_fired: "rule_fired";
|
|
441
|
+
}>;
|
|
442
|
+
occurred_at: z.ZodString;
|
|
443
|
+
actor: z.ZodObject<{
|
|
444
|
+
raw_label: z.ZodNullable<z.ZodString>;
|
|
445
|
+
label_kind: z.ZodNullable<z.ZodEnum<{
|
|
446
|
+
email: "email";
|
|
447
|
+
github_login: "github_login";
|
|
448
|
+
linear_email: "linear_email";
|
|
449
|
+
linear_user_id: "linear_user_id";
|
|
450
|
+
reviewer_session: "reviewer_session";
|
|
451
|
+
hook_operator: "hook_operator";
|
|
452
|
+
queue_field: "queue_field";
|
|
453
|
+
brief_variable: "brief_variable";
|
|
454
|
+
}>>;
|
|
455
|
+
actor_class: z.ZodEnum<{
|
|
456
|
+
human: "human";
|
|
457
|
+
agent: "agent";
|
|
458
|
+
}>;
|
|
459
|
+
resolution: z.ZodEnum<{
|
|
460
|
+
resolved: "resolved";
|
|
461
|
+
unknown_person: "unknown_person";
|
|
462
|
+
ambiguous: "ambiguous";
|
|
463
|
+
no_person_named: "no_person_named";
|
|
464
|
+
}>;
|
|
465
|
+
person_id: z.ZodNullable<z.ZodString>;
|
|
466
|
+
unresolved_actor_id: z.ZodNullable<z.ZodString>;
|
|
467
|
+
}, z.core.$strict>;
|
|
468
|
+
subject: z.ZodObject<{
|
|
469
|
+
repo: z.ZodNullable<z.ZodString>;
|
|
470
|
+
branch: z.ZodNullable<z.ZodString>;
|
|
471
|
+
ticket_id: z.ZodNullable<z.ZodString>;
|
|
472
|
+
pull_request_number: z.ZodNullable<z.ZodNumber>;
|
|
473
|
+
head_sha: z.ZodNullable<z.ZodString>;
|
|
474
|
+
}, z.core.$strict>;
|
|
475
|
+
outcome: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
476
|
+
kind: z.ZodLiteral<"delegation_issued">;
|
|
477
|
+
eta_minutes: z.ZodNullable<z.ZodNumber>;
|
|
478
|
+
model: z.ZodNullable<z.ZodString>;
|
|
479
|
+
auto_merge_authorized: z.ZodBoolean;
|
|
480
|
+
blast_radius_tags: z.ZodArray<z.ZodString>;
|
|
481
|
+
depends_on: z.ZodArray<z.ZodString>;
|
|
482
|
+
acceptance_criteria_count: z.ZodNumber;
|
|
483
|
+
scope_opens_with_why: z.ZodBoolean;
|
|
484
|
+
plan_step: z.ZodEnum<{
|
|
485
|
+
skipped: "skipped";
|
|
486
|
+
required: "required";
|
|
487
|
+
}>;
|
|
488
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
489
|
+
kind: z.ZodLiteral<"review_verdict">;
|
|
490
|
+
verdict: z.ZodEnum<{
|
|
491
|
+
approve: "approve";
|
|
492
|
+
request_changes: "request_changes";
|
|
493
|
+
comment: "comment";
|
|
494
|
+
}>;
|
|
495
|
+
score: z.ZodNullable<z.ZodNumber>;
|
|
496
|
+
must_fix_count: z.ZodNumber;
|
|
497
|
+
should_fix_count: z.ZodNumber;
|
|
498
|
+
gate: z.ZodNullable<z.ZodEnum<{
|
|
499
|
+
CONTINUE: "CONTINUE";
|
|
500
|
+
ITERATE: "ITERATE";
|
|
501
|
+
BLOCK: "BLOCK";
|
|
502
|
+
}>>;
|
|
503
|
+
brief_contract_checked: z.ZodBoolean;
|
|
504
|
+
brief_missed_high_count: z.ZodNumber;
|
|
505
|
+
review_pass: z.ZodNumber;
|
|
506
|
+
reviewer_model: z.ZodNullable<z.ZodString>;
|
|
507
|
+
blast_radius: z.ZodNullable<z.ZodString>;
|
|
508
|
+
auto_merge_eligible: z.ZodNullable<z.ZodBoolean>;
|
|
509
|
+
needs_preview_signoff: z.ZodNullable<z.ZodBoolean>;
|
|
510
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
511
|
+
kind: z.ZodLiteral<"human_approval">;
|
|
512
|
+
approval_kind: z.ZodEnum<{
|
|
513
|
+
explicit_tap: "explicit_tap";
|
|
514
|
+
standing_tap: "standing_tap";
|
|
515
|
+
preview_confirmed: "preview_confirmed";
|
|
516
|
+
merge_authorized: "merge_authorized";
|
|
517
|
+
ruling: "ruling";
|
|
518
|
+
}>;
|
|
519
|
+
granted: z.ZodBoolean;
|
|
520
|
+
scope: z.ZodEnum<{
|
|
521
|
+
this_pr: "this_pr";
|
|
522
|
+
wave: "wave";
|
|
523
|
+
standing: "standing";
|
|
524
|
+
}>;
|
|
525
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
526
|
+
kind: z.ZodLiteral<"intervention">;
|
|
527
|
+
intervention_kind: z.ZodEnum<{
|
|
528
|
+
ruling: "ruling";
|
|
529
|
+
hook_bypass: "hook_bypass";
|
|
530
|
+
send_back: "send_back";
|
|
531
|
+
scope_correction: "scope_correction";
|
|
532
|
+
decision_block: "decision_block";
|
|
533
|
+
re_review_requested: "re_review_requested";
|
|
534
|
+
}>;
|
|
535
|
+
self_reported: z.ZodBoolean;
|
|
536
|
+
hook_name: z.ZodNullable<z.ZodString>;
|
|
537
|
+
blocked_action: z.ZodNullable<z.ZodString>;
|
|
538
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
539
|
+
kind: z.ZodLiteral<"takeover">;
|
|
540
|
+
takeover_kind: z.ZodEnum<{
|
|
541
|
+
human_finished_work: "human_finished_work";
|
|
542
|
+
work_abandoned: "work_abandoned";
|
|
543
|
+
reassigned: "reassigned";
|
|
544
|
+
output_discarded: "output_discarded";
|
|
545
|
+
}>;
|
|
546
|
+
prior_actor: z.ZodObject<{
|
|
547
|
+
raw_label: z.ZodNullable<z.ZodString>;
|
|
548
|
+
label_kind: z.ZodNullable<z.ZodEnum<{
|
|
549
|
+
email: "email";
|
|
550
|
+
github_login: "github_login";
|
|
551
|
+
linear_email: "linear_email";
|
|
552
|
+
linear_user_id: "linear_user_id";
|
|
553
|
+
reviewer_session: "reviewer_session";
|
|
554
|
+
hook_operator: "hook_operator";
|
|
555
|
+
queue_field: "queue_field";
|
|
556
|
+
brief_variable: "brief_variable";
|
|
557
|
+
}>>;
|
|
558
|
+
actor_class: z.ZodEnum<{
|
|
559
|
+
human: "human";
|
|
560
|
+
agent: "agent";
|
|
561
|
+
}>;
|
|
562
|
+
resolution: z.ZodEnum<{
|
|
563
|
+
resolved: "resolved";
|
|
564
|
+
unknown_person: "unknown_person";
|
|
565
|
+
ambiguous: "ambiguous";
|
|
566
|
+
no_person_named: "no_person_named";
|
|
567
|
+
}>;
|
|
568
|
+
person_id: z.ZodNullable<z.ZodString>;
|
|
569
|
+
unresolved_actor_id: z.ZodNullable<z.ZodString>;
|
|
570
|
+
}, z.core.$strict>;
|
|
571
|
+
turns_before_takeover: z.ZodNullable<z.ZodNumber>;
|
|
572
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
573
|
+
kind: z.ZodLiteral<"rule_fired">;
|
|
574
|
+
hook_name: z.ZodString;
|
|
575
|
+
decision: z.ZodEnum<{
|
|
576
|
+
blocked: "blocked";
|
|
577
|
+
allowed: "allowed";
|
|
578
|
+
allowed_bypass: "allowed_bypass";
|
|
579
|
+
warned: "warned";
|
|
580
|
+
}>;
|
|
581
|
+
bypass_token_present: z.ZodBoolean;
|
|
582
|
+
}, z.core.$strict>], "kind">;
|
|
583
|
+
capture_source: z.ZodEnum<{
|
|
584
|
+
claude_state_verdict_json: "claude_state_verdict_json";
|
|
585
|
+
claude_state_queue_jsonl: "claude_state_queue_jsonl";
|
|
586
|
+
claude_state_worker_brief: "claude_state_worker_brief";
|
|
587
|
+
claude_state_retro_jsonl: "claude_state_retro_jsonl";
|
|
588
|
+
claude_state_bypass_log: "claude_state_bypass_log";
|
|
589
|
+
claude_state_merge_log: "claude_state_merge_log";
|
|
590
|
+
hook_runtime: "hook_runtime";
|
|
591
|
+
}>;
|
|
592
|
+
source_artifact_hash_sha256: z.ZodNullable<z.ZodString>;
|
|
593
|
+
source_line_number: z.ZodNullable<z.ZodNumber>;
|
|
594
|
+
rule_id: z.ZodNullable<z.ZodString>;
|
|
595
|
+
rulebook_version: z.ZodNullable<z.ZodString>;
|
|
596
|
+
evidence_pointers: z.ZodArray<z.ZodObject<{
|
|
597
|
+
kind: z.ZodEnum<{
|
|
598
|
+
claude_state_path: "claude_state_path";
|
|
599
|
+
github_pull_request: "github_pull_request";
|
|
600
|
+
linear_issue: "linear_issue";
|
|
601
|
+
commit: "commit";
|
|
602
|
+
reviewer_session_id: "reviewer_session_id";
|
|
603
|
+
}>;
|
|
604
|
+
locator: z.ZodString;
|
|
605
|
+
}, z.core.$strict>>;
|
|
606
|
+
attributes: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>]>>;
|
|
607
|
+
privacy_classification: z.ZodLiteral<"metadata">;
|
|
608
|
+
}, z.core.$strict>>;
|
|
609
|
+
}, z.core.$strict>;
|
|
610
|
+
export type DecisionEventEnvelope = z.infer<typeof DecisionEventEnvelopeSchema>;
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
// BLI-2425 / Tower Wave 1: what a person committed to, as a record.
|
|
2
|
+
//
|
|
3
|
+
// A decision event is one durable record that a person committed to something:
|
|
4
|
+
// handed work to someone, judged work, approved a merge, stepped in, took work
|
|
5
|
+
// over, or had a rule fire on them. It is not a session, not a turn, and not a
|
|
6
|
+
// diff — the ambient path already owns those, and this vocabulary deliberately
|
|
7
|
+
// does not touch it.
|
|
8
|
+
//
|
|
9
|
+
// Three properties drive every field below.
|
|
10
|
+
//
|
|
11
|
+
// 1. The actor is a person, resolved through the one resolver. No fuzzy
|
|
12
|
+
// matching, ever. But an unresolved actor is not a dead end either: it gets
|
|
13
|
+
// a stable id so that "who did what, and when" stays answerable before
|
|
14
|
+
// anybody knows the name, and so that one later human assertion resolves
|
|
15
|
+
// every event that actor ever produced at once. See ActorRefSchema.
|
|
16
|
+
//
|
|
17
|
+
// 2. The event carries pointers, never content. Verdict prose and brief bodies
|
|
18
|
+
// name client repos and quote people. v1 carries enums, counts, ids, hashes
|
|
19
|
+
// and evidence pointers. Nothing else. `privacy_classification` is pinned to
|
|
20
|
+
// the literal "metadata" rather than reusing the full enum, so the type
|
|
21
|
+
// system refuses a decision event that could legally carry raw evidence.
|
|
22
|
+
//
|
|
23
|
+
// 3. There is a rule slot even though no rule IDs exist yet. Nothing anywhere
|
|
24
|
+
// records which rules were in force when something happened. The slot ships
|
|
25
|
+
// nullable in v1 so Wave 2 fills it by backfill instead of by migration.
|
|
26
|
+
//
|
|
27
|
+
// This vocabulary is disjoint from KNOWN_EVENT_TYPES and payloadSchemas. No
|
|
28
|
+
// kind here reuses a legacy event name and nothing here lands on
|
|
29
|
+
// /api/events/emit. That separation is the point: the known three-way
|
|
30
|
+
// event-vocabulary drift is not a dependency of this work.
|
|
31
|
+
import { z } from "zod";
|
|
32
|
+
import { IsoDateTimeSchema, JsonAttributeRecordSchema, NonEmptyStringSchema, Sha256Schema, VersionStringSchema, } from "./common.js";
|
|
33
|
+
export const DECISION_EVENT_ENVELOPE_VERSION = "decision-event.v1";
|
|
34
|
+
/** A git commit sha. Sha256Schema is 64 hex and does not fit a 40-hex commit. */
|
|
35
|
+
export const CommitShaSchema = z.string().regex(/^[a-f0-9]{7,40}$/);
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Kinds
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
export const DecisionEventKindSchema = z.enum([
|
|
40
|
+
"delegation_issued",
|
|
41
|
+
"review_verdict",
|
|
42
|
+
"human_approval",
|
|
43
|
+
"intervention",
|
|
44
|
+
"takeover",
|
|
45
|
+
"rule_fired",
|
|
46
|
+
]);
|
|
47
|
+
/**
|
|
48
|
+
* Which artifact this event was read out of.
|
|
49
|
+
*
|
|
50
|
+
* Deliberately its own enum rather than a member added to CaptureSourceSchema.
|
|
51
|
+
* That enum describes what a collector watched on a coding machine; these are
|
|
52
|
+
* files a manager's harness wrote as a side effect of managing. Sharing the
|
|
53
|
+
* enum would imply a shared envelope, and the whole reason this path exists is
|
|
54
|
+
* that decision records have no session, no work context and no capture
|
|
55
|
+
* provenance to fill.
|
|
56
|
+
*/
|
|
57
|
+
export const DecisionCaptureSourceSchema = z.enum([
|
|
58
|
+
"claude_state_verdict_json",
|
|
59
|
+
"claude_state_queue_jsonl",
|
|
60
|
+
"claude_state_worker_brief",
|
|
61
|
+
"claude_state_retro_jsonl",
|
|
62
|
+
"claude_state_bypass_log",
|
|
63
|
+
"claude_state_merge_log",
|
|
64
|
+
"hook_runtime",
|
|
65
|
+
]);
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Who
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
/** Which field of the source artifact produced this label. */
|
|
70
|
+
export const ActorLabelKindSchema = z.enum([
|
|
71
|
+
"email",
|
|
72
|
+
"github_login",
|
|
73
|
+
"linear_email",
|
|
74
|
+
"linear_user_id",
|
|
75
|
+
"reviewer_session",
|
|
76
|
+
"hook_operator",
|
|
77
|
+
"queue_field",
|
|
78
|
+
"brief_variable",
|
|
79
|
+
]);
|
|
80
|
+
/** Whether the thing that acted was a person or something a person ran. */
|
|
81
|
+
export const ActorClassSchema = z.enum(["human", "agent"]);
|
|
82
|
+
/**
|
|
83
|
+
* What the resolver concluded. These are the resolver's own four outcomes, kept
|
|
84
|
+
* apart rather than collapsed to null, because "nobody was named" and "somebody
|
|
85
|
+
* was named and we do not know them" call for different follow-up.
|
|
86
|
+
*/
|
|
87
|
+
export const ActorResolutionSchema = z.enum([
|
|
88
|
+
"resolved",
|
|
89
|
+
"unknown_person",
|
|
90
|
+
"ambiguous",
|
|
91
|
+
"no_person_named",
|
|
92
|
+
]);
|
|
93
|
+
/**
|
|
94
|
+
* Who acted — resolved to a person when we know them, and stably identified
|
|
95
|
+
* when we do not.
|
|
96
|
+
*
|
|
97
|
+
* The second half is the part worth reading. A backfill of ~2,400 historical
|
|
98
|
+
* decisions predates the person spine, so a large share of them name somebody
|
|
99
|
+
* the roster has never heard of. Collapsing all of those to one null makes the
|
|
100
|
+
* corpus useless: unresolved actor #1, #2 and #3 become indistinguishable and
|
|
101
|
+
* "who did what, and when" is unanswerable even in principle.
|
|
102
|
+
*
|
|
103
|
+
* So every distinct unresolved label gets `unresolved_actor_id`, derived
|
|
104
|
+
* deterministically from the normalized label (see `unresolvedActorId`). Events
|
|
105
|
+
* from the same label cluster under the same id forever. Later a human asserts
|
|
106
|
+
* "unresolved actor X is Brandon" and every event under that id resolves at
|
|
107
|
+
* once, retroactively, without a single event row being rewritten — the
|
|
108
|
+
* assertion lives in its own append-only alias table.
|
|
109
|
+
*
|
|
110
|
+
* That is not fuzzy matching sneaking back in. The ban is on the machine
|
|
111
|
+
* guessing; a human asserting an identity is the sanctioned escape hatch, and
|
|
112
|
+
* it is the same shape as jarvis_corrections, where the human outranks the
|
|
113
|
+
* pipeline and nothing is edited in place.
|
|
114
|
+
*/
|
|
115
|
+
export const ActorRefSchema = z
|
|
116
|
+
.object({
|
|
117
|
+
/** Exactly what the artifact said, before any normalization. Kept forever. */
|
|
118
|
+
raw_label: NonEmptyStringSchema.max(300).nullable(),
|
|
119
|
+
label_kind: ActorLabelKindSchema.nullable(),
|
|
120
|
+
actor_class: ActorClassSchema,
|
|
121
|
+
resolution: ActorResolutionSchema,
|
|
122
|
+
/** The roster person, when the resolver found exactly one. */
|
|
123
|
+
person_id: NonEmptyStringSchema.nullable(),
|
|
124
|
+
/**
|
|
125
|
+
* The stable identity of an actor we cannot name yet. Set whenever a real
|
|
126
|
+
* label failed to resolve; null when the resolver matched, and null when
|
|
127
|
+
* the artifact named nobody at all (there is no actor to be stable about).
|
|
128
|
+
*/
|
|
129
|
+
unresolved_actor_id: NonEmptyStringSchema.nullable(),
|
|
130
|
+
})
|
|
131
|
+
.strict()
|
|
132
|
+
.superRefine((actor, context) => {
|
|
133
|
+
if (actor.resolution === "resolved") {
|
|
134
|
+
if (!actor.person_id) {
|
|
135
|
+
context.addIssue({
|
|
136
|
+
code: "custom",
|
|
137
|
+
path: ["person_id"],
|
|
138
|
+
message: "A resolved actor must carry the person it resolved to.",
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
if (actor.unresolved_actor_id) {
|
|
142
|
+
context.addIssue({
|
|
143
|
+
code: "custom",
|
|
144
|
+
path: ["unresolved_actor_id"],
|
|
145
|
+
message: "A resolved actor has no unresolved id. Carrying both invites two answers to one question.",
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (actor.person_id) {
|
|
151
|
+
context.addIssue({
|
|
152
|
+
code: "custom",
|
|
153
|
+
path: ["person_id"],
|
|
154
|
+
message: "Only a resolved actor carries a person. An unresolved actor that names one is a guess.",
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
if (actor.resolution === "no_person_named") {
|
|
158
|
+
if (actor.raw_label) {
|
|
159
|
+
context.addIssue({
|
|
160
|
+
code: "custom",
|
|
161
|
+
path: ["raw_label"],
|
|
162
|
+
message: "no_person_named means the artifact named nobody. A label present here is a different outcome.",
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
if (actor.unresolved_actor_id) {
|
|
166
|
+
context.addIssue({
|
|
167
|
+
code: "custom",
|
|
168
|
+
path: ["unresolved_actor_id"],
|
|
169
|
+
message: "Nobody was named, so there is no actor to identify stably.",
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
// unknown_person and ambiguous: a real label that we could not turn into
|
|
175
|
+
// exactly one person. Both must be stably identified, or the backfill loses
|
|
176
|
+
// the ability to tell two strangers apart.
|
|
177
|
+
if (!actor.raw_label) {
|
|
178
|
+
context.addIssue({
|
|
179
|
+
code: "custom",
|
|
180
|
+
path: ["raw_label"],
|
|
181
|
+
message: "An unresolved actor must keep the label it failed to resolve, or nobody can ever name it.",
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
if (!actor.unresolved_actor_id) {
|
|
185
|
+
context.addIssue({
|
|
186
|
+
code: "custom",
|
|
187
|
+
path: ["unresolved_actor_id"],
|
|
188
|
+
message: "An unresolved actor must carry a stable id so its events cluster and can be named later.",
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
// What it was about
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
/**
|
|
196
|
+
* The thing decided upon. `repo` is stored canonically as `owner/name` because
|
|
197
|
+
* the source artifacts disagree — the same pull request appears as
|
|
198
|
+
* `bli-cockpit` in the queue and `veetesh-glitch/bli-cockpit` in the verdict.
|
|
199
|
+
* Normalizing is an exact rule, never a prefix guess.
|
|
200
|
+
*/
|
|
201
|
+
export const DecisionSubjectSchema = z
|
|
202
|
+
.object({
|
|
203
|
+
repo: NonEmptyStringSchema.max(200).nullable(),
|
|
204
|
+
branch: NonEmptyStringSchema.max(300).nullable(),
|
|
205
|
+
ticket_id: NonEmptyStringSchema.max(40).nullable(),
|
|
206
|
+
pull_request_number: z.number().int().positive().nullable(),
|
|
207
|
+
head_sha: CommitShaSchema.nullable(),
|
|
208
|
+
})
|
|
209
|
+
.strict();
|
|
210
|
+
/**
|
|
211
|
+
* Where to go to see the thing itself. Pointers, never content — the prose
|
|
212
|
+
* these point at is exactly what v1 refuses to carry.
|
|
213
|
+
*/
|
|
214
|
+
export const DecisionEvidencePointerSchema = z
|
|
215
|
+
.object({
|
|
216
|
+
kind: z.enum([
|
|
217
|
+
"claude_state_path",
|
|
218
|
+
"github_pull_request",
|
|
219
|
+
"linear_issue",
|
|
220
|
+
"commit",
|
|
221
|
+
"reviewer_session_id",
|
|
222
|
+
]),
|
|
223
|
+
locator: NonEmptyStringSchema.max(500),
|
|
224
|
+
})
|
|
225
|
+
.strict();
|
|
226
|
+
// ---------------------------------------------------------------------------
|
|
227
|
+
// Per-kind outcomes
|
|
228
|
+
// ---------------------------------------------------------------------------
|
|
229
|
+
// Each is deliberately small: counts and enums, no narrative. Where the source
|
|
230
|
+
// artifact holds a list of prose items, only its length survives.
|
|
231
|
+
const DelegationIssuedOutcomeSchema = z
|
|
232
|
+
.object({
|
|
233
|
+
kind: z.literal("delegation_issued"),
|
|
234
|
+
eta_minutes: z.number().int().nonnegative().nullable(),
|
|
235
|
+
model: NonEmptyStringSchema.max(120).nullable(),
|
|
236
|
+
auto_merge_authorized: z.boolean(),
|
|
237
|
+
blast_radius_tags: z.array(NonEmptyStringSchema.max(60)).max(40),
|
|
238
|
+
depends_on: z.array(NonEmptyStringSchema.max(40)).max(40),
|
|
239
|
+
acceptance_criteria_count: z.number().int().nonnegative(),
|
|
240
|
+
scope_opens_with_why: z.boolean(),
|
|
241
|
+
plan_step: z.enum(["required", "skipped"]),
|
|
242
|
+
})
|
|
243
|
+
.strict();
|
|
244
|
+
const ReviewVerdictOutcomeSchema = z
|
|
245
|
+
.object({
|
|
246
|
+
kind: z.literal("review_verdict"),
|
|
247
|
+
verdict: z.enum(["approve", "request_changes", "comment"]),
|
|
248
|
+
score: z.number().min(0).max(10).nullable(),
|
|
249
|
+
must_fix_count: z.number().int().nonnegative(),
|
|
250
|
+
should_fix_count: z.number().int().nonnegative(),
|
|
251
|
+
gate: z.enum(["CONTINUE", "ITERATE", "BLOCK"]).nullable(),
|
|
252
|
+
brief_contract_checked: z.boolean(),
|
|
253
|
+
brief_missed_high_count: z.number().int().nonnegative(),
|
|
254
|
+
review_pass: z.number().int().positive(),
|
|
255
|
+
reviewer_model: NonEmptyStringSchema.max(120).nullable(),
|
|
256
|
+
blast_radius: NonEmptyStringSchema.max(60).nullable(),
|
|
257
|
+
auto_merge_eligible: z.boolean().nullable(),
|
|
258
|
+
needs_preview_signoff: z.boolean().nullable(),
|
|
259
|
+
})
|
|
260
|
+
.strict();
|
|
261
|
+
const HumanApprovalOutcomeSchema = z
|
|
262
|
+
.object({
|
|
263
|
+
kind: z.literal("human_approval"),
|
|
264
|
+
approval_kind: z.enum([
|
|
265
|
+
"explicit_tap",
|
|
266
|
+
"standing_tap",
|
|
267
|
+
"preview_confirmed",
|
|
268
|
+
"merge_authorized",
|
|
269
|
+
"ruling",
|
|
270
|
+
]),
|
|
271
|
+
granted: z.boolean(),
|
|
272
|
+
scope: z.enum(["this_pr", "wave", "standing"]),
|
|
273
|
+
})
|
|
274
|
+
.strict();
|
|
275
|
+
const InterventionOutcomeSchema = z
|
|
276
|
+
.object({
|
|
277
|
+
kind: z.literal("intervention"),
|
|
278
|
+
intervention_kind: z.enum([
|
|
279
|
+
"hook_bypass",
|
|
280
|
+
"send_back",
|
|
281
|
+
"scope_correction",
|
|
282
|
+
"decision_block",
|
|
283
|
+
"ruling",
|
|
284
|
+
"re_review_requested",
|
|
285
|
+
]),
|
|
286
|
+
self_reported: z.boolean(),
|
|
287
|
+
hook_name: NonEmptyStringSchema.max(120).nullable(),
|
|
288
|
+
blocked_action: NonEmptyStringSchema.max(120).nullable(),
|
|
289
|
+
})
|
|
290
|
+
.strict();
|
|
291
|
+
/**
|
|
292
|
+
* Nothing writes this yet — failure handling is modeled nowhere, which the
|
|
293
|
+
* audit called the loudest misdelegation signal being invisible. It is
|
|
294
|
+
* specified now anyway, because adding a kind later is additive within v1 while
|
|
295
|
+
* reshaping the envelope later is not.
|
|
296
|
+
*/
|
|
297
|
+
const TakeoverOutcomeSchema = z
|
|
298
|
+
.object({
|
|
299
|
+
kind: z.literal("takeover"),
|
|
300
|
+
takeover_kind: z.enum([
|
|
301
|
+
"human_finished_work",
|
|
302
|
+
"work_abandoned",
|
|
303
|
+
"reassigned",
|
|
304
|
+
"output_discarded",
|
|
305
|
+
]),
|
|
306
|
+
prior_actor: ActorRefSchema,
|
|
307
|
+
turns_before_takeover: z.number().int().nonnegative().nullable(),
|
|
308
|
+
})
|
|
309
|
+
.strict();
|
|
310
|
+
const RuleFiredOutcomeSchema = z
|
|
311
|
+
.object({
|
|
312
|
+
kind: z.literal("rule_fired"),
|
|
313
|
+
hook_name: NonEmptyStringSchema.max(120),
|
|
314
|
+
decision: z.enum(["allowed", "allowed_bypass", "blocked", "warned"]),
|
|
315
|
+
bypass_token_present: z.boolean(),
|
|
316
|
+
})
|
|
317
|
+
.strict();
|
|
318
|
+
export const DecisionOutcomeSchema = z.discriminatedUnion("kind", [
|
|
319
|
+
DelegationIssuedOutcomeSchema,
|
|
320
|
+
ReviewVerdictOutcomeSchema,
|
|
321
|
+
HumanApprovalOutcomeSchema,
|
|
322
|
+
InterventionOutcomeSchema,
|
|
323
|
+
TakeoverOutcomeSchema,
|
|
324
|
+
RuleFiredOutcomeSchema,
|
|
325
|
+
]);
|
|
326
|
+
// ---------------------------------------------------------------------------
|
|
327
|
+
// The event
|
|
328
|
+
// ---------------------------------------------------------------------------
|
|
329
|
+
export const DecisionEventSchema = z
|
|
330
|
+
.object({
|
|
331
|
+
/**
|
|
332
|
+
* Deterministic, so re-running the backfill is idempotent and an edited
|
|
333
|
+
* artifact produces a new id rather than mutating an old one. See
|
|
334
|
+
* `decisionEventId`.
|
|
335
|
+
*/
|
|
336
|
+
decision_event_id: NonEmptyStringSchema.max(64),
|
|
337
|
+
kind: DecisionEventKindSchema,
|
|
338
|
+
occurred_at: IsoDateTimeSchema,
|
|
339
|
+
actor: ActorRefSchema,
|
|
340
|
+
subject: DecisionSubjectSchema,
|
|
341
|
+
outcome: DecisionOutcomeSchema,
|
|
342
|
+
capture_source: DecisionCaptureSourceSchema,
|
|
343
|
+
/** The artifact this was read out of, so an edit is detectable. */
|
|
344
|
+
source_artifact_hash_sha256: Sha256Schema.nullable(),
|
|
345
|
+
source_line_number: z.number().int().nonnegative().nullable(),
|
|
346
|
+
/**
|
|
347
|
+
* Which rule this event was governed by, once rules have IDs. Nullable in
|
|
348
|
+
* v1 on purpose: the slot exists so Wave 2 backfills into it rather than
|
|
349
|
+
* migrating for it.
|
|
350
|
+
*/
|
|
351
|
+
rule_id: NonEmptyStringSchema.max(40).nullable(),
|
|
352
|
+
rulebook_version: VersionStringSchema.nullable(),
|
|
353
|
+
evidence_pointers: z.array(DecisionEvidencePointerSchema).max(20),
|
|
354
|
+
/**
|
|
355
|
+
* Small, flat, non-narrative extras. Enforced as scalar-or-scalar-array by
|
|
356
|
+
* the shared record schema, which is what keeps prose from arriving here
|
|
357
|
+
* under a different name.
|
|
358
|
+
*/
|
|
359
|
+
attributes: JsonAttributeRecordSchema,
|
|
360
|
+
/**
|
|
361
|
+
* Pinned to the literal rather than the full enum. A decision event that
|
|
362
|
+
* could legally carry raw evidence is one that will eventually carry
|
|
363
|
+
* verdict prose, so the type system refuses it outright.
|
|
364
|
+
*/
|
|
365
|
+
privacy_classification: z.literal("metadata"),
|
|
366
|
+
})
|
|
367
|
+
.strict()
|
|
368
|
+
.superRefine((event, context) => {
|
|
369
|
+
if (event.outcome.kind !== event.kind) {
|
|
370
|
+
context.addIssue({
|
|
371
|
+
code: "custom",
|
|
372
|
+
path: ["outcome", "kind"],
|
|
373
|
+
message: `Outcome is ${event.outcome.kind} but the event says ${event.kind}.`,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
export const DecisionEventEnvelopeSchema = z
|
|
378
|
+
.object({
|
|
379
|
+
envelope_version: z.literal(DECISION_EVENT_ENVELOPE_VERSION),
|
|
380
|
+
/** Who is uploading, e.g. the hook, or the one-off backfill run. */
|
|
381
|
+
writer: NonEmptyStringSchema.max(120),
|
|
382
|
+
writer_version: VersionStringSchema,
|
|
383
|
+
events: z.array(DecisionEventSchema).min(1).max(500),
|
|
384
|
+
})
|
|
385
|
+
.strict();
|
|
@@ -389,9 +389,9 @@ export type RawEvidenceUploadCommitResponse = z.infer<typeof RawEvidenceUploadCo
|
|
|
389
389
|
export declare const CODEX_SESSION_ATTRIBUTION_STATES: readonly ["attributed", "attributed_fallback", "ambiguous", "unattributed", "skipped"];
|
|
390
390
|
export declare const CodexSessionAttributionStateSchema: z.ZodEnum<{
|
|
391
391
|
skipped: "skipped";
|
|
392
|
+
ambiguous: "ambiguous";
|
|
392
393
|
attributed: "attributed";
|
|
393
394
|
attributed_fallback: "attributed_fallback";
|
|
394
|
-
ambiguous: "ambiguous";
|
|
395
395
|
unattributed: "unattributed";
|
|
396
396
|
}>;
|
|
397
397
|
export type CodexSessionAttributionState = z.infer<typeof CodexSessionAttributionStateSchema>;
|
|
@@ -423,9 +423,9 @@ export declare const CodexSessionAttributionSchema: z.ZodObject<{
|
|
|
423
423
|
observed_at: z.ZodString;
|
|
424
424
|
attribution_state: z.ZodEnum<{
|
|
425
425
|
skipped: "skipped";
|
|
426
|
+
ambiguous: "ambiguous";
|
|
426
427
|
attributed: "attributed";
|
|
427
428
|
attributed_fallback: "attributed_fallback";
|
|
428
|
-
ambiguous: "ambiguous";
|
|
429
429
|
unattributed: "unattributed";
|
|
430
430
|
}>;
|
|
431
431
|
attribution_reason: z.ZodString;
|
|
@@ -491,9 +491,9 @@ export declare const CodexSessionAttributionReportRequestSchema: z.ZodObject<{
|
|
|
491
491
|
observed_at: z.ZodString;
|
|
492
492
|
attribution_state: z.ZodEnum<{
|
|
493
493
|
skipped: "skipped";
|
|
494
|
+
ambiguous: "ambiguous";
|
|
494
495
|
attributed: "attributed";
|
|
495
496
|
attributed_fallback: "attributed_fallback";
|
|
496
|
-
ambiguous: "ambiguous";
|
|
497
497
|
unattributed: "unattributed";
|
|
498
498
|
}>;
|
|
499
499
|
attribution_reason: z.ZodString;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export * from "./agent-artifacts.js";
|
|
2
2
|
export * from "./common.js";
|
|
3
|
+
export * from "./decision-event.js";
|
|
4
|
+
export * from "./decision-event-id.js";
|
|
3
5
|
export * from "./evidence-completeness.js";
|
|
4
6
|
export * from "./evidence-upload.js";
|
|
5
7
|
export * from "./ingest-dto.js";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export * from "./agent-artifacts.js";
|
|
2
2
|
export * from "./common.js";
|
|
3
|
+
export * from "./decision-event.js";
|
|
4
|
+
export * from "./decision-event-id.js";
|
|
3
5
|
export * from "./evidence-completeness.js";
|
|
4
6
|
export * from "./evidence-upload.js";
|
|
5
7
|
export * from "./ingest-dto.js";
|