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/read-index.d.ts
CHANGED
|
@@ -1,59 +1,47 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as v from 'valibot';
|
|
2
2
|
import { type LedgerEntry, type LedgerKind, type LedgerPhase } from './schema/entry.ts';
|
|
3
3
|
import type { LedgerManifest } from './schema/manifest.ts';
|
|
4
|
-
declare const ReadIndexEntrySchema:
|
|
5
|
-
id:
|
|
6
|
-
kind:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
rollback: "rollback";
|
|
46
|
-
"rework.test_retired": "rework.test_retired";
|
|
47
|
-
}>;
|
|
48
|
-
ts: z.ZodString;
|
|
49
|
-
}, z.core.$strip>>>;
|
|
50
|
-
entryCount: z.ZodNumber;
|
|
51
|
-
lastEntryId: z.ZodNullable<z.ZodString>;
|
|
52
|
-
lastSequence: z.ZodNumber;
|
|
53
|
-
schemaVersion: z.ZodLiteral<"ushman-ledger-read-index/v1">;
|
|
54
|
-
}, z.core.$strip>;
|
|
55
|
-
export type LedgerReadIndex = z.infer<typeof LedgerReadIndexSchema>;
|
|
56
|
-
export type ReadIndexEntry = z.infer<typeof ReadIndexEntrySchema>;
|
|
4
|
+
declare const ReadIndexEntrySchema: v.ObjectSchema<{
|
|
5
|
+
readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
|
|
6
|
+
readonly kind: v.PicklistSchema<readonly ["tool-invocation", "agent-patch", "operator-patch", "operator-decision", "validator-result", "runtime-event", "note", "correction", "strip-decision-reverted"], undefined>;
|
|
7
|
+
readonly ts: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>;
|
|
8
|
+
}, undefined>;
|
|
9
|
+
declare const LedgerReadIndexSchema: v.SchemaWithPipe<readonly [v.ObjectSchema<{
|
|
10
|
+
readonly coveredFiles: v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, readonly []>;
|
|
11
|
+
readonly entries: v.OptionalSchema<v.ArraySchema<v.ObjectSchema<{
|
|
12
|
+
readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
|
|
13
|
+
readonly kind: v.PicklistSchema<readonly ["tool-invocation", "agent-patch", "operator-patch", "operator-decision", "validator-result", "runtime-event", "note", "correction", "strip-decision-reverted"], undefined>;
|
|
14
|
+
readonly ts: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>;
|
|
15
|
+
}, undefined>, undefined>, readonly []>;
|
|
16
|
+
readonly entryCount: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
17
|
+
readonly lastEntryId: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
|
|
18
|
+
readonly lastSequence: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
19
|
+
readonly schemaVersion: v.LiteralSchema<"ushman-ledger-read-index/v1", undefined>;
|
|
20
|
+
}, undefined>, v.CheckAction<{
|
|
21
|
+
coveredFiles: string[];
|
|
22
|
+
entries: {
|
|
23
|
+
id: string;
|
|
24
|
+
kind: "tool-invocation" | "agent-patch" | "operator-patch" | "operator-decision" | "validator-result" | "runtime-event" | "note" | "correction" | "strip-decision-reverted";
|
|
25
|
+
ts: string;
|
|
26
|
+
}[];
|
|
27
|
+
entryCount: number;
|
|
28
|
+
lastEntryId: string | null;
|
|
29
|
+
lastSequence: number;
|
|
30
|
+
schemaVersion: "ushman-ledger-read-index/v1";
|
|
31
|
+
}, "entryCount must match entries.length">, v.CheckAction<{
|
|
32
|
+
coveredFiles: string[];
|
|
33
|
+
entries: {
|
|
34
|
+
id: string;
|
|
35
|
+
kind: "tool-invocation" | "agent-patch" | "operator-patch" | "operator-decision" | "validator-result" | "runtime-event" | "note" | "correction" | "strip-decision-reverted";
|
|
36
|
+
ts: string;
|
|
37
|
+
}[];
|
|
38
|
+
entryCount: number;
|
|
39
|
+
lastEntryId: string | null;
|
|
40
|
+
lastSequence: number;
|
|
41
|
+
schemaVersion: "ushman-ledger-read-index/v1";
|
|
42
|
+
}, "lastEntryId must match the final indexed entry">]>;
|
|
43
|
+
export type LedgerReadIndex = v.InferOutput<typeof LedgerReadIndexSchema>;
|
|
44
|
+
export type ReadIndexEntry = v.InferOutput<typeof ReadIndexEntrySchema>;
|
|
57
45
|
export type ManifestEntryLocation = readonly [string, {
|
|
58
46
|
phase: LedgerPhase;
|
|
59
47
|
sequence: number;
|
|
@@ -62,7 +50,7 @@ export declare const buildReadIndexFromManifest: (workspaceRoot: string, manifes
|
|
|
62
50
|
coveredFiles: string[];
|
|
63
51
|
entries: {
|
|
64
52
|
id: string;
|
|
65
|
-
kind: "tool-invocation" | "agent-patch" | "operator-patch" | "
|
|
53
|
+
kind: "tool-invocation" | "agent-patch" | "operator-patch" | "operator-decision" | "validator-result" | "runtime-event" | "note" | "correction" | "strip-decision-reverted";
|
|
66
54
|
ts: string;
|
|
67
55
|
}[];
|
|
68
56
|
entryCount: number;
|
|
@@ -77,7 +65,7 @@ export declare const ensureReadIndexUnderLock: (workspaceRoot: string, manifest:
|
|
|
77
65
|
coveredFiles: string[];
|
|
78
66
|
entries: {
|
|
79
67
|
id: string;
|
|
80
|
-
kind: "tool-invocation" | "agent-patch" | "operator-patch" | "
|
|
68
|
+
kind: "tool-invocation" | "agent-patch" | "operator-patch" | "operator-decision" | "validator-result" | "runtime-event" | "note" | "correction" | "strip-decision-reverted";
|
|
81
69
|
ts: string;
|
|
82
70
|
}[];
|
|
83
71
|
entryCount: number;
|
|
@@ -93,7 +81,7 @@ export declare const appendEntryToReadIndex: ({ entry, readIndex, sequence, }: {
|
|
|
93
81
|
coveredFiles: string[];
|
|
94
82
|
entries: {
|
|
95
83
|
id: string;
|
|
96
|
-
kind: "tool-invocation" | "agent-patch" | "operator-patch" | "
|
|
84
|
+
kind: "tool-invocation" | "agent-patch" | "operator-patch" | "operator-decision" | "validator-result" | "runtime-event" | "note" | "correction" | "strip-decision-reverted";
|
|
97
85
|
ts: string;
|
|
98
86
|
}[];
|
|
99
87
|
entryCount: number;
|
package/dist/read-index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read-index.d.ts","sourceRoot":"","sources":["../src/read-index.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"read-index.d.ts","sourceRoot":"","sources":["../src/read-index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAC;AAG7B,OAAO,EAAgB,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAoB,MAAM,mBAAmB,CAAC;AACxH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAO3D,QAAA,MAAM,oBAAoB;;;;aAIxB,CAAC;AAEH,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sDAiB1B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC1E,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACxE,MAAM,MAAM,qBAAqB,GAAG,SAAS,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AA0HhG,eAAO,MAAM,0BAA0B,GAAU,eAAe,MAAM,EAAE,UAAU,cAAc;;;;;;;;;;;EAuB/F,CAAC;AAYF,eAAO,MAAM,kBAAkB,GAAI,OAAO,eAAe,EAAE,UAAU,cAAc,YAG1C,CAAC;AAE1C,eAAO,MAAM,aAAa,GAAU,eAAe,MAAM,KAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAWzF,CAAC;AAEF,eAAO,MAAM,aAAa,GAAU,eAAe,MAAM,EAAE,WAAW,eAAe,kBAGpF,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,eAAe,MAAM,EAAE,UAAU,cAAc;;;;;;;;;;;EAiB7F,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,iCAIpC;IACC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC7B;;;;;;;;;;;CAYA,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,uCAIpC;IACC,QAAQ,CAAC,MAAM,EAAE;QACb,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;QAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;QAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;CAC3C,YAeA,CAAC"}
|
package/dist/read-index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import
|
|
2
|
+
import * as v from 'valibot';
|
|
3
3
|
import { mapWithConcurrencyLimit } from "./async.js";
|
|
4
4
|
import { stableStringify } from "./json.js";
|
|
5
5
|
import { LEDGER_KINDS, parseLedgerEntry } from "./schema/entry.js";
|
|
@@ -7,40 +7,22 @@ import { readPhaseEntryText, resolveLedgerPaths, writeAtomicTextFile } from "./s
|
|
|
7
7
|
const READ_INDEX_SCHEMA_VERSION = 'ushman-ledger-read-index/v1';
|
|
8
8
|
const ENTRY_READ_BATCH_SIZE = 32;
|
|
9
9
|
const ENTRY_READ_CONCURRENCY = 16;
|
|
10
|
-
const ReadIndexEntrySchema =
|
|
11
|
-
id:
|
|
12
|
-
kind:
|
|
13
|
-
ts:
|
|
14
|
-
});
|
|
15
|
-
const LedgerReadIndexSchema = z
|
|
16
|
-
.object({
|
|
17
|
-
coveredFiles: z.array(z.string()).default([]),
|
|
18
|
-
entries: z.array(ReadIndexEntrySchema).default([]),
|
|
19
|
-
entryCount: z.number().int().nonnegative(),
|
|
20
|
-
lastEntryId: z.string().min(1).nullable(),
|
|
21
|
-
lastSequence: z.number().int().nonnegative(),
|
|
22
|
-
schemaVersion: z.literal(READ_INDEX_SCHEMA_VERSION),
|
|
23
|
-
})
|
|
24
|
-
.superRefine((value, ctx) => {
|
|
25
|
-
if (value.entryCount !== value.entries.length) {
|
|
26
|
-
ctx.addIssue({
|
|
27
|
-
code: 'custom',
|
|
28
|
-
message: 'entryCount must match entries.length',
|
|
29
|
-
path: ['entryCount'],
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
const expectedLastEntryId = value.entries.at(-1)?.id ?? null;
|
|
33
|
-
if (value.lastEntryId !== expectedLastEntryId) {
|
|
34
|
-
ctx.addIssue({
|
|
35
|
-
code: 'custom',
|
|
36
|
-
message: 'lastEntryId must match the final indexed entry',
|
|
37
|
-
path: ['lastEntryId'],
|
|
38
|
-
});
|
|
39
|
-
}
|
|
10
|
+
const ReadIndexEntrySchema = v.object({
|
|
11
|
+
id: v.pipe(v.string(), v.minLength(1)),
|
|
12
|
+
kind: v.picklist(LEDGER_KINDS),
|
|
13
|
+
ts: v.pipe(v.string(), v.isoTimestamp()),
|
|
40
14
|
});
|
|
15
|
+
const LedgerReadIndexSchema = v.pipe(v.object({
|
|
16
|
+
coveredFiles: v.optional(v.array(v.string()), []),
|
|
17
|
+
entries: v.optional(v.array(ReadIndexEntrySchema), []),
|
|
18
|
+
entryCount: v.pipe(v.number(), v.integer(), v.minValue(0)),
|
|
19
|
+
lastEntryId: v.nullable(v.pipe(v.string(), v.minLength(1))),
|
|
20
|
+
lastSequence: v.pipe(v.number(), v.integer(), v.minValue(0)),
|
|
21
|
+
schemaVersion: v.literal(READ_INDEX_SCHEMA_VERSION),
|
|
22
|
+
}), v.check((value) => value.entryCount === value.entries.length, 'entryCount must match entries.length'), v.check((value) => value.lastEntryId === (value.entries.at(-1)?.id ?? null), 'lastEntryId must match the final indexed entry'));
|
|
41
23
|
const parseReadIndexText = (filePath, text) => {
|
|
42
24
|
try {
|
|
43
|
-
return
|
|
25
|
+
return v.parse(LedgerReadIndexSchema, JSON.parse(text));
|
|
44
26
|
}
|
|
45
27
|
catch (error) {
|
|
46
28
|
throw new Error(`Invalid ledger read index at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -89,7 +71,7 @@ const mergeSortedUniquePaths = (existingPaths, additionalPaths) => {
|
|
|
89
71
|
}
|
|
90
72
|
return mergedPaths;
|
|
91
73
|
};
|
|
92
|
-
const buildReadIndex = ({ coveredFiles, entries, lastSequence, }) =>
|
|
74
|
+
const buildReadIndex = ({ coveredFiles, entries, lastSequence, }) => v.parse(LedgerReadIndexSchema, {
|
|
93
75
|
coveredFiles,
|
|
94
76
|
entries,
|
|
95
77
|
entryCount: entries.length,
|
|
@@ -162,7 +144,7 @@ export const readReadIndex = async (workspaceRoot) => {
|
|
|
162
144
|
};
|
|
163
145
|
export const saveReadIndex = async (workspaceRoot, readIndex) => {
|
|
164
146
|
const filePath = resolveLedgerPaths(workspaceRoot).readIndexFile;
|
|
165
|
-
await writeAtomicTextFile(filePath, `${stableStringify(
|
|
147
|
+
await writeAtomicTextFile(filePath, `${stableStringify(v.parse(LedgerReadIndexSchema, readIndex), true)}\n`);
|
|
166
148
|
};
|
|
167
149
|
export const ensureReadIndexUnderLock = async (workspaceRoot, manifest) => {
|
|
168
150
|
let currentReadIndex = null;
|
package/dist/record.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../src/record.ts"],"names":[],"mappings":"AAQA,OAAO,EAEH,KAAK,WAAW,
|
|
1
|
+
{"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../src/record.ts"],"names":[],"mappings":"AAQA,OAAO,EAEH,KAAK,WAAW,EAKnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAU3D,KAAK,qBAAqB,GAAG;IACzB,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,WAAW,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,WAAW,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChF,QAAQ,CAAC,uBAAuB,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,WAAW,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,WAAW,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACpF,CAAC;AAUF,eAAO,MAAM,wBAAwB,GAAI,eAAe,MAAM,EAAE,OAAO,qBAAqB,GAAG,IAAI,SAOlG,CAAC;AAoHF,eAAO,MAAM,YAAY,GACrB,eAAe,MAAM,EACrB,OAAO,OAAO,KACf,OAAO,CAAC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAqF5C,CAAC;AAEF,eAAO,MAAM,aAAa,GACtB,eAAe,MAAM,EACrB,UAAU,cAAc,EACxB,SAAS,MAAM,KAChB,OAAO,CAAC,WAAW,CAMrB,CAAC"}
|
package/dist/record.js
CHANGED
|
@@ -1,29 +1,17 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import * as v from 'valibot';
|
|
3
4
|
import { resolveBlobPath, storePatchBlob } from "./blobs.js";
|
|
4
5
|
import { sha256Hex, stableStringify } from "./json.js";
|
|
5
6
|
import { updateManifestForEntry } from "./manifest-update.js";
|
|
6
|
-
import { derivePatchPayloadFromDiffText } from "./patch-metadata.js";
|
|
7
7
|
import { appendEntryToReadIndex, saveReadIndex } from "./read-index.js";
|
|
8
8
|
import { reconcileLedgerStateUnderLock, removePendingCommit, writePendingCommit } from "./recovery.js";
|
|
9
|
-
import { LedgerEntrySchema,
|
|
9
|
+
import { LedgerEntrySchema, parseLedgerEntry, parseLedgerRecord, } from "./schema/entry.js";
|
|
10
10
|
import { ensureLedgerDirectories, readManifest, resolveLedgerPaths, saveManifest, writeEntryFile, } from "./storage/filesystem.js";
|
|
11
11
|
import { acquireLock } from "./storage/lock.js";
|
|
12
|
-
import { createDeterministicUuidV7 } from "./uuid.js";
|
|
13
12
|
const appendRecordTestHooks = new Map();
|
|
14
13
|
const resolveWorkspaceKey = (workspaceRoot) => path.resolve(workspaceRoot);
|
|
15
14
|
const getAppendRecordTestHooks = (workspaceRoot) => appendRecordTestHooks.get(resolveWorkspaceKey(workspaceRoot));
|
|
16
|
-
const warnedDeprecations = new Set();
|
|
17
|
-
const warnOnce = (key, message) => {
|
|
18
|
-
if (warnedDeprecations.has(key)) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
warnedDeprecations.add(key);
|
|
22
|
-
process.emitWarning(message, {
|
|
23
|
-
code: key,
|
|
24
|
-
type: 'DeprecationWarning',
|
|
25
|
-
});
|
|
26
|
-
};
|
|
27
15
|
export const setAppendRecordTestHooks = (workspaceRoot, hooks) => {
|
|
28
16
|
const key = resolveWorkspaceKey(workspaceRoot);
|
|
29
17
|
if (!hooks) {
|
|
@@ -64,118 +52,47 @@ const buildEntryId = (entryWithoutId, sequence) => {
|
|
|
64
52
|
const bodyHash = sha256Hex(stableStringify(entryWithoutId));
|
|
65
53
|
return `${toEntryIdTimestamp(entryWithoutId.ts)}-${sequence.toString().padStart(8, '0')}-${bodyHash.slice(0, 12)}`;
|
|
66
54
|
};
|
|
67
|
-
const
|
|
68
|
-
if (record.kind !== 'validator-result') {
|
|
69
|
-
return record;
|
|
70
|
-
}
|
|
71
|
-
const payloadId = record.payload?.id;
|
|
72
|
-
if (payloadId) {
|
|
73
|
-
return {
|
|
74
|
-
...record,
|
|
75
|
-
payload: ValidatorResultPayloadSchema.parse(record.payload),
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
warnOnce('USHMAN_LEDGER_VALIDATOR_ID_COMPAT', 'validator-result records should provide payload.id. A compatibility UUIDv7 was synthesized for this append.');
|
|
79
|
-
return {
|
|
80
|
-
...record,
|
|
81
|
-
payload: {
|
|
82
|
-
id: createDeterministicUuidV7({
|
|
83
|
-
seed: stableStringify(record),
|
|
84
|
-
timestamp: 0,
|
|
85
|
-
}),
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
};
|
|
89
|
-
const resolvePatchTextAndBlob = async ({ record, workspaceRoot, }) => {
|
|
55
|
+
const loadPatchText = async ({ record, workspaceRoot, }) => {
|
|
90
56
|
if (record.diffPath) {
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
patchText,
|
|
94
|
-
storedDiff: await storePatchBlob(workspaceRoot, patchText),
|
|
95
|
-
};
|
|
57
|
+
return readFile(path.resolve(record.diffPath), 'utf8');
|
|
96
58
|
}
|
|
97
|
-
if (record.
|
|
98
|
-
|
|
99
|
-
if (record.diff && record.diff.blobSha256 !== storedDiff.blobSha256) {
|
|
100
|
-
throw new Error(`Provided diff blob ${record.diff.blobSha256} does not match payload.diff hash ${storedDiff.blobSha256}.`);
|
|
101
|
-
}
|
|
102
|
-
return {
|
|
103
|
-
patchText: record.payload.diff,
|
|
104
|
-
storedDiff,
|
|
105
|
-
};
|
|
59
|
+
if (record.diffText) {
|
|
60
|
+
return record.diffText;
|
|
106
61
|
}
|
|
107
|
-
if (
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
catch (error) {
|
|
118
|
-
if (error.code === 'ENOENT') {
|
|
119
|
-
throw new Error(`Patch blob ${record.diff.blobSha256} was not found. Store it first or use diffPath.`);
|
|
62
|
+
if (record.diff) {
|
|
63
|
+
try {
|
|
64
|
+
return await readBlobText(workspaceRoot, record.diff);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
if (error.code === 'ENOENT') {
|
|
68
|
+
throw new Error(`Patch blob ${record.diff.blobSha256} was not found. Store it first or use diffPath.`);
|
|
69
|
+
}
|
|
70
|
+
throw error;
|
|
120
71
|
}
|
|
121
|
-
throw error;
|
|
122
72
|
}
|
|
123
|
-
};
|
|
124
|
-
const resolvePatchPayload = async ({ patchText, record, workspaceRoot, }) => {
|
|
125
|
-
const derivedPayload = await derivePatchPayloadFromDiffText({
|
|
126
|
-
patchText,
|
|
127
|
-
workspaceRoot,
|
|
128
|
-
});
|
|
129
|
-
const mergedPayload = PatchPayloadWriteSchema.parse({
|
|
130
|
-
...derivedPayload,
|
|
131
|
-
...record.payload,
|
|
132
|
-
diff: record.payload?.diff ?? patchText,
|
|
133
|
-
diffSha256: record.payload?.diffSha256 ?? derivedPayload.diffSha256,
|
|
134
|
-
fileSha256After: record.payload?.fileSha256After ?? derivedPayload.fileSha256After,
|
|
135
|
-
fileSha256Before: record.payload?.fileSha256Before ?? derivedPayload.fileSha256Before,
|
|
136
|
-
hunks: record.payload?.hunks ?? derivedPayload.hunks,
|
|
137
|
-
touchedPaths: record.payload?.touchedPaths ?? derivedPayload.touchedPaths,
|
|
138
|
-
});
|
|
139
|
-
return mergedPayload;
|
|
73
|
+
throw new Error(`${record.kind} records require diff, diffPath, or diffText.`);
|
|
140
74
|
};
|
|
141
75
|
const resolvePatchRecord = async ({ record, workspaceRoot, }) => {
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const payload = await resolvePatchPayload({
|
|
147
|
-
patchText,
|
|
148
|
-
record,
|
|
149
|
-
workspaceRoot,
|
|
150
|
-
});
|
|
151
|
-
if (payload.diffSha256 !== storedDiff.blobSha256) {
|
|
152
|
-
throw new Error(`Patch payload hash ${payload.diffSha256} does not match stored blob hash ${storedDiff.blobSha256}.`);
|
|
76
|
+
const patchText = await loadPatchText({ record, workspaceRoot });
|
|
77
|
+
const storedDiff = await storePatchBlob(workspaceRoot, patchText);
|
|
78
|
+
if (record.diff && record.diff.blobSha256 !== storedDiff.blobSha256) {
|
|
79
|
+
throw new Error(`Provided diff blob ${record.diff.blobSha256} does not match patch text hash ${storedDiff.blobSha256}.`);
|
|
153
80
|
}
|
|
81
|
+
const { diffPath: _diffPath, diffText: _diffText, ...rest } = record;
|
|
154
82
|
return {
|
|
155
|
-
...
|
|
83
|
+
...rest,
|
|
156
84
|
diff: storedDiff,
|
|
157
85
|
links: {
|
|
158
86
|
...record.links,
|
|
159
|
-
affectedFiles: [...new Set([...(record.links?.affectedFiles ?? []), ...payload.touchedPaths])],
|
|
160
87
|
blobs: [...new Set([...(record.links?.blobs ?? []), storedDiff.blobSha256])],
|
|
161
88
|
},
|
|
162
|
-
payload,
|
|
163
89
|
};
|
|
164
90
|
};
|
|
165
91
|
const normalizeRecord = async ({ record, workspaceRoot, }) => {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return resolvePatchRecord({
|
|
169
|
-
record: normalizedRecord,
|
|
170
|
-
workspaceRoot,
|
|
171
|
-
});
|
|
92
|
+
if (record.kind === 'agent-patch' || record.kind === 'operator-patch') {
|
|
93
|
+
return resolvePatchRecord({ record, workspaceRoot });
|
|
172
94
|
}
|
|
173
|
-
return
|
|
174
|
-
? {
|
|
175
|
-
...normalizedRecord,
|
|
176
|
-
payload: OperatorDecisionPayloadSchema.parse(normalizedRecord.payload),
|
|
177
|
-
}
|
|
178
|
-
: normalizedRecord;
|
|
95
|
+
return record;
|
|
179
96
|
};
|
|
180
97
|
export const appendRecord = async (workspaceRoot, input) => {
|
|
181
98
|
const parsed = parseLedgerRecord(input);
|
|
@@ -222,7 +139,7 @@ export const appendRecord = async (workspaceRoot, input) => {
|
|
|
222
139
|
schemaVersion: 'ushman-ledger-entry/v1',
|
|
223
140
|
ts: new Date().toISOString(),
|
|
224
141
|
};
|
|
225
|
-
const entry =
|
|
142
|
+
const entry = v.parse(LedgerEntrySchema, {
|
|
226
143
|
...entryWithoutId,
|
|
227
144
|
id: buildEntryId(entryWithoutId, nextSequence),
|
|
228
145
|
});
|
|
@@ -259,10 +176,6 @@ export const appendRecord = async (workspaceRoot, input) => {
|
|
|
259
176
|
}
|
|
260
177
|
};
|
|
261
178
|
export const readEntryById = async (workspaceRoot, manifest, entryId) => {
|
|
262
|
-
const
|
|
263
|
-
if (!location) {
|
|
264
|
-
throw new Error(`Ledger entry not found: ${entryId}`);
|
|
265
|
-
}
|
|
266
|
-
const text = await readFile(path.join(resolveLedgerPaths(workspaceRoot).phaseDir(location.phase), `${entryId}.json`), 'utf8');
|
|
179
|
+
const text = await readFile(path.join(resolveLedgerPaths(workspaceRoot).phaseDir(manifest.entryLocations[entryId].phase), `${entryId}.json`), 'utf8');
|
|
267
180
|
return parseLedgerEntry(JSON.parse(text));
|
|
268
181
|
};
|
package/dist/recovery.d.ts
CHANGED
|
@@ -13,7 +13,6 @@ export declare const writePendingCommit: ({ entry, logicalHash, manifest, worksp
|
|
|
13
13
|
}) => Promise<string>;
|
|
14
14
|
export declare const removePendingCommit: (filePath: string) => Promise<void>;
|
|
15
15
|
export declare const reconcilePendingCommitsUnderLock: (workspaceRoot: string) => Promise<{
|
|
16
|
-
[x: string]: unknown;
|
|
17
16
|
archives: {
|
|
18
17
|
createdAt: string;
|
|
19
18
|
integrityHash: string;
|
|
@@ -21,17 +20,29 @@ export declare const reconcilePendingCommitsUnderLock: (workspaceRoot: string) =
|
|
|
21
20
|
}[];
|
|
22
21
|
createdAt: string;
|
|
23
22
|
entryCount: number;
|
|
24
|
-
entryLocations:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
entryLocations: {
|
|
24
|
+
[x: string]: {
|
|
25
|
+
phase: "capture" | "intake" | "seed" | "vendor-extract" | "cleanup" | "parity" | "characterize" | "equiv" | "analyze" | "recover" | "ship" | "migration";
|
|
26
|
+
sequence: number;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
idempotencyIndex: {
|
|
30
|
+
[x: string]: {
|
|
31
|
+
[x: string]: string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
29
34
|
lastSequence: number;
|
|
30
|
-
perPhaseCounts:
|
|
31
|
-
|
|
35
|
+
perPhaseCounts: {
|
|
36
|
+
[x: string]: number;
|
|
37
|
+
};
|
|
38
|
+
perPhaseLatest: {
|
|
39
|
+
[x: string]: string;
|
|
40
|
+
};
|
|
32
41
|
schemaVersion: "ushman-ledger-manifest/v1";
|
|
33
42
|
updatedAt: string;
|
|
34
43
|
workspaceId: string;
|
|
44
|
+
} & {
|
|
45
|
+
[key: string]: unknown;
|
|
35
46
|
}>;
|
|
36
47
|
export declare const reconcileLedgerStateUnderLock: (workspaceRoot: string) => Promise<PreparedLedgerState>;
|
|
37
48
|
export declare const loadLedgerState: (workspaceRoot: string) => Promise<PreparedLedgerState>;
|
package/dist/recovery.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"AAKA,OAAO,EAA4B,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACjF,OAAO,EAAE,KAAK,WAAW,EAAqB,MAAM,mBAAmB,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AA0B3D,MAAM,MAAM,mBAAmB,GAAG;IAC9B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;CACvC,CAAC;AA0JF,eAAO,MAAM,kBAAkB,GAAU,kDAKtC;IACC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC,oBAeA,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAU,UAAU,MAAM,kBAEzD,CAAC;AAEF,eAAO,MAAM,gCAAgC,GAAU,eAAe,MAAM
|
|
1
|
+
{"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"AAKA,OAAO,EAA4B,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACjF,OAAO,EAAE,KAAK,WAAW,EAAqB,MAAM,mBAAmB,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AA0B3D,MAAM,MAAM,mBAAmB,GAAG;IAC9B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;CACvC,CAAC;AA0JF,eAAO,MAAM,kBAAkB,GAAU,kDAKtC;IACC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC,oBAeA,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAU,UAAU,MAAM,kBAEzD,CAAC;AAEF,eAAO,MAAM,gCAAgC,GAAU,eAAe,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyB3E,CAAC;AAEF,eAAO,MAAM,6BAA6B,GAAU,eAAe,MAAM,KAAG,OAAO,CAAC,mBAAmB,CAQtG,CAAC;AAEF,eAAO,MAAM,eAAe,GAAU,eAAe,MAAM,KAAG,OAAO,CAAC,mBAAmB,CAQxF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAU,eAAe,MAAM,kBAE7D,CAAC"}
|
package/dist/recovery.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readdir, readFile, rm, stat } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import
|
|
3
|
+
import * as v from 'valibot';
|
|
4
4
|
import { reconcilePendingArchivesUnderLock } from "./archive-journal.js";
|
|
5
5
|
import { getNextManifestSequence, updateManifestForEntry } from "./manifest-update.js";
|
|
6
6
|
import { ensureReadIndexUnderLock } from "./read-index.js";
|
|
@@ -10,18 +10,18 @@ import { acquireLock } from "./storage/lock.js";
|
|
|
10
10
|
const PendingCommitSchemaVersion = 'ushman-ledger-pending-commit/v1';
|
|
11
11
|
// Pending commits capture the intended sequence and logical hash before the
|
|
12
12
|
// entry file and manifest update diverge, so recovery can replay safely.
|
|
13
|
-
const PendingCommitSchema =
|
|
14
|
-
baseLastSequence:
|
|
15
|
-
createdAt:
|
|
13
|
+
const PendingCommitSchema = v.object({
|
|
14
|
+
baseLastSequence: v.pipe(v.number(), v.integer(), v.minValue(0)),
|
|
15
|
+
createdAt: v.pipe(v.string(), v.isoTimestamp()),
|
|
16
16
|
entry: LedgerEntrySchema,
|
|
17
|
-
logicalHash:
|
|
18
|
-
schemaVersion:
|
|
19
|
-
sequence:
|
|
17
|
+
logicalHash: v.pipe(v.string(), v.minLength(1)),
|
|
18
|
+
schemaVersion: v.literal(PendingCommitSchemaVersion),
|
|
19
|
+
sequence: v.pipe(v.number(), v.integer(), v.minValue(1)),
|
|
20
20
|
});
|
|
21
21
|
const formatSequence = (sequence) => sequence.toString().padStart(8, '0');
|
|
22
|
-
const PendingCommitFileNameSchema =
|
|
23
|
-
entryId:
|
|
24
|
-
sequence:
|
|
22
|
+
const PendingCommitFileNameSchema = v.object({
|
|
23
|
+
entryId: v.pipe(v.string(), v.minLength(1)),
|
|
24
|
+
sequence: v.pipe(v.number(), v.integer(), v.minValue(1), v.maxValue(Number.MAX_SAFE_INTEGER)),
|
|
25
25
|
});
|
|
26
26
|
const buildPendingCommitFilePath = (workspaceRoot, sequence, entryId) => path.join(resolveLedgerPaths(workspaceRoot).pendingCommitsDir, `${formatSequence(sequence)}-${entryId}.json`);
|
|
27
27
|
const parsePendingCommitFileName = (name) => {
|
|
@@ -29,7 +29,7 @@ const parsePendingCommitFileName = (name) => {
|
|
|
29
29
|
if (!match) {
|
|
30
30
|
return null;
|
|
31
31
|
}
|
|
32
|
-
return
|
|
32
|
+
return v.parse(PendingCommitFileNameSchema, {
|
|
33
33
|
entryId: match[2],
|
|
34
34
|
sequence: Number.parseInt(match[1], 10),
|
|
35
35
|
});
|
|
@@ -37,7 +37,7 @@ const parsePendingCommitFileName = (name) => {
|
|
|
37
37
|
const readPendingCommit = async (filePath) => {
|
|
38
38
|
try {
|
|
39
39
|
const text = await readFile(filePath, 'utf8');
|
|
40
|
-
return
|
|
40
|
+
return v.parse(PendingCommitSchema, JSON.parse(text));
|
|
41
41
|
}
|
|
42
42
|
catch (error) {
|
|
43
43
|
throw new Error(`Invalid pending commit at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -128,7 +128,7 @@ const applyPendingCommit = async ({ manifest, pending, workspaceRoot, }) => {
|
|
|
128
128
|
export const writePendingCommit = async ({ entry, logicalHash, manifest, workspaceRoot, }) => {
|
|
129
129
|
const nextSequence = getNextManifestSequence(manifest.lastSequence);
|
|
130
130
|
const filePath = buildPendingCommitFilePath(workspaceRoot, nextSequence, entry.id);
|
|
131
|
-
await writeAtomicJsonFile(filePath,
|
|
131
|
+
await writeAtomicJsonFile(filePath, v.parse(PendingCommitSchema, {
|
|
132
132
|
baseLastSequence: manifest.lastSequence,
|
|
133
133
|
createdAt: new Date().toISOString(),
|
|
134
134
|
entry,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retro.d.ts","sourceRoot":"","sources":["../../src/render/retro.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"retro.d.ts","sourceRoot":"","sources":["../../src/render/retro.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AA6GtD,eAAO,MAAM,mBAAmB,GAAU,wBAGvC;IACC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAC7C,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC9B,KAAG,OAAO,CAAC,MAAM,CA6CjB,CAAC"}
|
package/dist/render/retro.js
CHANGED
|
@@ -13,10 +13,7 @@ const createRetroBuckets = () => ({
|
|
|
13
13
|
});
|
|
14
14
|
const isProblemEntry = (entry) => (entry.kind === 'note' && entry.subkind === 'regression') ||
|
|
15
15
|
(entry.kind === 'runtime-event' && entry.level === 'error');
|
|
16
|
-
const isToolEntry = (entry) => entry.kind === 'tool-invocation' ||
|
|
17
|
-
entry.kind === 'agent-patch' ||
|
|
18
|
-
entry.kind === 'operator-patch' ||
|
|
19
|
-
entry.kind === 'stage-transition';
|
|
16
|
+
const isToolEntry = (entry) => entry.kind === 'tool-invocation' || entry.kind === 'agent-patch' || entry.kind === 'operator-patch';
|
|
20
17
|
const isToolingEntry = (entry) => (entry.kind === 'note' && entry.subkind === 'automation') ||
|
|
21
18
|
(entry.kind === 'note' && entry.subkind === 'tooling-gap');
|
|
22
19
|
const isRetroNote = (entry) => entry.kind === 'note' && entry.subkind === 'retro';
|