ushman-ledger 1.2.1 → 1.3.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/AGENTS.md +7 -5
- package/ARCHITECTURE.md +85 -0
- package/CHANGELOG.md +11 -0
- package/README.md +114 -5
- package/TROUBLESHOOTING.md +184 -0
- package/dist/blobs.d.ts +3 -0
- package/dist/blobs.d.ts.map +1 -1
- package/dist/blobs.js +41 -15
- package/dist/builders.d.ts +33 -0
- package/dist/builders.d.ts.map +1 -1
- package/dist/builders.js +10 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +176 -59
- package/dist/coverage.d.ts.map +1 -1
- package/dist/coverage.js +3 -2
- package/dist/doctor.d.ts +17 -4
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +263 -62
- package/dist/handle.d.ts.map +1 -1
- package/dist/handle.js +67 -30
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +23 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/list.d.ts +34 -1
- package/dist/list.d.ts.map +1 -1
- package/dist/list.js +19 -9
- package/dist/patch-resolver.d.ts.map +1 -1
- package/dist/patch-resolver.js +193 -53
- package/dist/process.d.ts +2 -0
- package/dist/process.d.ts.map +1 -0
- package/dist/process.js +16 -0
- package/dist/read-index.d.ts +7 -7
- package/dist/read-index.d.ts.map +1 -1
- package/dist/read-index.js +18 -13
- package/dist/record.js +2 -2
- package/dist/recovery.d.ts +8 -0
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +142 -30
- package/dist/render/retro.d.ts.map +1 -1
- package/dist/render/retro.js +4 -1
- package/dist/runtime-config.d.ts +14 -0
- package/dist/runtime-config.d.ts.map +1 -0
- package/dist/runtime-config.js +97 -0
- package/dist/schema/entry-core.d.ts +5 -2
- package/dist/schema/entry-core.d.ts.map +1 -1
- package/dist/schema/entry-core.js +3 -0
- package/dist/schema/entry-read.d.ts +57 -0
- package/dist/schema/entry-read.d.ts.map +1 -1
- package/dist/schema/entry-read.js +9 -1
- package/dist/schema/entry-write.d.ts +51 -0
- package/dist/schema/entry-write.d.ts.map +1 -1
- package/dist/schema/entry-write.js +9 -1
- package/dist/storage/filesystem.d.ts +15 -2
- package/dist/storage/filesystem.d.ts.map +1 -1
- package/dist/storage/filesystem.js +234 -37
- package/dist/storage/lock.d.ts.map +1 -1
- package/dist/storage/lock.js +38 -16
- package/dist/text-lines.d.ts +8 -0
- package/dist/text-lines.d.ts.map +1 -0
- package/dist/text-lines.js +20 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +2 -1
- package/package.json +4 -2
package/dist/builders.d.ts
CHANGED
|
@@ -147,6 +147,39 @@ export declare const buildChangeLogRecord: (input: BuildRecordInput<Extract<Ledg
|
|
|
147
147
|
phase: "capture" | "intake" | "seed" | "vendor-extract" | "cleanup" | "parity" | "characterize" | "equiv" | "analyze" | "recover" | "ship" | "migration";
|
|
148
148
|
summary: string;
|
|
149
149
|
};
|
|
150
|
+
/** Build a `stage-write` record. */
|
|
151
|
+
export declare const buildStageWriteRecord: (input: BuildRecordInput<Extract<LedgerRecord, {
|
|
152
|
+
kind: "stage-write";
|
|
153
|
+
}>>) => {
|
|
154
|
+
filePath: string;
|
|
155
|
+
kind: "stage-write";
|
|
156
|
+
rationale: string;
|
|
157
|
+
stage: "intake" | "seed" | "vendor-extract" | "cleanup" | "candidate-promotion";
|
|
158
|
+
details?: {
|
|
159
|
+
[x: string]: unknown;
|
|
160
|
+
} | undefined;
|
|
161
|
+
emitter: {
|
|
162
|
+
tool: string;
|
|
163
|
+
user?: string | undefined;
|
|
164
|
+
version: string;
|
|
165
|
+
};
|
|
166
|
+
idempotencyKey?: string | undefined;
|
|
167
|
+
links?: ({
|
|
168
|
+
affectedFiles?: string[] | undefined;
|
|
169
|
+
blobs?: string[] | undefined;
|
|
170
|
+
briefId?: string | undefined;
|
|
171
|
+
correctsLedgerId?: string | undefined;
|
|
172
|
+
gitRef?: string | undefined;
|
|
173
|
+
idempotencyKey?: string | undefined;
|
|
174
|
+
stripDecisionId?: string | undefined;
|
|
175
|
+
supersedesLedgerId?: string | undefined;
|
|
176
|
+
validatorVerdictId?: string | undefined;
|
|
177
|
+
} & {
|
|
178
|
+
[key: string]: unknown;
|
|
179
|
+
}) | undefined;
|
|
180
|
+
phase: "capture" | "intake" | "seed" | "vendor-extract" | "cleanup" | "parity" | "characterize" | "equiv" | "analyze" | "recover" | "ship" | "migration";
|
|
181
|
+
summary: string;
|
|
182
|
+
};
|
|
150
183
|
/** Build a `strip-decision-reverted` record. */
|
|
151
184
|
export declare const buildStripDecisionRevertedRecord: (input: BuildRecordInput<Extract<LedgerRecord, {
|
|
152
185
|
kind: "strip-decision-reverted";
|
package/dist/builders.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../src/builders.ts"],"names":[],"mappings":"AACA,OAAO,EAGH,KAAK,YAAY,
|
|
1
|
+
{"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../src/builders.ts"],"names":[],"mappings":"AACA,OAAO,EAGH,KAAK,YAAY,EAKpB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,gBAAgB,CAAC,OAAO,SAAS,YAAY,IAAI,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG;IACjF,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;CACnC,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,2BAA2B,GACpC,OAAO,gBAAgB,CAAC,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAM3E,CAAC;AAEP,yCAAyC;AACzC,eAAO,MAAM,0BAA0B,GACnC,OAAO,gBAAgB,CAAC,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,kBAAkB,CAAA;CAAE,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAK1E,CAAC;AAEP,sEAAsE;AACtE,eAAO,MAAM,qBAAqB,GAAI,OAAO,gBAAgB,CAAC,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;CAItG,CAAC;AAEP,mCAAmC;AACnC,eAAO,MAAM,oBAAoB,GAAI,OAAO,gBAAgB,CAAC,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAIrG,CAAC;AAEP,oCAAoC;AACpC,eAAO,MAAM,qBAAqB,GAAI,OAAO,gBAAgB,CAAC,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAQvG,CAAC;AAEP,gDAAgD;AAChD,eAAO,MAAM,gCAAgC,GACzC,OAAO,gBAAgB,CAAC,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,yBAAyB,CAAA;CAAE,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAKjF,CAAC"}
|
package/dist/builders.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as v from 'valibot';
|
|
2
|
-
import { ChangeLogRecordSchema, CorrectionRecordSchema, OperatorDecisionRecordSchema, StripDecisionRevertedRecordSchema, ValidatorResultRecordSchema, } from "./schema/entry.js";
|
|
2
|
+
import { ChangeLogRecordSchema, CorrectionRecordSchema, OperatorDecisionRecordSchema, StageWriteRecordSchema, StripDecisionRevertedRecordSchema, ValidatorResultRecordSchema, } from "./schema/entry.js";
|
|
3
3
|
/** Build an `operator-decision` record with a validated structured payload. */
|
|
4
4
|
export const buildOperatorDecisionRecord = (input) => v.parse(OperatorDecisionRecordSchema, {
|
|
5
5
|
...input,
|
|
@@ -21,6 +21,15 @@ export const buildChangeLogRecord = (input) => v.parse(ChangeLogRecordSchema, {
|
|
|
21
21
|
...input,
|
|
22
22
|
kind: 'change-log',
|
|
23
23
|
});
|
|
24
|
+
/** Build a `stage-write` record. */
|
|
25
|
+
export const buildStageWriteRecord = (input) => v.parse(StageWriteRecordSchema, {
|
|
26
|
+
...input,
|
|
27
|
+
kind: 'stage-write',
|
|
28
|
+
links: {
|
|
29
|
+
...(input.links ?? {}),
|
|
30
|
+
affectedFiles: [...new Set([...(input.links?.affectedFiles ?? []), input.filePath])],
|
|
31
|
+
},
|
|
32
|
+
});
|
|
24
33
|
/** Build a `strip-decision-reverted` record. */
|
|
25
34
|
export const buildStripDecisionRevertedRecord = (input) => v.parse(StripDecisionRevertedRecordSchema, {
|
|
26
35
|
...input,
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AA6DA,KAAK,UAAU,GAAG;IACd,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE;QACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,cAAc,GAAG,aAAa,CAAC,UAAU,GAAG,MAAM,CAAC,CAAC;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;CAC1C,CAAC;AAsjCF,eAAO,MAAM,YAAY,GAAU,MAAM,SAAS,MAAM,EAAE,EAAE,UAAS,OAAO,CAAC,UAAU,CAAM,KAAG,OAAO,CAAC,MAAM,CAqC7G,CAAC;AAEF,eAAO,MAAM,IAAI,GAAU,OAAM,SAAS,MAAM,EAA0B,KAAG,OAAO,CAAC,MAAM,CAE1F,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -6,14 +6,17 @@ import path from 'node:path';
|
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { promisify } from 'node:util';
|
|
8
8
|
import * as v from 'valibot';
|
|
9
|
+
import { readPatchTextFromFile } from "./blobs.js";
|
|
9
10
|
import { openLedger } from "./handle.js";
|
|
10
11
|
import { deriveFilesChangedFromPatch } from "./patch-resolver.js";
|
|
11
|
-
import { ChangeLogParityStatusSchema, ChangeLogSmokeResultSchema, ChangeLogSubkindSchema, LEDGER_KINDS, LEDGER_PHASES, parseLedgerRecord, WorkspaceRelativePathSchema, } from "./schema/entry.js";
|
|
12
|
+
import { ChangeLogParityStatusSchema, ChangeLogSmokeResultSchema, ChangeLogSubkindSchema, LEDGER_KINDS, LEDGER_PHASES, STAGE_WRITE_STAGES, parseLedgerRecord, WorkspaceRelativePathSchema, } from "./schema/entry.js";
|
|
12
13
|
import { NoteSubkindSchema } from "./schema/note.js";
|
|
13
14
|
import { LEDGER_LIBRARY_VERSION } from "./version.js";
|
|
14
15
|
const execFileAsync = promisify(execFile);
|
|
15
16
|
const DEFAULT_GIT_DIFF_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
|
|
16
17
|
const DEFAULT_GIT_DIFF_TIMEOUT_MS = 30_000;
|
|
18
|
+
const BOOLEAN_FLAG_NAMES = new Set(['from-stdin', 'help', 'json']);
|
|
19
|
+
const GIT_DIFF_FORMAT_ARGS = ['--no-color', '--no-ext-diff', '--src-prefix=a/', '--dst-prefix=b/'];
|
|
17
20
|
const RENDER_TARGETS = [
|
|
18
21
|
'retro',
|
|
19
22
|
'jsonl',
|
|
@@ -35,7 +38,8 @@ const CHANGE_LOG_RECORD_ONLY_FLAGS = [
|
|
|
35
38
|
'smoke-result',
|
|
36
39
|
'subkind',
|
|
37
40
|
];
|
|
38
|
-
const
|
|
41
|
+
const STAGE_WRITE_ONLY_FLAGS = ['file-path', 'stage'];
|
|
42
|
+
const renderRecordUsage = (commandName) => `${commandName} record [--workspace=<ws>] --kind=<kind> --phase=<phase> --summary="..." [--rationale="..."] [--file-path=<path>] [--stage=<stage>] [--action=<operator-action>] [--check-id=<check-id>] [--diff=<patch-file>] [--diff-from-git=<ref>] [--git-paths=<csv>] [--git-diff-timeout-ms=<ms>] [--git-diff-max-buffer-bytes=<bytes>] [--idempotency-key=<key>] [--subkind=<change-log-subkind>] [--files-changed=<csv>] [--hypothesis="..."] [--commands="cmd1\ncmd2" | --commands-from=<file>] [--smoke-result=<result>] [--smoke-notes="..."] [--parity-status=<status>] [--rollback-plan="..."] [--rolls-back=<entry-id>] [--from-stdin]`;
|
|
39
43
|
class CliUsageError extends Error {
|
|
40
44
|
}
|
|
41
45
|
const DEFAULT_CONTEXT = {
|
|
@@ -51,9 +55,21 @@ const DEFAULT_CONTEXT = {
|
|
|
51
55
|
const renderValidValues = () => `Valid values:
|
|
52
56
|
kinds: ${LEDGER_KINDS.join(', ')}
|
|
53
57
|
phases: ${LEDGER_PHASES.join(', ')}
|
|
58
|
+
stage-write stages: ${STAGE_WRITE_STAGES.join(', ')}
|
|
54
59
|
note subkinds: ${NoteSubkindSchema.options.join(', ')}
|
|
55
60
|
render targets: ${RENDER_TARGETS.join(', ')}
|
|
56
61
|
`;
|
|
62
|
+
const renderRuntimeTuningHelp = () => `Runtime tuning env vars:
|
|
63
|
+
USHMAN_LEDGER_SCAN_BATCH_SIZE (default: 32)
|
|
64
|
+
USHMAN_LEDGER_SCAN_CONCURRENCY (default: 16)
|
|
65
|
+
USHMAN_LEDGER_READ_INDEX_REBUILD_BATCH_SIZE (default: USHMAN_LEDGER_SCAN_BATCH_SIZE)
|
|
66
|
+
USHMAN_LEDGER_READ_INDEX_REBUILD_CONCURRENCY (default: USHMAN_LEDGER_SCAN_CONCURRENCY)
|
|
67
|
+
USHMAN_LEDGER_COVERAGE_FILE_STAT_CONCURRENCY (default: USHMAN_LEDGER_SCAN_CONCURRENCY)
|
|
68
|
+
USHMAN_LEDGER_BLOB_HASH_CONCURRENCY (default: USHMAN_LEDGER_SCAN_CONCURRENCY)
|
|
69
|
+
USHMAN_LEDGER_DOCTOR_CHECKPOINT_MAX_AGE_MS (default: 86400000)
|
|
70
|
+
USHMAN_LEDGER_DOCTOR_OPEN_ISSUE_MAX_AGE_MS (default: 2592000000)
|
|
71
|
+
USHMAN_LEDGER_MAX_PATCH_BYTES (default: 10485760)
|
|
72
|
+
`;
|
|
57
73
|
const renderHelp = (commandName) => `${commandName}
|
|
58
74
|
|
|
59
75
|
Commands:
|
|
@@ -64,28 +80,37 @@ Commands:
|
|
|
64
80
|
${commandName} tail [--workspace=<ws>] [--phase=<phase>] [--limit=<n>]
|
|
65
81
|
${commandName} render [--workspace=<ws>] [--to=retro|jsonl|timeline-html|dependency-graph|migration-log-md|workspace-narrative-md] [--phase=<phase>] [--since=<iso>] [--limit=<n>] [--out=<file>]
|
|
66
82
|
${commandName} archive [--workspace=<ws>] --out=<file.tgz>
|
|
67
|
-
${commandName} doctor [--workspace=<ws>]
|
|
83
|
+
${commandName} doctor [--workspace=<ws>] [--json]
|
|
68
84
|
${commandName} --version
|
|
69
85
|
|
|
70
|
-
${renderValidValues()}
|
|
86
|
+
${renderValidValues()}
|
|
87
|
+
${renderRuntimeTuningHelp()}`;
|
|
71
88
|
const renderCommandHelp = (commandName, command) => {
|
|
72
89
|
switch (command) {
|
|
73
90
|
case 'record':
|
|
74
91
|
return `${renderRecordUsage(commandName)}
|
|
75
92
|
|
|
76
|
-
${renderValidValues()}
|
|
93
|
+
${renderValidValues()}
|
|
94
|
+
${renderRuntimeTuningHelp()}`;
|
|
77
95
|
case 'note':
|
|
78
96
|
return `${commandName} note <subkind> [--workspace=<ws>] --phase=<phase> --summary="..." [--body=<markdown-file>] [--from-stdin]
|
|
79
97
|
|
|
80
|
-
${renderValidValues()}
|
|
98
|
+
${renderValidValues()}
|
|
99
|
+
${renderRuntimeTuningHelp()}`;
|
|
81
100
|
case 'list':
|
|
82
101
|
return `${commandName} list [--workspace=<ws>] [--phase=<phase>] [--kind=<kind>] [--since=<iso>] [--limit=<n>] [--json]
|
|
83
102
|
|
|
84
|
-
${renderValidValues()}
|
|
103
|
+
${renderValidValues()}
|
|
104
|
+
${renderRuntimeTuningHelp()}`;
|
|
85
105
|
case 'render':
|
|
86
106
|
return `${commandName} render [--workspace=<ws>] [--to=<target>] [--phase=<phase>] [--since=<iso>] [--limit=<n>] [--out=<file>]
|
|
87
107
|
|
|
88
|
-
${renderValidValues()}
|
|
108
|
+
${renderValidValues()}
|
|
109
|
+
${renderRuntimeTuningHelp()}`;
|
|
110
|
+
case 'doctor':
|
|
111
|
+
return `${commandName} doctor [--workspace=<ws>] [--json]
|
|
112
|
+
|
|
113
|
+
${renderRuntimeTuningHelp()}`;
|
|
89
114
|
default:
|
|
90
115
|
return renderHelp(commandName);
|
|
91
116
|
}
|
|
@@ -111,7 +136,7 @@ const parseArgv = (argv) => {
|
|
|
111
136
|
continue;
|
|
112
137
|
}
|
|
113
138
|
const next = argv[index + 1];
|
|
114
|
-
if (!next || next.startsWith('--')) {
|
|
139
|
+
if (BOOLEAN_FLAG_NAMES.has(name) || !next || next.startsWith('--')) {
|
|
115
140
|
flags[name] = true;
|
|
116
141
|
continue;
|
|
117
142
|
}
|
|
@@ -192,6 +217,18 @@ const parseWorkspaceRelativePathCsv = ({ flagName, raw }) => {
|
|
|
192
217
|
}
|
|
193
218
|
return uniquePaths;
|
|
194
219
|
};
|
|
220
|
+
const parseWorkspaceRelativePathFlag = ({ flagName, flags, required = false, }) => {
|
|
221
|
+
const raw = required ? getRequiredString(flags, flagName) : getFlag(flags, flagName);
|
|
222
|
+
if (!raw) {
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
try {
|
|
226
|
+
return v.parse(WorkspaceRelativePathSchema, raw);
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
throw new CliUsageError(`--${flagName} path is not a normalized workspace-relative path: ${raw}`);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
195
232
|
const isGitDiffMaxBufferError = (error) => {
|
|
196
233
|
const code = getErrorCode(error);
|
|
197
234
|
if (code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') {
|
|
@@ -211,11 +248,31 @@ const isGitDiffTimeoutError = (error) => {
|
|
|
211
248
|
error.killed === true &&
|
|
212
249
|
error.signal === 'SIGTERM');
|
|
213
250
|
};
|
|
251
|
+
const buildGitDiffArgs = (gitOptions, gitRef) => gitOptions.scopedPaths.length === 0
|
|
252
|
+
? ['diff', ...GIT_DIFF_FORMAT_ARGS, gitRef]
|
|
253
|
+
: ['diff', ...GIT_DIFF_FORMAT_ARGS, gitRef, '--', ...gitOptions.scopedPaths];
|
|
254
|
+
const throwGitDiffFailure = ({ error, gitOptions, gitRef, }) => {
|
|
255
|
+
if (getErrorCode(error) === 'ENOENT') {
|
|
256
|
+
throw new CliUsageError('git is required for --diff-from-git and was not found in PATH. Install git or use --diff with a patch file.');
|
|
257
|
+
}
|
|
258
|
+
if (isGitDiffTimeoutError(error)) {
|
|
259
|
+
throw new CliUsageError(`git diff ${gitRef} timed out after ${gitOptions.timeoutMs}ms. Narrow the diff or increase --git-diff-timeout-ms.`);
|
|
260
|
+
}
|
|
261
|
+
if (isGitDiffMaxBufferError(error)) {
|
|
262
|
+
throw new CliUsageError(`git diff ${gitRef} exceeded the configured stdout buffer (${gitOptions.maxBufferBytes} bytes). Narrow the diff or increase --git-diff-max-buffer-bytes.`);
|
|
263
|
+
}
|
|
264
|
+
const stderr = typeof error === 'object' && error !== null && 'stderr' in error
|
|
265
|
+
? String(error.stderr ?? '').trim()
|
|
266
|
+
: '';
|
|
267
|
+
if (stderr.length > 0) {
|
|
268
|
+
throw new CliUsageError(`git diff ${gitRef} failed: ${stderr}`);
|
|
269
|
+
}
|
|
270
|
+
throw new CliUsageError(`git diff ${gitRef} failed. Ensure the ref exists and the workspace is a git repository.`);
|
|
271
|
+
};
|
|
214
272
|
const materializeGitDiff = async ({ gitOptions, gitRef, workspaceRoot, }) => {
|
|
215
273
|
let tempDir;
|
|
216
274
|
try {
|
|
217
|
-
const
|
|
218
|
-
const { stdout } = await execFileAsync('git', gitArgs, {
|
|
275
|
+
const { stdout } = await execFileAsync('git', buildGitDiffArgs(gitOptions, gitRef), {
|
|
219
276
|
cwd: workspaceRoot,
|
|
220
277
|
maxBuffer: gitOptions.maxBufferBytes,
|
|
221
278
|
timeout: gitOptions.timeoutMs,
|
|
@@ -229,16 +286,11 @@ const materializeGitDiff = async ({ gitOptions, gitRef, workspaceRoot, }) => {
|
|
|
229
286
|
if (tempDir) {
|
|
230
287
|
await rm(tempDir, { force: true, recursive: true });
|
|
231
288
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
if (isGitDiffMaxBufferError(error)) {
|
|
239
|
-
throw new CliUsageError(`git diff ${gitRef} exceeded the configured stdout buffer (${gitOptions.maxBufferBytes} bytes). Narrow the diff or increase --git-diff-max-buffer-bytes.`);
|
|
240
|
-
}
|
|
241
|
-
throw error;
|
|
289
|
+
return throwGitDiffFailure({
|
|
290
|
+
error,
|
|
291
|
+
gitOptions,
|
|
292
|
+
gitRef,
|
|
293
|
+
});
|
|
242
294
|
}
|
|
243
295
|
};
|
|
244
296
|
const parseJsonInput = (text, sourceLabel) => {
|
|
@@ -252,6 +304,9 @@ const parseJsonInput = (text, sourceLabel) => {
|
|
|
252
304
|
const print = (context, text) => {
|
|
253
305
|
context.stdout.write(text.endsWith('\n') ? text : `${text}\n`);
|
|
254
306
|
};
|
|
307
|
+
const printJson = (context, value) => {
|
|
308
|
+
print(context, JSON.stringify(value, null, 2));
|
|
309
|
+
};
|
|
255
310
|
const parseLimit = (flags) => getFlag(flags, 'limit')
|
|
256
311
|
? parsePositiveIntegerFlag({
|
|
257
312
|
defaultValue: 0,
|
|
@@ -311,6 +366,13 @@ const parseRenderTarget = (flags) => {
|
|
|
311
366
|
}
|
|
312
367
|
return target;
|
|
313
368
|
};
|
|
369
|
+
const parseRequiredStageWriteStage = (flags) => {
|
|
370
|
+
const stage = getRequiredString(flags, 'stage');
|
|
371
|
+
if (!STAGE_WRITE_STAGES.includes(stage)) {
|
|
372
|
+
throw new CliUsageError(`Invalid --stage value: ${stage}. Expected one of: ${STAGE_WRITE_STAGES.join(', ')}.`);
|
|
373
|
+
}
|
|
374
|
+
return stage;
|
|
375
|
+
};
|
|
314
376
|
const splitCommandLines = (value) => value
|
|
315
377
|
.split(/\r?\n/u)
|
|
316
378
|
.map((command) => command.trim())
|
|
@@ -397,7 +459,7 @@ const assertDiffRecordKindSupported = (kind) => {
|
|
|
397
459
|
}
|
|
398
460
|
throw new CliUsageError('--diff and --diff-from-git are only supported for patch and change-log records.');
|
|
399
461
|
};
|
|
400
|
-
const readPatchText = async (diffPath) =>
|
|
462
|
+
const readPatchText = async (diffPath) => readPatchTextFromFile(diffPath);
|
|
401
463
|
const readPatchTextForRecordKind = async (kind, diffPath) => {
|
|
402
464
|
if (kind === 'change-log' || kind === 'agent-patch' || kind === 'operator-patch') {
|
|
403
465
|
return readPatchText(diffPath);
|
|
@@ -461,11 +523,13 @@ const validateRecordStdinFlags = (flags) => {
|
|
|
461
523
|
'operator',
|
|
462
524
|
'parity-status',
|
|
463
525
|
'phase',
|
|
526
|
+
'file-path',
|
|
464
527
|
'rationale',
|
|
465
528
|
'rollback-plan',
|
|
466
529
|
'rolls-back',
|
|
467
530
|
'smoke-notes',
|
|
468
531
|
'smoke-result',
|
|
532
|
+
'stage',
|
|
469
533
|
'subkind',
|
|
470
534
|
'summary',
|
|
471
535
|
].filter((flagName) => hasFlag(flags, flagName));
|
|
@@ -476,53 +540,87 @@ const validateRecordStdinFlags = (flags) => {
|
|
|
476
540
|
.map((flagName) => `--${flagName}`)
|
|
477
541
|
.join(', ')}. When using --from-stdin, provide all record fields in the JSON input.`);
|
|
478
542
|
};
|
|
479
|
-
const
|
|
480
|
-
const
|
|
481
|
-
if (
|
|
482
|
-
|
|
543
|
+
const applyRationaleFlag = ({ flags, kind, record, }) => {
|
|
544
|
+
const rationale = getFlag(flags, 'rationale');
|
|
545
|
+
if (!rationale) {
|
|
546
|
+
return rationale;
|
|
483
547
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
const rationale = getFlag(parsed.flags, 'rationale');
|
|
494
|
-
if (rationale) {
|
|
495
|
-
if (kind !== 'agent-patch' &&
|
|
496
|
-
kind !== 'operator-patch' &&
|
|
497
|
-
kind !== 'operator-decision' &&
|
|
498
|
-
kind !== 'correction') {
|
|
499
|
-
throw new CliUsageError('--rationale is only supported for patch, operator-decision, and correction records.');
|
|
500
|
-
}
|
|
501
|
-
if (kind !== 'operator-decision') {
|
|
502
|
-
record.rationale = rationale;
|
|
503
|
-
}
|
|
548
|
+
if (kind !== 'agent-patch' &&
|
|
549
|
+
kind !== 'operator-patch' &&
|
|
550
|
+
kind !== 'operator-decision' &&
|
|
551
|
+
kind !== 'correction' &&
|
|
552
|
+
kind !== 'stage-write') {
|
|
553
|
+
throw new CliUsageError('--rationale is only supported for patch, stage-write, operator-decision, and correction records.');
|
|
554
|
+
}
|
|
555
|
+
if (kind !== 'operator-decision') {
|
|
556
|
+
record.rationale = rationale;
|
|
504
557
|
}
|
|
558
|
+
return rationale;
|
|
559
|
+
};
|
|
560
|
+
const applyKindSpecificRecordFields = ({ defaultTool, flags, kind, rationale, record, }) => {
|
|
505
561
|
if (kind === 'agent-patch') {
|
|
506
562
|
record.agent = {
|
|
507
|
-
name: getFlag(
|
|
563
|
+
name: getFlag(flags, 'agent') ?? defaultTool,
|
|
508
564
|
};
|
|
565
|
+
return;
|
|
509
566
|
}
|
|
510
567
|
if (kind === 'operator-patch') {
|
|
511
568
|
record.operator = {
|
|
512
|
-
name: getFlag(
|
|
569
|
+
name: getFlag(flags, 'operator') ?? defaultTool,
|
|
513
570
|
};
|
|
571
|
+
return;
|
|
514
572
|
}
|
|
515
573
|
if (kind === 'operator-decision') {
|
|
516
|
-
const action = getRequiredString(parsed.flags, 'action');
|
|
517
574
|
record.payload = {
|
|
518
|
-
action,
|
|
519
|
-
checkId: getFlag(
|
|
575
|
+
action: getRequiredString(flags, 'action'),
|
|
576
|
+
checkId: getFlag(flags, 'check-id'),
|
|
520
577
|
rationale: rationale ?? '',
|
|
521
578
|
};
|
|
579
|
+
return;
|
|
522
580
|
}
|
|
523
581
|
if (kind === 'change-log') {
|
|
524
|
-
record.subkind = parseRequiredChangeLogSubkind(
|
|
582
|
+
record.subkind = parseRequiredChangeLogSubkind(flags);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (kind === 'stage-write') {
|
|
586
|
+
record.filePath = parseWorkspaceRelativePathFlag({
|
|
587
|
+
flagName: 'file-path',
|
|
588
|
+
flags,
|
|
589
|
+
required: true,
|
|
590
|
+
});
|
|
591
|
+
record.rationale = getRequiredString(flags, 'rationale');
|
|
592
|
+
record.stage = parseRequiredStageWriteStage(flags);
|
|
525
593
|
}
|
|
594
|
+
};
|
|
595
|
+
const buildBaseRecordFromFlags = (parsed, context) => {
|
|
596
|
+
const kind = getRequiredKind(parsed.flags);
|
|
597
|
+
if (kind !== 'change-log') {
|
|
598
|
+
rejectUnsupportedFlags('change-log', parsed.flags, CHANGE_LOG_RECORD_ONLY_FLAGS);
|
|
599
|
+
}
|
|
600
|
+
if (kind !== 'stage-write') {
|
|
601
|
+
rejectUnsupportedFlags('stage-write', parsed.flags, STAGE_WRITE_ONLY_FLAGS);
|
|
602
|
+
}
|
|
603
|
+
const record = {
|
|
604
|
+
emitter: {
|
|
605
|
+
tool: context.defaultEmitter.tool,
|
|
606
|
+
version: context.defaultEmitter.version,
|
|
607
|
+
},
|
|
608
|
+
kind,
|
|
609
|
+
phase: getRequiredPhase(parsed.flags),
|
|
610
|
+
summary: getRequiredString(parsed.flags, 'summary'),
|
|
611
|
+
};
|
|
612
|
+
const rationale = applyRationaleFlag({
|
|
613
|
+
flags: parsed.flags,
|
|
614
|
+
kind,
|
|
615
|
+
record,
|
|
616
|
+
});
|
|
617
|
+
applyKindSpecificRecordFields({
|
|
618
|
+
defaultTool: context.defaultEmitter.tool,
|
|
619
|
+
flags: parsed.flags,
|
|
620
|
+
kind,
|
|
621
|
+
rationale,
|
|
622
|
+
record,
|
|
623
|
+
});
|
|
526
624
|
const idempotencyKey = getFlag(parsed.flags, 'idempotency-key');
|
|
527
625
|
if (idempotencyKey) {
|
|
528
626
|
record.idempotencyKey = idempotencyKey;
|
|
@@ -695,16 +793,22 @@ const runNoteCli = async (parsed, context) => {
|
|
|
695
793
|
const runListCli = async (parsed, context) => {
|
|
696
794
|
const ledger = await openLedger(getWorkspaceRoot(parsed.flags));
|
|
697
795
|
if (hasFlag(parsed.flags, 'json')) {
|
|
698
|
-
|
|
796
|
+
let wroteEntry = false;
|
|
797
|
+
context.stdout.write('[\n');
|
|
699
798
|
for await (const entry of ledger.list({
|
|
700
799
|
kind: parseOptionalKind(parsed.flags),
|
|
701
800
|
limit: parseLimit(parsed.flags),
|
|
702
801
|
phase: parseOptionalPhase(parsed.flags),
|
|
703
802
|
since: parseSince(parsed.flags),
|
|
704
803
|
})) {
|
|
705
|
-
|
|
804
|
+
if (wroteEntry) {
|
|
805
|
+
context.stdout.write(',\n');
|
|
806
|
+
}
|
|
807
|
+
const renderedEntry = JSON.stringify(entry, null, 2).replaceAll('\n', '\n ');
|
|
808
|
+
context.stdout.write(` ${renderedEntry}`);
|
|
809
|
+
wroteEntry = true;
|
|
706
810
|
}
|
|
707
|
-
|
|
811
|
+
context.stdout.write(wroteEntry ? '\n]\n' : ']\n');
|
|
708
812
|
return 0;
|
|
709
813
|
}
|
|
710
814
|
for await (const entry of ledger.list({
|
|
@@ -762,16 +866,29 @@ const runArchiveCli = async (parsed, context) => {
|
|
|
762
866
|
print(context, result.integrityHash);
|
|
763
867
|
return 0;
|
|
764
868
|
};
|
|
869
|
+
const formatDoctorFinding = (finding) => [`[${finding.code}] ${finding.message}`, `Next step: ${finding.remediation}`].join('\n');
|
|
870
|
+
const printDoctorFindings = (context, report) => {
|
|
871
|
+
context.stderr.write(`doctor found ${report.issueCount} issue${report.issueCount === 1 ? '' : 's'}.\n`);
|
|
872
|
+
for (const [index, finding] of report.findings.entries()) {
|
|
873
|
+
context.stderr.write(`\n${formatDoctorFinding(finding)}`);
|
|
874
|
+
if (index < report.findings.length - 1) {
|
|
875
|
+
context.stderr.write('\n');
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
context.stderr.write('\n');
|
|
879
|
+
};
|
|
765
880
|
const runDoctorCli = async (parsed, context) => {
|
|
766
881
|
const ledger = await openLedger(getWorkspaceRoot(parsed.flags));
|
|
767
882
|
const result = await ledger.doctor();
|
|
883
|
+
if (hasFlag(parsed.flags, 'json')) {
|
|
884
|
+
printJson(context, result);
|
|
885
|
+
return result.ok ? 0 : 1;
|
|
886
|
+
}
|
|
768
887
|
if (result.ok) {
|
|
769
888
|
print(context, 'ok');
|
|
770
889
|
return 0;
|
|
771
890
|
}
|
|
772
|
-
|
|
773
|
-
context.stderr.write(`${issue}\n`);
|
|
774
|
-
}
|
|
891
|
+
printDoctorFindings(context, result);
|
|
775
892
|
return 1;
|
|
776
893
|
};
|
|
777
894
|
const formatCliError = (error) => {
|
|
@@ -787,7 +904,7 @@ const formatCliError = (error) => {
|
|
|
787
904
|
})
|
|
788
905
|
.join('\n');
|
|
789
906
|
}
|
|
790
|
-
return error instanceof Error ?
|
|
907
|
+
return error instanceof Error ? error.message : String(error);
|
|
791
908
|
};
|
|
792
909
|
export const runLedgerCli = async (argv, context = {}) => {
|
|
793
910
|
const mergedContext = { ...DEFAULT_CONTEXT, ...context };
|
package/dist/coverage.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverage.d.ts","sourceRoot":"","sources":["../src/coverage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"coverage.d.ts","sourceRoot":"","sources":["../src/coverage.ts"],"names":[],"mappings":"AA+FA,MAAM,MAAM,cAAc,GAAG;IACzB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC;CACrC,CAAC;AAEF,eAAO,MAAM,eAAe,GAAU,eAAe,MAAM,KAAG,OAAO,CAAC,cAAc,CAiDnF,CAAC"}
|
package/dist/coverage.js
CHANGED
|
@@ -4,10 +4,10 @@ import { mapWithConcurrencyLimit } from "./async.js";
|
|
|
4
4
|
import { CANDIDATE_EXCLUDE_GLOBS, CANDIDATE_FILE_GLOBS } from "./candidate-paths.js";
|
|
5
5
|
import { readLabManifestMin } from "./lab-min.js";
|
|
6
6
|
import { loadLedgerState } from "./recovery.js";
|
|
7
|
+
import { getLedgerRuntimeConfig } from "./runtime-config.js";
|
|
7
8
|
const EXCLUDED_ROOTS = new Set(CANDIDATE_EXCLUDE_GLOBS.map((glob) => glob.replace(/\/\*\*$/u, '')));
|
|
8
9
|
const CANDIDATE_DIRECTORIES = CANDIDATE_FILE_GLOBS.filter((glob) => glob.endsWith('/**/*')).map((glob) => glob.slice(0, -5));
|
|
9
10
|
const CANDIDATE_FILES = CANDIDATE_FILE_GLOBS.filter((glob) => !glob.endsWith('/**/*'));
|
|
10
|
-
const FILE_STAT_CONCURRENCY = 16;
|
|
11
11
|
const toPosix = (value) => value.replaceAll(path.sep, '/');
|
|
12
12
|
const isMissingPathError = (error) => {
|
|
13
13
|
const code = error.code;
|
|
@@ -85,13 +85,14 @@ const getWorkspaceInitMs = async (workspaceRoot) => {
|
|
|
85
85
|
return labManifestStat.birthtimeMs || labManifestStat.mtimeMs;
|
|
86
86
|
};
|
|
87
87
|
export const computeCoverage = async (workspaceRoot) => {
|
|
88
|
+
const { coverageFileStatConcurrency } = getLedgerRuntimeConfig();
|
|
88
89
|
const [{ readIndex }, candidateFiles, workspaceInitMs] = await Promise.all([
|
|
89
90
|
loadLedgerState(workspaceRoot),
|
|
90
91
|
collectCandidateFiles(workspaceRoot),
|
|
91
92
|
getWorkspaceInitMs(workspaceRoot),
|
|
92
93
|
]);
|
|
93
94
|
const coverageIndex = new Set(readIndex.coveredFiles.map((filePath) => toPosix(filePath)));
|
|
94
|
-
const candidateStats = await mapWithConcurrencyLimit(candidateFiles,
|
|
95
|
+
const candidateStats = await mapWithConcurrencyLimit(candidateFiles, coverageFileStatConcurrency, async (relativePath) => {
|
|
95
96
|
try {
|
|
96
97
|
return {
|
|
97
98
|
mtimeMs: (await stat(path.join(workspaceRoot, relativePath))).mtimeMs,
|
package/dist/doctor.d.ts
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import { type PreparedLedgerState } from './recovery.ts';
|
|
2
|
+
export declare const DOCTOR_FINDING_CODES: readonly ["blob-corrupt", "blob-missing", "blob-unreadable", "change-log-rollback-missing-target", "change-log-smoke-failure-missing-rollback-plan", "manifest-entry-count-mismatch", "manifest-entry-location-missing", "manifest-entry-missing-on-disk", "manifest-last-sequence-mismatch", "manifest-per-phase-latest-mismatch", "manifest-phase-mismatch", "manifest-sequence-mismatch", "open-issue-stale", "pending-commit-quarantined", "phase-prev-entry-mismatch", "pre-change-checkpoint-stale", "read-failure"];
|
|
3
|
+
export type DoctorFindingCode = (typeof DOCTOR_FINDING_CODES)[number];
|
|
4
|
+
export type DoctorFindingMetadataValue = boolean | null | number | string;
|
|
5
|
+
export type DoctorFinding = {
|
|
6
|
+
readonly code: DoctorFindingCode;
|
|
7
|
+
readonly message: string;
|
|
8
|
+
readonly metadata?: Record<string, DoctorFindingMetadataValue>;
|
|
9
|
+
readonly remediation: string;
|
|
10
|
+
};
|
|
11
|
+
export type DoctorReport = {
|
|
12
|
+
readonly checkedAt: string;
|
|
13
|
+
readonly findings: DoctorFinding[];
|
|
14
|
+
readonly issueCount: number;
|
|
15
|
+
readonly issues: string[];
|
|
16
|
+
readonly ok: boolean;
|
|
17
|
+
};
|
|
2
18
|
export declare const runLedgerDoctor: (workspaceRoot: string, options?: {
|
|
3
19
|
readonly skipPrepare?: boolean;
|
|
4
20
|
readonly state?: PreparedLedgerState;
|
|
5
|
-
}) => Promise<
|
|
6
|
-
issues: string[];
|
|
7
|
-
ok: boolean;
|
|
8
|
-
}>;
|
|
21
|
+
}) => Promise<DoctorReport>;
|
|
9
22
|
//# sourceMappingURL=doctor.d.ts.map
|
package/dist/doctor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAMA,OAAO,
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAMA,OAAO,EAAiD,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAUxG,eAAO,MAAM,oBAAoB,4fAkBvB,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AACtE,MAAM,MAAM,0BAA0B,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;AAE1E,MAAM,MAAM,aAAa,GAAG;IACxB,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAAC;IAC/D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,aAAa,EAAE,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;CACxB,CAAC;AAwlBF,eAAO,MAAM,eAAe,GACxB,eAAe,MAAM,EACrB,UAAS;IAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,mBAAmB,CAAA;CAAO,KACvF,OAAO,CAAC,YAAY,CAsCtB,CAAC"}
|