ushman-ledger 0.3.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/AGENTS.md +11 -7
  2. package/CHANGELOG.md +8 -12
  3. package/README.md +28 -57
  4. package/dist/archive-journal.d.ts +29 -18
  5. package/dist/archive-journal.d.ts.map +1 -1
  6. package/dist/archive-journal.js +17 -17
  7. package/dist/blobs.js +3 -3
  8. package/dist/builders.d.ts +79 -358
  9. package/dist/builders.d.ts.map +1 -1
  10. package/dist/builders.js +15 -60
  11. package/dist/cli.d.ts.map +1 -1
  12. package/dist/cli.js +227 -52
  13. package/dist/doctor.d.ts.map +1 -1
  14. package/dist/doctor.js +104 -4
  15. package/dist/handle.d.ts +4 -2
  16. package/dist/handle.d.ts.map +1 -1
  17. package/dist/handle.js +20 -15
  18. package/dist/helpers.d.ts +7 -0
  19. package/dist/helpers.d.ts.map +1 -0
  20. package/dist/helpers.js +38 -0
  21. package/dist/index.d.ts +4 -5
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +3 -4
  24. package/dist/lab-min.d.ts +7 -7
  25. package/dist/lab-min.d.ts.map +1 -1
  26. package/dist/lab-min.js +7 -9
  27. package/dist/list.d.ts +104 -303
  28. package/dist/list.d.ts.map +1 -1
  29. package/dist/note.d.ts +20 -0
  30. package/dist/note.d.ts.map +1 -1
  31. package/dist/note.js +5 -0
  32. package/dist/patch-resolver.d.ts +27 -0
  33. package/dist/patch-resolver.d.ts.map +1 -0
  34. package/dist/patch-resolver.js +184 -0
  35. package/dist/read-index.d.ts +45 -57
  36. package/dist/read-index.d.ts.map +1 -1
  37. package/dist/read-index.js +16 -34
  38. package/dist/record.d.ts.map +1 -1
  39. package/dist/record.js +19 -130
  40. package/dist/recovery.d.ts +19 -8
  41. package/dist/recovery.d.ts.map +1 -1
  42. package/dist/recovery.js +13 -13
  43. package/dist/render/migration-log.d.ts +3 -0
  44. package/dist/render/migration-log.d.ts.map +1 -0
  45. package/dist/render/migration-log.js +72 -0
  46. package/dist/render/retro.d.ts.map +1 -1
  47. package/dist/render/retro.js +41 -25
  48. package/dist/render/workspace-narrative.d.ts +6 -0
  49. package/dist/render/workspace-narrative.d.ts.map +1 -0
  50. package/dist/render/workspace-narrative.js +69 -0
  51. package/dist/schema/entry-core.d.ts +110 -0
  52. package/dist/schema/entry-core.d.ts.map +1 -0
  53. package/dist/schema/entry-core.js +143 -0
  54. package/dist/schema/entry-migrations.d.ts +3 -0
  55. package/dist/schema/entry-migrations.d.ts.map +1 -0
  56. package/dist/schema/entry-migrations.js +48 -0
  57. package/dist/schema/entry-read.d.ts +694 -0
  58. package/dist/schema/entry-read.d.ts.map +1 -0
  59. package/dist/schema/entry-read.js +92 -0
  60. package/dist/schema/entry-write.d.ts +865 -0
  61. package/dist/schema/entry-write.d.ts.map +1 -0
  62. package/dist/schema/entry-write.js +105 -0
  63. package/dist/schema/entry.d.ts +6 -3295
  64. package/dist/schema/entry.d.ts.map +1 -1
  65. package/dist/schema/entry.js +10 -619
  66. package/dist/schema/manifest.d.ts +28 -41
  67. package/dist/schema/manifest.d.ts.map +1 -1
  68. package/dist/schema/manifest.js +20 -24
  69. package/dist/schema/note.d.ts +3 -9
  70. package/dist/schema/note.d.ts.map +1 -1
  71. package/dist/schema/note.js +13 -2
  72. package/dist/storage/filesystem.d.ts +2 -1
  73. package/dist/storage/filesystem.d.ts.map +1 -1
  74. package/dist/storage/filesystem.js +6 -4
  75. package/dist/storage/lock-reclaimer.d.ts +2 -0
  76. package/dist/storage/lock-reclaimer.d.ts.map +1 -0
  77. package/dist/storage/lock-reclaimer.js +45 -0
  78. package/dist/version.d.ts +1 -1
  79. package/dist/version.js +1 -1
  80. package/package.json +3 -4
package/AGENTS.md CHANGED
@@ -23,13 +23,17 @@ An append-only ledger library and CLI for ushman v4 workspaces. It owns ledger s
23
23
  ## Read order
24
24
 
25
25
  1. `README.md`
26
- 2. `src/schema/entry.ts`
27
- 3. `src/storage/filesystem.ts`
28
- 4. `src/record.ts`
29
- 5. `src/handle.ts`
30
- 6. `src/coverage.ts`
31
- 7. `src/doctor.ts`
32
- 8. `src/cli.ts`
26
+ 2. `src/schema/entry-core.ts`
27
+ 3. `src/schema/entry-read.ts`
28
+ 4. `src/schema/entry-write.ts`
29
+ 5. `src/schema/entry-migrations.ts`
30
+ 6. `src/schema/entry.ts`
31
+ 7. `src/storage/filesystem.ts`
32
+ 8. `src/record.ts`
33
+ 9. `src/handle.ts`
34
+ 10. `src/coverage.ts`
35
+ 11. `src/doctor.ts`
36
+ 12. `src/cli.ts`
33
37
 
34
38
  ## Commands
35
39
 
package/CHANGELOG.md CHANGED
@@ -1,22 +1,18 @@
1
1
  # Changelog
2
2
 
3
- ## [1.0.0] - 2026-05-14
3
+ ## [1.1.0] - 2026-05-23
4
4
 
5
- - Added structured patch payloads with touched paths, diff hashes, line hunks, and before/after file hashes.
6
- - Added UUIDv7-backed `validator-result` payload ids, structured `stage-transition` and `operator-decision` payloads, and the new first-class ledger kinds required by ushman v4.1 analytics.
7
- - Added cached `render --to=analytics-summary` output, legacy-entry read migration markers, and write-side builder helpers for the structured record kinds.
5
+ - Added `change-log` records, narrative note subkinds, and `migration-log-md` / `workspace-narrative-md` render targets.
6
+ - Split entry schema parsing into focused read/write/core modules and moved compatibility coercions behind dedicated parse migrations.
7
+ - Extracted patch-source resolution behind a dedicated resolver with focused tests and more actionable hash mismatch failures.
8
8
 
9
- ### Breaking changes
9
+ ## [1.0.1] - 2026-05-16
10
10
 
11
- - New `operator-decision` writes must use the structured `payload.action` and `payload.rationale` fields.
12
- - Stored `validator-result` entries now persist `payload.id`; helper-side synthesis is only a temporary migration convenience.
13
- - Structured patch writes now enforce the machine-readable payload fields instead of accepting prose-only patch metadata.
11
+ - Tightened the schema surface to the ledger primitives the v4 orchestrator actively uses.
14
12
 
15
- ## [0.2.0] - 2026-05-14
13
+ ## [1.0.0] - 2026-05-14
16
14
 
17
- - Added durable `read-index.json` sidecar rebuilds and persisted coverage indexing for large-ledger scans.
18
- - Made archive creation crash-recoverable with pending archive journals and post-create tar verification.
19
- - Added archive recovery tests, read-index rebuild tests, and a manual scale benchmark harness.
15
+ - First stable release: structured `operator-decision`, `strip-decision-reverted`, and `validator-result` payloads; durable read-index sidecar with crash-recoverable archive creation.
20
16
 
21
17
  ## [0.1.0] - 2026-05-12
22
18
 
package/README.md CHANGED
@@ -5,11 +5,11 @@ Append-only workspace ledger library and CLI for ushman v4 workspaces.
5
5
  ## What it owns
6
6
 
7
7
  - Ledger entry schemas
8
- - Structured patch / validator / stage payloads for analytics consumers
9
8
  - Serialized manifest-safe entry writes
10
9
  - Content-hash idempotency
11
10
  - Patch blob storage
12
- - Retro / JSONL / timeline rendering
11
+ - Retro / JSONL / timeline / dependency-graph rendering
12
+ - Change-log / workspace narrative markdown rendering
13
13
  - Archive integrity output
14
14
  - Doctor and coverage helpers
15
15
 
@@ -59,7 +59,7 @@ await ledger.record(
59
59
  );
60
60
 
61
61
  await ledger.render({ to: 'retro' });
62
- await ledger.render({ to: 'analytics-summary' });
62
+ await ledger.render({ to: 'migration-log-md' });
63
63
  await ledger.archive('/tmp/ledger.tgz');
64
64
  ```
65
65
 
@@ -69,25 +69,37 @@ await ledger.archive('/tmp/ledger.tgz');
69
69
  ushman-ledger record --workspace=<ws> --kind=tool-invocation --phase=capture --summary="capture started"
70
70
  ushman-ledger record --workspace=<ws> --kind=agent-patch --phase=cleanup --summary="capture git diff" --rationale="track working tree change" --diff-from-git=HEAD
71
71
  ushman-ledger record --workspace=<ws> --kind=operator-decision --phase=cleanup --summary="manual override" --action=ledger-hand-edit --check-id=manual-review --rationale="operator edited the ledger after audit"
72
+ ushman-ledger record --workspace=<ws> --kind=change-log --subkind=semantic-cleanup --phase=cleanup --summary="split schema modules" --diff=/tmp/change.patch --hypothesis="smaller schema modules keep the public API stable" --commands=$'bun test\nbun run typecheck' --smoke-result=pass --parity-status=green --rollback-plan="revert the schema split"
72
73
  ushman-ledger note regression --workspace=<ws> --phase=cleanup --summary="runtime drift" --body=/tmp/note.md
74
+ ushman-ledger note cleanup-wave --workspace=<ws> --phase=cleanup --summary="wave 1" --body=/tmp/narrative.md
73
75
  ushman-ledger list --workspace=<ws> --json
74
76
  ushman-ledger render --workspace=<ws> --to=retro
77
+ ushman-ledger render --workspace=<ws> --to=migration-log-md
78
+ ushman-ledger render --workspace=<ws> --to=workspace-narrative-md
75
79
  ushman-ledger render --workspace=<ws> --to=jsonl --out=/tmp/ledger.jsonl
76
80
  ushman-ledger render --workspace=<ws> --to=dependency-graph --out=/tmp/ledger.mmd
77
- ushman-ledger render --workspace=<ws> --to=analytics-summary
78
- ushman-ledger render --workspace=<ws> --to=analytics-summary --fresh
79
- ushman-ledger render --workspace=<ws> --to=analytics-summary --json
80
81
  ushman-ledger archive --workspace=<ws> --out=/tmp/ledger.tgz
81
82
  ushman-ledger doctor --workspace=<ws>
82
83
  ```
83
84
 
84
- Valid record kinds: `tool-invocation`, `agent-patch`, `operator-patch`, `stage-transition`, `operator-decision`, `validator-result`, `runtime-event`, `note`, `correction`, `strip-decision-reverted`, `descope-brief`, `merge-return`, `merge-return-rejected`, `revert`, `rollback`, `rework.test_retired`
85
+ Valid record kinds: `tool-invocation`, `agent-patch`, `operator-patch`, `operator-decision`, `validator-result`, `runtime-event`, `note`, `correction`, `strip-decision-reverted`, `change-log`
85
86
 
86
87
  Valid phases: `capture`, `intake`, `seed`, `vendor-extract`, `cleanup`, `parity`, `characterize`, `equiv`, `analyze`, `recover`, `ship`, `migration`
87
88
 
88
- Valid note subkinds: `regression`, `automation`, `retro`, `operator`, `tooling-gap`
89
+ Valid note subkinds: `regression`, `automation`, `retro`, `operator`, `tooling-gap`, `cleanup-wave`, `verified-flow`, `open-issue`, `decomposition-wave`, `semantic-cleanup-summary`
89
90
 
90
- Valid render targets: `retro`, `jsonl`, `timeline-html`, `dependency-graph`, `analytics-summary`
91
+ Valid render targets: `retro`, `jsonl`, `timeline-html`, `dependency-graph`, `migration-log-md`, `workspace-narrative-md`
92
+
93
+ ## Change-log records
94
+
95
+ `change-log` entries are append-only structured narrative records for migration and cleanup work. They accept:
96
+
97
+ - `subkind`: `pre-change-checkpoint`, `semantic-cleanup`, `vendor-extract`, `decomposition`, `rollback`, `hotfix`, `smoke`
98
+ - `filesChanged`: explicit CSV paths via `--files-changed`, or automatic diffstat-style derivation from `--diff` / `--diff-from-git`
99
+ - optional narrative fields: `hypothesis`, `commandsRun`, `smokeResult`, `smokeNotes`, `parityStatus`, `rollbackPlan`
100
+ - `rollsBack`: required when `subkind=rollback`
101
+
102
+ `migration-log-md` renders only `change-log` entries. `workspace-narrative-md` renders only the dedicated narrative note subkinds.
91
103
 
92
104
  ## Workspace prerequisite
93
105
 
@@ -99,53 +111,12 @@ Valid render targets: `retro`, `jsonl`, `timeline-html`, `dependency-graph`, `an
99
111
  - With `idempotencyKey`, the key is authoritative within a phase. Reusing the same key with different content in the same phase returns the first recorded entry unchanged.
100
112
  - Stored entries copy the idempotency key into `links.idempotencyKey` for auditability.
101
113
 
102
- ## Structured payloads
103
-
104
- - Patch entries (`agent-patch`, `operator-patch`) store `payload.touchedPaths`, `payload.fileSha256Before`, `payload.fileSha256After`, `payload.hunks`, `payload.diffSha256`, and `payload.diff`.
105
- - Stored `validator-result` entries require a UUIDv7 `payload.id`. Callers should provide it in normal operation; the write helpers only synthesize one during the migration window when legacy callers omit it.
106
- - `stage-transition` entries capture `{ stage, startedAt, endedAt, exitCode, doctorBaselineId? }` explicitly in the ledger.
107
- - `operator-decision` entries capture `{ action, checkId?, rationale }` in `payload`.
108
- - `rework.test_retired` is the structured way to retire a seed-emitted test from the pipeline corpus.
109
- - Legacy on-disk entries still read successfully. The library normalizes them into the current shape and marks them with `_legacy: true`.
110
-
111
- Example payloads:
112
-
113
- ```json
114
- {
115
- "kind": "stage-transition",
116
- "payload": {
117
- "stage": "cleanup",
118
- "startedAt": "2026-05-14T12:00:00.000Z",
119
- "endedAt": "2026-05-14T12:00:05.000Z",
120
- "exitCode": 0
121
- }
122
- }
123
- ```
124
-
125
- ```json
126
- {
127
- "kind": "rework.test_retired",
128
- "payload": {
129
- "removedTestPath": "tests/pure/app.test.ts",
130
- "removedTestSymbols": ["renderApp"],
131
- "removedBy": "descope",
132
- "sourceSymbolDeletedFrom": "src/app.ts"
133
- }
134
- }
135
- ```
136
-
137
- ## Migration guide
138
-
139
- - Historical entries are not rewritten. Old on-disk entries continue to load through read-side normalization.
140
- - Normalized legacy entries are marked with `_legacy: true` so analytics can distinguish migrated reads from fully structured writes.
141
- - New writes are expected to use structured payloads. Temporary compatibility logic only remains for omitted `validator-result.payload.id` values.
142
-
143
- ## Analytics summary
114
+ ## Payload shapes
144
115
 
145
- - `render --to=analytics-summary` emits a compact JSON envelope for downstream analytics consumers.
146
- - The summary is cached at `.lab/ledger/analytics-summary.json` and invalidates automatically when the ledger tip changes.
147
- - `render --to=analytics-summary --fresh` bypasses the cache and recomputes the envelope for one-off verification.
148
- - `render --to=analytics-summary --json` remains as a compatibility alias for `--fresh`.
116
+ - `operator-decision` entries capture `{ action, checkId?, rationale }` in `payload`. `action` is one of `bypass-doctor`, `skip-check`, `override-strip-decision`, `override-ship-state`, `manual-parity-assertion`, `ledger-hand-edit`, `escalation`.
117
+ - `strip-decision-reverted` entries capture `{ stripDecisionId, rationale, invalidatedStages? }`.
118
+ - `correction` entries require `links.correctsLedgerId` pointing at the entry they correct.
119
+ - Patch entries (`agent-patch`, `operator-patch`) store a `diff` blob reference and a `rationale`. Patch text is stored once under `.lab/ledger/blobs/` and shared by hash.
149
120
 
150
121
  ## Links and coverage
151
122
 
@@ -176,7 +147,6 @@ Example payloads:
176
147
 
177
148
  - `.lab/ledger/pending/` contains append journals that let the ledger recover incomplete writes after crashes. Do not edit these files by hand.
178
149
  - If startup or `doctor` reports a pending commit mismatch, re-open the ledger or rerun the command first so reconciliation can replay or discard the journal safely.
179
- - If `analytics-summary.json` is invalid or stale, rerun `ushman-ledger render --to=analytics-summary --fresh` to recompute it from the ledger tip.
180
150
  - If `doctor` reports manifest or blob corruption, fix the underlying entry/blob mismatch first and rerun `doctor` before attempting `archive`.
181
151
 
182
152
  ## Scan behavior
@@ -205,14 +175,15 @@ Example payloads:
205
175
  ```text
206
176
  <ws>/.lab/ledger/
207
177
  .manifest.lock
208
- analytics-summary.json
209
178
  manifest.json
210
179
  read-index.json
211
180
  pending/
212
181
  pending-archives/
213
182
  blobs/
214
183
  render.md
184
+ render.migration-log.md
215
185
  render.timeline.html
186
+ render.workspace-narrative.md
216
187
  <phase>/
217
188
  <timestamp>-<sequence>-<hash>.json
218
189
  ```
@@ -1,14 +1,14 @@
1
- import { z } from 'zod';
1
+ import * as v from 'valibot';
2
2
  import type { LedgerManifest } from './schema/manifest.ts';
3
- export declare const ArchiveManifestSchema: z.ZodObject<{
4
- fileHashes: z.ZodArray<z.ZodObject<{
5
- path: z.ZodString;
6
- sha256: z.ZodString;
7
- }, z.core.$strip>>;
8
- integrityHash: z.ZodString;
9
- schemaVersion: z.ZodLiteral<"ushman-ledger-archive-manifest/v1">;
10
- }, z.core.$strip>;
11
- export type ArchiveManifest = z.infer<typeof ArchiveManifestSchema>;
3
+ export declare const ArchiveManifestSchema: v.ObjectSchema<{
4
+ readonly fileHashes: v.ArraySchema<v.ObjectSchema<{
5
+ readonly path: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
6
+ readonly sha256: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.LengthAction<string, 64, undefined>]>;
7
+ }, undefined>, undefined>;
8
+ readonly integrityHash: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.LengthAction<string, 64, undefined>]>;
9
+ readonly schemaVersion: v.LiteralSchema<"ushman-ledger-archive-manifest/v1", undefined>;
10
+ }, undefined>;
11
+ export type ArchiveManifest = v.InferOutput<typeof ArchiveManifestSchema>;
12
12
  export declare const collectFileHashes: (root: string, prefix?: string) => Promise<Array<{
13
13
  path: string;
14
14
  sha256: string;
@@ -40,7 +40,6 @@ export declare const reconcilePendingArchivesUnderLock: ({ manifest, workspaceRo
40
40
  readonly manifest: LedgerManifest;
41
41
  readonly workspaceRoot: string;
42
42
  }) => Promise<{
43
- [x: string]: unknown;
44
43
  archives: {
45
44
  createdAt: string;
46
45
  integrityHash: string;
@@ -48,16 +47,28 @@ export declare const reconcilePendingArchivesUnderLock: ({ manifest, workspaceRo
48
47
  }[];
49
48
  createdAt: string;
50
49
  entryCount: number;
51
- entryLocations: Record<string, {
52
- phase: "seed" | "capture" | "intake" | "vendor-extract" | "cleanup" | "parity" | "characterize" | "equiv" | "analyze" | "recover" | "ship" | "migration";
53
- sequence: number;
54
- }>;
55
- idempotencyIndex: Record<string, Record<string, string>>;
50
+ entryLocations: {
51
+ [x: string]: {
52
+ phase: "capture" | "intake" | "seed" | "vendor-extract" | "cleanup" | "parity" | "characterize" | "equiv" | "analyze" | "recover" | "ship" | "migration";
53
+ sequence: number;
54
+ };
55
+ };
56
+ idempotencyIndex: {
57
+ [x: string]: {
58
+ [x: string]: string;
59
+ };
60
+ };
56
61
  lastSequence: number;
57
- perPhaseCounts: Record<string, number>;
58
- perPhaseLatest: Record<string, string>;
62
+ perPhaseCounts: {
63
+ [x: string]: number;
64
+ };
65
+ perPhaseLatest: {
66
+ [x: string]: string;
67
+ };
59
68
  schemaVersion: "ushman-ledger-manifest/v1";
60
69
  updatedAt: string;
61
70
  workspaceId: string;
71
+ } & {
72
+ [key: string]: unknown;
62
73
  }>;
63
74
  //# sourceMappingURL=archive-journal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"archive-journal.d.ts","sourceRoot":"","sources":["../src/archive-journal.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAW3D,eAAO,MAAM,qBAAqB;;;;;;;iBAIhC,CAAC;AASH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAyCpE,eAAO,MAAM,iBAAiB,GAC1B,MAAM,MAAM,EACZ,eAAW,KACZ,OAAO,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAcjD,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAU,YAAY,MAAM;;;;;;;EAO7D,CAAC;AA+DF,eAAO,MAAM,iBAAiB,GAAU,2CAGrC;IACC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,uBAAuB,EAAE,eAAe,CAAC;CACrD,kBA2BA,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,mCAG5C;IACC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAChC,kBAMA,CAAC;AA4CF,eAAO,MAAM,mBAAmB,GAAU,yDAKvC;IACC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC,oBAgBA,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAU,UAAU,MAAM,kBAE1D,CAAC;AAEF,eAAO,MAAM,iCAAiC,GAAU,8BAGrD;IACC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC;;;;;;;;;;;;;;;;;;;;EA+CA,CAAC"}
1
+ {"version":3,"file":"archive-journal.d.ts","sourceRoot":"","sources":["../src/archive-journal.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAC;AAG7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAW3D,eAAO,MAAM,qBAAqB;;;;;;;aAIhC,CAAC;AASH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAyC1E,eAAO,MAAM,iBAAiB,GAC1B,MAAM,MAAM,EACZ,eAAW,KACZ,OAAO,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAcjD,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAU,YAAY,MAAM;;;;;;;EAO7D,CAAC;AA+DF,eAAO,MAAM,iBAAiB,GAAU,2CAGrC;IACC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,uBAAuB,EAAE,eAAe,CAAC;CACrD,kBA2BA,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,mCAG5C;IACC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAChC,kBAMA,CAAC;AA4CF,eAAO,MAAM,mBAAmB,GAAU,yDAKvC;IACC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC,oBAgBA,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAU,UAAU,MAAM,kBAE1D,CAAC;AAEF,eAAO,MAAM,iCAAiC,GAAU,8BAGrD;IACC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+CA,CAAC"}
@@ -2,32 +2,32 @@ import { mkdtemp, readdir, readFile, rm, stat, writeFile } from 'node:fs/promise
2
2
  import os from 'node:os';
3
3
  import path from 'node:path';
4
4
  import { extract as extractTar } from 'tar';
5
- import { z } from 'zod';
5
+ import * as v from 'valibot';
6
6
  import { sha256File, sha256Hex, stableStringify } from "./json.js";
7
7
  import { updateManifestForArchive } from "./manifest-update.js";
8
8
  import { resolveLedgerPaths, saveManifest, writeAtomicJsonFile } from "./storage/filesystem.js";
9
9
  const ARCHIVE_MANIFEST_SCHEMA_VERSION = 'ushman-ledger-archive-manifest/v1';
10
10
  const PENDING_ARCHIVE_SCHEMA_VERSION = 'ushman-ledger-pending-archive/v1';
11
- const ArchiveManifestFileHashSchema = z.object({
12
- path: z.string().min(1),
13
- sha256: z.string().length(64),
11
+ const ArchiveManifestFileHashSchema = v.object({
12
+ path: v.pipe(v.string(), v.minLength(1)),
13
+ sha256: v.pipe(v.string(), v.length(64)),
14
14
  });
15
- export const ArchiveManifestSchema = z.object({
16
- fileHashes: z.array(ArchiveManifestFileHashSchema),
17
- integrityHash: z.string().length(64),
18
- schemaVersion: z.literal(ARCHIVE_MANIFEST_SCHEMA_VERSION),
15
+ export const ArchiveManifestSchema = v.object({
16
+ fileHashes: v.array(ArchiveManifestFileHashSchema),
17
+ integrityHash: v.pipe(v.string(), v.length(64)),
18
+ schemaVersion: v.literal(ARCHIVE_MANIFEST_SCHEMA_VERSION),
19
19
  });
20
- const PendingArchiveSchema = z.object({
20
+ const PendingArchiveSchema = v.object({
21
21
  archiveManifest: ArchiveManifestSchema,
22
- createdAt: z.string().datetime({ offset: true }),
23
- outPath: z.string().min(1),
24
- schemaVersion: z.literal(PENDING_ARCHIVE_SCHEMA_VERSION),
22
+ createdAt: v.pipe(v.string(), v.isoTimestamp()),
23
+ outPath: v.pipe(v.string(), v.minLength(1)),
24
+ schemaVersion: v.literal(PENDING_ARCHIVE_SCHEMA_VERSION),
25
25
  });
26
26
  const formatPendingArchiveId = (createdAt, outPath) => `${createdAt.replaceAll(':', '-').replaceAll('.', '-')}-${sha256Hex(outPath).slice(0, 12)}`;
27
27
  const buildPendingArchivePath = ({ createdAt, outPath, workspaceRoot, }) => path.join(resolveLedgerPaths(workspaceRoot).pendingArchivesDir, `${formatPendingArchiveId(createdAt, outPath)}.json`);
28
28
  const parsePendingArchiveText = (filePath, text) => {
29
29
  try {
30
- return PendingArchiveSchema.parse(JSON.parse(text));
30
+ return v.parse(PendingArchiveSchema, JSON.parse(text));
31
31
  }
32
32
  catch (error) {
33
33
  throw new Error(`Invalid pending archive at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
@@ -35,7 +35,7 @@ const parsePendingArchiveText = (filePath, text) => {
35
35
  };
36
36
  const parseArchiveManifestText = (archivePath, text) => {
37
37
  try {
38
- return ArchiveManifestSchema.parse(JSON.parse(text));
38
+ return v.parse(ArchiveManifestSchema, JSON.parse(text));
39
39
  }
40
40
  catch (error) {
41
41
  throw new Error(`Invalid archive manifest inside ${archivePath}: ${error instanceof Error ? error.message : String(error)}`);
@@ -58,7 +58,7 @@ export const collectFileHashes = async (root, prefix = '') => {
58
58
  };
59
59
  export const createArchiveManifest = async (ledgerRoot) => {
60
60
  const fileHashes = await collectFileHashes(ledgerRoot);
61
- return ArchiveManifestSchema.parse({
61
+ return v.parse(ArchiveManifestSchema, {
62
62
  fileHashes,
63
63
  integrityHash: sha256Hex(stableStringify(fileHashes)),
64
64
  schemaVersion: ARCHIVE_MANIFEST_SCHEMA_VERSION,
@@ -125,7 +125,7 @@ export const verifyArchiveFile = async ({ archivePath, expectedArchiveManifest,
125
125
  }
126
126
  };
127
127
  export const writeArchiveManifestFile = async ({ archiveManifest, stagingRoot, }) => {
128
- await writeFile(path.join(stagingRoot, 'archive-manifest.json'), `${stableStringify(ArchiveManifestSchema.parse(archiveManifest), true)}\n`, 'utf8');
128
+ await writeFile(path.join(stagingRoot, 'archive-manifest.json'), `${stableStringify(v.parse(ArchiveManifestSchema, archiveManifest), true)}\n`, 'utf8');
129
129
  };
130
130
  const readPendingArchive = async (filePath) => parsePendingArchiveText(filePath, await readFile(filePath, 'utf8'));
131
131
  const readPendingArchives = async (workspaceRoot) => {
@@ -163,7 +163,7 @@ export const writePendingArchive = async ({ archiveManifest, createdAt, outPath,
163
163
  outPath,
164
164
  workspaceRoot,
165
165
  });
166
- await writeAtomicJsonFile(filePath, PendingArchiveSchema.parse({
166
+ await writeAtomicJsonFile(filePath, v.parse(PendingArchiveSchema, {
167
167
  archiveManifest,
168
168
  createdAt,
169
169
  outPath,
package/dist/blobs.js CHANGED
@@ -1,4 +1,4 @@
1
- import { mkdir, readFile, stat } from 'node:fs/promises';
1
+ import { mkdir, readFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { sha256File, sha256Hex } from "./json.js";
4
4
  import { resolveLedgerPaths, writeAtomicTextFile } from "./storage/filesystem.js";
@@ -34,11 +34,11 @@ export const storePatchBlob = async (workspaceRoot, patchText) => {
34
34
  const blobPath = buildBlobPath(workspaceRoot, blobSha256);
35
35
  let shouldWrite = true;
36
36
  try {
37
- await stat(blobPath);
38
37
  shouldWrite = (await sha256File(blobPath)) !== blobSha256;
39
38
  }
40
39
  catch (error) {
41
- if (error.code !== 'ENOENT') {
40
+ const code = error.code;
41
+ if (code !== 'ENOENT') {
42
42
  throw error;
43
43
  }
44
44
  }