ushman-ledger 0.3.0 → 1.1.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.
- package/CHANGELOG.md +4 -14
- package/README.md +8 -56
- package/dist/archive-journal.d.ts +29 -18
- package/dist/archive-journal.d.ts.map +1 -1
- package/dist/archive-journal.js +17 -17
- package/dist/builders.d.ts +52 -374
- package/dist/builders.d.ts.map +1 -1
- package/dist/builders.js +10 -60
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +8 -14
- package/dist/handle.d.ts +2 -2
- package/dist/handle.d.ts.map +1 -1
- package/dist/handle.js +1 -14
- package/dist/index.d.ts +3 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -4
- package/dist/lab-min.d.ts +7 -7
- package/dist/lab-min.d.ts.map +1 -1
- package/dist/lab-min.js +7 -9
- package/dist/list.d.ts +84 -325
- package/dist/list.d.ts.map +1 -1
- package/dist/read-index.d.ts +45 -57
- package/dist/read-index.d.ts.map +1 -1
- package/dist/read-index.js +16 -34
- package/dist/record.d.ts.map +1 -1
- package/dist/record.js +27 -114
- package/dist/recovery.d.ts +19 -8
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +13 -13
- package/dist/render/retro.d.ts.map +1 -1
- package/dist/render/retro.js +1 -4
- package/dist/schema/entry.d.ts +1365 -3291
- package/dist/schema/entry.d.ts.map +1 -1
- package/dist/schema/entry.js +184 -516
- package/dist/schema/manifest.d.ts +28 -41
- package/dist/schema/manifest.d.ts.map +1 -1
- package/dist/schema/manifest.js +20 -24
- package/dist/schema/note.d.ts +3 -9
- package/dist/schema/note.d.ts.map +1 -1
- package/dist/schema/note.js +2 -2
- package/dist/storage/filesystem.d.ts +0 -1
- package/dist/storage/filesystem.d.ts.map +1 -1
- package/dist/storage/filesystem.js +4 -4
- package/package.json +3 -4
package/dist/schema/entry.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import
|
|
3
|
-
import { createDeterministicUuidV7, isUuidV7 } from "../uuid.js";
|
|
2
|
+
import * as v from 'valibot';
|
|
4
3
|
import { NoteSubkindSchema } from "./note.js";
|
|
5
4
|
export const LedgerSchemaVersion = 'ushman-ledger-entry/v1';
|
|
6
5
|
export const LEDGER_PHASES = [
|
|
@@ -21,27 +20,12 @@ export const LEDGER_KINDS = [
|
|
|
21
20
|
'tool-invocation',
|
|
22
21
|
'agent-patch',
|
|
23
22
|
'operator-patch',
|
|
24
|
-
'stage-transition',
|
|
25
23
|
'operator-decision',
|
|
26
24
|
'validator-result',
|
|
27
25
|
'runtime-event',
|
|
28
26
|
'note',
|
|
29
27
|
'correction',
|
|
30
28
|
'strip-decision-reverted',
|
|
31
|
-
'descope-brief',
|
|
32
|
-
'merge-return',
|
|
33
|
-
'merge-return-rejected',
|
|
34
|
-
'revert',
|
|
35
|
-
'rollback',
|
|
36
|
-
'rework.test_retired',
|
|
37
|
-
];
|
|
38
|
-
export const STAGE_TRANSITION_STAGES = [
|
|
39
|
-
'intake',
|
|
40
|
-
'seed',
|
|
41
|
-
'vendor-extract',
|
|
42
|
-
'cleanup',
|
|
43
|
-
'parity',
|
|
44
|
-
'characterize',
|
|
45
29
|
];
|
|
46
30
|
export const OPERATOR_DECISION_ACTIONS = [
|
|
47
31
|
'bypass-doctor',
|
|
@@ -52,16 +36,18 @@ export const OPERATOR_DECISION_ACTIONS = [
|
|
|
52
36
|
'ledger-hand-edit',
|
|
53
37
|
'escalation',
|
|
54
38
|
];
|
|
39
|
+
const STRIP_DECISION_INVALIDATED_STAGES = [
|
|
40
|
+
'intake',
|
|
41
|
+
'seed',
|
|
42
|
+
'vendor-extract',
|
|
43
|
+
'cleanup',
|
|
44
|
+
'parity',
|
|
45
|
+
'characterize',
|
|
46
|
+
];
|
|
55
47
|
const SHA256_HEX_PATTERN = /^[a-f0-9]{64}$/u;
|
|
56
48
|
const WINDOWS_ABSOLUTE_PATH_PATTERN = /^[a-z]:[\\/]/iu;
|
|
57
49
|
const normalizeWorkspaceRelativePath = (value) => value.replaceAll('\\', '/');
|
|
58
|
-
const
|
|
59
|
-
...new Set([...(left ?? []), ...right]),
|
|
60
|
-
];
|
|
61
|
-
const WorkspaceRelativePathSchema = z
|
|
62
|
-
.string()
|
|
63
|
-
.min(1)
|
|
64
|
-
.refine((value) => {
|
|
50
|
+
const isNormalizedWorkspaceRelativePath = (value) => {
|
|
65
51
|
const normalized = normalizeWorkspaceRelativePath(value);
|
|
66
52
|
if (normalized.startsWith('/') || WINDOWS_ABSOLUTE_PATH_PATTERN.test(value)) {
|
|
67
53
|
return false;
|
|
@@ -77,543 +63,225 @@ const WorkspaceRelativePathSchema = z
|
|
|
77
63
|
return false;
|
|
78
64
|
}
|
|
79
65
|
return !normalized.split('/').includes('..');
|
|
80
|
-
}, 'Expected a normalized workspace-relative path.');
|
|
81
|
-
const SHA256HexSchema = z.string().regex(SHA256_HEX_PATTERN, 'Expected a lowercase SHA-256 hex digest.');
|
|
82
|
-
const UuidV7Schema = z.string().refine(isUuidV7, 'Expected a UUIDv7 value.');
|
|
83
|
-
const LegacyOperatorDecisionTypeSchema = z.enum([
|
|
84
|
-
'strip-decision',
|
|
85
|
-
'strip-decision-revised',
|
|
86
|
-
'vendor-boundary',
|
|
87
|
-
'intent-exclusion',
|
|
88
|
-
'other',
|
|
89
|
-
]);
|
|
90
|
-
export const mapLegacyDecisionTypeToAction = (decisionType) => {
|
|
91
|
-
switch (decisionType) {
|
|
92
|
-
case 'strip-decision':
|
|
93
|
-
case 'strip-decision-revised':
|
|
94
|
-
return 'override-strip-decision';
|
|
95
|
-
default:
|
|
96
|
-
return 'escalation';
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
const makeDigestRecordSchema = () => z.record(z.string().min(1), SHA256HexSchema);
|
|
100
|
-
const validatePatchPayload = ({ ctx, payload, }) => {
|
|
101
|
-
const touchedPaths = payload.touchedPaths ?? [];
|
|
102
|
-
const beforeMap = payload.fileSha256Before ?? {};
|
|
103
|
-
const afterMap = payload.fileSha256After ?? {};
|
|
104
|
-
if (touchedPaths.length === 0) {
|
|
105
|
-
ctx.addIssue({
|
|
106
|
-
code: 'custom',
|
|
107
|
-
message: 'payload.touchedPaths must contain at least one path',
|
|
108
|
-
path: ['payload', 'touchedPaths'],
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
for (const touchedPath of touchedPaths) {
|
|
112
|
-
if (!(touchedPath in beforeMap)) {
|
|
113
|
-
ctx.addIssue({
|
|
114
|
-
code: 'custom',
|
|
115
|
-
message: `payload.fileSha256Before is missing ${touchedPath}`,
|
|
116
|
-
path: ['payload', 'fileSha256Before', touchedPath],
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
if (!(touchedPath in afterMap)) {
|
|
120
|
-
ctx.addIssue({
|
|
121
|
-
code: 'custom',
|
|
122
|
-
message: `payload.fileSha256After is missing ${touchedPath}`,
|
|
123
|
-
path: ['payload', 'fileSha256After', touchedPath],
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
for (const hunk of payload.hunks ?? []) {
|
|
128
|
-
if (!touchedPaths.includes(hunk.path)) {
|
|
129
|
-
ctx.addIssue({
|
|
130
|
-
code: 'custom',
|
|
131
|
-
message: `payload.hunks path ${hunk.path} must appear in payload.touchedPaths`,
|
|
132
|
-
path: ['payload', 'hunks'],
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
66
|
};
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
export const
|
|
140
|
-
export const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
details: z.record(z.string(), z.unknown()).optional(),
|
|
67
|
+
const WorkspaceRelativePathSchema = v.pipe(v.string(), v.minLength(1), v.check(isNormalizedWorkspaceRelativePath, 'Expected a normalized workspace-relative path.'));
|
|
68
|
+
const SHA256HexSchema = v.pipe(v.string(), v.regex(SHA256_HEX_PATTERN, 'Expected a lowercase SHA-256 hex digest.'));
|
|
69
|
+
export const LedgerPhaseSchema = v.picklist(LEDGER_PHASES);
|
|
70
|
+
export const OperatorDecisionActionSchema = v.picklist(OPERATOR_DECISION_ACTIONS);
|
|
71
|
+
const StripDecisionInvalidatedStageSchema = v.picklist(STRIP_DECISION_INVALIDATED_STAGES);
|
|
72
|
+
export const EmitterSchema = v.object({
|
|
73
|
+
tool: v.pipe(v.string(), v.minLength(1)),
|
|
74
|
+
user: v.optional(v.pipe(v.string(), v.minLength(1))),
|
|
75
|
+
version: v.pipe(v.string(), v.minLength(1)),
|
|
76
|
+
});
|
|
77
|
+
export const LedgerLinksSchema = v.objectWithRest({
|
|
78
|
+
affectedFiles: v.optional(v.array(WorkspaceRelativePathSchema)),
|
|
79
|
+
blobs: v.optional(v.array(v.string())),
|
|
80
|
+
briefId: v.optional(v.string()),
|
|
81
|
+
correctsLedgerId: v.optional(v.string()),
|
|
82
|
+
gitRef: v.optional(v.string()),
|
|
83
|
+
idempotencyKey: v.optional(v.string()),
|
|
84
|
+
stripDecisionId: v.optional(v.string()),
|
|
85
|
+
supersedesLedgerId: v.optional(v.string()),
|
|
86
|
+
validatorVerdictId: v.optional(v.string()),
|
|
87
|
+
}, v.unknown());
|
|
88
|
+
const ledgerEntryBaseEntries = {
|
|
89
|
+
details: v.optional(v.record(v.string(), v.unknown())),
|
|
161
90
|
emitter: EmitterSchema,
|
|
162
|
-
id:
|
|
163
|
-
|
|
164
|
-
links: LedgerLinksSchema.default({}),
|
|
91
|
+
id: v.pipe(v.string(), v.minLength(1)),
|
|
92
|
+
links: v.optional(LedgerLinksSchema, {}),
|
|
165
93
|
phase: LedgerPhaseSchema,
|
|
166
|
-
prevEntryId:
|
|
167
|
-
schemaVersion:
|
|
168
|
-
summary:
|
|
169
|
-
ts:
|
|
170
|
-
}
|
|
171
|
-
export const
|
|
172
|
-
.
|
|
173
|
-
endLine: z.number().int().positive(),
|
|
174
|
-
path: WorkspaceRelativePathSchema,
|
|
175
|
-
startLine: z.number().int().positive(),
|
|
176
|
-
})
|
|
177
|
-
.refine((value) => value.endLine >= value.startLine, {
|
|
178
|
-
message: 'payload.hunks endLine must be greater than or equal to startLine',
|
|
179
|
-
path: ['endLine'],
|
|
180
|
-
});
|
|
181
|
-
export const PatchPayloadSchema = z.object({
|
|
182
|
-
diff: z.string().min(1).optional(),
|
|
183
|
-
diffSha256: SHA256HexSchema,
|
|
184
|
-
fileSha256After: makeDigestRecordSchema().default({}),
|
|
185
|
-
fileSha256Before: makeDigestRecordSchema().default({}),
|
|
186
|
-
hunks: z.array(PatchHunkSchema).default([]),
|
|
187
|
-
touchedPaths: z.array(WorkspaceRelativePathSchema).default([]),
|
|
188
|
-
});
|
|
189
|
-
export const PatchPayloadWriteSchema = PatchPayloadSchema.superRefine((value, ctx) => {
|
|
190
|
-
validatePatchPayload({
|
|
191
|
-
ctx,
|
|
192
|
-
payload: value,
|
|
193
|
-
});
|
|
194
|
-
if (!value.diff) {
|
|
195
|
-
ctx.addIssue({
|
|
196
|
-
code: 'custom',
|
|
197
|
-
message: 'payload.diff is required for new patch records',
|
|
198
|
-
path: ['diff'],
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
export const OperatorDecisionPayloadSchema = z.object({
|
|
203
|
-
action: OperatorDecisionActionSchema,
|
|
204
|
-
checkId: z.string().min(1).optional(),
|
|
205
|
-
rationale: z.string().trim().min(1, 'payload.rationale must not be empty'),
|
|
206
|
-
});
|
|
207
|
-
export const ValidatorResultPayloadSchema = z.object({
|
|
208
|
-
id: UuidV7Schema,
|
|
209
|
-
});
|
|
210
|
-
export const CorrectionPayloadSchema = z.object({
|
|
211
|
-
correctsValidatorResultId: UuidV7Schema.optional(),
|
|
212
|
-
});
|
|
213
|
-
export const StageTransitionPayloadSchema = z.object({
|
|
214
|
-
doctorBaselineId: z.string().min(1).optional(),
|
|
215
|
-
endedAt: z.string().datetime({ offset: true }),
|
|
216
|
-
exitCode: z.number().int(),
|
|
217
|
-
stage: StageTransitionStageSchema,
|
|
218
|
-
startedAt: z.string().datetime({ offset: true }),
|
|
219
|
-
});
|
|
220
|
-
export const StripDecisionRevertedPayloadSchema = z.object({
|
|
221
|
-
invalidatedStages: z.array(StageTransitionStageSchema).optional(),
|
|
222
|
-
rationale: z.string().trim().min(1),
|
|
223
|
-
stripDecisionId: z.string().min(1),
|
|
224
|
-
});
|
|
225
|
-
export const DescopeBriefPayloadSchema = z.object({
|
|
226
|
-
briefId: z.string().min(1),
|
|
227
|
-
reason: z.string().trim().min(1).optional(),
|
|
228
|
-
round: z.number().int().positive().optional(),
|
|
229
|
-
stripDecisionIds: z.array(z.string().min(1)).optional(),
|
|
230
|
-
});
|
|
231
|
-
export const MergeReturnPayloadSchema = z.object({
|
|
232
|
-
briefId: z.string().min(1).optional(),
|
|
233
|
-
returnId: z.string().min(1),
|
|
234
|
-
source: z.string().min(1).optional(),
|
|
235
|
-
});
|
|
236
|
-
export const MergeReturnRejectedPayloadSchema = MergeReturnPayloadSchema.extend({
|
|
237
|
-
reason: z.string().trim().min(1),
|
|
238
|
-
});
|
|
239
|
-
export const RevertPayloadSchema = z.object({
|
|
240
|
-
reason: z.string().trim().min(1),
|
|
241
|
-
targetEntryId: z.string().min(1).optional(),
|
|
242
|
-
});
|
|
243
|
-
export const RollbackPayloadSchema = RevertPayloadSchema.extend({
|
|
244
|
-
targetStage: StageTransitionStageSchema.optional(),
|
|
245
|
-
});
|
|
246
|
-
export const ReworkTestRetiredPayloadSchema = z.object({
|
|
247
|
-
briefId: z.string().min(1).optional(),
|
|
248
|
-
removedBy: z.enum(['descope', 'decompose', 'operator']),
|
|
249
|
-
removedTestPath: WorkspaceRelativePathSchema,
|
|
250
|
-
removedTestSymbols: z.array(z.string().min(1)),
|
|
251
|
-
sourceSymbolDeletedFrom: WorkspaceRelativePathSchema,
|
|
252
|
-
});
|
|
253
|
-
export const ToolInvocationEntrySchema = LedgerEntryBaseSchema.extend({
|
|
254
|
-
args: z.array(z.string()).optional(),
|
|
255
|
-
cwd: z.string().optional(),
|
|
256
|
-
exitCode: z.number().int().optional(),
|
|
257
|
-
kind: z.literal('tool-invocation'),
|
|
258
|
-
});
|
|
259
|
-
export const AgentPatchDiffSchema = z.object({
|
|
260
|
-
addedLines: z.number().int().nonnegative(),
|
|
94
|
+
prevEntryId: v.nullable(v.pipe(v.string(), v.minLength(1))),
|
|
95
|
+
schemaVersion: v.literal(LedgerSchemaVersion),
|
|
96
|
+
summary: v.pipe(v.string(), v.minLength(1)),
|
|
97
|
+
ts: v.pipe(v.string(), v.isoTimestamp()),
|
|
98
|
+
};
|
|
99
|
+
export const AgentPatchDiffSchema = v.object({
|
|
100
|
+
addedLines: v.pipe(v.number(), v.integer(), v.minValue(0)),
|
|
261
101
|
blobSha256: SHA256HexSchema,
|
|
262
|
-
bytes:
|
|
263
|
-
removedLines:
|
|
102
|
+
bytes: v.pipe(v.number(), v.integer(), v.minValue(0)),
|
|
103
|
+
removedLines: v.pipe(v.number(), v.integer(), v.minValue(0)),
|
|
264
104
|
});
|
|
265
|
-
const
|
|
105
|
+
export const OperatorDecisionPayloadSchema = v.object({
|
|
106
|
+
action: OperatorDecisionActionSchema,
|
|
107
|
+
checkId: v.optional(v.pipe(v.string(), v.minLength(1))),
|
|
108
|
+
rationale: v.pipe(v.string(), v.trim(), v.minLength(1, 'payload.rationale must not be empty')),
|
|
109
|
+
});
|
|
110
|
+
export const StripDecisionRevertedPayloadSchema = v.object({
|
|
111
|
+
invalidatedStages: v.optional(v.array(StripDecisionInvalidatedStageSchema)),
|
|
112
|
+
rationale: v.pipe(v.string(), v.trim(), v.minLength(1)),
|
|
113
|
+
stripDecisionId: v.pipe(v.string(), v.minLength(1)),
|
|
114
|
+
});
|
|
115
|
+
export const ToolInvocationEntrySchema = v.object({
|
|
116
|
+
...ledgerEntryBaseEntries,
|
|
117
|
+
args: v.optional(v.array(v.string())),
|
|
118
|
+
cwd: v.optional(v.string()),
|
|
119
|
+
exitCode: v.optional(v.pipe(v.number(), v.integer())),
|
|
120
|
+
kind: v.literal('tool-invocation'),
|
|
121
|
+
});
|
|
122
|
+
const patchEntryEntries = {
|
|
123
|
+
...ledgerEntryBaseEntries,
|
|
266
124
|
diff: AgentPatchDiffSchema,
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
path: ['payload', 'diffSha256'],
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
validatePatchPayload({
|
|
278
|
-
ctx,
|
|
279
|
-
payload: value.payload,
|
|
280
|
-
});
|
|
281
|
-
});
|
|
282
|
-
export const AgentPatchEntrySchema = PatchEntryBaseSchema.safeExtend({
|
|
283
|
-
agent: z.object({
|
|
284
|
-
name: z.string().min(1),
|
|
285
|
-
sessionId: z.string().min(1).optional(),
|
|
125
|
+
rationale: v.pipe(v.string(), v.minLength(1)),
|
|
126
|
+
};
|
|
127
|
+
export const AgentPatchEntrySchema = v.object({
|
|
128
|
+
...patchEntryEntries,
|
|
129
|
+
agent: v.object({
|
|
130
|
+
name: v.pipe(v.string(), v.minLength(1)),
|
|
131
|
+
sessionId: v.optional(v.pipe(v.string(), v.minLength(1))),
|
|
286
132
|
}),
|
|
287
|
-
kind:
|
|
133
|
+
kind: v.literal('agent-patch'),
|
|
288
134
|
});
|
|
289
|
-
export const OperatorPatchEntrySchema =
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
135
|
+
export const OperatorPatchEntrySchema = v.object({
|
|
136
|
+
...patchEntryEntries,
|
|
137
|
+
kind: v.literal('operator-patch'),
|
|
138
|
+
operator: v.object({
|
|
139
|
+
name: v.pipe(v.string(), v.minLength(1)),
|
|
293
140
|
}),
|
|
294
141
|
});
|
|
295
|
-
export const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
});
|
|
299
|
-
export const OperatorDecisionEntrySchema = LedgerEntryBaseSchema.extend({
|
|
300
|
-
body: z.record(z.string(), z.unknown()).optional(),
|
|
301
|
-
decisionType: LegacyOperatorDecisionTypeSchema.optional(),
|
|
302
|
-
kind: z.literal('operator-decision'),
|
|
142
|
+
export const OperatorDecisionEntrySchema = v.object({
|
|
143
|
+
...ledgerEntryBaseEntries,
|
|
144
|
+
kind: v.literal('operator-decision'),
|
|
303
145
|
payload: OperatorDecisionPayloadSchema,
|
|
304
146
|
});
|
|
305
|
-
export const ValidatorResultEntrySchema =
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
resultPath:
|
|
310
|
-
validator:
|
|
311
|
-
verdict:
|
|
312
|
-
});
|
|
313
|
-
export const RuntimeEventEntrySchema =
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
147
|
+
export const ValidatorResultEntrySchema = v.object({
|
|
148
|
+
...ledgerEntryBaseEntries,
|
|
149
|
+
kind: v.literal('validator-result'),
|
|
150
|
+
metrics: v.optional(v.record(v.string(), v.unknown())),
|
|
151
|
+
resultPath: v.optional(v.string()),
|
|
152
|
+
validator: v.picklist(['parity', 'equiv', 'characterize', 'verify', 'doctor']),
|
|
153
|
+
verdict: v.picklist(['green', 'yellow', 'red']),
|
|
154
|
+
});
|
|
155
|
+
export const RuntimeEventEntrySchema = v.object({
|
|
156
|
+
...ledgerEntryBaseEntries,
|
|
157
|
+
kind: v.literal('runtime-event'),
|
|
158
|
+
level: v.picklist(['info', 'warn', 'error']),
|
|
159
|
+
message: v.pipe(v.string(), v.minLength(1)),
|
|
160
|
+
source: v.pipe(v.string(), v.minLength(1)),
|
|
161
|
+
stripDecisionId: v.optional(v.string()),
|
|
162
|
+
});
|
|
163
|
+
export const NoteEntrySchema = v.object({
|
|
164
|
+
...ledgerEntryBaseEntries,
|
|
165
|
+
body: v.string(),
|
|
166
|
+
kind: v.literal('note'),
|
|
323
167
|
subkind: NoteSubkindSchema,
|
|
324
168
|
});
|
|
325
|
-
export const CorrectionEntrySchema =
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
rationale:
|
|
329
|
-
}).
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
message: 'correction entries require links.correctsLedgerId',
|
|
334
|
-
path: ['links', 'correctsLedgerId'],
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
});
|
|
338
|
-
export const StripDecisionRevertedEntrySchema = LedgerEntryBaseSchema.extend({
|
|
339
|
-
kind: z.literal('strip-decision-reverted'),
|
|
169
|
+
export const CorrectionEntrySchema = v.pipe(v.object({
|
|
170
|
+
...ledgerEntryBaseEntries,
|
|
171
|
+
kind: v.literal('correction'),
|
|
172
|
+
rationale: v.pipe(v.string(), v.minLength(1)),
|
|
173
|
+
}), v.check((value) => Boolean(value.links?.correctsLedgerId), 'correction entries require links.correctsLedgerId'));
|
|
174
|
+
export const StripDecisionRevertedEntrySchema = v.object({
|
|
175
|
+
...ledgerEntryBaseEntries,
|
|
176
|
+
kind: v.literal('strip-decision-reverted'),
|
|
340
177
|
payload: StripDecisionRevertedPayloadSchema,
|
|
341
178
|
});
|
|
342
|
-
export const
|
|
343
|
-
kind: z.literal('descope-brief'),
|
|
344
|
-
payload: DescopeBriefPayloadSchema,
|
|
345
|
-
});
|
|
346
|
-
export const MergeReturnEntrySchema = LedgerEntryBaseSchema.extend({
|
|
347
|
-
kind: z.literal('merge-return'),
|
|
348
|
-
payload: MergeReturnPayloadSchema,
|
|
349
|
-
});
|
|
350
|
-
export const MergeReturnRejectedEntrySchema = LedgerEntryBaseSchema.extend({
|
|
351
|
-
kind: z.literal('merge-return-rejected'),
|
|
352
|
-
payload: MergeReturnRejectedPayloadSchema,
|
|
353
|
-
});
|
|
354
|
-
export const RevertEntrySchema = LedgerEntryBaseSchema.extend({
|
|
355
|
-
kind: z.literal('revert'),
|
|
356
|
-
payload: RevertPayloadSchema,
|
|
357
|
-
});
|
|
358
|
-
export const RollbackEntrySchema = LedgerEntryBaseSchema.extend({
|
|
359
|
-
kind: z.literal('rollback'),
|
|
360
|
-
payload: RollbackPayloadSchema,
|
|
361
|
-
});
|
|
362
|
-
export const ReworkTestRetiredEntrySchema = LedgerEntryBaseSchema.extend({
|
|
363
|
-
kind: z.literal('rework.test_retired'),
|
|
364
|
-
payload: ReworkTestRetiredPayloadSchema,
|
|
365
|
-
});
|
|
366
|
-
export const LedgerEntrySchema = z.discriminatedUnion('kind', [
|
|
179
|
+
export const LedgerEntrySchema = v.variant('kind', [
|
|
367
180
|
ToolInvocationEntrySchema,
|
|
368
181
|
AgentPatchEntrySchema,
|
|
369
182
|
OperatorPatchEntrySchema,
|
|
370
|
-
StageTransitionEntrySchema,
|
|
371
183
|
OperatorDecisionEntrySchema,
|
|
372
184
|
ValidatorResultEntrySchema,
|
|
373
185
|
RuntimeEventEntrySchema,
|
|
374
186
|
NoteEntrySchema,
|
|
375
187
|
CorrectionEntrySchema,
|
|
376
188
|
StripDecisionRevertedEntrySchema,
|
|
377
|
-
DescopeBriefEntrySchema,
|
|
378
|
-
MergeReturnEntrySchema,
|
|
379
|
-
MergeReturnRejectedEntrySchema,
|
|
380
|
-
RevertEntrySchema,
|
|
381
|
-
RollbackEntrySchema,
|
|
382
|
-
ReworkTestRetiredEntrySchema,
|
|
383
189
|
]);
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
name: z.string().min(1),
|
|
387
|
-
sessionId: z.string().min(1).optional(),
|
|
388
|
-
}),
|
|
389
|
-
diff: AgentPatchDiffSchema,
|
|
390
|
-
kind: z.literal('agent-patch'),
|
|
391
|
-
rationale: z.string().min(1),
|
|
392
|
-
});
|
|
393
|
-
const LegacyOperatorDecisionEntrySchema = LedgerEntryBaseSchema.extend({
|
|
394
|
-
body: z.record(z.string(), z.unknown()),
|
|
395
|
-
decisionType: LegacyOperatorDecisionTypeSchema,
|
|
396
|
-
kind: z.literal('operator-decision'),
|
|
397
|
-
});
|
|
398
|
-
const LegacyValidatorResultEntrySchema = LedgerEntryBaseSchema.extend({
|
|
399
|
-
kind: z.literal('validator-result'),
|
|
400
|
-
metrics: z.record(z.string(), z.unknown()).optional(),
|
|
401
|
-
resultPath: z.string().optional(),
|
|
402
|
-
validator: z.enum(['parity', 'equiv', 'characterize', 'verify', 'doctor']),
|
|
403
|
-
verdict: z.enum(['green', 'yellow', 'red']),
|
|
404
|
-
});
|
|
405
|
-
const RecordEntryBaseSchema = z.object({
|
|
406
|
-
details: z.record(z.string(), z.unknown()).optional(),
|
|
190
|
+
const recordEntryBaseEntries = {
|
|
191
|
+
details: v.optional(v.record(v.string(), v.unknown())),
|
|
407
192
|
emitter: EmitterSchema,
|
|
408
|
-
idempotencyKey:
|
|
409
|
-
links:
|
|
193
|
+
idempotencyKey: v.optional(v.pipe(v.string(), v.minLength(1))),
|
|
194
|
+
links: v.optional(LedgerLinksSchema),
|
|
410
195
|
phase: LedgerPhaseSchema,
|
|
411
|
-
summary:
|
|
412
|
-
}
|
|
413
|
-
export const ToolInvocationRecordSchema =
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
}
|
|
427
|
-
const
|
|
428
|
-
diff
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
rationale: z.string().min(1),
|
|
432
|
-
}).superRefine((value, ctx) => {
|
|
433
|
-
if (!value.payload && !value.diff && !value.diffPath) {
|
|
434
|
-
ctx.addIssue({
|
|
435
|
-
code: 'custom',
|
|
436
|
-
message: 'patch records require payload, diff, or diffPath',
|
|
437
|
-
path: ['payload'],
|
|
438
|
-
});
|
|
196
|
+
summary: v.pipe(v.string(), v.minLength(1)),
|
|
197
|
+
};
|
|
198
|
+
export const ToolInvocationRecordSchema = v.object({
|
|
199
|
+
...recordEntryBaseEntries,
|
|
200
|
+
args: v.optional(v.array(v.string())),
|
|
201
|
+
cwd: v.optional(v.string()),
|
|
202
|
+
exitCode: v.optional(v.pipe(v.number(), v.integer())),
|
|
203
|
+
kind: v.literal('tool-invocation'),
|
|
204
|
+
});
|
|
205
|
+
const patchRecordEntries = {
|
|
206
|
+
...recordEntryBaseEntries,
|
|
207
|
+
diff: v.optional(AgentPatchDiffSchema),
|
|
208
|
+
diffPath: v.optional(v.pipe(v.string(), v.minLength(1))),
|
|
209
|
+
diffText: v.optional(v.pipe(v.string(), v.minLength(1))),
|
|
210
|
+
rationale: v.pipe(v.string(), v.minLength(1)),
|
|
211
|
+
};
|
|
212
|
+
const validatePatchRecord = (value) => {
|
|
213
|
+
const provided = [value.diff, value.diffPath, value.diffText].filter((entry) => Boolean(entry));
|
|
214
|
+
if (provided.length === 0) {
|
|
215
|
+
return false;
|
|
439
216
|
}
|
|
440
|
-
if (value.
|
|
441
|
-
|
|
442
|
-
code: 'custom',
|
|
443
|
-
message: 'patch records must provide either diff or diffPath, not both',
|
|
444
|
-
path: ['diffPath'],
|
|
445
|
-
});
|
|
217
|
+
if (value.diffPath && value.diffText) {
|
|
218
|
+
return false;
|
|
446
219
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
220
|
+
return true;
|
|
221
|
+
};
|
|
222
|
+
const PATCH_RECORD_REFINEMENT_MESSAGE = 'patch records require exactly one source: diff, diffPath, or diffText (diffPath and diffText cannot be combined).';
|
|
223
|
+
export const AgentPatchRecordSchema = v.pipe(v.object({
|
|
224
|
+
...patchRecordEntries,
|
|
225
|
+
agent: v.object({
|
|
226
|
+
name: v.pipe(v.string(), v.minLength(1)),
|
|
227
|
+
sessionId: v.optional(v.pipe(v.string(), v.minLength(1))),
|
|
452
228
|
}),
|
|
453
|
-
kind:
|
|
454
|
-
});
|
|
455
|
-
export const OperatorPatchRecordSchema =
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
229
|
+
kind: v.literal('agent-patch'),
|
|
230
|
+
}), v.check(validatePatchRecord, PATCH_RECORD_REFINEMENT_MESSAGE));
|
|
231
|
+
export const OperatorPatchRecordSchema = v.pipe(v.object({
|
|
232
|
+
...patchRecordEntries,
|
|
233
|
+
kind: v.literal('operator-patch'),
|
|
234
|
+
operator: v.object({
|
|
235
|
+
name: v.pipe(v.string(), v.minLength(1)),
|
|
459
236
|
}),
|
|
460
|
-
});
|
|
461
|
-
export const
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
});
|
|
465
|
-
export const OperatorDecisionRecordSchema = RecordEntryBaseSchema.extend({
|
|
466
|
-
kind: z.literal('operator-decision'),
|
|
237
|
+
}), v.check(validatePatchRecord, PATCH_RECORD_REFINEMENT_MESSAGE));
|
|
238
|
+
export const OperatorDecisionRecordSchema = v.object({
|
|
239
|
+
...recordEntryBaseEntries,
|
|
240
|
+
kind: v.literal('operator-decision'),
|
|
467
241
|
payload: OperatorDecisionPayloadSchema,
|
|
468
242
|
});
|
|
469
|
-
export const ValidatorResultRecordSchema =
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
body: z.string(),
|
|
490
|
-
kind: z.literal('note'),
|
|
243
|
+
export const ValidatorResultRecordSchema = v.object({
|
|
244
|
+
...recordEntryBaseEntries,
|
|
245
|
+
kind: v.literal('validator-result'),
|
|
246
|
+
metrics: v.optional(v.record(v.string(), v.unknown())),
|
|
247
|
+
resultPath: v.optional(v.string()),
|
|
248
|
+
validator: v.picklist(['parity', 'equiv', 'characterize', 'verify', 'doctor']),
|
|
249
|
+
verdict: v.picklist(['green', 'yellow', 'red']),
|
|
250
|
+
});
|
|
251
|
+
export const RuntimeEventRecordSchema = v.object({
|
|
252
|
+
...recordEntryBaseEntries,
|
|
253
|
+
kind: v.literal('runtime-event'),
|
|
254
|
+
level: v.picklist(['info', 'warn', 'error']),
|
|
255
|
+
message: v.pipe(v.string(), v.minLength(1)),
|
|
256
|
+
source: v.pipe(v.string(), v.minLength(1)),
|
|
257
|
+
stripDecisionId: v.optional(v.string()),
|
|
258
|
+
});
|
|
259
|
+
export const NoteRecordSchema = v.object({
|
|
260
|
+
...recordEntryBaseEntries,
|
|
261
|
+
body: v.string(),
|
|
262
|
+
kind: v.literal('note'),
|
|
491
263
|
subkind: NoteSubkindSchema,
|
|
492
264
|
});
|
|
493
|
-
export const CorrectionRecordSchema =
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
rationale:
|
|
497
|
-
}).
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
message: 'correction records require links.correctsLedgerId',
|
|
502
|
-
path: ['links', 'correctsLedgerId'],
|
|
503
|
-
});
|
|
504
|
-
}
|
|
505
|
-
});
|
|
506
|
-
export const StripDecisionRevertedRecordSchema = RecordEntryBaseSchema.extend({
|
|
507
|
-
kind: z.literal('strip-decision-reverted'),
|
|
265
|
+
export const CorrectionRecordSchema = v.pipe(v.object({
|
|
266
|
+
...recordEntryBaseEntries,
|
|
267
|
+
kind: v.literal('correction'),
|
|
268
|
+
rationale: v.pipe(v.string(), v.minLength(1)),
|
|
269
|
+
}), v.check((value) => Boolean(value.links?.correctsLedgerId), 'correction records require links.correctsLedgerId'));
|
|
270
|
+
export const StripDecisionRevertedRecordSchema = v.object({
|
|
271
|
+
...recordEntryBaseEntries,
|
|
272
|
+
kind: v.literal('strip-decision-reverted'),
|
|
508
273
|
payload: StripDecisionRevertedPayloadSchema,
|
|
509
274
|
});
|
|
510
|
-
export const
|
|
511
|
-
kind: z.literal('descope-brief'),
|
|
512
|
-
payload: DescopeBriefPayloadSchema,
|
|
513
|
-
});
|
|
514
|
-
export const MergeReturnRecordSchema = RecordEntryBaseSchema.extend({
|
|
515
|
-
kind: z.literal('merge-return'),
|
|
516
|
-
payload: MergeReturnPayloadSchema,
|
|
517
|
-
});
|
|
518
|
-
export const MergeReturnRejectedRecordSchema = RecordEntryBaseSchema.extend({
|
|
519
|
-
kind: z.literal('merge-return-rejected'),
|
|
520
|
-
payload: MergeReturnRejectedPayloadSchema,
|
|
521
|
-
});
|
|
522
|
-
export const RevertRecordSchema = RecordEntryBaseSchema.extend({
|
|
523
|
-
kind: z.literal('revert'),
|
|
524
|
-
payload: RevertPayloadSchema,
|
|
525
|
-
});
|
|
526
|
-
export const RollbackRecordSchema = RecordEntryBaseSchema.extend({
|
|
527
|
-
kind: z.literal('rollback'),
|
|
528
|
-
payload: RollbackPayloadSchema,
|
|
529
|
-
});
|
|
530
|
-
export const ReworkTestRetiredRecordSchema = RecordEntryBaseSchema.extend({
|
|
531
|
-
kind: z.literal('rework.test_retired'),
|
|
532
|
-
payload: ReworkTestRetiredPayloadSchema,
|
|
533
|
-
});
|
|
534
|
-
export const LedgerRecordSchema = z.discriminatedUnion('kind', [
|
|
275
|
+
export const LedgerRecordSchema = v.variant('kind', [
|
|
535
276
|
ToolInvocationRecordSchema,
|
|
536
277
|
AgentPatchRecordSchema,
|
|
537
278
|
OperatorPatchRecordSchema,
|
|
538
|
-
StageTransitionRecordSchema,
|
|
539
279
|
OperatorDecisionRecordSchema,
|
|
540
280
|
ValidatorResultRecordSchema,
|
|
541
281
|
RuntimeEventRecordSchema,
|
|
542
282
|
NoteRecordSchema,
|
|
543
283
|
CorrectionRecordSchema,
|
|
544
284
|
StripDecisionRevertedRecordSchema,
|
|
545
|
-
DescopeBriefRecordSchema,
|
|
546
|
-
MergeReturnRecordSchema,
|
|
547
|
-
MergeReturnRejectedRecordSchema,
|
|
548
|
-
RevertRecordSchema,
|
|
549
|
-
RollbackRecordSchema,
|
|
550
|
-
ReworkTestRetiredRecordSchema,
|
|
551
285
|
]);
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
links: {
|
|
555
|
-
...entry.links,
|
|
556
|
-
affectedFiles: mergeAffectedFiles(entry.links.affectedFiles, entry.payload.touchedPaths),
|
|
557
|
-
},
|
|
558
|
-
});
|
|
559
|
-
const normalizeLegacyAgentPatchEntry = (entry) => normalizePatchEntry({
|
|
560
|
-
...entry,
|
|
561
|
-
_legacy: true,
|
|
562
|
-
payload: {
|
|
563
|
-
diffSha256: entry.diff.blobSha256,
|
|
564
|
-
fileSha256After: {},
|
|
565
|
-
fileSha256Before: {},
|
|
566
|
-
hunks: [],
|
|
567
|
-
touchedPaths: entry.links.affectedFiles ?? [],
|
|
568
|
-
},
|
|
569
|
-
});
|
|
570
|
-
const normalizeLegacyOperatorDecisionEntry = (entry) => ({
|
|
571
|
-
...entry,
|
|
572
|
-
_legacy: true,
|
|
573
|
-
payload: {
|
|
574
|
-
action: mapLegacyDecisionTypeToAction(entry.decisionType),
|
|
575
|
-
checkId: typeof entry.links.stripDecisionId === 'string' ? entry.links.stripDecisionId : undefined,
|
|
576
|
-
rationale: typeof entry.body.rationale === 'string' && entry.body.rationale.trim().length > 0
|
|
577
|
-
? entry.body.rationale
|
|
578
|
-
: entry.summary,
|
|
579
|
-
},
|
|
580
|
-
});
|
|
581
|
-
const normalizeLegacyValidatorResultEntry = (entry) => ({
|
|
582
|
-
...entry,
|
|
583
|
-
_legacy: true,
|
|
584
|
-
payload: {
|
|
585
|
-
id: createDeterministicUuidV7({
|
|
586
|
-
seed: `${entry.id}:${entry.summary}:${entry.validator}:${entry.verdict}`,
|
|
587
|
-
timestamp: entry.ts,
|
|
588
|
-
}),
|
|
589
|
-
},
|
|
590
|
-
});
|
|
591
|
-
const normalizeCurrentEntry = (entry) => {
|
|
592
|
-
switch (entry.kind) {
|
|
593
|
-
case 'agent-patch':
|
|
594
|
-
case 'operator-patch':
|
|
595
|
-
return normalizePatchEntry(entry);
|
|
596
|
-
default:
|
|
597
|
-
return entry;
|
|
598
|
-
}
|
|
599
|
-
};
|
|
600
|
-
export const parseLedgerEntry = (input) => {
|
|
601
|
-
const current = LedgerEntrySchema.safeParse(input);
|
|
602
|
-
if (current.success) {
|
|
603
|
-
return normalizeCurrentEntry(current.data);
|
|
604
|
-
}
|
|
605
|
-
const legacyAgentPatch = LegacyAgentPatchEntrySchema.safeParse(input);
|
|
606
|
-
if (legacyAgentPatch.success) {
|
|
607
|
-
return normalizeLegacyAgentPatchEntry(legacyAgentPatch.data);
|
|
608
|
-
}
|
|
609
|
-
const legacyOperatorDecision = LegacyOperatorDecisionEntrySchema.safeParse(input);
|
|
610
|
-
if (legacyOperatorDecision.success) {
|
|
611
|
-
return normalizeLegacyOperatorDecisionEntry(legacyOperatorDecision.data);
|
|
612
|
-
}
|
|
613
|
-
const legacyValidatorResult = LegacyValidatorResultEntrySchema.safeParse(input);
|
|
614
|
-
if (legacyValidatorResult.success) {
|
|
615
|
-
return normalizeLegacyValidatorResultEntry(legacyValidatorResult.data);
|
|
616
|
-
}
|
|
617
|
-
return LedgerEntrySchema.parse(input);
|
|
618
|
-
};
|
|
619
|
-
export const parseLedgerRecord = (input) => LedgerRecordSchema.parse(input);
|
|
286
|
+
export const parseLedgerEntry = (input) => v.parse(LedgerEntrySchema, input);
|
|
287
|
+
export const parseLedgerRecord = (input) => v.parse(LedgerRecordSchema, input);
|