mustflow 2.22.5 → 2.22.12

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 (56) hide show
  1. package/README.md +8 -0
  2. package/dist/cli/commands/check.js +2 -2
  3. package/dist/cli/commands/classify.js +2 -0
  4. package/dist/cli/commands/dashboard.js +9 -69
  5. package/dist/cli/commands/help.js +1 -3
  6. package/dist/cli/commands/run/receipt.js +1 -0
  7. package/dist/cli/commands/run.js +14 -1
  8. package/dist/cli/commands/verify/evidence-input.js +269 -0
  9. package/dist/cli/commands/verify/input.js +212 -0
  10. package/dist/cli/commands/verify/state-paths.js +33 -0
  11. package/dist/cli/commands/verify.js +29 -511
  12. package/dist/cli/i18n/en.js +3 -0
  13. package/dist/cli/i18n/es.js +3 -0
  14. package/dist/cli/i18n/fr.js +3 -0
  15. package/dist/cli/i18n/hi.js +3 -0
  16. package/dist/cli/i18n/ko.js +3 -0
  17. package/dist/cli/i18n/zh.js +3 -0
  18. package/dist/cli/lib/dashboard-export.js +2 -0
  19. package/dist/cli/lib/dashboard-mutations.js +79 -0
  20. package/dist/cli/lib/doc-review-ledger.js +1 -3
  21. package/dist/cli/lib/local-index/command-effect-index.js +25 -0
  22. package/dist/cli/lib/local-index/hashing.js +7 -0
  23. package/dist/cli/lib/local-index/index.js +127 -826
  24. package/dist/cli/lib/local-index/source-index.js +137 -0
  25. package/dist/cli/lib/local-index/verification-evidence.js +451 -0
  26. package/dist/cli/lib/local-index/workflow-documents.js +204 -0
  27. package/dist/cli/lib/manifest-lock.js +1 -3
  28. package/dist/cli/lib/repo-map-frontmatter.js +53 -0
  29. package/dist/cli/lib/repo-map.js +10 -57
  30. package/dist/cli/lib/run-root-trust.js +27 -0
  31. package/dist/cli/lib/validation/index.js +6 -2
  32. package/dist/core/change-classification-policy.js +47 -0
  33. package/dist/core/change-classification.js +10 -43
  34. package/dist/core/check-issues.js +11 -7
  35. package/dist/core/command-contract-validation.js +22 -20
  36. package/dist/core/contract-lint.js +6 -2
  37. package/dist/core/correlation-id.js +16 -0
  38. package/dist/core/run-receipt.js +1 -0
  39. package/package.json +4 -1
  40. package/schemas/README.md +4 -0
  41. package/schemas/change-verification-report.schema.json +4 -0
  42. package/schemas/classify-report.schema.json +4 -0
  43. package/schemas/dashboard-export.schema.json +4 -0
  44. package/schemas/latest-run-pointer.schema.json +4 -0
  45. package/schemas/run-receipt.schema.json +4 -0
  46. package/schemas/verify-report.schema.json +4 -0
  47. package/schemas/verify-run-manifest.schema.json +4 -0
  48. package/templates/default/i18n.toml +3 -3
  49. package/templates/default/locales/en/.mustflow/skills/architecture-deepening-review/SKILL.md +25 -2
  50. package/templates/default/locales/en/.mustflow/skills/security-privacy-review/SKILL.md +9 -1
  51. package/templates/default/locales/en/.mustflow/skills/test-design-guard/SKILL.md +9 -1
  52. package/templates/default/locales/ko/.mustflow/context/INDEX.md +12 -12
  53. package/templates/default/locales/ko/.mustflow/context/PROJECT.md +6 -6
  54. package/templates/default/locales/ko/.mustflow/docs/agent-workflow.md +70 -70
  55. package/templates/default/locales/ko/AGENTS.md +14 -14
  56. package/templates/default/manifest.toml +1 -1
@@ -1,11 +1,9 @@
1
1
  import { createHash } from 'node:crypto';
2
- import path from 'node:path';
3
- import { createClassifyOutput } from './classify.js';
4
2
  import { runRun } from './run.js';
5
3
  import { createChangeVerificationReport, } from '../../core/change-verification.js';
4
+ import { createCorrelationId } from '../../core/correlation-id.js';
6
5
  import { readUtf8FileInsideWithoutSymlinks, writeJsonFileInsideWithoutSymlinks } from '../../core/safe-filesystem.js';
7
6
  import { createVerifyCompletionVerdict, } from '../../core/completion-verdict.js';
8
- import { createStateRunId } from '../../core/atomic-state-write.js';
9
7
  import { createExternalEvidenceRisks, } from '../../core/external-evidence.js';
10
8
  import { createRepeatedFailureRisks, createVerificationFailureFingerprint, updateRepeatedFailureState, } from '../../core/repeated-failure.js';
11
9
  import { countReproEvidenceVerdictEffects, createReproEvidenceRisks, } from '../../core/repro-evidence.js';
@@ -15,13 +13,15 @@ import { countValidationRatchetVerdictEffects, createValidationRatchetRisks, } f
15
13
  import { finishRunWriteBatchTracking, startRunWriteBatchTracking, } from '../../core/run-write-drift.js';
16
14
  import { readCommandContract } from '../../core/config-loading.js';
17
15
  import { DEFAULT_VERIFY_PARALLELISM, parseVerifyArgs, resolveVerifyParallelism, } from './verify/args.js';
16
+ import { createInputFromChanged, createSyntheticClassificationReport, planErrorMessageKey, readInputFromClassificationReport, resolveVerifyInputPath, writeChangedPlan, } from './verify/input.js';
17
+ import { readExternalEvidenceFile, readReproEvidenceFile } from './verify/evidence-input.js';
18
+ import { createVerifyIntentReceiptPath, createVerifyRunStatePaths, resolveLatestVerifyRunReceiptPath, } from './verify/state-paths.js';
18
19
  import { printUsageError, renderHelp } from '../lib/cli-output.js';
19
20
  import { t } from '../lib/i18n.js';
20
21
  import { readLocalCommandEffectGraphs, readLocalPathSurfaces, readLocalSourceAnchorVerdictRisks, } from '../lib/local-index.js';
21
22
  import { resolveMustflowRoot } from '../lib/project-root.js';
23
+ export { planErrorMessageKey, readInputFromClassificationReport } from './verify/input.js';
22
24
  const VERIFY_SCHEMA_VERSION = '1';
23
- const RUN_STATE_DIR = path.join('.mustflow', 'state', 'runs');
24
- const LATEST_RUN_RECEIPT_PATH = path.join(RUN_STATE_DIR, 'latest.json');
25
25
  function createBufferedOutput() {
26
26
  const stdout = [];
27
27
  const stderr = [];
@@ -77,493 +77,6 @@ export function getVerifyHelp(lang = 'en') {
77
77
  function uniqueStrings(values) {
78
78
  return [...new Set(values.map((value) => value.trim()).filter((value) => value.length > 0))];
79
79
  }
80
- function toPosixPath(value) {
81
- return value.split(path.sep).join('/');
82
- }
83
- function createVerifyRunStatePaths(projectRoot) {
84
- const runDir = toPosixPath(path.join(RUN_STATE_DIR, createStateRunId('verify')));
85
- const manifestPath = toPosixPath(path.join(runDir, 'manifest.json'));
86
- const absoluteRunDir = path.join(projectRoot, runDir);
87
- return {
88
- runDir,
89
- manifestPath,
90
- absoluteRunDir,
91
- absoluteIntentDir: path.join(absoluteRunDir, 'intents'),
92
- absoluteManifestPath: path.join(projectRoot, manifestPath),
93
- };
94
- }
95
- function sanitizeIntentFilePart(value) {
96
- const sanitized = value.replace(/[^A-Za-z0-9._-]+/g, '_').replace(/^_+|_+$/g, '');
97
- return sanitized.length > 0 ? sanitized.slice(0, 80) : 'intent';
98
- }
99
- function readStringArray(value) {
100
- if (!Array.isArray(value)) {
101
- return [];
102
- }
103
- return value.filter((item) => typeof item === 'string');
104
- }
105
- function isStringArray(value) {
106
- return Array.isArray(value) && value.every((item) => typeof item === 'string');
107
- }
108
- function isPlainRecord(value) {
109
- return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
110
- }
111
- function isUpdatePolicy(value) {
112
- return value === 'update' || value === 'update_or_mark_stale' || value === 'not_applicable';
113
- }
114
- function isUpdatePolicyArray(value) {
115
- return Array.isArray(value) && value.every((item) => isUpdatePolicy(item));
116
- }
117
- function readPlanRoot(value) {
118
- if (!isPlainRecord(value)) {
119
- return null;
120
- }
121
- const root = value.mustflow_root;
122
- return typeof root === 'string' && root.length > 0 ? root : null;
123
- }
124
- function readStrictClassificationSummary(value) {
125
- if (!isPlainRecord(value)) {
126
- return null;
127
- }
128
- if (!Number.isInteger(value.fileCount) ||
129
- Number(value.fileCount) < 0 ||
130
- !Number.isInteger(value.publicSurfaceCount) ||
131
- Number(value.publicSurfaceCount) < 0 ||
132
- !isStringArray(value.changeKinds) ||
133
- !isStringArray(value.validationReasons) ||
134
- !isUpdatePolicyArray(value.updatePolicies) ||
135
- !isStringArray(value.driftChecks) ||
136
- !isStringArray(value.affectedContracts)) {
137
- return null;
138
- }
139
- return {
140
- fileCount: Number(value.fileCount),
141
- publicSurfaceCount: Number(value.publicSurfaceCount),
142
- changeKinds: [...value.changeKinds],
143
- validationReasons: uniqueStrings(value.validationReasons),
144
- updatePolicies: [...value.updatePolicies],
145
- driftChecks: [...value.driftChecks],
146
- affectedContracts: [...value.affectedContracts],
147
- };
148
- }
149
- function readStrictClassification(value) {
150
- if (!isPlainRecord(value) || typeof value.path !== 'string' || !isStringArray(value.changeKinds)) {
151
- return null;
152
- }
153
- const surface = value.surface;
154
- if (!isPlainRecord(surface)) {
155
- return null;
156
- }
157
- if (typeof surface.kind !== 'string' ||
158
- typeof surface.category !== 'string' ||
159
- typeof surface.isPublicSurface !== 'boolean' ||
160
- !isStringArray(surface.validationReasons) ||
161
- !isStringArray(surface.affectedContracts) ||
162
- !isUpdatePolicy(surface.updatePolicy) ||
163
- !isStringArray(surface.driftChecks)) {
164
- return null;
165
- }
166
- return {
167
- path: value.path,
168
- changeKinds: [...value.changeKinds],
169
- surface: {
170
- kind: surface.kind,
171
- category: surface.category,
172
- isPublicSurface: surface.isPublicSurface,
173
- validationReasons: [...surface.validationReasons],
174
- affectedContracts: [...surface.affectedContracts],
175
- updatePolicy: surface.updatePolicy,
176
- driftChecks: [...surface.driftChecks],
177
- },
178
- };
179
- }
180
- function readStrictClassifications(value) {
181
- if (!Array.isArray(value)) {
182
- return null;
183
- }
184
- const classifications = value.map(readStrictClassification);
185
- return classifications.every((classification) => classification !== null)
186
- ? classifications
187
- : null;
188
- }
189
- function readStrictClassifyPlan(projectRoot, plan) {
190
- if (!isPlainRecord(plan)) {
191
- throw new Error('unsupported_plan_source');
192
- }
193
- if (plan.schema_version !== '1' || plan.command !== 'classify') {
194
- throw new Error('unsupported_plan_source');
195
- }
196
- if (readPlanRoot(plan) !== projectRoot) {
197
- throw new Error('plan_root_mismatch');
198
- }
199
- const source = plan.source;
200
- const files = plan.files;
201
- const classifications = readStrictClassifications(plan.classifications);
202
- const summary = readStrictClassificationSummary(plan.summary);
203
- if ((source !== 'changed' && source !== 'paths') || !isStringArray(files) || !classifications || !summary) {
204
- throw new Error('invalid_plan_file');
205
- }
206
- if (summary.validationReasons.length === 0) {
207
- throw new Error('missing_plan_reasons');
208
- }
209
- return {
210
- source,
211
- files: [...files],
212
- classifications,
213
- summary,
214
- };
215
- }
216
- function emptyClassificationSummary(validationReasons) {
217
- return {
218
- fileCount: 0,
219
- publicSurfaceCount: 0,
220
- changeKinds: [],
221
- validationReasons,
222
- updatePolicies: [],
223
- driftChecks: [],
224
- affectedContracts: [],
225
- };
226
- }
227
- function createSyntheticClassificationReport(reasons, source = 'paths', files = []) {
228
- return {
229
- source,
230
- files,
231
- classifications: [],
232
- summary: emptyClassificationSummary(reasons),
233
- };
234
- }
235
- function resolvePlanPath(projectRoot, inputPath) {
236
- const resolved = path.resolve(projectRoot, inputPath);
237
- const relative = path.relative(projectRoot, resolved);
238
- if (relative.startsWith('..') || path.isAbsolute(relative)) {
239
- throw new Error('plan_path_outside_root');
240
- }
241
- return resolved;
242
- }
243
- function readJsonInputFile(projectRoot, inputPath, invalidCode) {
244
- const inputFilePath = resolvePlanPath(projectRoot, inputPath);
245
- let content;
246
- try {
247
- content = readUtf8FileInsideWithoutSymlinks(projectRoot, inputFilePath);
248
- }
249
- catch (error) {
250
- if (error instanceof Error && error.message.startsWith('Path must not contain symlinks:')) {
251
- throw new Error('input_path_contains_symlink');
252
- }
253
- throw new Error(invalidCode);
254
- }
255
- try {
256
- return JSON.parse(content);
257
- }
258
- catch {
259
- throw new Error(invalidCode);
260
- }
261
- }
262
- export function readInputFromClassificationReport(projectRoot, inputPath) {
263
- const parsed = readJsonInputFile(projectRoot, inputPath, 'invalid_plan_file');
264
- const classificationReport = readStrictClassifyPlan(projectRoot, parsed);
265
- return {
266
- reasons: classificationReport.summary.validationReasons,
267
- classificationReport,
268
- };
269
- }
270
- function isExternalEvidenceStatus(value) {
271
- return value === 'passed' || value === 'failed' || value === 'cancelled' || value === 'unknown';
272
- }
273
- function isLegacyReproEvidenceStatus(value) {
274
- return value === 'present' || value === 'unavailable' || value === 'missing';
275
- }
276
- function isReproBeforeFixStatus(value) {
277
- return value === 'reproduced' || value === 'unavailable' || value === 'missing';
278
- }
279
- function isReproBeforeFixOutcome(value) {
280
- return value === 'failed_as_expected' || value === 'failed_differently' || value === 'passed_unexpectedly' || value === null;
281
- }
282
- function isReproAfterFixStatus(value) {
283
- return value === 'passed' || value === 'failed' || value === 'unavailable' || value === 'missing';
284
- }
285
- function isReproAfterFixOutcome(value) {
286
- return value === 'passed_expected_behavior' || value === 'failed_same_route' || value === 'failed_differently' || value === null;
287
- }
288
- function isReproRegressionGuardStatus(value) {
289
- return value === 'passed' || value === 'failed' || value === 'unavailable' || value === 'missing';
290
- }
291
- function isReproRouteKind(value) {
292
- return (value === 'test' ||
293
- value === 'cli' ||
294
- value === 'browser' ||
295
- value === 'api' ||
296
- value === 'manual' ||
297
- value === 'unknown' ||
298
- value === null);
299
- }
300
- function readOptionalString(value) {
301
- return typeof value === 'string' && value.length > 0 ? value : null;
302
- }
303
- function readRouteStep(value, index) {
304
- if (!isPlainRecord(value)) {
305
- return {
306
- ordinal: index + 1,
307
- action: null,
308
- target: null,
309
- input_digest: null,
310
- observation_digest: null,
311
- summary: null,
312
- };
313
- }
314
- const ordinal = typeof value.ordinal === 'number' && Number.isInteger(value.ordinal) && value.ordinal > 0 ? value.ordinal : index + 1;
315
- return {
316
- ordinal,
317
- action: readOptionalString(value.action),
318
- target: readOptionalString(value.target),
319
- input_digest: readOptionalString(value.input_digest),
320
- observation_digest: readOptionalString(value.observation_digest),
321
- summary: readOptionalString(value.summary),
322
- };
323
- }
324
- function readReproductionRoute(value) {
325
- if (!isPlainRecord(value)) {
326
- return {
327
- route_id: null,
328
- route_kind: null,
329
- route_digest: null,
330
- failure_oracle_hash: null,
331
- steps: [],
332
- };
333
- }
334
- const routeKind = value.route_kind ?? null;
335
- if (!isReproRouteKind(routeKind)) {
336
- throw new Error('invalid_repro_evidence_file');
337
- }
338
- const rawSteps = Array.isArray(value.steps) ? value.steps : [];
339
- return {
340
- route_id: readOptionalString(value.route_id),
341
- route_kind: routeKind,
342
- route_digest: readOptionalString(value.route_digest),
343
- failure_oracle_hash: readOptionalString(value.failure_oracle_hash),
344
- steps: rawSteps.map((step, index) => readRouteStep(step, index)),
345
- };
346
- }
347
- function readLegacyReproEvidenceItem(value) {
348
- if (!isPlainRecord(value)) {
349
- return {
350
- status: 'missing',
351
- summary: null,
352
- reason: null,
353
- };
354
- }
355
- if (!isLegacyReproEvidenceStatus(value.status)) {
356
- throw new Error('invalid_repro_evidence_file');
357
- }
358
- return {
359
- status: value.status,
360
- summary: readOptionalString(value.summary),
361
- reason: readOptionalString(value.reason),
362
- };
363
- }
364
- function legacyBeforeFixEvidence(value) {
365
- const item = readLegacyReproEvidenceItem(value);
366
- return {
367
- status: item.status === 'present' ? 'reproduced' : item.status,
368
- outcome: item.status === 'present' ? 'failed_as_expected' : null,
369
- receipt_path: null,
370
- receipt_sha256: null,
371
- verification_plan_id: null,
372
- summary: item.summary,
373
- reason: item.reason,
374
- };
375
- }
376
- function legacyAfterFixEvidence(value) {
377
- const item = readLegacyReproEvidenceItem(value);
378
- return {
379
- status: item.status === 'present' ? 'passed' : item.status,
380
- outcome: item.status === 'present' ? 'passed_expected_behavior' : null,
381
- same_route_as: null,
382
- receipt_path: null,
383
- receipt_sha256: null,
384
- verification_plan_id: null,
385
- summary: item.summary,
386
- reason: item.reason,
387
- };
388
- }
389
- function legacyRegressionGuardEvidence(value) {
390
- const item = readLegacyReproEvidenceItem(value);
391
- return {
392
- status: item.status === 'present' ? 'passed' : item.status,
393
- intent: null,
394
- test_path: null,
395
- receipt_path: null,
396
- receipt_sha256: null,
397
- verification_plan_id: null,
398
- summary: item.summary,
399
- reason: item.reason,
400
- };
401
- }
402
- function readBeforeFixEvidence(value) {
403
- if (!isPlainRecord(value)) {
404
- return {
405
- status: 'missing',
406
- outcome: null,
407
- receipt_path: null,
408
- receipt_sha256: null,
409
- verification_plan_id: null,
410
- summary: null,
411
- reason: null,
412
- };
413
- }
414
- const outcome = value.outcome ?? null;
415
- if (!isReproBeforeFixStatus(value.status) || !isReproBeforeFixOutcome(outcome)) {
416
- throw new Error('invalid_repro_evidence_file');
417
- }
418
- return {
419
- status: value.status,
420
- outcome,
421
- receipt_path: readOptionalString(value.receipt_path),
422
- receipt_sha256: readOptionalString(value.receipt_sha256),
423
- verification_plan_id: readOptionalString(value.verification_plan_id),
424
- summary: readOptionalString(value.summary),
425
- reason: readOptionalString(value.reason),
426
- };
427
- }
428
- function readAfterFixEvidence(value) {
429
- if (!isPlainRecord(value)) {
430
- return {
431
- status: 'missing',
432
- outcome: null,
433
- same_route_as: null,
434
- receipt_path: null,
435
- receipt_sha256: null,
436
- verification_plan_id: null,
437
- summary: null,
438
- reason: null,
439
- };
440
- }
441
- const outcome = value.outcome ?? null;
442
- if (!isReproAfterFixStatus(value.status) || !isReproAfterFixOutcome(outcome)) {
443
- throw new Error('invalid_repro_evidence_file');
444
- }
445
- return {
446
- status: value.status,
447
- outcome,
448
- same_route_as: readOptionalString(value.same_route_as),
449
- receipt_path: readOptionalString(value.receipt_path),
450
- receipt_sha256: readOptionalString(value.receipt_sha256),
451
- verification_plan_id: readOptionalString(value.verification_plan_id),
452
- summary: readOptionalString(value.summary),
453
- reason: readOptionalString(value.reason),
454
- };
455
- }
456
- function readRegressionGuardEvidence(value) {
457
- if (!isPlainRecord(value)) {
458
- return {
459
- status: 'missing',
460
- intent: null,
461
- test_path: null,
462
- receipt_path: null,
463
- receipt_sha256: null,
464
- verification_plan_id: null,
465
- summary: null,
466
- reason: null,
467
- };
468
- }
469
- if (!isReproRegressionGuardStatus(value.status)) {
470
- throw new Error('invalid_repro_evidence_file');
471
- }
472
- return {
473
- status: value.status,
474
- intent: readOptionalString(value.intent),
475
- test_path: readOptionalString(value.test_path),
476
- receipt_path: readOptionalString(value.receipt_path),
477
- receipt_sha256: readOptionalString(value.receipt_sha256),
478
- verification_plan_id: readOptionalString(value.verification_plan_id),
479
- summary: readOptionalString(value.summary),
480
- reason: readOptionalString(value.reason),
481
- };
482
- }
483
- function readReproEvidenceFile(projectRoot, inputPath) {
484
- const parsed = readJsonInputFile(projectRoot, inputPath, 'invalid_repro_evidence_file');
485
- if (!isPlainRecord(parsed) || parsed.schema_version !== '1' || parsed.command !== 'repro-evidence') {
486
- throw new Error('unsupported_repro_evidence_source');
487
- }
488
- const regressionGuard = isPlainRecord(parsed.regression_guard) && isReproRegressionGuardStatus(parsed.regression_guard.status)
489
- ? readRegressionGuardEvidence(parsed.regression_guard)
490
- : legacyRegressionGuardEvidence(parsed.regression_guard);
491
- return {
492
- source: 'repro_first_debug',
493
- authority: 'claim_evidence',
494
- reported_symptom: readOptionalString(parsed.reported_symptom),
495
- expected_behavior: readOptionalString(parsed.expected_behavior),
496
- observed_behavior: readOptionalString(parsed.observed_behavior),
497
- reproduction_route: readReproductionRoute(parsed.reproduction_route),
498
- before_fix: isPlainRecord(parsed.before_fix)
499
- ? readBeforeFixEvidence(parsed.before_fix)
500
- : legacyBeforeFixEvidence(parsed.evidence_before_fix),
501
- after_fix: isPlainRecord(parsed.after_fix)
502
- ? readAfterFixEvidence(parsed.after_fix)
503
- : legacyAfterFixEvidence(parsed.evidence_after_fix),
504
- regression_guard: regressionGuard,
505
- };
506
- }
507
- function readExternalEvidenceFile(projectRoot, inputPath) {
508
- const parsed = readJsonInputFile(projectRoot, inputPath, 'invalid_external_evidence_file');
509
- if (!isPlainRecord(parsed) || parsed.schema_version !== '1' || parsed.command !== 'external-evidence') {
510
- throw new Error('unsupported_external_evidence_source');
511
- }
512
- if (!Array.isArray(parsed.checks)) {
513
- throw new Error('invalid_external_evidence_file');
514
- }
515
- return parsed.checks.map((check) => {
516
- if (!isPlainRecord(check) ||
517
- typeof check.provider !== 'string' ||
518
- check.provider.length === 0 ||
519
- typeof check.name !== 'string' ||
520
- check.name.length === 0 ||
521
- !isExternalEvidenceStatus(check.status)) {
522
- throw new Error('invalid_external_evidence_file');
523
- }
524
- return {
525
- source: 'external_ci',
526
- authority: 'supporting_only',
527
- provider: check.provider,
528
- name: check.name,
529
- status: check.status,
530
- url: readOptionalString(check.url),
531
- summary: readOptionalString(check.summary),
532
- };
533
- });
534
- }
535
- function createInputFromChanged(projectRoot) {
536
- const plan = createClassifyOutput(projectRoot, 'changed', []);
537
- return {
538
- plan,
539
- input: {
540
- reasons: plan.summary.validationReasons,
541
- classificationReport: plan,
542
- },
543
- };
544
- }
545
- function writeChangedPlan(projectRoot, inputPath, plan) {
546
- const planPath = resolvePlanPath(projectRoot, inputPath);
547
- writeJsonFileInsideWithoutSymlinks(projectRoot, planPath, plan);
548
- }
549
- export function planErrorMessageKey(code) {
550
- switch (code) {
551
- case 'plan_path_outside_root':
552
- return 'verify.error.plan_path_outside_root';
553
- case 'input_path_contains_symlink':
554
- return 'verify.error.input_path_contains_symlink';
555
- case 'missing_plan_reasons':
556
- return 'verify.error.missing_plan_reasons';
557
- case 'unsupported_plan_source':
558
- return 'verify.error.unsupported_plan_source';
559
- case 'plan_root_mismatch':
560
- return 'verify.error.plan_root_mismatch';
561
- case 'git_changed_files_unavailable':
562
- return 'verify.error.changed_files_unavailable';
563
- default:
564
- return 'verify.error.invalid_plan_file';
565
- }
566
- }
567
80
  function skippedResult(candidate) {
568
81
  return {
569
82
  intent: candidate.intent || null,
@@ -628,9 +141,10 @@ function testTargetsByScheduledIntent(report) {
628
141
  candidate.appliedTestTargets.length > 0)
629
142
  .map((candidate) => [candidate.intent, candidate.appliedTestTargets]));
630
143
  }
631
- async function runVerificationIntent(intent, lang, verificationPlanId, testTargets = [], additionalDeclaredWritePaths = []) {
144
+ async function runVerificationIntent(intent, lang, verificationPlanId, correlationId, testTargets = [], additionalDeclaredWritePaths = []) {
632
145
  const output = createBufferedOutput();
633
146
  const exitCode = await runRun([intent, '--json'], output.reporter, lang, {
147
+ correlationId,
634
148
  writeLatestReceipt: false,
635
149
  writeLatestProfile: false,
636
150
  recordPerformanceHistory: false,
@@ -672,19 +186,19 @@ function entriesForScheduleBatch(entries, batch) {
672
186
  const batchIntents = new Set(batch.intents);
673
187
  return entries.filter((entry) => batchIntents.has(entry.intent));
674
188
  }
675
- async function runVerificationEntriesSequentially(entries, lang, verificationPlanId, scheduledTestTargets) {
189
+ async function runVerificationEntriesSequentially(entries, lang, verificationPlanId, correlationId, scheduledTestTargets) {
676
190
  const results = [];
677
191
  for (const entry of entries) {
678
- results.push(await runVerificationIntent(entry.intent, lang, verificationPlanId, scheduledTestTargets.get(entry.intent) ?? []));
192
+ results.push(await runVerificationIntent(entry.intent, lang, verificationPlanId, correlationId, scheduledTestTargets.get(entry.intent) ?? []));
679
193
  }
680
194
  return results;
681
195
  }
682
- async function runVerificationEntriesInParallelChunks(projectRoot, entries, parallelism, lang, verificationPlanId, scheduledTestTargets) {
196
+ async function runVerificationEntriesInParallelChunks(projectRoot, entries, parallelism, lang, verificationPlanId, correlationId, scheduledTestTargets) {
683
197
  const results = [];
684
198
  for (let index = 0; index < entries.length; index += parallelism) {
685
199
  const chunk = entries.slice(index, index + parallelism);
686
200
  const batchTracker = startRunWriteBatchTracking(projectRoot);
687
- const chunkResults = await Promise.all(chunk.map((entry) => runVerificationIntent(entry.intent, lang, verificationPlanId, scheduledTestTargets.get(entry.intent) ?? [])));
201
+ const chunkResults = await Promise.all(chunk.map((entry) => runVerificationIntent(entry.intent, lang, verificationPlanId, correlationId, scheduledTestTargets.get(entry.intent) ?? [])));
688
202
  const writeDriftByIntent = finishRunWriteBatchTracking(batchTracker, chunk.map((entry) => ({
689
203
  intentName: entry.intent,
690
204
  declaredPaths: declaredWritePathsForScheduleEntry(entry),
@@ -732,7 +246,7 @@ function verificationResultFailed(result) {
732
246
  result.status === 'start_failed' ||
733
247
  result.status === 'output_limit_exceeded'));
734
248
  }
735
- async function runScheduledVerificationIntents(report, projectRoot, lang, verificationPlanId, scheduledTestTargets, parallelism) {
249
+ async function runScheduledVerificationIntents(report, projectRoot, lang, verificationPlanId, correlationId, scheduledTestTargets, parallelism) {
736
250
  const results = [];
737
251
  for (let batchIndex = 0; batchIndex < report.schedule.batches.length; batchIndex += 1) {
738
252
  const batch = report.schedule.batches[batchIndex];
@@ -744,11 +258,11 @@ async function runScheduledVerificationIntents(report, projectRoot, lang, verifi
744
258
  if (entries.length > 1 && entries.every((entry) => entry.parallelEligible)) {
745
259
  batchResults =
746
260
  parallelism > DEFAULT_VERIFY_PARALLELISM
747
- ? await runVerificationEntriesInParallelChunks(projectRoot, entries, parallelism, lang, verificationPlanId, scheduledTestTargets)
748
- : await runVerificationEntriesSequentially(entries, lang, verificationPlanId, scheduledTestTargets);
261
+ ? await runVerificationEntriesInParallelChunks(projectRoot, entries, parallelism, lang, verificationPlanId, correlationId, scheduledTestTargets)
262
+ : await runVerificationEntriesSequentially(entries, lang, verificationPlanId, correlationId, scheduledTestTargets);
749
263
  }
750
264
  else {
751
- batchResults = await runVerificationEntriesSequentially(entries, lang, verificationPlanId, scheduledTestTargets);
265
+ batchResults = await runVerificationEntriesSequentially(entries, lang, verificationPlanId, correlationId, scheduledTestTargets);
752
266
  }
753
267
  results.push(...batchResults);
754
268
  if (!batchResults.some(verificationResultFailed)) {
@@ -1036,7 +550,7 @@ function readVerificationFailureFingerprint(value) {
1036
550
  }
1037
551
  function readPreviousVerifyLatestSummary(projectRoot) {
1038
552
  try {
1039
- const parsed = JSON.parse(readUtf8FileInsideWithoutSymlinks(projectRoot, path.join(projectRoot, LATEST_RUN_RECEIPT_PATH)));
553
+ const parsed = JSON.parse(readUtf8FileInsideWithoutSymlinks(projectRoot, resolveLatestVerifyRunReceiptPath(projectRoot)));
1040
554
  if (parsed.command !== 'verify' ||
1041
555
  parsed.kind !== 'verify_run_summary' ||
1042
556
  typeof parsed.verification_plan_id !== 'string' ||
@@ -1116,9 +630,8 @@ function writeVerifyRunReceipts(projectRoot, output, report, sourceAnchorRisks,
1116
630
  let receiptSha256 = null;
1117
631
  let receipt = result.receipt;
1118
632
  if (result.intent && result.receipt) {
1119
- const fileName = `${String(index + 1).padStart(3, '0')}-${sanitizeIntentFilePart(result.intent)}.json`;
1120
- const absoluteReceiptPath = path.join(statePaths.absoluteIntentDir, fileName);
1121
- receiptPath = toPosixPath(path.join(statePaths.runDir, 'intents', fileName));
633
+ const receiptStatePath = createVerifyIntentReceiptPath(statePaths, index + 1, result.intent);
634
+ receiptPath = receiptStatePath.receiptPath;
1122
635
  receipt = {
1123
636
  ...result.receipt,
1124
637
  verification_plan_id: output.verification_plan_id,
@@ -1126,7 +639,7 @@ function writeVerifyRunReceipts(projectRoot, output, report, sourceAnchorRisks,
1126
639
  };
1127
640
  const receiptContent = `${JSON.stringify(receipt, null, 2)}\n`;
1128
641
  receiptSha256 = hashTextSha256(receiptContent);
1129
- writeJsonFileInsideWithoutSymlinks(projectRoot, absoluteReceiptPath, receipt);
642
+ writeJsonFileInsideWithoutSymlinks(projectRoot, receiptStatePath.absoluteReceiptPath, receipt);
1130
643
  }
1131
644
  receipts.push({
1132
645
  intent: result.intent,
@@ -1217,6 +730,7 @@ function writeVerifyRunReceipts(projectRoot, output, report, sourceAnchorRisks,
1217
730
  const manifest = {
1218
731
  schema_version: '1',
1219
732
  command: 'verify',
733
+ correlation_id: outputWithReceiptPaths.correlation_id,
1220
734
  reason: outputWithReceiptPaths.reason,
1221
735
  reasons: outputWithReceiptPaths.reasons,
1222
736
  plan_source: outputWithReceiptPaths.plan_source,
@@ -1237,6 +751,7 @@ function writeVerifyRunReceipts(projectRoot, output, report, sourceAnchorRisks,
1237
751
  schema_version: '1',
1238
752
  command: 'verify',
1239
753
  kind: 'verify_run_summary',
754
+ correlation_id: outputWithReceiptPaths.correlation_id,
1240
755
  reason: outputWithReceiptPaths.reason,
1241
756
  reasons: outputWithReceiptPaths.reasons,
1242
757
  plan_source: outputWithReceiptPaths.plan_source,
@@ -1253,7 +768,7 @@ function writeVerifyRunReceipts(projectRoot, output, report, sourceAnchorRisks,
1253
768
  run_dir: statePaths.runDir,
1254
769
  manifest_path: statePaths.manifestPath,
1255
770
  };
1256
- writeJsonFileInsideWithoutSymlinks(projectRoot, path.join(projectRoot, LATEST_RUN_RECEIPT_PATH), latest);
771
+ writeJsonFileInsideWithoutSymlinks(projectRoot, resolveLatestVerifyRunReceiptPath(projectRoot), latest);
1257
772
  return outputWithReceiptPaths;
1258
773
  }
1259
774
  async function createVerifyOutput(input, planSource, projectRoot, lang, reproEvidence = null, externalChecks = [], parallelism = DEFAULT_VERIFY_PARALLELISM, parallelismReport = null) {
@@ -1269,7 +784,7 @@ async function createVerifyOutput(input, planSource, projectRoot, lang, reproEvi
1269
784
  const reproEvidenceRisks = createReproEvidenceRisks(reproEvidence, { verificationPlanId });
1270
785
  const reproEvidenceVerdictEffects = countReproEvidenceVerdictEffects(reproEvidenceRisks);
1271
786
  const externalEvidenceRisks = createExternalEvidenceRisks(externalChecks);
1272
- const results = await runScheduledVerificationIntents(report, projectRoot, lang, verificationPlanId, scheduledTestTargets, parallelism);
787
+ const results = await runScheduledVerificationIntents(report, projectRoot, lang, verificationPlanId, input.correlationId, scheduledTestTargets, parallelism);
1273
788
  results.push(...createSkippedResults(report.candidates, scheduledIntents, report.gaps));
1274
789
  const summary = summarizeResults(results);
1275
790
  const status = getVerificationStatus(summary);
@@ -1325,6 +840,7 @@ async function createVerifyOutput(input, planSource, projectRoot, lang, reproEvi
1325
840
  const output = {
1326
841
  schema_version: VERIFY_SCHEMA_VERSION,
1327
842
  command: 'verify',
843
+ correlation_id: input.correlationId,
1328
844
  mustflow_root: projectRoot,
1329
845
  reason: input.reasons.join(', '),
1330
846
  reasons: input.reasons,
@@ -1359,16 +875,17 @@ async function createPlanOnlyOutput(input, projectRoot) {
1359
875
  return surfaceReadModels.length > 0 ? { ...requirement, surfaceReadModels } : requirement;
1360
876
  });
1361
877
  if (!firstEntry) {
1362
- return { ...report, verification_plan_id: verificationPlanId, requirements };
878
+ return { ...report, correlation_id: input.correlationId, verification_plan_id: verificationPlanId, requirements };
1363
879
  }
1364
880
  const scheduledIntents = Array.from(new Set(report.schedule.entries.map((entry) => entry.intent)));
1365
881
  const graphsByIntent = await readLocalCommandEffectGraphs(projectRoot, scheduledIntents);
1366
882
  const firstGraph = graphsByIntent.get(firstEntry.intent);
1367
883
  if (!firstGraph) {
1368
- return { ...report, verification_plan_id: verificationPlanId, requirements };
884
+ return { ...report, correlation_id: input.correlationId, verification_plan_id: verificationPlanId, requirements };
1369
885
  }
1370
886
  return {
1371
887
  ...report,
888
+ correlation_id: input.correlationId,
1372
889
  verification_plan_id: verificationPlanId,
1373
890
  requirements,
1374
891
  schedule: {
@@ -1475,7 +992,7 @@ export async function runVerify(args, reporter, lang = 'en') {
1475
992
  let externalChecks = [];
1476
993
  try {
1477
994
  if (parsed.writePlan) {
1478
- resolvePlanPath(projectRoot, parsed.writePlan);
995
+ resolveVerifyInputPath(projectRoot, parsed.writePlan);
1479
996
  }
1480
997
  if (parsed.changed) {
1481
998
  const changedInput = createInputFromChanged(projectRoot);
@@ -1487,6 +1004,7 @@ export async function runVerify(args, reporter, lang = 'en') {
1487
1004
  }
1488
1005
  else {
1489
1006
  input = {
1007
+ correlationId: createCorrelationId('verify'),
1490
1008
  reasons: [parsed.reason],
1491
1009
  classificationReport: createSyntheticClassificationReport([parsed.reason]),
1492
1010
  };