mustflow 2.11.0 → 2.17.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/README.md +4 -4
- package/dist/cli/commands/dashboard.js +71 -2
- package/dist/cli/commands/explain-verify.js +11 -1
- package/dist/cli/commands/index.js +9 -0
- package/dist/cli/commands/upgrade.js +3 -1
- package/dist/cli/commands/verify.js +528 -30
- package/dist/cli/commands/version.js +1 -1
- package/dist/cli/i18n/en.js +1 -1
- package/dist/cli/i18n/es.js +1 -1
- package/dist/cli/i18n/fr.js +1 -1
- package/dist/cli/i18n/hi.js +1 -1
- package/dist/cli/lib/local-index/constants.js +1 -1
- package/dist/cli/lib/local-index/index.js +708 -13
- package/dist/cli/lib/npm-version-check.js +71 -1
- package/dist/core/completion-verdict.js +151 -19
- package/dist/core/repeated-failure.js +172 -10
- package/dist/core/repro-evidence.js +119 -38
- package/dist/core/validation-ratchet.js +161 -17
- package/package.json +3 -3
- package/schemas/dashboard-export.schema.json +83 -0
- package/schemas/explain-report.schema.json +173 -1
- package/schemas/latest-run-pointer.schema.json +227 -10
- package/schemas/verify-report.schema.json +227 -10
- package/schemas/verify-run-manifest.schema.json +227 -10
- package/templates/default/manifest.toml +1 -1
|
@@ -353,6 +353,25 @@ function queryRows(database, sql, params = []) {
|
|
|
353
353
|
return row;
|
|
354
354
|
});
|
|
355
355
|
}
|
|
356
|
+
const VALIDATION_RATCHET_RISK_CODES = new Set([
|
|
357
|
+
'related_test_deleted',
|
|
358
|
+
'skip_or_only_marker_present',
|
|
359
|
+
'todo_or_pending_marker_added',
|
|
360
|
+
'assertion_count_decreased',
|
|
361
|
+
'assertion_matcher_weakened',
|
|
362
|
+
'negative_assertion_removed',
|
|
363
|
+
'exception_assertion_removed',
|
|
364
|
+
'snapshot_mass_updated',
|
|
365
|
+
'golden_output_replaced',
|
|
366
|
+
'verification_intent_disabled',
|
|
367
|
+
'verification_required_after_removed',
|
|
368
|
+
'success_exit_codes_widened',
|
|
369
|
+
'command_allows_no_tests',
|
|
370
|
+
'command_forces_snapshot_update',
|
|
371
|
+
'command_hides_failure',
|
|
372
|
+
'coverage_threshold_lowered',
|
|
373
|
+
'test_selection_narrowed',
|
|
374
|
+
]);
|
|
356
375
|
function searchCapabilities(fts5Available) {
|
|
357
376
|
return {
|
|
358
377
|
backend: fts5Available ? SEARCH_BACKEND_FTS5 : SEARCH_BACKEND_TABLE_SCAN,
|
|
@@ -410,6 +429,34 @@ function stringArrayField(record, key) {
|
|
|
410
429
|
function joinedList(values) {
|
|
411
430
|
return [...values].sort((left, right) => left.localeCompare(right)).join(', ');
|
|
412
431
|
}
|
|
432
|
+
function hashJson(value) {
|
|
433
|
+
return sha256Text(JSON.stringify(value));
|
|
434
|
+
}
|
|
435
|
+
function stringListHash(values) {
|
|
436
|
+
const normalized = values.filter((value) => typeof value === 'string' && value.length > 0);
|
|
437
|
+
return normalized.length > 0 ? hashJson([...normalized].sort((left, right) => left.localeCompare(right))) : null;
|
|
438
|
+
}
|
|
439
|
+
function reproObservation(routeId, phase, evidence) {
|
|
440
|
+
const status = stringField(evidence, 'status');
|
|
441
|
+
const outcome = stringField(evidence, 'outcome') ?? status;
|
|
442
|
+
const receiptHash = stringField(evidence, 'receipt_sha256');
|
|
443
|
+
const diagnosticFingerprint = stringField(evidence, 'diagnostic_fingerprint') ??
|
|
444
|
+
stringField(evidence, 'diagnostic_hash') ??
|
|
445
|
+
hashJson({
|
|
446
|
+
phase,
|
|
447
|
+
status,
|
|
448
|
+
outcome,
|
|
449
|
+
summary: stringField(evidence, 'summary'),
|
|
450
|
+
reason: stringField(evidence, 'reason'),
|
|
451
|
+
});
|
|
452
|
+
return {
|
|
453
|
+
routeId,
|
|
454
|
+
phase,
|
|
455
|
+
outcome,
|
|
456
|
+
receiptHash,
|
|
457
|
+
diagnosticFingerprint,
|
|
458
|
+
};
|
|
459
|
+
}
|
|
413
460
|
function evidenceStatusForRunReceipt(latest) {
|
|
414
461
|
return stringField(latest, 'status') ?? (booleanField(latest, 'timed_out') ? 'timed_out' : 'unknown');
|
|
415
462
|
}
|
|
@@ -444,20 +491,38 @@ function createVerificationEvidenceIndex(projectRoot) {
|
|
|
444
491
|
if (!existsSync(latestPath)) {
|
|
445
492
|
return {
|
|
446
493
|
summaries: [],
|
|
494
|
+
verificationPlans: [],
|
|
495
|
+
acceptanceCriteria: [],
|
|
496
|
+
criterionCoverage: [],
|
|
447
497
|
receipts: [],
|
|
498
|
+
commandReceiptSummaries: [],
|
|
448
499
|
coverageStates: [],
|
|
449
500
|
riskSignals: [],
|
|
501
|
+
validationRatchetSignals: [],
|
|
502
|
+
completionVerdictSummaries: [],
|
|
450
503
|
failureFingerprints: [],
|
|
504
|
+
reproRoutes: [],
|
|
505
|
+
reproObservations: [],
|
|
506
|
+
failureFingerprintReadModels: [],
|
|
451
507
|
};
|
|
452
508
|
}
|
|
453
509
|
const latest = readJsonRecord(latestPath);
|
|
454
510
|
if (!latest) {
|
|
455
511
|
return {
|
|
456
512
|
summaries: [],
|
|
513
|
+
verificationPlans: [],
|
|
514
|
+
acceptanceCriteria: [],
|
|
515
|
+
criterionCoverage: [],
|
|
457
516
|
receipts: [],
|
|
517
|
+
commandReceiptSummaries: [],
|
|
458
518
|
coverageStates: [],
|
|
459
519
|
riskSignals: [],
|
|
520
|
+
validationRatchetSignals: [],
|
|
521
|
+
completionVerdictSummaries: [],
|
|
460
522
|
failureFingerprints: [],
|
|
523
|
+
reproRoutes: [],
|
|
524
|
+
reproObservations: [],
|
|
525
|
+
failureFingerprintReadModels: [],
|
|
461
526
|
};
|
|
462
527
|
}
|
|
463
528
|
const sourceHash = sha256Bytes(readFileSync(latestPath));
|
|
@@ -472,7 +537,13 @@ function createVerificationEvidenceIndex(projectRoot) {
|
|
|
472
537
|
const completionStatus = stringField(completionVerdict, 'status');
|
|
473
538
|
const rawReceipts = recordArrayField(evidenceModel, 'receipts');
|
|
474
539
|
const rawCoverage = recordArrayField(evidenceModel, 'coverage_matrix');
|
|
540
|
+
const rawRequirements = recordArrayField(evidenceModel, 'requirements');
|
|
475
541
|
const rawRisks = recordArrayField(evidenceModel, 'remaining_risks');
|
|
542
|
+
const recordedFailureFingerprintRecord = recordField(latest, 'failure_fingerprint');
|
|
543
|
+
const repeatedFailureSummary = recordField(latest, 'repeated_failure_summary');
|
|
544
|
+
const reproEvidence = recordField(latest, 'repro_evidence') ?? recordField(evidenceModel, 'repro_evidence');
|
|
545
|
+
const reproductionRoute = recordField(reproEvidence, 'reproduction_route');
|
|
546
|
+
const recordedFailureFingerprint = stringField(recordedFailureFingerprintRecord, 'fingerprint');
|
|
476
547
|
const receipts = rawReceipts.length > 0
|
|
477
548
|
? rawReceipts.map((receipt, index) => ({
|
|
478
549
|
sourcePath: LATEST_RUN_STATE_RELATIVE_PATH,
|
|
@@ -483,6 +554,12 @@ function createVerificationEvidenceIndex(projectRoot) {
|
|
|
483
554
|
verificationPlanId: stringField(receipt, 'verification_plan_id'),
|
|
484
555
|
receiptPath: stringField(receipt, 'receipt_path'),
|
|
485
556
|
receiptSha256: stringField(receipt, 'receipt_sha256'),
|
|
557
|
+
commandFingerprint: stringField(receipt, 'command_fingerprint'),
|
|
558
|
+
contractFingerprint: stringField(receipt, 'contract_fingerprint'),
|
|
559
|
+
currentStateHash: stringField(receipt, 'head_tree_hash') ??
|
|
560
|
+
stringField(receipt, 'changed_files_hash') ??
|
|
561
|
+
stringField(receipt, 'changed_file_hash'),
|
|
562
|
+
writeDriftStatus: stringField(receipt, 'write_drift_status'),
|
|
486
563
|
}))
|
|
487
564
|
: [
|
|
488
565
|
{
|
|
@@ -494,6 +571,10 @@ function createVerificationEvidenceIndex(projectRoot) {
|
|
|
494
571
|
verificationPlanId: null,
|
|
495
572
|
receiptPath: stringField(latest, 'receipt_path') ?? LATEST_RUN_STATE_RELATIVE_PATH,
|
|
496
573
|
receiptSha256: sourceHash,
|
|
574
|
+
commandFingerprint: stringField(recordField(latest, 'performance'), 'command_fingerprint'),
|
|
575
|
+
contractFingerprint: stringField(recordField(latest, 'performance'), 'contract_fingerprint'),
|
|
576
|
+
currentStateHash: stringField(latest, 'head_tree_hash') ?? stringField(latest, 'changed_files_hash'),
|
|
577
|
+
writeDriftStatus: stringField(recordField(latest, 'write_drift'), 'status'),
|
|
497
578
|
},
|
|
498
579
|
];
|
|
499
580
|
const coverageStates = rawCoverage.map((coverage) => {
|
|
@@ -517,19 +598,146 @@ function createVerificationEvidenceIndex(projectRoot) {
|
|
|
517
598
|
severity: stringField(risk, 'severity') ?? 'unknown',
|
|
518
599
|
detailHash: sha256Text(stringField(risk, 'detail') ?? ''),
|
|
519
600
|
}));
|
|
601
|
+
const validationRatchetSignals = rawRisks
|
|
602
|
+
.map((risk, index) => {
|
|
603
|
+
const code = stringField(risk, 'code') ?? 'unknown';
|
|
604
|
+
if (!VALIDATION_RATCHET_RISK_CODES.has(code)) {
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
const severity = stringField(risk, 'severity') ?? 'unknown';
|
|
608
|
+
const pathValue = stringField(risk, 'path');
|
|
609
|
+
const detailHash = sha256Text(stringField(risk, 'detail') ?? '');
|
|
610
|
+
const pathHash = pathValue === null ? hashJson({ code, detailHash }) : sha256Text(pathValue);
|
|
611
|
+
const beforeHash = stringField(risk, 'before_hash') ?? stringField(risk, 'before_digest');
|
|
612
|
+
const afterHash = stringField(risk, 'after_hash') ?? stringField(risk, 'after_digest');
|
|
613
|
+
return {
|
|
614
|
+
signalId: hashJson({
|
|
615
|
+
sourcePath: LATEST_RUN_STATE_RELATIVE_PATH,
|
|
616
|
+
ordinal: index + 1,
|
|
617
|
+
planId: verificationPlanId,
|
|
618
|
+
code,
|
|
619
|
+
pathHash,
|
|
620
|
+
beforeHash,
|
|
621
|
+
afterHash,
|
|
622
|
+
}),
|
|
623
|
+
planId: verificationPlanId,
|
|
624
|
+
code,
|
|
625
|
+
severity,
|
|
626
|
+
pathHash,
|
|
627
|
+
beforeHash,
|
|
628
|
+
afterHash,
|
|
629
|
+
};
|
|
630
|
+
})
|
|
631
|
+
.filter((signal) => signal !== null);
|
|
632
|
+
const verificationPlans = verificationPlanId === null
|
|
633
|
+
? []
|
|
634
|
+
: [
|
|
635
|
+
{
|
|
636
|
+
planId: verificationPlanId,
|
|
637
|
+
sourcePath: LATEST_RUN_STATE_RELATIVE_PATH,
|
|
638
|
+
classificationHash: rawRequirements.length > 0 || rawCoverage.length > 0
|
|
639
|
+
? hashJson({
|
|
640
|
+
requirements: rawRequirements.map((requirement) => ({
|
|
641
|
+
id: stringField(requirement, 'requirement_id') ?? stringField(requirement, 'id'),
|
|
642
|
+
reason: stringField(requirement, 'reason'),
|
|
643
|
+
source: stringField(requirement, 'source'),
|
|
644
|
+
})),
|
|
645
|
+
coverage: rawCoverage.map((coverage) => ({
|
|
646
|
+
id: stringField(coverage, 'criterion_id'),
|
|
647
|
+
reason: stringField(coverage, 'requirement_reason'),
|
|
648
|
+
source: stringField(coverage, 'source'),
|
|
649
|
+
status: stringField(coverage, 'status'),
|
|
650
|
+
})),
|
|
651
|
+
})
|
|
652
|
+
: null,
|
|
653
|
+
commandContractHash: stringListHash(receipts.map((receipt) => receipt.contractFingerprint)),
|
|
654
|
+
selectedIntentsHash: stringListHash(receipts.map((receipt) => receipt.intent)),
|
|
655
|
+
createdAt: stringField(latest, 'started_at') ?? stringField(latest, 'created_at'),
|
|
656
|
+
sourceHash,
|
|
657
|
+
},
|
|
658
|
+
];
|
|
659
|
+
const acceptanceCriteria = verificationPlanId === null
|
|
660
|
+
? []
|
|
661
|
+
: rawCoverage.map((coverage) => {
|
|
662
|
+
const evidence = recordField(coverage, 'evidence');
|
|
663
|
+
const pathRefs = [
|
|
664
|
+
...stringArrayField(evidence, 'paths'),
|
|
665
|
+
...stringArrayField(evidence, 'changed_paths'),
|
|
666
|
+
...stringArrayField(evidence, 'source_anchor_ids'),
|
|
667
|
+
];
|
|
668
|
+
return {
|
|
669
|
+
criterionId: stringField(coverage, 'criterion_id') ?? 'unknown',
|
|
670
|
+
planId: verificationPlanId,
|
|
671
|
+
source: stringField(coverage, 'source') ?? 'unknown',
|
|
672
|
+
statementHash: stringField(coverage, 'statement') ? sha256Text(stringField(coverage, 'statement') ?? '') : null,
|
|
673
|
+
reason: stringField(coverage, 'requirement_reason'),
|
|
674
|
+
surface: stringField(coverage, 'surface'),
|
|
675
|
+
pathHash: pathRefs.length > 0 ? stringListHash(pathRefs) : null,
|
|
676
|
+
};
|
|
677
|
+
});
|
|
678
|
+
const criterionCoverage = verificationPlanId === null
|
|
679
|
+
? []
|
|
680
|
+
: coverageStates.map((coverage) => ({
|
|
681
|
+
criterionId: coverage.criterionId,
|
|
682
|
+
planId: verificationPlanId,
|
|
683
|
+
status: coverage.status,
|
|
684
|
+
receiptCount: coverage.receiptCount,
|
|
685
|
+
gapCount: coverage.gapCount,
|
|
686
|
+
riskCount: coverage.sourceAnchorCount,
|
|
687
|
+
}));
|
|
688
|
+
const commandReceiptSummaries = verificationPlanId === null
|
|
689
|
+
? []
|
|
690
|
+
: receipts
|
|
691
|
+
.filter((receipt) => receipt.verificationPlanId === verificationPlanId || receipt.verificationPlanId === null)
|
|
692
|
+
.map((receipt) => ({
|
|
693
|
+
receiptHash: receipt.receiptSha256 ??
|
|
694
|
+
hashJson({
|
|
695
|
+
sourcePath: receipt.sourcePath,
|
|
696
|
+
ordinal: receipt.ordinal,
|
|
697
|
+
intent: receipt.intent,
|
|
698
|
+
status: receipt.status,
|
|
699
|
+
verificationPlanId,
|
|
700
|
+
}),
|
|
701
|
+
planId: verificationPlanId,
|
|
702
|
+
intent: receipt.intent,
|
|
703
|
+
status: receipt.status,
|
|
704
|
+
commandFingerprint: receipt.commandFingerprint,
|
|
705
|
+
contractFingerprint: receipt.contractFingerprint,
|
|
706
|
+
currentStateHash: receipt.currentStateHash,
|
|
707
|
+
writeDriftStatus: receipt.writeDriftStatus,
|
|
708
|
+
}));
|
|
709
|
+
const completionVerdictSummaries = verificationPlanId === null || completionStatus === null
|
|
710
|
+
? []
|
|
711
|
+
: [
|
|
712
|
+
{
|
|
713
|
+
claimId: hashJson({
|
|
714
|
+
sourceHash,
|
|
715
|
+
verificationPlanId,
|
|
716
|
+
completionStatus,
|
|
717
|
+
primaryReason,
|
|
718
|
+
}),
|
|
719
|
+
planId: verificationPlanId,
|
|
720
|
+
status: completionStatus,
|
|
721
|
+
primaryReason,
|
|
722
|
+
riskCount: riskSignals.length,
|
|
723
|
+
contradictionCount: stringArrayField(completionVerdict, 'contradictions').length,
|
|
724
|
+
blockerCount: stringArrayField(completionVerdict, 'blockers').length,
|
|
725
|
+
},
|
|
726
|
+
];
|
|
520
727
|
const failedIntents = failedIntentsFromReceipts(receipts);
|
|
521
|
-
const failureFingerprint =
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
728
|
+
const failureFingerprint = recordedFailureFingerprint ??
|
|
729
|
+
createFailureFingerprint({
|
|
730
|
+
command,
|
|
731
|
+
status: completionStatus ?? status,
|
|
732
|
+
verificationPlanId,
|
|
733
|
+
primaryReason,
|
|
734
|
+
failedIntents,
|
|
735
|
+
riskCodes: riskSignals.map((risk) => risk.code),
|
|
736
|
+
runIntent: stringField(latest, 'intent'),
|
|
737
|
+
timedOut: booleanField(latest, 'timed_out'),
|
|
738
|
+
exitCodeClass: stringField(recordField(recordField(latest, 'performance'), 'result_summary'), 'exit_code_class'),
|
|
739
|
+
errorKind: stringField(recordField(recordField(latest, 'performance'), 'result_summary'), 'error_kind'),
|
|
740
|
+
});
|
|
533
741
|
const failureFingerprints = failureFingerprint === null
|
|
534
742
|
? []
|
|
535
743
|
: [
|
|
@@ -540,8 +748,50 @@ function createVerificationEvidenceIndex(projectRoot) {
|
|
|
540
748
|
status: completionStatus ?? status,
|
|
541
749
|
failedIntents,
|
|
542
750
|
primaryReason,
|
|
751
|
+
failedIntentsHash: stringField(recordedFailureFingerprintRecord, 'failed_intents_hash') ??
|
|
752
|
+
stringField(repeatedFailureSummary, 'failed_intents_hash'),
|
|
753
|
+
riskCodesHash: stringField(recordedFailureFingerprintRecord, 'risk_codes_hash') ??
|
|
754
|
+
stringField(repeatedFailureSummary, 'risk_codes_hash'),
|
|
755
|
+
affectedSurfacesHash: stringField(recordedFailureFingerprintRecord, 'affected_surfaces_hash') ??
|
|
756
|
+
stringField(repeatedFailureSummary, 'affected_surfaces_hash'),
|
|
757
|
+
firstSeenAt: stringField(repeatedFailureSummary, 'first_seen_at'),
|
|
758
|
+
lastSeenAt: stringField(repeatedFailureSummary, 'last_seen_at'),
|
|
759
|
+
seenCount: Math.max(1, numberField(repeatedFailureSummary, 'seen_count')),
|
|
760
|
+
requiresNewEvidence: booleanField(repeatedFailureSummary, 'requires_new_evidence'),
|
|
543
761
|
},
|
|
544
762
|
];
|
|
763
|
+
const routeId = stringField(reproductionRoute, 'route_id');
|
|
764
|
+
const reproRoutes = routeId === null || reproEvidence === null
|
|
765
|
+
? []
|
|
766
|
+
: [
|
|
767
|
+
{
|
|
768
|
+
routeId,
|
|
769
|
+
taskHash: hashJson({
|
|
770
|
+
reported_symptom: stringField(reproEvidence, 'reported_symptom'),
|
|
771
|
+
expected_behavior: stringField(reproEvidence, 'expected_behavior'),
|
|
772
|
+
observed_behavior: stringField(reproEvidence, 'observed_behavior'),
|
|
773
|
+
}),
|
|
774
|
+
routeDigest: stringField(reproductionRoute, 'route_digest'),
|
|
775
|
+
routeKind: stringField(reproductionRoute, 'route_kind'),
|
|
776
|
+
failureOracleHash: stringField(reproductionRoute, 'failure_oracle_hash'),
|
|
777
|
+
},
|
|
778
|
+
];
|
|
779
|
+
const reproObservations = routeId === null || reproEvidence === null
|
|
780
|
+
? []
|
|
781
|
+
: [
|
|
782
|
+
reproObservation(routeId, 'before_fix', recordField(reproEvidence, 'before_fix')),
|
|
783
|
+
reproObservation(routeId, 'after_fix', recordField(reproEvidence, 'after_fix')),
|
|
784
|
+
reproObservation(routeId, 'regression_guard', recordField(reproEvidence, 'regression_guard')),
|
|
785
|
+
];
|
|
786
|
+
const failureFingerprintReadModels = failureFingerprints.map((fingerprint) => ({
|
|
787
|
+
fingerprint: fingerprint.fingerprint,
|
|
788
|
+
planId: fingerprint.verificationPlanId,
|
|
789
|
+
failedIntentsHash: fingerprint.failedIntentsHash ?? stringListHash(fingerprint.failedIntents),
|
|
790
|
+
riskCodesHash: fingerprint.riskCodesHash,
|
|
791
|
+
seenCount: fingerprint.seenCount,
|
|
792
|
+
firstSeenAt: fingerprint.firstSeenAt,
|
|
793
|
+
lastSeenAt: fingerprint.lastSeenAt,
|
|
794
|
+
}));
|
|
545
795
|
return {
|
|
546
796
|
summaries: [
|
|
547
797
|
{
|
|
@@ -566,10 +816,19 @@ function createVerificationEvidenceIndex(projectRoot) {
|
|
|
566
816
|
failureFingerprint,
|
|
567
817
|
},
|
|
568
818
|
],
|
|
819
|
+
verificationPlans,
|
|
820
|
+
acceptanceCriteria,
|
|
821
|
+
criterionCoverage,
|
|
569
822
|
receipts,
|
|
823
|
+
commandReceiptSummaries,
|
|
570
824
|
coverageStates,
|
|
571
825
|
riskSignals,
|
|
826
|
+
validationRatchetSignals,
|
|
827
|
+
completionVerdictSummaries,
|
|
572
828
|
failureFingerprints,
|
|
829
|
+
reproRoutes,
|
|
830
|
+
reproObservations,
|
|
831
|
+
failureFingerprintReadModels,
|
|
573
832
|
};
|
|
574
833
|
}
|
|
575
834
|
function readMetadataValue(database, key) {
|
|
@@ -1032,6 +1291,37 @@ CREATE TABLE verification_evidence_summaries (
|
|
|
1032
1291
|
failure_fingerprint TEXT
|
|
1033
1292
|
);
|
|
1034
1293
|
|
|
1294
|
+
CREATE TABLE verification_plans (
|
|
1295
|
+
plan_id TEXT PRIMARY KEY,
|
|
1296
|
+
source_path TEXT NOT NULL,
|
|
1297
|
+
classification_hash TEXT,
|
|
1298
|
+
command_contract_hash TEXT,
|
|
1299
|
+
selected_intents_hash TEXT,
|
|
1300
|
+
created_at TEXT,
|
|
1301
|
+
source_hash TEXT NOT NULL
|
|
1302
|
+
);
|
|
1303
|
+
|
|
1304
|
+
CREATE TABLE acceptance_criteria (
|
|
1305
|
+
criterion_id TEXT NOT NULL,
|
|
1306
|
+
plan_id TEXT NOT NULL,
|
|
1307
|
+
source TEXT NOT NULL,
|
|
1308
|
+
statement_hash TEXT,
|
|
1309
|
+
reason TEXT,
|
|
1310
|
+
surface TEXT,
|
|
1311
|
+
path_hash TEXT,
|
|
1312
|
+
PRIMARY KEY (plan_id, criterion_id)
|
|
1313
|
+
);
|
|
1314
|
+
|
|
1315
|
+
CREATE TABLE criterion_coverage (
|
|
1316
|
+
criterion_id TEXT NOT NULL,
|
|
1317
|
+
plan_id TEXT NOT NULL,
|
|
1318
|
+
status TEXT NOT NULL,
|
|
1319
|
+
receipt_count INTEGER NOT NULL,
|
|
1320
|
+
gap_count INTEGER NOT NULL,
|
|
1321
|
+
risk_count INTEGER NOT NULL,
|
|
1322
|
+
PRIMARY KEY (plan_id, criterion_id)
|
|
1323
|
+
);
|
|
1324
|
+
|
|
1035
1325
|
CREATE TABLE verification_receipt_summaries (
|
|
1036
1326
|
source_path TEXT NOT NULL,
|
|
1037
1327
|
ordinal INTEGER NOT NULL,
|
|
@@ -1044,6 +1334,18 @@ CREATE TABLE verification_receipt_summaries (
|
|
|
1044
1334
|
PRIMARY KEY (source_path, ordinal)
|
|
1045
1335
|
);
|
|
1046
1336
|
|
|
1337
|
+
CREATE TABLE command_receipt_summaries (
|
|
1338
|
+
receipt_hash TEXT NOT NULL,
|
|
1339
|
+
plan_id TEXT NOT NULL,
|
|
1340
|
+
intent TEXT,
|
|
1341
|
+
status TEXT NOT NULL,
|
|
1342
|
+
command_fingerprint TEXT,
|
|
1343
|
+
contract_fingerprint TEXT,
|
|
1344
|
+
current_state_hash TEXT,
|
|
1345
|
+
write_drift_status TEXT,
|
|
1346
|
+
PRIMARY KEY (plan_id, receipt_hash)
|
|
1347
|
+
);
|
|
1348
|
+
|
|
1047
1349
|
CREATE TABLE verification_coverage_states (
|
|
1048
1350
|
source_path TEXT NOT NULL,
|
|
1049
1351
|
criterion_id TEXT NOT NULL,
|
|
@@ -1066,6 +1368,53 @@ CREATE TABLE verification_risk_signals (
|
|
|
1066
1368
|
PRIMARY KEY (source_path, ordinal)
|
|
1067
1369
|
);
|
|
1068
1370
|
|
|
1371
|
+
CREATE TABLE validation_ratchet_signals (
|
|
1372
|
+
signal_id TEXT PRIMARY KEY,
|
|
1373
|
+
plan_id TEXT,
|
|
1374
|
+
code TEXT NOT NULL,
|
|
1375
|
+
severity TEXT NOT NULL,
|
|
1376
|
+
path_hash TEXT NOT NULL,
|
|
1377
|
+
before_hash TEXT,
|
|
1378
|
+
after_hash TEXT
|
|
1379
|
+
);
|
|
1380
|
+
|
|
1381
|
+
CREATE TABLE completion_verdict_summaries (
|
|
1382
|
+
claim_id TEXT PRIMARY KEY,
|
|
1383
|
+
plan_id TEXT NOT NULL,
|
|
1384
|
+
status TEXT NOT NULL,
|
|
1385
|
+
primary_reason TEXT,
|
|
1386
|
+
risk_count INTEGER NOT NULL,
|
|
1387
|
+
contradiction_count INTEGER NOT NULL,
|
|
1388
|
+
blocker_count INTEGER NOT NULL
|
|
1389
|
+
);
|
|
1390
|
+
|
|
1391
|
+
CREATE TABLE repro_routes (
|
|
1392
|
+
route_id TEXT PRIMARY KEY,
|
|
1393
|
+
task_hash TEXT NOT NULL,
|
|
1394
|
+
route_digest TEXT,
|
|
1395
|
+
route_kind TEXT,
|
|
1396
|
+
failure_oracle_hash TEXT
|
|
1397
|
+
);
|
|
1398
|
+
|
|
1399
|
+
CREATE TABLE repro_observations (
|
|
1400
|
+
route_id TEXT NOT NULL,
|
|
1401
|
+
phase TEXT NOT NULL,
|
|
1402
|
+
outcome TEXT,
|
|
1403
|
+
receipt_hash TEXT,
|
|
1404
|
+
diagnostic_fingerprint TEXT NOT NULL,
|
|
1405
|
+
PRIMARY KEY (route_id, phase)
|
|
1406
|
+
);
|
|
1407
|
+
|
|
1408
|
+
CREATE TABLE failure_fingerprints (
|
|
1409
|
+
fingerprint TEXT PRIMARY KEY,
|
|
1410
|
+
plan_id TEXT,
|
|
1411
|
+
failed_intents_hash TEXT,
|
|
1412
|
+
risk_codes_hash TEXT,
|
|
1413
|
+
seen_count INTEGER NOT NULL,
|
|
1414
|
+
first_seen_at TEXT,
|
|
1415
|
+
last_seen_at TEXT
|
|
1416
|
+
);
|
|
1417
|
+
|
|
1069
1418
|
CREATE TABLE verification_failure_fingerprints (
|
|
1070
1419
|
source_path TEXT NOT NULL,
|
|
1071
1420
|
fingerprint TEXT NOT NULL,
|
|
@@ -1073,8 +1422,25 @@ CREATE TABLE verification_failure_fingerprints (
|
|
|
1073
1422
|
status TEXT NOT NULL,
|
|
1074
1423
|
failed_intents TEXT NOT NULL,
|
|
1075
1424
|
primary_reason TEXT,
|
|
1425
|
+
failed_intents_hash TEXT,
|
|
1426
|
+
risk_codes_hash TEXT,
|
|
1427
|
+
affected_surfaces_hash TEXT,
|
|
1428
|
+
first_seen_at TEXT,
|
|
1429
|
+
last_seen_at TEXT,
|
|
1430
|
+
seen_count INTEGER NOT NULL,
|
|
1431
|
+
requires_new_evidence INTEGER NOT NULL,
|
|
1076
1432
|
PRIMARY KEY (source_path, fingerprint)
|
|
1077
1433
|
);
|
|
1434
|
+
|
|
1435
|
+
CREATE TABLE source_anchor_risk_signals (
|
|
1436
|
+
anchor_id TEXT PRIMARY KEY,
|
|
1437
|
+
path_hash TEXT NOT NULL,
|
|
1438
|
+
status TEXT NOT NULL,
|
|
1439
|
+
risk_signal TEXT NOT NULL,
|
|
1440
|
+
confidence REAL NOT NULL,
|
|
1441
|
+
navigation_only INTEGER NOT NULL,
|
|
1442
|
+
can_instruct_agent INTEGER NOT NULL
|
|
1443
|
+
);
|
|
1078
1444
|
`);
|
|
1079
1445
|
if (capabilities.backend === SEARCH_BACKEND_FTS5) {
|
|
1080
1446
|
database.run(`
|
|
@@ -1258,7 +1624,21 @@ function populateSearchTables(database, capabilities, documents, skills, skillRo
|
|
|
1258
1624
|
}
|
|
1259
1625
|
}
|
|
1260
1626
|
}
|
|
1627
|
+
function createSourceAnchorRiskSignals(sourceAnchors) {
|
|
1628
|
+
return sourceAnchors
|
|
1629
|
+
.filter((anchor) => ['changed', 'review', 'stale'].includes(anchor.status))
|
|
1630
|
+
.map((anchor) => ({
|
|
1631
|
+
anchorId: anchor.id,
|
|
1632
|
+
pathHash: sha256Text(anchor.path),
|
|
1633
|
+
status: anchor.status,
|
|
1634
|
+
riskSignal: anchor.signals.risk,
|
|
1635
|
+
confidence: anchor.confidence,
|
|
1636
|
+
navigationOnly: anchor.navigationOnly,
|
|
1637
|
+
canInstructAgent: anchor.canInstructAgent,
|
|
1638
|
+
}));
|
|
1639
|
+
}
|
|
1261
1640
|
function populateDatabase(database, capabilities, documents, skills, skillRoutes, commandIntents, sourceAnchors, indexedFiles, verificationEvidence, indexMode, sourceScopeHash, sourceIndexEnabled, indexedAt) {
|
|
1641
|
+
const sourceAnchorRiskSignals = createSourceAnchorRiskSignals(sourceAnchors);
|
|
1262
1642
|
database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', ['schema_version', LOCAL_INDEX_SCHEMA_VERSION]);
|
|
1263
1643
|
database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', ['parser_version', LOCAL_INDEX_PARSER_VERSION]);
|
|
1264
1644
|
database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', ['content_mode', LOCAL_INDEX_CONTENT_MODE]);
|
|
@@ -1418,6 +1798,31 @@ function populateDatabase(database, capabilities, documents, skills, skillRoutes
|
|
|
1418
1798
|
summary.failureFingerprint,
|
|
1419
1799
|
]);
|
|
1420
1800
|
}
|
|
1801
|
+
for (const plan of verificationEvidence.verificationPlans) {
|
|
1802
|
+
database.run('INSERT INTO verification_plans (plan_id, source_path, classification_hash, command_contract_hash, selected_intents_hash, created_at, source_hash) VALUES (?, ?, ?, ?, ?, ?, ?)', [
|
|
1803
|
+
plan.planId,
|
|
1804
|
+
plan.sourcePath,
|
|
1805
|
+
plan.classificationHash,
|
|
1806
|
+
plan.commandContractHash,
|
|
1807
|
+
plan.selectedIntentsHash,
|
|
1808
|
+
plan.createdAt,
|
|
1809
|
+
plan.sourceHash,
|
|
1810
|
+
]);
|
|
1811
|
+
}
|
|
1812
|
+
for (const criterion of verificationEvidence.acceptanceCriteria) {
|
|
1813
|
+
database.run('INSERT INTO acceptance_criteria (criterion_id, plan_id, source, statement_hash, reason, surface, path_hash) VALUES (?, ?, ?, ?, ?, ?, ?)', [
|
|
1814
|
+
criterion.criterionId,
|
|
1815
|
+
criterion.planId,
|
|
1816
|
+
criterion.source,
|
|
1817
|
+
criterion.statementHash,
|
|
1818
|
+
criterion.reason,
|
|
1819
|
+
criterion.surface,
|
|
1820
|
+
criterion.pathHash,
|
|
1821
|
+
]);
|
|
1822
|
+
}
|
|
1823
|
+
for (const coverage of verificationEvidence.criterionCoverage) {
|
|
1824
|
+
database.run('INSERT INTO criterion_coverage (criterion_id, plan_id, status, receipt_count, gap_count, risk_count) VALUES (?, ?, ?, ?, ?, ?)', [coverage.criterionId, coverage.planId, coverage.status, coverage.receiptCount, coverage.gapCount, coverage.riskCount]);
|
|
1825
|
+
}
|
|
1421
1826
|
for (const receipt of verificationEvidence.receipts) {
|
|
1422
1827
|
database.run('INSERT INTO verification_receipt_summaries (source_path, ordinal, intent, status, skipped, verification_plan_id, receipt_path, receipt_sha256) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
1423
1828
|
receipt.sourcePath,
|
|
@@ -1430,6 +1835,18 @@ function populateDatabase(database, capabilities, documents, skills, skillRoutes
|
|
|
1430
1835
|
receipt.receiptSha256,
|
|
1431
1836
|
]);
|
|
1432
1837
|
}
|
|
1838
|
+
for (const receipt of verificationEvidence.commandReceiptSummaries) {
|
|
1839
|
+
database.run('INSERT INTO command_receipt_summaries (receipt_hash, plan_id, intent, status, command_fingerprint, contract_fingerprint, current_state_hash, write_drift_status) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
1840
|
+
receipt.receiptHash,
|
|
1841
|
+
receipt.planId,
|
|
1842
|
+
receipt.intent,
|
|
1843
|
+
receipt.status,
|
|
1844
|
+
receipt.commandFingerprint,
|
|
1845
|
+
receipt.contractFingerprint,
|
|
1846
|
+
receipt.currentStateHash,
|
|
1847
|
+
receipt.writeDriftStatus,
|
|
1848
|
+
]);
|
|
1849
|
+
}
|
|
1433
1850
|
for (const coverage of verificationEvidence.coverageStates) {
|
|
1434
1851
|
database.run('INSERT INTO verification_coverage_states (source_path, criterion_id, source, status, requirement_reason, intents, receipt_count, gap_count, source_anchor_count) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
1435
1852
|
coverage.sourcePath,
|
|
@@ -1446,14 +1863,69 @@ function populateDatabase(database, capabilities, documents, skills, skillRoutes
|
|
|
1446
1863
|
for (const risk of verificationEvidence.riskSignals) {
|
|
1447
1864
|
database.run('INSERT INTO verification_risk_signals (source_path, ordinal, code, severity, detail_hash) VALUES (?, ?, ?, ?, ?)', [risk.sourcePath, risk.ordinal, risk.code, risk.severity, risk.detailHash]);
|
|
1448
1865
|
}
|
|
1866
|
+
for (const signal of verificationEvidence.validationRatchetSignals) {
|
|
1867
|
+
database.run('INSERT INTO validation_ratchet_signals (signal_id, plan_id, code, severity, path_hash, before_hash, after_hash) VALUES (?, ?, ?, ?, ?, ?, ?)', [signal.signalId, signal.planId, signal.code, signal.severity, signal.pathHash, signal.beforeHash, signal.afterHash]);
|
|
1868
|
+
}
|
|
1869
|
+
for (const verdict of verificationEvidence.completionVerdictSummaries) {
|
|
1870
|
+
database.run('INSERT INTO completion_verdict_summaries (claim_id, plan_id, status, primary_reason, risk_count, contradiction_count, blocker_count) VALUES (?, ?, ?, ?, ?, ?, ?)', [
|
|
1871
|
+
verdict.claimId,
|
|
1872
|
+
verdict.planId,
|
|
1873
|
+
verdict.status,
|
|
1874
|
+
verdict.primaryReason,
|
|
1875
|
+
verdict.riskCount,
|
|
1876
|
+
verdict.contradictionCount,
|
|
1877
|
+
verdict.blockerCount,
|
|
1878
|
+
]);
|
|
1879
|
+
}
|
|
1880
|
+
for (const route of verificationEvidence.reproRoutes) {
|
|
1881
|
+
database.run('INSERT INTO repro_routes (route_id, task_hash, route_digest, route_kind, failure_oracle_hash) VALUES (?, ?, ?, ?, ?)', [route.routeId, route.taskHash, route.routeDigest, route.routeKind, route.failureOracleHash]);
|
|
1882
|
+
}
|
|
1883
|
+
for (const observation of verificationEvidence.reproObservations) {
|
|
1884
|
+
database.run('INSERT INTO repro_observations (route_id, phase, outcome, receipt_hash, diagnostic_fingerprint) VALUES (?, ?, ?, ?, ?)', [
|
|
1885
|
+
observation.routeId,
|
|
1886
|
+
observation.phase,
|
|
1887
|
+
observation.outcome,
|
|
1888
|
+
observation.receiptHash,
|
|
1889
|
+
observation.diagnosticFingerprint,
|
|
1890
|
+
]);
|
|
1891
|
+
}
|
|
1892
|
+
for (const fingerprint of verificationEvidence.failureFingerprintReadModels) {
|
|
1893
|
+
database.run('INSERT INTO failure_fingerprints (fingerprint, plan_id, failed_intents_hash, risk_codes_hash, seen_count, first_seen_at, last_seen_at) VALUES (?, ?, ?, ?, ?, ?, ?)', [
|
|
1894
|
+
fingerprint.fingerprint,
|
|
1895
|
+
fingerprint.planId,
|
|
1896
|
+
fingerprint.failedIntentsHash,
|
|
1897
|
+
fingerprint.riskCodesHash,
|
|
1898
|
+
fingerprint.seenCount,
|
|
1899
|
+
fingerprint.firstSeenAt,
|
|
1900
|
+
fingerprint.lastSeenAt,
|
|
1901
|
+
]);
|
|
1902
|
+
}
|
|
1449
1903
|
for (const fingerprint of verificationEvidence.failureFingerprints) {
|
|
1450
|
-
database.run('INSERT INTO verification_failure_fingerprints (source_path, fingerprint, verification_plan_id, status, failed_intents, primary_reason) VALUES (?, ?, ?, ?, ?, ?)', [
|
|
1904
|
+
database.run('INSERT INTO verification_failure_fingerprints (source_path, fingerprint, verification_plan_id, status, failed_intents, primary_reason, failed_intents_hash, risk_codes_hash, affected_surfaces_hash, first_seen_at, last_seen_at, seen_count, requires_new_evidence) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
1451
1905
|
fingerprint.sourcePath,
|
|
1452
1906
|
fingerprint.fingerprint,
|
|
1453
1907
|
fingerprint.verificationPlanId,
|
|
1454
1908
|
fingerprint.status,
|
|
1455
1909
|
joinedList(fingerprint.failedIntents),
|
|
1456
1910
|
fingerprint.primaryReason,
|
|
1911
|
+
fingerprint.failedIntentsHash,
|
|
1912
|
+
fingerprint.riskCodesHash,
|
|
1913
|
+
fingerprint.affectedSurfacesHash,
|
|
1914
|
+
fingerprint.firstSeenAt,
|
|
1915
|
+
fingerprint.lastSeenAt,
|
|
1916
|
+
fingerprint.seenCount,
|
|
1917
|
+
fingerprint.requiresNewEvidence ? 1 : 0,
|
|
1918
|
+
]);
|
|
1919
|
+
}
|
|
1920
|
+
for (const signal of sourceAnchorRiskSignals) {
|
|
1921
|
+
database.run('INSERT INTO source_anchor_risk_signals (anchor_id, path_hash, status, risk_signal, confidence, navigation_only, can_instruct_agent) VALUES (?, ?, ?, ?, ?, ?, ?)', [
|
|
1922
|
+
signal.anchorId,
|
|
1923
|
+
signal.pathHash,
|
|
1924
|
+
signal.status,
|
|
1925
|
+
signal.riskSignal,
|
|
1926
|
+
signal.confidence,
|
|
1927
|
+
signal.navigationOnly ? 1 : 0,
|
|
1928
|
+
signal.canInstructAgent ? 1 : 0,
|
|
1457
1929
|
]);
|
|
1458
1930
|
}
|
|
1459
1931
|
populatePathSurfaceReadModel(database);
|
|
@@ -1582,12 +2054,21 @@ export async function createLocalIndex(projectRoot, options = {}) {
|
|
|
1582
2054
|
command_intent_count: commandIntents.length,
|
|
1583
2055
|
command_effect_count: commandIntents.reduce((count, intent) => count + intent.effects.length, 0),
|
|
1584
2056
|
verification_evidence_summary_count: verificationEvidence.summaries.length,
|
|
2057
|
+
verification_plan_count: verificationEvidence.verificationPlans.length,
|
|
2058
|
+
acceptance_criteria_count: verificationEvidence.acceptanceCriteria.length,
|
|
2059
|
+
criterion_coverage_count: verificationEvidence.criterionCoverage.length,
|
|
1585
2060
|
verification_receipt_summary_count: verificationEvidence.receipts.length,
|
|
2061
|
+
command_receipt_summary_count: verificationEvidence.commandReceiptSummaries.length,
|
|
1586
2062
|
verification_coverage_state_count: verificationEvidence.coverageStates.length,
|
|
1587
2063
|
verification_risk_signal_count: verificationEvidence.riskSignals.length,
|
|
2064
|
+
validation_ratchet_signal_count: verificationEvidence.validationRatchetSignals.length,
|
|
2065
|
+
completion_verdict_summary_count: verificationEvidence.completionVerdictSummaries.length,
|
|
2066
|
+
repro_route_count: verificationEvidence.reproRoutes.length,
|
|
2067
|
+
repro_observation_count: verificationEvidence.reproObservations.length,
|
|
1588
2068
|
failure_fingerprint_count: verificationEvidence.failureFingerprints.length,
|
|
1589
2069
|
source_index_enabled: includeSource,
|
|
1590
2070
|
source_anchor_count: sourceAnchors.length,
|
|
2071
|
+
source_anchor_risk_signal_count: createSourceAnchorRiskSignals(sourceAnchors).length,
|
|
1591
2072
|
search_backend: capabilities.backend,
|
|
1592
2073
|
search_fts5_available: capabilities.fts5Available,
|
|
1593
2074
|
content_mode: LOCAL_INDEX_CONTENT_MODE,
|
|
@@ -1786,6 +2267,220 @@ export async function readLocalIndexPromptContext(projectRoot) {
|
|
|
1786
2267
|
database?.close();
|
|
1787
2268
|
}
|
|
1788
2269
|
}
|
|
2270
|
+
function createVerificationReadModelQueryStatus(databasePath, status, planId, stalePaths = []) {
|
|
2271
|
+
return {
|
|
2272
|
+
source: 'local_index',
|
|
2273
|
+
authority: 'evidence_only',
|
|
2274
|
+
commandAuthority: '.mustflow/config/commands.toml',
|
|
2275
|
+
grantsCommandAuthority: false,
|
|
2276
|
+
status,
|
|
2277
|
+
databasePath,
|
|
2278
|
+
indexFresh: status === 'fresh',
|
|
2279
|
+
stalePaths,
|
|
2280
|
+
planId,
|
|
2281
|
+
uncoveredCriteria: [],
|
|
2282
|
+
severeRisks: [],
|
|
2283
|
+
nonPassingReceipts: [],
|
|
2284
|
+
repeatedFailureFingerprints: [],
|
|
2285
|
+
validationWeakeningSignals: [],
|
|
2286
|
+
refreshHint: status === 'fresh' ? null : 'Run `mf index` to refresh verification read-model evidence.',
|
|
2287
|
+
};
|
|
2288
|
+
}
|
|
2289
|
+
function readLatestVerificationPlanId(database) {
|
|
2290
|
+
const row = queryRows(database, `
|
|
2291
|
+
SELECT plan_id
|
|
2292
|
+
FROM verification_plans
|
|
2293
|
+
ORDER BY COALESCE(created_at, '') DESC, source_path DESC, plan_id DESC
|
|
2294
|
+
LIMIT 1
|
|
2295
|
+
`)[0];
|
|
2296
|
+
return toSearchString(row?.plan_id) || null;
|
|
2297
|
+
}
|
|
2298
|
+
function readUncoveredCriteria(database, planId) {
|
|
2299
|
+
return queryRows(database, `
|
|
2300
|
+
SELECT
|
|
2301
|
+
acceptance_criteria.criterion_id,
|
|
2302
|
+
acceptance_criteria.source,
|
|
2303
|
+
acceptance_criteria.reason,
|
|
2304
|
+
acceptance_criteria.surface,
|
|
2305
|
+
acceptance_criteria.path_hash,
|
|
2306
|
+
criterion_coverage.status AS coverage_status,
|
|
2307
|
+
criterion_coverage.receipt_count,
|
|
2308
|
+
criterion_coverage.gap_count,
|
|
2309
|
+
criterion_coverage.risk_count
|
|
2310
|
+
FROM acceptance_criteria
|
|
2311
|
+
LEFT JOIN criterion_coverage
|
|
2312
|
+
ON criterion_coverage.plan_id = acceptance_criteria.plan_id
|
|
2313
|
+
AND criterion_coverage.criterion_id = acceptance_criteria.criterion_id
|
|
2314
|
+
WHERE acceptance_criteria.plan_id = ?
|
|
2315
|
+
AND (criterion_coverage.status IS NULL OR criterion_coverage.status != 'covered')
|
|
2316
|
+
ORDER BY acceptance_criteria.criterion_id
|
|
2317
|
+
`, [planId]).map((row) => ({
|
|
2318
|
+
criterionId: toSearchString(row.criterion_id),
|
|
2319
|
+
source: toSearchString(row.source),
|
|
2320
|
+
reason: toSearchString(row.reason) || null,
|
|
2321
|
+
surface: toSearchString(row.surface) || null,
|
|
2322
|
+
pathHash: toSearchString(row.path_hash) || null,
|
|
2323
|
+
coverageStatus: toSearchString(row.coverage_status) || null,
|
|
2324
|
+
receiptCount: toNullableNumber(row.receipt_count) ?? 0,
|
|
2325
|
+
gapCount: toNullableNumber(row.gap_count) ?? 0,
|
|
2326
|
+
riskCount: toNullableNumber(row.risk_count) ?? 0,
|
|
2327
|
+
}));
|
|
2328
|
+
}
|
|
2329
|
+
function readSevereVerificationRisks(database, planId) {
|
|
2330
|
+
return queryRows(database, `
|
|
2331
|
+
SELECT
|
|
2332
|
+
verification_risk_signals.source_path,
|
|
2333
|
+
verification_risk_signals.ordinal,
|
|
2334
|
+
verification_risk_signals.code,
|
|
2335
|
+
verification_risk_signals.severity,
|
|
2336
|
+
verification_risk_signals.detail_hash
|
|
2337
|
+
FROM verification_risk_signals
|
|
2338
|
+
JOIN verification_evidence_summaries
|
|
2339
|
+
ON verification_evidence_summaries.source_path = verification_risk_signals.source_path
|
|
2340
|
+
WHERE verification_evidence_summaries.verification_plan_id = ?
|
|
2341
|
+
AND verification_risk_signals.severity IN ('high', 'critical')
|
|
2342
|
+
ORDER BY verification_risk_signals.source_path, verification_risk_signals.ordinal
|
|
2343
|
+
`, [planId]).map((row) => ({
|
|
2344
|
+
sourcePath: toSearchString(row.source_path),
|
|
2345
|
+
ordinal: toNullableNumber(row.ordinal) ?? 0,
|
|
2346
|
+
code: toSearchString(row.code),
|
|
2347
|
+
severity: toSearchString(row.severity),
|
|
2348
|
+
detailHash: toSearchString(row.detail_hash),
|
|
2349
|
+
}));
|
|
2350
|
+
}
|
|
2351
|
+
function readNonPassingReceipts(database, planId) {
|
|
2352
|
+
return queryRows(database, `
|
|
2353
|
+
SELECT
|
|
2354
|
+
receipt_hash,
|
|
2355
|
+
plan_id,
|
|
2356
|
+
intent,
|
|
2357
|
+
status,
|
|
2358
|
+
command_fingerprint,
|
|
2359
|
+
contract_fingerprint,
|
|
2360
|
+
current_state_hash,
|
|
2361
|
+
write_drift_status
|
|
2362
|
+
FROM command_receipt_summaries
|
|
2363
|
+
WHERE plan_id = ?
|
|
2364
|
+
AND (
|
|
2365
|
+
status != 'passed'
|
|
2366
|
+
OR write_drift_status IS NULL
|
|
2367
|
+
OR write_drift_status NOT IN ('clean', 'none')
|
|
2368
|
+
)
|
|
2369
|
+
ORDER BY intent, receipt_hash
|
|
2370
|
+
`, [planId]).map((row) => ({
|
|
2371
|
+
receiptHash: toSearchString(row.receipt_hash),
|
|
2372
|
+
planId: toSearchString(row.plan_id),
|
|
2373
|
+
intent: toSearchString(row.intent) || null,
|
|
2374
|
+
status: toSearchString(row.status),
|
|
2375
|
+
commandFingerprint: toSearchString(row.command_fingerprint) || null,
|
|
2376
|
+
contractFingerprint: toSearchString(row.contract_fingerprint) || null,
|
|
2377
|
+
currentStateHash: toSearchString(row.current_state_hash) || null,
|
|
2378
|
+
writeDriftStatus: toSearchString(row.write_drift_status) || null,
|
|
2379
|
+
}));
|
|
2380
|
+
}
|
|
2381
|
+
function readRepeatedFailureFingerprints(database, planId) {
|
|
2382
|
+
return queryRows(database, `
|
|
2383
|
+
SELECT
|
|
2384
|
+
source_path,
|
|
2385
|
+
fingerprint,
|
|
2386
|
+
verification_plan_id,
|
|
2387
|
+
status,
|
|
2388
|
+
failed_intents,
|
|
2389
|
+
primary_reason,
|
|
2390
|
+
failed_intents_hash,
|
|
2391
|
+
risk_codes_hash,
|
|
2392
|
+
affected_surfaces_hash,
|
|
2393
|
+
seen_count,
|
|
2394
|
+
requires_new_evidence
|
|
2395
|
+
FROM verification_failure_fingerprints
|
|
2396
|
+
WHERE verification_plan_id = ?
|
|
2397
|
+
AND requires_new_evidence = 1
|
|
2398
|
+
ORDER BY fingerprint
|
|
2399
|
+
`, [planId]).map((row) => ({
|
|
2400
|
+
sourcePath: toSearchString(row.source_path),
|
|
2401
|
+
fingerprint: toSearchString(row.fingerprint),
|
|
2402
|
+
verificationPlanId: toSearchString(row.verification_plan_id) || null,
|
|
2403
|
+
status: toSearchString(row.status),
|
|
2404
|
+
failedIntents: splitIndexedList(row.failed_intents),
|
|
2405
|
+
primaryReason: toSearchString(row.primary_reason) || null,
|
|
2406
|
+
failedIntentsHash: toSearchString(row.failed_intents_hash) || null,
|
|
2407
|
+
riskCodesHash: toSearchString(row.risk_codes_hash) || null,
|
|
2408
|
+
affectedSurfacesHash: toSearchString(row.affected_surfaces_hash) || null,
|
|
2409
|
+
seenCount: toNullableNumber(row.seen_count) ?? 0,
|
|
2410
|
+
requiresNewEvidence: Number(row.requires_new_evidence) === 1,
|
|
2411
|
+
}));
|
|
2412
|
+
}
|
|
2413
|
+
function readValidationWeakeningSignals(database, planId) {
|
|
2414
|
+
return queryRows(database, `
|
|
2415
|
+
SELECT signal_id, plan_id, code, severity, path_hash, before_hash, after_hash
|
|
2416
|
+
FROM validation_ratchet_signals
|
|
2417
|
+
WHERE plan_id = ?
|
|
2418
|
+
ORDER BY severity DESC, code, signal_id
|
|
2419
|
+
`, [planId]).map((row) => ({
|
|
2420
|
+
signalId: toSearchString(row.signal_id),
|
|
2421
|
+
planId: toSearchString(row.plan_id) || null,
|
|
2422
|
+
code: toSearchString(row.code),
|
|
2423
|
+
severity: toSearchString(row.severity),
|
|
2424
|
+
pathHash: toSearchString(row.path_hash),
|
|
2425
|
+
beforeHash: toSearchString(row.before_hash) || null,
|
|
2426
|
+
afterHash: toSearchString(row.after_hash) || null,
|
|
2427
|
+
}));
|
|
2428
|
+
}
|
|
2429
|
+
function queryLocalVerificationReadModel(databasePath, database, planId) {
|
|
2430
|
+
const selectedPlanId = planId ?? readLatestVerificationPlanId(database);
|
|
2431
|
+
const base = createVerificationReadModelQueryStatus(databasePath, 'fresh', selectedPlanId);
|
|
2432
|
+
if (!selectedPlanId) {
|
|
2433
|
+
return base;
|
|
2434
|
+
}
|
|
2435
|
+
return {
|
|
2436
|
+
...base,
|
|
2437
|
+
uncoveredCriteria: readUncoveredCriteria(database, selectedPlanId),
|
|
2438
|
+
severeRisks: readSevereVerificationRisks(database, selectedPlanId),
|
|
2439
|
+
nonPassingReceipts: readNonPassingReceipts(database, selectedPlanId),
|
|
2440
|
+
repeatedFailureFingerprints: readRepeatedFailureFingerprints(database, selectedPlanId),
|
|
2441
|
+
validationWeakeningSignals: readValidationWeakeningSignals(database, selectedPlanId),
|
|
2442
|
+
};
|
|
2443
|
+
}
|
|
2444
|
+
export async function readLocalVerificationReadModelQueries(projectRoot, planId) {
|
|
2445
|
+
return readLocalVerificationReadModelQueriesForPlan(projectRoot, planId);
|
|
2446
|
+
}
|
|
2447
|
+
export async function readLocalVerificationReadModelQueriesForPlan(projectRoot, planId) {
|
|
2448
|
+
const databasePath = getLocalIndexDatabasePath(projectRoot);
|
|
2449
|
+
if (!existsSync(databasePath)) {
|
|
2450
|
+
return createVerificationReadModelQueryStatus(databasePath, 'missing', planId);
|
|
2451
|
+
}
|
|
2452
|
+
const SQL = await loadSqlJs();
|
|
2453
|
+
const database = new SQL.Database(readFileSync(databasePath));
|
|
2454
|
+
try {
|
|
2455
|
+
const requiredTables = [
|
|
2456
|
+
'acceptance_criteria',
|
|
2457
|
+
'criterion_coverage',
|
|
2458
|
+
'verification_evidence_summaries',
|
|
2459
|
+
'verification_plans',
|
|
2460
|
+
'verification_risk_signals',
|
|
2461
|
+
'command_receipt_summaries',
|
|
2462
|
+
'verification_failure_fingerprints',
|
|
2463
|
+
'validation_ratchet_signals',
|
|
2464
|
+
];
|
|
2465
|
+
if (requiredTables.some((tableName) => !hasTable(database, tableName))) {
|
|
2466
|
+
return createVerificationReadModelQueryStatus(databasePath, 'unreadable', planId);
|
|
2467
|
+
}
|
|
2468
|
+
const stalePaths = getStalePaths(projectRoot, database);
|
|
2469
|
+
if (stalePaths.length > 0) {
|
|
2470
|
+
return createVerificationReadModelQueryStatus(databasePath, 'stale', planId, stalePaths);
|
|
2471
|
+
}
|
|
2472
|
+
return queryLocalVerificationReadModel(databasePath, database, planId);
|
|
2473
|
+
}
|
|
2474
|
+
catch {
|
|
2475
|
+
return createVerificationReadModelQueryStatus(databasePath, 'unreadable', planId);
|
|
2476
|
+
}
|
|
2477
|
+
finally {
|
|
2478
|
+
database.close();
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
export async function readLatestLocalVerificationReadModelQueries(projectRoot) {
|
|
2482
|
+
return readLocalVerificationReadModelQueriesForPlan(projectRoot, null);
|
|
2483
|
+
}
|
|
1789
2484
|
function pathSurfaceReadModelWithMatch(base, match) {
|
|
1790
2485
|
return {
|
|
1791
2486
|
...base,
|