ushman-ledger 1.1.0 → 1.2.1

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 (68) hide show
  1. package/AGENTS.md +11 -7
  2. package/CHANGELOG.md +6 -0
  3. package/README.md +79 -8
  4. package/dist/blobs.js +3 -3
  5. package/dist/builders.d.ts +44 -2
  6. package/dist/builders.d.ts.map +1 -1
  7. package/dist/builders.js +7 -2
  8. package/dist/cli.d.ts.map +1 -1
  9. package/dist/cli.js +346 -62
  10. package/dist/doctor.d.ts.map +1 -1
  11. package/dist/doctor.js +104 -4
  12. package/dist/handle.d.ts +28 -6
  13. package/dist/handle.d.ts.map +1 -1
  14. package/dist/handle.js +105 -11
  15. package/dist/helpers.d.ts +7 -0
  16. package/dist/helpers.d.ts.map +1 -0
  17. package/dist/helpers.js +38 -0
  18. package/dist/index.d.ts +5 -3
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +5 -3
  21. package/dist/list.d.ts +44 -2
  22. package/dist/list.d.ts.map +1 -1
  23. package/dist/list.js +7 -5
  24. package/dist/note.d.ts +27 -0
  25. package/dist/note.d.ts.map +1 -1
  26. package/dist/note.js +11 -0
  27. package/dist/patch-resolver.d.ts +39 -0
  28. package/dist/patch-resolver.d.ts.map +1 -0
  29. package/dist/patch-resolver.js +196 -0
  30. package/dist/read-index.d.ts +7 -7
  31. package/dist/read-index.d.ts.map +1 -1
  32. package/dist/record.d.ts.map +1 -1
  33. package/dist/record.js +15 -40
  34. package/dist/render/migration-log.d.ts +10 -0
  35. package/dist/render/migration-log.d.ts.map +1 -0
  36. package/dist/render/migration-log.js +79 -0
  37. package/dist/render/retro.d.ts.map +1 -1
  38. package/dist/render/retro.js +34 -21
  39. package/dist/render/workspace-narrative.d.ts +12 -0
  40. package/dist/render/workspace-narrative.d.ts.map +1 -0
  41. package/dist/render/workspace-narrative.js +137 -0
  42. package/dist/schema/entry-core.d.ts +110 -0
  43. package/dist/schema/entry-core.d.ts.map +1 -0
  44. package/dist/schema/entry-core.js +143 -0
  45. package/dist/schema/entry-migrations.d.ts +3 -0
  46. package/dist/schema/entry-migrations.d.ts.map +1 -0
  47. package/dist/schema/entry-migrations.js +48 -0
  48. package/dist/schema/entry-read.d.ts +694 -0
  49. package/dist/schema/entry-read.d.ts.map +1 -0
  50. package/dist/schema/entry-read.js +92 -0
  51. package/dist/schema/entry-write.d.ts +865 -0
  52. package/dist/schema/entry-write.d.ts.map +1 -0
  53. package/dist/schema/entry-write.js +105 -0
  54. package/dist/schema/entry.d.ts +6 -1369
  55. package/dist/schema/entry.d.ts.map +1 -1
  56. package/dist/schema/entry.js +9 -286
  57. package/dist/schema/note.d.ts +1 -1
  58. package/dist/schema/note.d.ts.map +1 -1
  59. package/dist/schema/note.js +12 -1
  60. package/dist/storage/filesystem.d.ts +9 -0
  61. package/dist/storage/filesystem.d.ts.map +1 -1
  62. package/dist/storage/filesystem.js +82 -5
  63. package/dist/storage/lock-reclaimer.d.ts +2 -0
  64. package/dist/storage/lock-reclaimer.d.ts.map +1 -0
  65. package/dist/storage/lock-reclaimer.js +45 -0
  66. package/dist/version.d.ts +1 -1
  67. package/dist/version.js +1 -1
  68. package/package.json +3 -3
package/dist/doctor.js CHANGED
@@ -7,7 +7,9 @@ import { isReadIndexCurrent, readReadIndex } from "./read-index.js";
7
7
  import { loadLedgerState } from "./recovery.js";
8
8
  import { readManifest } from "./storage/filesystem.js";
9
9
  const BLOB_HASH_CONCURRENCY = 16;
10
+ const CHECKPOINT_MAX_AGE_MS = 24 * 60 * 60 * 1_000;
10
11
  const ENTRY_READ_BATCH_SIZE = 32;
12
+ const OPEN_ISSUE_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1_000;
11
13
  const checkPrevChain = (entry, previousByPhase, issues) => {
12
14
  const expectedPrev = previousByPhase.get(entry.phase) ?? null;
13
15
  if (entry.prevEntryId !== expectedPrev) {
@@ -66,13 +68,51 @@ const buildReadFailure = (error) => ({
66
68
  issues: [`Failed to read ledger state: ${error instanceof Error ? (error.message ?? error.name) : String(error)}.`],
67
69
  ok: false,
68
70
  });
69
- const inspectDoctorEntry = ({ blobChecks, entry, issues, latestByPhase, manifest, previousByPhase, unseenManifestEntryIds, }) => {
70
- unseenManifestEntryIds.delete(entry.id);
71
- checkPrevChain(entry, previousByPhase, issues);
71
+ const isChangeLogEntry = (entry) => entry.kind === 'change-log';
72
+ const isOpenIssueNote = (entry) => entry.kind === 'note' && entry.subkind === 'open-issue';
73
+ const trackResolutionLinks = (entry, resolvedLedgerIds) => {
74
+ if (entry.links.correctsLedgerId) {
75
+ resolvedLedgerIds.add(entry.links.correctsLedgerId);
76
+ }
77
+ if (entry.links.supersedesLedgerId) {
78
+ resolvedLedgerIds.add(entry.links.supersedesLedgerId);
79
+ }
80
+ };
81
+ const trackIdempotencyEntry = (entry, entriesByIdempotencyKey) => {
82
+ const idempotencyKey = entry.links.idempotencyKey;
83
+ if (!idempotencyKey) {
84
+ return;
85
+ }
86
+ const existingEntries = entriesByIdempotencyKey.get(idempotencyKey) ?? [];
87
+ existingEntries.push({ id: entry.id, ts: entry.ts });
88
+ entriesByIdempotencyKey.set(idempotencyKey, existingEntries);
89
+ };
90
+ const checkChangeLogWarnings = ({ checkpointEntries, entriesByIdempotencyKey, issues, nowMs, openIssueEntries, resolvedLedgerIds, }) => {
91
+ for (const checkpointEntry of checkpointEntries) {
92
+ const ageMs = nowMs - Date.parse(checkpointEntry.ts);
93
+ if (ageMs <= CHECKPOINT_MAX_AGE_MS) {
94
+ continue;
95
+ }
96
+ const idempotencyKey = checkpointEntry.links.idempotencyKey;
97
+ const hasFollowUp = typeof idempotencyKey === 'string' &&
98
+ (entriesByIdempotencyKey.get(idempotencyKey) ?? []).some((candidate) => candidate.id !== checkpointEntry.id && candidate.ts >= checkpointEntry.ts);
99
+ if (!hasFollowUp) {
100
+ issues.push(`Pre-change checkpoint ${checkpointEntry.id} is older than 24h and has no follow-up entry with matching idempotencyKey.`);
101
+ }
102
+ }
103
+ for (const openIssueEntry of openIssueEntries) {
104
+ const ageMs = nowMs - Date.parse(openIssueEntry.ts);
105
+ if (ageMs <= OPEN_ISSUE_MAX_AGE_MS || resolvedLedgerIds.has(openIssueEntry.id)) {
106
+ continue;
107
+ }
108
+ issues.push(`Open issue note ${openIssueEntry.id} is older than 30 days and has no resolution link.`);
109
+ }
110
+ };
111
+ const inspectManifestLocation = ({ entry, issues, latestByPhase, manifest, }) => {
72
112
  const manifestLocation = manifest.entryLocations[entry.id];
73
113
  if (!manifestLocation) {
74
114
  issues.push(`Manifest is missing entry location for ${entry.id}.`);
75
- return;
115
+ return null;
76
116
  }
77
117
  if (manifestLocation.phase !== entry.phase) {
78
118
  issues.push(`Manifest phase mismatch for ${entry.id}: expected ${entry.phase}, found ${manifestLocation.phase}.`);
@@ -81,6 +121,25 @@ const inspectDoctorEntry = ({ blobChecks, entry, issues, latestByPhase, manifest
81
121
  if (!currentLatest || manifestLocation.sequence > currentLatest.sequence) {
82
122
  latestByPhase.set(entry.phase, { entryId: entry.id, sequence: manifestLocation.sequence });
83
123
  }
124
+ return manifestLocation;
125
+ };
126
+ const inspectNarrativeEntry = ({ checkpointEntries, entry, issues, openIssueEntries, }) => {
127
+ if (isChangeLogEntry(entry)) {
128
+ if (entry.subkind === 'pre-change-checkpoint') {
129
+ checkpointEntries.push(entry);
130
+ }
131
+ if (entry.smokeResult === 'fail' && !entry.rollbackPlan) {
132
+ issues.push(`Change-log entry ${entry.id} has smokeResult=fail but no rollbackPlan.`);
133
+ }
134
+ if (entry.subkind === 'rollback' && !entry.rollsBack) {
135
+ issues.push(`Change-log rollback entry ${entry.id} is missing rollsBack.`);
136
+ }
137
+ }
138
+ if (isOpenIssueNote(entry)) {
139
+ openIssueEntries.push(entry);
140
+ }
141
+ };
142
+ const inspectPatchEntry = ({ blobChecks, entry, }) => {
84
143
  if (entry.kind !== 'agent-patch' && entry.kind !== 'operator-patch') {
85
144
  return;
86
145
  }
@@ -88,13 +147,42 @@ const inspectDoctorEntry = ({ blobChecks, entry, issues, latestByPhase, manifest
88
147
  blobChecks.push({ blobHash, entryId: entry.id });
89
148
  }
90
149
  };
150
+ const inspectDoctorEntry = ({ blobChecks, checkpointEntries, entry, entriesByIdempotencyKey, issues, latestByPhase, manifest, openIssueEntries, previousByPhase, resolvedLedgerIds, unseenManifestEntryIds, }) => {
151
+ unseenManifestEntryIds.delete(entry.id);
152
+ checkPrevChain(entry, previousByPhase, issues);
153
+ trackIdempotencyEntry(entry, entriesByIdempotencyKey);
154
+ trackResolutionLinks(entry, resolvedLedgerIds);
155
+ if (!inspectManifestLocation({
156
+ entry,
157
+ issues,
158
+ latestByPhase,
159
+ manifest,
160
+ })) {
161
+ return;
162
+ }
163
+ inspectNarrativeEntry({
164
+ checkpointEntries,
165
+ entry,
166
+ issues,
167
+ openIssueEntries,
168
+ });
169
+ inspectPatchEntry({
170
+ blobChecks,
171
+ entry,
172
+ });
173
+ };
91
174
  const collectDoctorState = async (workspaceRoot, manifest, readIndex) => {
92
175
  const issues = [];
93
176
  const previousByPhase = new Map();
94
177
  const latestByPhase = new Map();
95
178
  const unseenManifestEntryIds = new Set(Object.keys(manifest.entryLocations));
96
179
  const blobChecks = [];
180
+ const checkpointEntries = [];
181
+ const entriesByIdempotencyKey = new Map();
182
+ const openIssueEntries = [];
183
+ const resolvedLedgerIds = new Set();
97
184
  let entryCount = 0;
185
+ const nowMs = Date.now();
98
186
  const orderedEntries = getOrderedEntryLocations(manifest, readIndex, {});
99
187
  checkManifestSequenceOrder(orderedEntries, issues);
100
188
  for (let index = 0; index < orderedEntries.length; index += ENTRY_READ_BATCH_SIZE) {
@@ -111,15 +199,27 @@ const collectDoctorState = async (workspaceRoot, manifest, readIndex) => {
111
199
  entryCount += 1;
112
200
  inspectDoctorEntry({
113
201
  blobChecks,
202
+ checkpointEntries,
203
+ entriesByIdempotencyKey,
114
204
  entry: resolvedEntry.entry,
115
205
  issues,
116
206
  latestByPhase,
117
207
  manifest,
208
+ openIssueEntries,
118
209
  previousByPhase,
210
+ resolvedLedgerIds,
119
211
  unseenManifestEntryIds,
120
212
  });
121
213
  }
122
214
  }
215
+ checkChangeLogWarnings({
216
+ checkpointEntries,
217
+ entriesByIdempotencyKey,
218
+ issues,
219
+ nowMs,
220
+ openIssueEntries,
221
+ resolvedLedgerIds,
222
+ });
123
223
  return {
124
224
  blobChecks,
125
225
  entryCount,
package/dist/handle.d.ts CHANGED
@@ -3,7 +3,31 @@ import { runLedgerDoctor } from './doctor.ts';
3
3
  import { type LedgerFilter } from './list.ts';
4
4
  import { appendNote, type NoteBody } from './note.ts';
5
5
  import type { LedgerEntry, LedgerPhase } from './schema/entry.ts';
6
- export type RenderTarget = 'dependency-graph' | 'jsonl' | 'retro' | 'timeline-html';
6
+ export type RenderTarget = 'dependency-graph' | 'jsonl' | 'migration-log-md' | 'retro' | 'timeline-html' | 'workspace-narrative-md';
7
+ /**
8
+ * Chunk writer used by `renderTo()` for bounded render emission.
9
+ *
10
+ * Writers may complete synchronously or asynchronously. Throwing rejects the render and causes any in-progress
11
+ * atomic output file to be aborted.
12
+ */
13
+ export type RenderWriter = (chunk: string) => Promise<void> | void;
14
+ /** Options for `render()`, which returns the rendered text and optionally mirrors it to disk. */
15
+ export type LedgerRenderOptions = {
16
+ readonly limit?: number;
17
+ readonly out?: string;
18
+ readonly phase?: LedgerPhase;
19
+ readonly since?: string;
20
+ readonly to: RenderTarget;
21
+ };
22
+ /** Options for `renderTo()`, which writes to a callback and/or canonical render file without buffering the full output. */
23
+ export type LedgerRenderToOptions = {
24
+ readonly limit?: number;
25
+ readonly out?: string;
26
+ readonly phase?: LedgerPhase;
27
+ readonly since?: string;
28
+ readonly to: RenderTarget;
29
+ readonly write?: RenderWriter;
30
+ };
7
31
  export type LedgerHandle = {
8
32
  readonly archive: (outPath: string) => Promise<{
9
33
  integrityHash: string;
@@ -17,12 +41,10 @@ export type LedgerHandle = {
17
41
  readonly record: (entry: unknown) => Promise<{
18
42
  id: string;
19
43
  }>;
20
- readonly render: (options: {
21
- out?: string;
22
- phase?: LedgerPhase;
23
- to: RenderTarget;
24
- }) => Promise<string>;
44
+ readonly render: (options: LedgerRenderOptions) => Promise<string>;
45
+ readonly renderTo: (options: LedgerRenderToOptions) => Promise<void>;
25
46
  readonly show: (entryId: string) => Promise<LedgerEntry | null>;
26
47
  };
48
+ /** Open a workspace ledger handle after reconciling any pending crash-recovery state. */
27
49
  export declare const openLedger: (workspaceRoot: string) => Promise<LedgerHandle>;
28
50
  //# sourceMappingURL=handle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../src/handle.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAA8B,KAAK,YAAY,EAAe,MAAM,WAAW,CAAC;AACvF,OAAO,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAOtD,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGlE,MAAM,MAAM,YAAY,GAAG,kBAAkB,GAAG,OAAO,GAAG,OAAO,GAAG,eAAe,CAAC;AAuCpF,MAAM,MAAM,YAAY,GAAG;IACvB,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1E,QAAQ,CAAC,eAAe,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC;IACrF,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC;IAC5E,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,YAAY,KAAK,aAAa,CAAC,WAAW,CAAC,CAAC;IACrE,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtG,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,WAAW,CAAC;QAAC,EAAE,EAAE,YAAY,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACvG,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CACnE,CAAC;AAEF,eAAO,MAAM,UAAU,GAAU,eAAe,MAAM,KAAG,OAAO,CAAC,YAAY,CAqC5E,CAAC"}
1
+ {"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../src/handle.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAA8B,KAAK,YAAY,EAAe,MAAM,WAAW,CAAC;AACvF,OAAO,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAStD,OAAO,KAAK,EAAE,WAAW,EAAc,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAG9E,MAAM,MAAM,YAAY,GAClB,kBAAkB,GAClB,OAAO,GACP,kBAAkB,GAClB,OAAO,GACP,eAAe,GACf,wBAAwB,CAAC;AAE/B;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAEnE,iGAAiG;AACjG,MAAM,MAAM,mBAAmB,GAAG;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,YAAY,CAAC;CAC7B,CAAC;AAEF,2HAA2H;AAC3H,MAAM,MAAM,qBAAqB,GAAG;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,YAAY,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC;CACjC,CAAC;AA4GF,MAAM,MAAM,YAAY,GAAG;IACvB,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1E,QAAQ,CAAC,eAAe,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC;IACrF,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC;IAC5E,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,YAAY,KAAK,aAAa,CAAC,WAAW,CAAC,CAAC;IACrE,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtG,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACnE,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CACnE,CAAC;AAEF,yFAAyF;AACzF,eAAO,MAAM,UAAU,GAAU,eAAe,MAAM,KAAG,OAAO,CAAC,YAAY,CAyF5E,CAAC"}
package/dist/handle.js CHANGED
@@ -1,3 +1,4 @@
1
+ import path from 'node:path';
1
2
  import { archiveLedger } from "./archive.js";
2
3
  import { computeCoverage } from "./coverage.js";
3
4
  import { runLedgerDoctor } from "./doctor.js";
@@ -8,23 +9,62 @@ import { appendRecord, readEntryById } from "./record.js";
8
9
  import { loadLedgerState, prepareLedgerState } from "./recovery.js";
9
10
  import { renderDependencyGraph } from "./render/dependency-graph.js";
10
11
  import { renderJsonl } from "./render/jsonl.js";
12
+ import { renderMigrationLogMarkdown } from "./render/migration-log.js";
11
13
  import { renderRetroMarkdown } from "./render/retro.js";
12
14
  import { renderTimelineHtml } from "./render/timeline-html.js";
13
- import { resolveLedgerPaths, writeAtomicTextFile } from "./storage/filesystem.js";
14
- const renderEntries = async ({ filter, target, workspaceRoot, }) => {
15
- const { manifest, readIndex } = await loadLedgerState(workspaceRoot);
15
+ import { renderWorkspaceNarrativeMarkdown } from "./render/workspace-narrative.js";
16
+ import { createAtomicTextFileWriter, resolveLedgerPaths, writeAtomicTextFile } from "./storage/filesystem.js";
17
+ const createEntryIteratorFactory = ({ filter, state, workspaceRoot, }) => {
18
+ return (entryOptions = {}) => iterateEntriesFromManifest(workspaceRoot, state.manifest, state.readIndex, {
19
+ kind: entryOptions.kind,
20
+ limit: filter.limit,
21
+ phase: filter.phase,
22
+ since: filter.since,
23
+ }, entryOptions.direction);
24
+ };
25
+ const collectRenderedText = async (emit) => {
26
+ const chunks = [];
27
+ await emit((chunk) => {
28
+ chunks.push(chunk);
29
+ });
30
+ return chunks.join('');
31
+ };
32
+ const emitRenderedTarget = async ({ createEntries, manifest, target, write, workspaceName, }) => {
16
33
  switch (target) {
17
34
  case 'dependency-graph':
18
- return renderDependencyGraph(iterateEntriesFromManifest(workspaceRoot, manifest, readIndex, filter));
35
+ await write(await renderDependencyGraph(createEntries()));
36
+ return;
19
37
  case 'jsonl':
20
- return renderJsonl(iterateEntriesFromManifest(workspaceRoot, manifest, readIndex, filter));
38
+ await write(await renderJsonl(createEntries()));
39
+ return;
40
+ case 'migration-log-md':
41
+ await renderMigrationLogMarkdown({
42
+ entries: createEntries({
43
+ direction: 'desc',
44
+ kind: 'change-log',
45
+ }),
46
+ write,
47
+ });
48
+ return;
21
49
  case 'timeline-html':
22
- return renderTimelineHtml(iterateEntriesFromManifest(workspaceRoot, manifest, readIndex, filter));
50
+ await write(await renderTimelineHtml(createEntries()));
51
+ return;
23
52
  case 'retro':
24
- return renderRetroMarkdown({
25
- entries: iterateEntriesFromManifest(workspaceRoot, manifest, readIndex, filter),
53
+ await write(await renderRetroMarkdown({
54
+ entries: createEntries(),
26
55
  manifest,
56
+ }));
57
+ return;
58
+ case 'workspace-narrative-md':
59
+ await renderWorkspaceNarrativeMarkdown({
60
+ entries: createEntries({
61
+ direction: 'desc',
62
+ kind: 'note',
63
+ }),
64
+ workspaceName,
65
+ write,
27
66
  });
67
+ return;
28
68
  }
29
69
  };
30
70
  const resolveRenderOutputPath = (workspaceRoot, target) => {
@@ -32,12 +72,17 @@ const resolveRenderOutputPath = (workspaceRoot, target) => {
32
72
  switch (target) {
33
73
  case 'retro':
34
74
  return paths.renderFile;
75
+ case 'migration-log-md':
76
+ return paths.renderMigrationLogFile;
35
77
  case 'timeline-html':
36
78
  return paths.renderTimelineFile;
79
+ case 'workspace-narrative-md':
80
+ return paths.renderWorkspaceNarrativeFile;
37
81
  default:
38
82
  return null;
39
83
  }
40
84
  };
85
+ /** Open a workspace ledger handle after reconciling any pending crash-recovery state. */
41
86
  export const openLedger = async (workspaceRoot) => {
42
87
  await readLabManifestMin(workspaceRoot);
43
88
  await prepareLedgerState(workspaceRoot);
@@ -55,17 +100,66 @@ export const openLedger = async (workspaceRoot) => {
55
100
  return { id: result.id };
56
101
  },
57
102
  render: async (options) => {
58
- const content = await renderEntries({
59
- filter: { phase: options.phase },
60
- target: options.to,
103
+ const state = await loadLedgerState(workspaceRoot);
104
+ const createEntries = createEntryIteratorFactory({
105
+ filter: options,
106
+ state,
61
107
  workspaceRoot,
62
108
  });
109
+ const content = await collectRenderedText((write) => emitRenderedTarget({
110
+ createEntries,
111
+ manifest: state.manifest,
112
+ target: options.to,
113
+ workspaceName: path.basename(workspaceRoot),
114
+ write,
115
+ }));
63
116
  const outputPath = options.out ?? resolveRenderOutputPath(workspaceRoot, options.to);
64
117
  if (outputPath) {
65
118
  await writeAtomicTextFile(outputPath, `${content}${content.endsWith('\n') ? '' : '\n'}`);
66
119
  }
67
120
  return content;
68
121
  },
122
+ renderTo: async (options) => {
123
+ const state = await loadLedgerState(workspaceRoot);
124
+ const createEntries = createEntryIteratorFactory({
125
+ filter: options,
126
+ state,
127
+ workspaceRoot,
128
+ });
129
+ const outputPath = options.out ?? resolveRenderOutputPath(workspaceRoot, options.to);
130
+ const atomicWriter = outputPath ? await createAtomicTextFileWriter(outputPath) : null;
131
+ const writer = options.write;
132
+ let hasWrittenContent = false;
133
+ let endsWithNewline = false;
134
+ if (!atomicWriter && !writer) {
135
+ throw new Error(`Render target ${options.to} requires either a writer callback or an output path.`);
136
+ }
137
+ try {
138
+ await emitRenderedTarget({
139
+ createEntries,
140
+ manifest: state.manifest,
141
+ target: options.to,
142
+ workspaceName: path.basename(workspaceRoot),
143
+ write: async (chunk) => {
144
+ if (chunk.length > 0) {
145
+ hasWrittenContent = true;
146
+ endsWithNewline = chunk.endsWith('\n');
147
+ }
148
+ await writer?.(chunk);
149
+ await atomicWriter?.write(chunk);
150
+ },
151
+ });
152
+ if (!hasWrittenContent || !endsWithNewline) {
153
+ await writer?.('\n');
154
+ await atomicWriter?.write('\n');
155
+ }
156
+ await atomicWriter?.close();
157
+ }
158
+ catch (error) {
159
+ await atomicWriter?.abort();
160
+ throw error;
161
+ }
162
+ },
69
163
  show: async (entryId) => {
70
164
  const { manifest } = await loadLedgerState(workspaceRoot);
71
165
  if (!manifest.entryLocations[entryId]) {
@@ -0,0 +1,7 @@
1
+ export declare const createWorkspaceFixture: () => Promise<{
2
+ cleanup: () => Promise<void>;
3
+ workspaceRoot: string;
4
+ }>;
5
+ export declare const ageFile: (filePath: string, ageMs?: number) => Promise<void>;
6
+ export declare const waitForFile: (filePath: string, timeoutMs?: number) => Promise<void>;
7
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB;;;EAuBlC,CAAC;AAEF,eAAO,MAAM,OAAO,GAAU,UAAU,MAAM,EAAE,cAAc,kBAG7D,CAAC;AAEF,eAAO,MAAM,WAAW,GAAU,UAAU,MAAM,EAAE,kBAAiB,kBAcpE,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { mkdir, mkdtemp, rm, stat, utimes, writeFile } from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ export const createWorkspaceFixture = async () => {
5
+ const workspaceRoot = await mkdtemp(path.join(os.tmpdir(), 'ushman-ledger-'));
6
+ await mkdir(path.join(workspaceRoot, '.lab'), { recursive: true });
7
+ await mkdir(path.join(workspaceRoot, 'src'), { recursive: true });
8
+ await writeFile(path.join(workspaceRoot, '.lab', 'lab.json'), `${JSON.stringify({
9
+ schemaVersion: 'ushman-lab/v4.0',
10
+ workspaceId: '3d71d4fb-dca0-4d63-9ad6-69d59a2395f4',
11
+ }, null, 2)}\n`, 'utf8');
12
+ return {
13
+ cleanup: async () => {
14
+ await rm(workspaceRoot, { force: true, recursive: true });
15
+ },
16
+ workspaceRoot,
17
+ };
18
+ };
19
+ export const ageFile = async (filePath, ageMs = 61_000) => {
20
+ const staleTime = new Date(Date.now() - ageMs);
21
+ await utimes(filePath, staleTime, staleTime);
22
+ };
23
+ export const waitForFile = async (filePath, timeoutMs = 1_000) => {
24
+ const deadline = Date.now() + timeoutMs;
25
+ while (Date.now() <= deadline) {
26
+ try {
27
+ await stat(filePath);
28
+ return;
29
+ }
30
+ catch (error) {
31
+ if (error.code !== 'ENOENT') {
32
+ throw error;
33
+ }
34
+ }
35
+ await new Promise((resolve) => setTimeout(resolve, 10));
36
+ }
37
+ throw new Error(`Timed out waiting for file: ${filePath}`);
38
+ };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,11 @@
1
- export { buildCorrectionRecord, buildOperatorDecisionRecord, buildStripDecisionRevertedRecord, buildValidatorResultRecord, } from './builders.ts';
1
+ export { type BuildRecordInput, buildChangeLogRecord, buildCorrectionRecord, buildOperatorDecisionRecord, buildStripDecisionRevertedRecord, buildValidatorResultRecord, } from './builders.ts';
2
2
  export { runLedgerCli } from './cli.ts';
3
3
  export { type CoverageReport, computeCoverage } from './coverage.ts';
4
- export { type LedgerHandle, type RenderTarget, openLedger } from './handle.ts';
4
+ export { type LedgerHandle, type LedgerRenderOptions, type LedgerRenderToOptions, openLedger, type RenderTarget, type RenderWriter, } from './handle.ts';
5
5
  export type { LedgerFilter } from './list.ts';
6
- export { type AgentPatchDiff, AgentPatchDiffSchema, AgentPatchEntrySchema, AgentPatchRecordSchema, CorrectionEntrySchema, CorrectionRecordSchema, EmitterSchema, type LedgerEntry, LedgerEntrySchema, type LedgerKind, LedgerLinksSchema, type LedgerPhase, LedgerPhaseSchema, type LedgerRecord, LedgerRecordSchema, NoteEntrySchema, type OperatorDecisionAction, OperatorDecisionActionSchema, OperatorDecisionEntrySchema, type OperatorDecisionPayload, OperatorDecisionPayloadSchema, OperatorDecisionRecordSchema, OperatorPatchEntrySchema, OperatorPatchRecordSchema, parseLedgerEntry, parseLedgerRecord, RuntimeEventEntrySchema, StripDecisionRevertedEntrySchema, type StripDecisionRevertedPayload, StripDecisionRevertedPayloadSchema, StripDecisionRevertedRecordSchema, ToolInvocationEntrySchema, ToolInvocationRecordSchema, ValidatorResultEntrySchema, ValidatorResultRecordSchema, } from './schema/entry.ts';
6
+ export { appendCleanupWaveNote, appendDecompositionWaveNote, appendNote, appendOpenIssueNote, appendSemanticCleanupSummaryNote, appendVerifiedFlowNote, type NoteBody, } from './note.ts';
7
+ export { deriveFilesChangedFromPatch } from './patch-resolver.ts';
8
+ export { type AgentPatchDiff, AgentPatchDiffSchema, AgentPatchEntrySchema, AgentPatchRecordSchema, type ChangeLogEntry, ChangeLogEntrySchema, type ChangeLogFileChange, ChangeLogFileChangeSchema, type ChangeLogParityStatus, ChangeLogParityStatusSchema, type ChangeLogRecord, ChangeLogRecordSchema, type ChangeLogSmokeResult, ChangeLogSmokeResultSchema, type ChangeLogSubkind, ChangeLogSubkindSchema, CorrectionEntrySchema, CorrectionRecordSchema, EmitterSchema, type LedgerEntry, LedgerEntrySchema, type LedgerKind, type LedgerLinks, LedgerLinksSchema, type LedgerPhase, LedgerPhaseSchema, type LedgerRecord, LedgerRecordSchema, type NoteEntry, NoteEntrySchema, type OperatorDecisionAction, OperatorDecisionActionSchema, OperatorDecisionEntrySchema, type OperatorDecisionPayload, OperatorDecisionPayloadSchema, OperatorDecisionRecordSchema, OperatorPatchEntrySchema, OperatorPatchRecordSchema, parseLedgerEntry, parseLedgerRecord, RuntimeEventEntrySchema, StripDecisionRevertedEntrySchema, type StripDecisionRevertedPayload, StripDecisionRevertedPayloadSchema, StripDecisionRevertedRecordSchema, ToolInvocationEntrySchema, ToolInvocationRecordSchema, ValidatorResultEntrySchema, ValidatorResultRecordSchema, } from './schema/entry.ts';
7
9
  export { type NoteSubkind, NoteSubkindSchema } from './schema/note.ts';
8
10
  export { resolveLedgerPaths } from './storage/filesystem.ts';
9
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,qBAAqB,EACrB,2BAA2B,EAC3B,gCAAgC,EAChC,0BAA0B,GAC7B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,KAAK,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC/E,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EACH,KAAK,cAAc,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,aAAa,EACb,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,UAAU,EACf,iBAAiB,EACjB,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,YAAY,EACjB,kBAAkB,EAClB,eAAe,EACf,KAAK,sBAAsB,EAC3B,4BAA4B,EAC5B,2BAA2B,EAC3B,KAAK,uBAAuB,EAC5B,6BAA6B,EAC7B,4BAA4B,EAC5B,wBAAwB,EACxB,yBAAyB,EACzB,gBAAgB,EAChB,iBAAiB,EACjB,uBAAuB,EACvB,gCAAgC,EAChC,KAAK,4BAA4B,EACjC,kCAAkC,EAClC,iCAAiC,EACjC,yBAAyB,EACzB,0BAA0B,EAC1B,0BAA0B,EAC1B,2BAA2B,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,KAAK,WAAW,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,gBAAgB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,2BAA2B,EAC3B,gCAAgC,EAChC,0BAA0B,GAC7B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,KAAK,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EACH,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,UAAU,EACV,KAAK,YAAY,EACjB,KAAK,YAAY,GACpB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EACH,qBAAqB,EACrB,2BAA2B,EAC3B,UAAU,EACV,mBAAmB,EACnB,gCAAgC,EAChC,sBAAsB,EACtB,KAAK,QAAQ,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EACH,KAAK,cAAc,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACtB,KAAK,cAAc,EACnB,oBAAoB,EACpB,KAAK,mBAAmB,EACxB,yBAAyB,EACzB,KAAK,qBAAqB,EAC1B,2BAA2B,EAC3B,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,oBAAoB,EACzB,0BAA0B,EAC1B,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,aAAa,EACb,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,YAAY,EACjB,kBAAkB,EAClB,KAAK,SAAS,EACd,eAAe,EACf,KAAK,sBAAsB,EAC3B,4BAA4B,EAC5B,2BAA2B,EAC3B,KAAK,uBAAuB,EAC5B,6BAA6B,EAC7B,4BAA4B,EAC5B,wBAAwB,EACxB,yBAAyB,EACzB,gBAAgB,EAChB,iBAAiB,EACjB,uBAAuB,EACvB,gCAAgC,EAChC,KAAK,4BAA4B,EACjC,kCAAkC,EAClC,iCAAiC,EACjC,yBAAyB,EACzB,0BAA0B,EAC1B,0BAA0B,EAC1B,2BAA2B,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,KAAK,WAAW,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC"}
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
- export { buildCorrectionRecord, buildOperatorDecisionRecord, buildStripDecisionRevertedRecord, buildValidatorResultRecord, } from "./builders.js";
1
+ export { buildChangeLogRecord, buildCorrectionRecord, buildOperatorDecisionRecord, buildStripDecisionRevertedRecord, buildValidatorResultRecord, } from "./builders.js";
2
2
  export { runLedgerCli } from "./cli.js";
3
3
  export { computeCoverage } from "./coverage.js";
4
- export { openLedger } from "./handle.js";
5
- export { AgentPatchDiffSchema, AgentPatchEntrySchema, AgentPatchRecordSchema, CorrectionEntrySchema, CorrectionRecordSchema, EmitterSchema, LedgerEntrySchema, LedgerLinksSchema, LedgerPhaseSchema, LedgerRecordSchema, NoteEntrySchema, OperatorDecisionActionSchema, OperatorDecisionEntrySchema, OperatorDecisionPayloadSchema, OperatorDecisionRecordSchema, OperatorPatchEntrySchema, OperatorPatchRecordSchema, parseLedgerEntry, parseLedgerRecord, RuntimeEventEntrySchema, StripDecisionRevertedEntrySchema, StripDecisionRevertedPayloadSchema, StripDecisionRevertedRecordSchema, ToolInvocationEntrySchema, ToolInvocationRecordSchema, ValidatorResultEntrySchema, ValidatorResultRecordSchema, } from "./schema/entry.js";
4
+ export { openLedger, } from "./handle.js";
5
+ export { appendCleanupWaveNote, appendDecompositionWaveNote, appendNote, appendOpenIssueNote, appendSemanticCleanupSummaryNote, appendVerifiedFlowNote, } from "./note.js";
6
+ export { deriveFilesChangedFromPatch } from "./patch-resolver.js";
7
+ export { AgentPatchDiffSchema, AgentPatchEntrySchema, AgentPatchRecordSchema, ChangeLogEntrySchema, ChangeLogFileChangeSchema, ChangeLogParityStatusSchema, ChangeLogRecordSchema, ChangeLogSmokeResultSchema, ChangeLogSubkindSchema, CorrectionEntrySchema, CorrectionRecordSchema, EmitterSchema, LedgerEntrySchema, LedgerLinksSchema, LedgerPhaseSchema, LedgerRecordSchema, NoteEntrySchema, OperatorDecisionActionSchema, OperatorDecisionEntrySchema, OperatorDecisionPayloadSchema, OperatorDecisionRecordSchema, OperatorPatchEntrySchema, OperatorPatchRecordSchema, parseLedgerEntry, parseLedgerRecord, RuntimeEventEntrySchema, StripDecisionRevertedEntrySchema, StripDecisionRevertedPayloadSchema, StripDecisionRevertedRecordSchema, ToolInvocationEntrySchema, ToolInvocationRecordSchema, ValidatorResultEntrySchema, ValidatorResultRecordSchema, } from "./schema/entry.js";
6
8
  export { NoteSubkindSchema } from "./schema/note.js";
7
9
  export { resolveLedgerPaths } from "./storage/filesystem.js";
package/dist/list.d.ts CHANGED
@@ -259,7 +259,7 @@ export declare const readManifestEntryBatch: ({ allowMissing, entryLocations, wo
259
259
  } | {
260
260
  body: string;
261
261
  kind: "note";
262
- subkind: "regression" | "automation" | "retro" | "operator" | "tooling-gap";
262
+ subkind: "regression" | "automation" | "retro" | "operator" | "tooling-gap" | "cleanup-wave" | "verified-flow" | "open-issue" | "decomposition-wave" | "semantic-cleanup-summary";
263
263
  details?: {
264
264
  [x: string]: unknown;
265
265
  } | undefined;
@@ -321,6 +321,48 @@ export declare const readManifestEntryBatch: ({ allowMissing, entryLocations, wo
321
321
  schemaVersion: "ushman-ledger-entry/v1";
322
322
  summary: string;
323
323
  ts: string;
324
+ } | {
325
+ commandsRun?: string[] | undefined;
326
+ filesChanged: {
327
+ added?: number | undefined;
328
+ path: string;
329
+ removed?: number | undefined;
330
+ }[];
331
+ hypothesis?: string | undefined;
332
+ kind: "change-log";
333
+ parityStatus?: "not-run" | "green" | "yellow" | "red" | undefined;
334
+ rollbackPlan?: string | undefined;
335
+ rollsBack?: string | undefined;
336
+ smokeNotes?: string | undefined;
337
+ smokeResult?: "pass" | "fail" | "partial" | "not-run" | undefined;
338
+ subkind: "vendor-extract" | "pre-change-checkpoint" | "semantic-cleanup" | "decomposition" | "rollback" | "hotfix" | "smoke";
339
+ details?: {
340
+ [x: string]: unknown;
341
+ } | undefined;
342
+ emitter: {
343
+ tool: string;
344
+ user?: string | undefined;
345
+ version: string;
346
+ };
347
+ id: string;
348
+ links: {
349
+ affectedFiles?: string[] | undefined;
350
+ blobs?: string[] | undefined;
351
+ briefId?: string | undefined;
352
+ correctsLedgerId?: string | undefined;
353
+ gitRef?: string | undefined;
354
+ idempotencyKey?: string | undefined;
355
+ stripDecisionId?: string | undefined;
356
+ supersedesLedgerId?: string | undefined;
357
+ validatorVerdictId?: string | undefined;
358
+ } & {
359
+ [key: string]: unknown;
360
+ };
361
+ phase: "capture" | "intake" | "seed" | "vendor-extract" | "cleanup" | "parity" | "characterize" | "equiv" | "analyze" | "recover" | "ship" | "migration";
362
+ prevEntryId: string | null;
363
+ schemaVersion: "ushman-ledger-entry/v1";
364
+ summary: string;
365
+ ts: string;
324
366
  };
325
367
  entryId: string;
326
368
  location: {
@@ -335,7 +377,7 @@ export declare const readManifestEntryBatch: ({ allowMissing, entryLocations, wo
335
377
  sequence: number;
336
378
  };
337
379
  })[]>;
338
- export declare const iterateEntriesFromManifest: (workspaceRoot: string, manifest: LedgerManifest, readIndex: LedgerReadIndex, filter?: LedgerFilter) => AsyncIterable<LedgerEntry>;
380
+ export declare const iterateEntriesFromManifest: (workspaceRoot: string, manifest: LedgerManifest, readIndex: LedgerReadIndex, filter?: LedgerFilter, direction?: "asc" | "desc") => AsyncIterable<LedgerEntry>;
339
381
  export declare const listEntries: (workspaceRoot: string, filter?: LedgerFilter) => AsyncIterable<LedgerEntry>;
340
382
  export declare const readAllEntries: (workspaceRoot: string, filter?: LedgerFilter) => Promise<LedgerEntry[]>;
341
383
  //# sourceMappingURL=list.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../src/list.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAG7B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAoB,MAAM,mBAAmB,CAAC;AAC1G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D,MAAM,MAAM,YAAY,GAAG;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAmDF,eAAO,MAAM,wBAAwB,GACjC,UAAU,cAAc,EACxB,WAAW,eAAe,EAC1B,QAAQ,YAAY,EACpB,YAAW,KAAK,GAAG,MAAc,4BAgBpC,CAAC;AAgCF,eAAO,MAAM,sBAAsB,GAAU,kDAI1C;IACC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAC1D,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAQI,CAAC;AA6CN,eAAO,MAAM,0BAA0B,GACnC,eAAe,MAAM,EACrB,UAAU,cAAc,EACxB,WAAW,eAAe,EAC1B,SAAQ,YAAiB,KAC1B,aAAa,CAAC,WAAW,CAwB3B,CAAC;AAEF,eAAO,MAAM,WAAW,GACpB,eAAe,MAAM,EACrB,SAAQ,YAAiB,KAC1B,aAAa,CAAC,WAAW,CAG3B,CAAC;AAEF,eAAO,MAAM,cAAc,GAAU,eAAe,MAAM,EAAE,SAAQ,YAAiB,KAAG,OAAO,CAAC,WAAW,EAAE,CAM5G,CAAC"}
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../src/list.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAG7B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAoB,MAAM,mBAAmB,CAAC;AAC1G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D,MAAM,MAAM,YAAY,GAAG;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAmDF,eAAO,MAAM,wBAAwB,GACjC,UAAU,cAAc,EACxB,WAAW,eAAe,EAC1B,QAAQ,YAAY,EACpB,YAAW,KAAK,GAAG,MAAc,4BAgBpC,CAAC;AAgCF,eAAO,MAAM,sBAAsB,GAAU,kDAI1C;IACC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAC1D,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAQI,CAAC;AAiDN,eAAO,MAAM,0BAA0B,GACnC,eAAe,MAAM,EACrB,UAAU,cAAc,EACxB,WAAW,eAAe,EAC1B,SAAQ,YAAiB,EACzB,YAAW,KAAK,GAAG,MAAc,KAClC,aAAa,CAAC,WAAW,CAwB3B,CAAC;AAEF,eAAO,MAAM,WAAW,GACpB,eAAe,MAAM,EACrB,SAAQ,YAAiB,KAC1B,aAAa,CAAC,WAAW,CAG3B,CAAC;AAEF,eAAO,MAAM,cAAc,GAAU,eAAe,MAAM,EAAE,SAAQ,YAAiB,KAAG,OAAO,CAAC,WAAW,EAAE,CAM5G,CAAC"}
package/dist/list.js CHANGED
@@ -77,7 +77,7 @@ export const readManifestEntryBatch = async ({ allowMissing = false, entryLocati
77
77
  location,
78
78
  workspaceRoot,
79
79
  }));
80
- const collectLimitedEntries = async ({ filter, manifest, readIndex, workspaceRoot, }) => {
80
+ const collectLimitedEntries = async ({ direction, filter, manifest, readIndex, workspaceRoot, }) => {
81
81
  const collectedEntries = [];
82
82
  const orderedEntries = getOrderedEntryLocations(manifest, readIndex, filter, 'desc');
83
83
  const limit = filter.limit ?? 0;
@@ -99,17 +99,19 @@ const collectLimitedEntries = async ({ filter, manifest, readIndex, workspaceRoo
99
99
  }
100
100
  }
101
101
  }
102
- collectedEntries.reverse();
102
+ if (direction === 'asc') {
103
+ collectedEntries.reverse();
104
+ }
103
105
  return collectedEntries;
104
106
  };
105
- export const iterateEntriesFromManifest = async function* (workspaceRoot, manifest, readIndex, filter = {}) {
107
+ export const iterateEntriesFromManifest = async function* (workspaceRoot, manifest, readIndex, filter = {}, direction = 'asc') {
106
108
  if (filter.limit) {
107
- for (const entry of await collectLimitedEntries({ filter, manifest, readIndex, workspaceRoot })) {
109
+ for (const entry of await collectLimitedEntries({ direction, filter, manifest, readIndex, workspaceRoot })) {
108
110
  yield entry;
109
111
  }
110
112
  return;
111
113
  }
112
- const orderedEntries = getOrderedEntryLocations(manifest, readIndex, filter);
114
+ const orderedEntries = getOrderedEntryLocations(manifest, readIndex, filter, direction);
113
115
  for (let index = 0; index < orderedEntries.length; index += ENTRY_READ_BATCH_SIZE) {
114
116
  const batch = orderedEntries.slice(index, index + ENTRY_READ_BATCH_SIZE);
115
117
  const resolvedEntries = await readManifestEntryBatch({
package/dist/note.d.ts CHANGED
@@ -1,13 +1,40 @@
1
1
  import { appendRecord } from './record.ts';
2
2
  import type { LedgerPhase } from './schema/entry.ts';
3
3
  import type { NoteSubkind } from './schema/note.ts';
4
+ /** Input shared by the narrative and note helper wrappers. */
4
5
  export type NoteBody = {
5
6
  readonly body: string;
6
7
  readonly phase: LedgerPhase;
7
8
  readonly summary: string;
8
9
  };
10
+ /** Append a typed note entry using the library emitter metadata. */
9
11
  export declare const appendNote: (workspaceRoot: string, subkind: NoteSubkind, noteBody: NoteBody) => Promise<{
10
12
  entry: Awaited<ReturnType<typeof appendRecord>>["entry"];
11
13
  id: string;
12
14
  }>;
15
+ /** Append a `cleanup-wave` narrative note. */
16
+ export declare const appendCleanupWaveNote: (workspaceRoot: string, noteBody: NoteBody) => Promise<{
17
+ entry: Awaited<ReturnType<typeof appendRecord>>["entry"];
18
+ id: string;
19
+ }>;
20
+ /** Append a `verified-flow` narrative note. */
21
+ export declare const appendVerifiedFlowNote: (workspaceRoot: string, noteBody: NoteBody) => Promise<{
22
+ entry: Awaited<ReturnType<typeof appendRecord>>["entry"];
23
+ id: string;
24
+ }>;
25
+ /** Append an `open-issue` narrative note. */
26
+ export declare const appendOpenIssueNote: (workspaceRoot: string, noteBody: NoteBody) => Promise<{
27
+ entry: Awaited<ReturnType<typeof appendRecord>>["entry"];
28
+ id: string;
29
+ }>;
30
+ /** Append a `decomposition-wave` narrative note. */
31
+ export declare const appendDecompositionWaveNote: (workspaceRoot: string, noteBody: NoteBody) => Promise<{
32
+ entry: Awaited<ReturnType<typeof appendRecord>>["entry"];
33
+ id: string;
34
+ }>;
35
+ /** Append the current `semantic-cleanup-summary` note for workspace narrative rendering. */
36
+ export declare const appendSemanticCleanupSummaryNote: (workspaceRoot: string, noteBody: NoteBody) => Promise<{
37
+ entry: Awaited<ReturnType<typeof appendRecord>>["entry"];
38
+ id: string;
39
+ }>;
13
40
  //# sourceMappingURL=note.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"note.d.ts","sourceRoot":"","sources":["../src/note.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,MAAM,MAAM,QAAQ,GAAG;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,eAAO,MAAM,UAAU,GACnB,eAAe,MAAM,EACrB,SAAS,WAAW,EACpB,UAAU,QAAQ,KACnB,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAYlF,CAAC"}
1
+ {"version":3,"file":"note.d.ts","sourceRoot":"","sources":["../src/note.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,8DAA8D;AAC9D,MAAM,MAAM,QAAQ,GAAG;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,oEAAoE;AACpE,eAAO,MAAM,UAAU,GACnB,eAAe,MAAM,EACrB,SAAS,WAAW,EACpB,UAAU,QAAQ,KACnB,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAYlF,CAAC;AAEF,8CAA8C;AAC9C,eAAO,MAAM,qBAAqB,GAAI,eAAe,MAAM,EAAE,UAAU,QAAQ;WAf3D,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC;QAAM,MAAM;EAgB1B,CAAC;AAExD,+CAA+C;AAC/C,eAAO,MAAM,sBAAsB,GAAI,eAAe,MAAM,EAAE,UAAU,QAAQ;WAnB5D,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC;QAAM,MAAM;EAoBzB,CAAC;AAEzD,6CAA6C;AAC7C,eAAO,MAAM,mBAAmB,GAAI,eAAe,MAAM,EAAE,UAAU,QAAQ;WAvBzD,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC;QAAM,MAAM;EAwB5B,CAAC;AAEtD,oDAAoD;AACpD,eAAO,MAAM,2BAA2B,GAAI,eAAe,MAAM,EAAE,UAAU,QAAQ;WA3BjE,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC;QAAM,MAAM;EA4BpB,CAAC;AAE9D,4FAA4F;AAC5F,eAAO,MAAM,gCAAgC,GAAI,eAAe,MAAM,EAAE,UAAU,QAAQ;WA/BtE,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC;QAAM,MAAM;EAgCd,CAAC"}