mustflow 2.11.0 → 2.16.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/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/verify.js +528 -30
- package/dist/cli/lib/local-index/constants.js +1 -1
- package/dist/cli/lib/local-index/index.js +708 -13
- 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
|
@@ -1,10 +1,33 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
1
2
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
const TEST_CHANGE_KINDS = new Set(['test', 'test_fixture']);
|
|
4
5
|
const SKIP_OR_ONLY_MARKER = /\b(?:describe|it|test)\s*\.\s*(?:skip|only)\s*\(/u;
|
|
6
|
+
const TODO_OR_PENDING_MARKER = /\b(?:describe|it|test)\s*\.\s*(?:todo|pending)\s*\(/u;
|
|
7
|
+
const ASSERTION_CALL = /\b(?:assert(?:\.\w+)?|expect)\s*\(/u;
|
|
8
|
+
const STRONG_ASSERTION = /\b(?:assert\.(?:equal|deepEqual|strictEqual|throws|rejects)|to(?:Equal|StrictEqual|Be|Throw)|throws|rejects)\b/u;
|
|
9
|
+
const WEAK_ASSERTION = /\b(?:assert\.ok|toBeDefined|toBeTruthy|toBeFalsy)\b/u;
|
|
10
|
+
const NEGATIVE_ASSERTION = /\b(?:notEqual|notDeepEqual|doesNotMatch|doesNotThrow|\.not\.)\b/u;
|
|
11
|
+
const EXCEPTION_ASSERTION = /\b(?:assert\.(?:throws|rejects|doesNotThrow|doesNotReject)|toThrow|rejects|throws)\b/u;
|
|
12
|
+
const SNAPSHOT_PATH = /(?:^|\/)(?:__snapshots__\/|snapshots\/)|\.snap$/u;
|
|
13
|
+
const GOLDEN_PATH = /(?:^|\/)(?:golden|fixtures|expected)(?:\/|-)|(?:\.golden\.|\.expected\.)/u;
|
|
14
|
+
const JAVASCRIPT_TEST_PATH = /(?:^|\/)[^/]+\.(?:test|spec)\.[cm]?[jt]sx?$/u;
|
|
15
|
+
const COMMAND_CONTRACT_PATH = '.mustflow/config/commands.toml';
|
|
16
|
+
const TEST_SELECTION_PATTERN = /(?:--(?:grep|testNamePattern|testPathPattern|runTestsByPath|test-name-pattern)|\bgrep\s*=|\btestNamePattern\b|\btestPathPattern\b)/u;
|
|
17
|
+
const COMMAND_ALLOWS_NO_TESTS_PATTERN = /(?:passWithNoTests|--pass-with-no-tests|--passWithNoTests)/u;
|
|
18
|
+
const COMMAND_FORCES_SNAPSHOT_UPDATE_PATTERN = /(?:updateSnapshot|--update-snapshot|--updateSnapshot|\s-u(?:\s|"))/u;
|
|
19
|
+
const COMMAND_HIDES_FAILURE_PATTERN = /(?:\|\|\s*true|;\s*true\b|&&\s*true\b|\bexit\s+0\b)/u;
|
|
20
|
+
const CRITICAL_RATCHET_CODES = new Set([
|
|
21
|
+
'success_exit_codes_widened',
|
|
22
|
+
'command_allows_no_tests',
|
|
23
|
+
'command_hides_failure',
|
|
24
|
+
]);
|
|
5
25
|
function isTestClassification(classification) {
|
|
6
26
|
return classification.surface.category === 'test' || classification.changeKinds.some((kind) => TEST_CHANGE_KINDS.has(kind));
|
|
7
27
|
}
|
|
28
|
+
function isJavaScriptTestPath(relativePath) {
|
|
29
|
+
return JAVASCRIPT_TEST_PATH.test(relativePath);
|
|
30
|
+
}
|
|
8
31
|
function resolveInsideRoot(projectRoot, relativePath) {
|
|
9
32
|
const resolvedPath = path.resolve(projectRoot, relativePath);
|
|
10
33
|
const relative = path.relative(projectRoot, resolvedPath);
|
|
@@ -25,27 +48,148 @@ function fileTextIfReadable(projectRoot, relativePath) {
|
|
|
25
48
|
return null;
|
|
26
49
|
}
|
|
27
50
|
}
|
|
51
|
+
function gitDiffLines(projectRoot, relativePath) {
|
|
52
|
+
const result = spawnSync('git', ['diff', '--no-ext-diff', '--unified=0', '--', relativePath], {
|
|
53
|
+
cwd: projectRoot,
|
|
54
|
+
encoding: 'utf8',
|
|
55
|
+
windowsHide: true,
|
|
56
|
+
});
|
|
57
|
+
if (result.status !== 0 || typeof result.stdout !== 'string' || result.stdout.length === 0) {
|
|
58
|
+
return { added: [], removed: [] };
|
|
59
|
+
}
|
|
60
|
+
const added = [];
|
|
61
|
+
const removed = [];
|
|
62
|
+
for (const line of result.stdout.split(/\r?\n/u)) {
|
|
63
|
+
if (line.startsWith('+++') || line.startsWith('---')) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (line.startsWith('+')) {
|
|
67
|
+
added.push(line.slice(1));
|
|
68
|
+
}
|
|
69
|
+
else if (line.startsWith('-')) {
|
|
70
|
+
removed.push(line.slice(1));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { added, removed };
|
|
74
|
+
}
|
|
75
|
+
function countMatching(lines, pattern) {
|
|
76
|
+
return lines.filter((line) => pattern.test(line)).length;
|
|
77
|
+
}
|
|
78
|
+
function hasAny(lines, pattern) {
|
|
79
|
+
return lines.some((line) => pattern.test(line));
|
|
80
|
+
}
|
|
81
|
+
function extractCoverageNumbers(lines) {
|
|
82
|
+
return lines
|
|
83
|
+
.filter((line) => /\b(?:coverage|threshold|branches|functions|lines|statements)\b/iu.test(line))
|
|
84
|
+
.flatMap((line) => [...line.matchAll(/\b\d+(?:\.\d+)?\b/gu)].map((match) => Number(match[0])))
|
|
85
|
+
.filter((value) => Number.isFinite(value));
|
|
86
|
+
}
|
|
87
|
+
function isCommandContractPath(relativePath) {
|
|
88
|
+
return relativePath === COMMAND_CONTRACT_PATH;
|
|
89
|
+
}
|
|
90
|
+
function isValidationConfigPath(relativePath) {
|
|
91
|
+
return (isCommandContractPath(relativePath) ||
|
|
92
|
+
/(?:^|\/)(?:package\.json|jest\.config\.[cm]?[jt]s|vitest\.config\.[cm]?[jt]s|nyc\.config\.[cm]?[jt]s)$/u.test(relativePath) ||
|
|
93
|
+
/\bcoverage\b/u.test(relativePath));
|
|
94
|
+
}
|
|
95
|
+
function riskDetail(pathText, message) {
|
|
96
|
+
return `Changed validation path ${pathText} ${message}`;
|
|
97
|
+
}
|
|
98
|
+
export function countValidationRatchetVerdictEffects(risks) {
|
|
99
|
+
return {
|
|
100
|
+
contradicted: risks.filter((risk) => risk.severity === 'critical' && CRITICAL_RATCHET_CODES.has(risk.code)).length,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
28
103
|
export function createValidationRatchetRisks(report, projectRoot) {
|
|
29
104
|
const risks = [];
|
|
30
|
-
|
|
105
|
+
const seenRisks = new Set();
|
|
106
|
+
function addRisk(code, severity, pathText, detail) {
|
|
107
|
+
const key = `${pathText}\0${code}`;
|
|
108
|
+
if (seenRisks.has(key)) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
seenRisks.add(key);
|
|
112
|
+
risks.push({ code, severity, path: pathText, detail });
|
|
113
|
+
}
|
|
114
|
+
for (const classification of report.classifications) {
|
|
31
115
|
const resolvedPath = resolveInsideRoot(projectRoot, classification.path);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
path
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
116
|
+
const diff = report.source === 'changed' ? gitDiffLines(projectRoot, classification.path) : { added: [], removed: [] };
|
|
117
|
+
const addedText = diff.added.join('\n');
|
|
118
|
+
if (isTestClassification(classification)) {
|
|
119
|
+
if (report.source === 'changed' && (resolvedPath === null || !existsSync(resolvedPath))) {
|
|
120
|
+
addRisk('related_test_deleted', 'high', classification.path, `Changed test path ${classification.path} is absent; deleted related tests require review before marking the task verified.`);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (isJavaScriptTestPath(classification.path)) {
|
|
124
|
+
const fileText = fileTextIfReadable(projectRoot, classification.path);
|
|
125
|
+
if (fileText !== null && SKIP_OR_ONLY_MARKER.test(fileText)) {
|
|
126
|
+
addRisk('skip_or_only_marker_present', 'medium', classification.path, `Changed test path ${classification.path} contains a .skip or .only marker; review whether validation was weakened before marking the task verified.`);
|
|
127
|
+
}
|
|
128
|
+
if ((fileText !== null && TODO_OR_PENDING_MARKER.test(fileText)) || TODO_OR_PENDING_MARKER.test(addedText)) {
|
|
129
|
+
addRisk('todo_or_pending_marker_added', 'medium', classification.path, `Changed test path ${classification.path} contains a todo or pending marker; review whether validation was deferred before marking the task verified.`);
|
|
130
|
+
}
|
|
131
|
+
const removedAssertionCount = countMatching(diff.removed, ASSERTION_CALL);
|
|
132
|
+
const addedAssertionCount = countMatching(diff.added, ASSERTION_CALL);
|
|
133
|
+
if (removedAssertionCount > addedAssertionCount) {
|
|
134
|
+
addRisk('assertion_count_decreased', 'high', classification.path, riskDetail(classification.path, `removes ${removedAssertionCount} assertion line(s) and adds ${addedAssertionCount}; review whether validation strength decreased.`));
|
|
135
|
+
}
|
|
136
|
+
if (hasAny(diff.removed, STRONG_ASSERTION) && hasAny(diff.added, WEAK_ASSERTION)) {
|
|
137
|
+
addRisk('assertion_matcher_weakened', 'high', classification.path, riskDetail(classification.path, 'replaces a stronger assertion with a weaker presence or truthiness assertion.'));
|
|
138
|
+
}
|
|
139
|
+
if (hasAny(diff.removed, NEGATIVE_ASSERTION)) {
|
|
140
|
+
addRisk('negative_assertion_removed', 'high', classification.path, riskDetail(classification.path, 'removes a negative assertion; confirm the denied behavior is still covered.'));
|
|
141
|
+
}
|
|
142
|
+
if (hasAny(diff.removed, EXCEPTION_ASSERTION)) {
|
|
143
|
+
addRisk('exception_assertion_removed', 'high', classification.path, riskDetail(classification.path, 'removes an exception assertion; confirm failure behavior is still covered.'));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (SNAPSHOT_PATH.test(classification.path) && diff.added.length + diff.removed.length >= 20) {
|
|
147
|
+
addRisk('snapshot_mass_updated', 'medium', classification.path, riskDetail(classification.path, 'changes a large snapshot region; review that the update does not hide a regression.'));
|
|
148
|
+
}
|
|
149
|
+
if (GOLDEN_PATH.test(`${classification.path} `) && diff.added.length + diff.removed.length >= 20) {
|
|
150
|
+
addRisk('golden_output_replaced', 'medium', classification.path, riskDetail(classification.path, 'replaces a broad golden or expected-output region; review the behavioral reason.'));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (isCommandContractPath(classification.path)) {
|
|
154
|
+
if (hasAny(diff.added, /\bstatus\s*=\s*"(?:manual_only|disabled|unknown)"/u)) {
|
|
155
|
+
addRisk('verification_intent_disabled', 'high', classification.path, riskDetail(classification.path, 'adds a non-runnable verification intent status.'));
|
|
156
|
+
}
|
|
157
|
+
if (hasAny(diff.removed, /\brequired_after\s*=/u) && !hasAny(diff.added, /\brequired_after\s*=/u)) {
|
|
158
|
+
addRisk('verification_required_after_removed', 'high', classification.path, riskDetail(classification.path, 'removes a required_after mapping from the command contract.'));
|
|
159
|
+
}
|
|
160
|
+
if (hasAny(diff.added, /\bsuccess_exit_codes\s*=\s*\[[^\]]*(?:[1-9]\d*|true)[^\]]*\]/u)) {
|
|
161
|
+
addRisk('success_exit_codes_widened', 'critical', classification.path, riskDetail(classification.path, 'widens success exit codes beyond the normal zero-exit contract.'));
|
|
162
|
+
}
|
|
163
|
+
if (hasAny(diff.added, COMMAND_ALLOWS_NO_TESTS_PATTERN)) {
|
|
164
|
+
addRisk('command_allows_no_tests', 'critical', classification.path, riskDetail(classification.path, 'allows a test command to pass when no tests run.'));
|
|
165
|
+
}
|
|
166
|
+
if (hasAny(diff.added, COMMAND_FORCES_SNAPSHOT_UPDATE_PATTERN)) {
|
|
167
|
+
addRisk('command_forces_snapshot_update', 'medium', classification.path, riskDetail(classification.path, 'adds snapshot update behavior to a verification command.'));
|
|
168
|
+
}
|
|
169
|
+
if (hasAny(diff.added, COMMAND_HIDES_FAILURE_PATTERN)) {
|
|
170
|
+
addRisk('command_hides_failure', 'critical', classification.path, riskDetail(classification.path, 'adds shell behavior that can hide command failure.'));
|
|
171
|
+
}
|
|
40
172
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
173
|
+
if (isValidationConfigPath(classification.path)) {
|
|
174
|
+
const removedCoverageNumbers = extractCoverageNumbers(diff.removed);
|
|
175
|
+
const addedCoverageNumbers = extractCoverageNumbers(diff.added);
|
|
176
|
+
if (removedCoverageNumbers.length > 0 &&
|
|
177
|
+
addedCoverageNumbers.length > 0 &&
|
|
178
|
+
Math.min(...addedCoverageNumbers) < Math.max(...removedCoverageNumbers)) {
|
|
179
|
+
addRisk('coverage_threshold_lowered', 'medium', classification.path, riskDetail(classification.path, 'lowers a coverage-related numeric threshold.'));
|
|
180
|
+
}
|
|
181
|
+
if (hasAny(diff.added, COMMAND_ALLOWS_NO_TESTS_PATTERN)) {
|
|
182
|
+
addRisk('command_allows_no_tests', 'critical', classification.path, riskDetail(classification.path, 'allows a validation script to pass when no tests run.'));
|
|
183
|
+
}
|
|
184
|
+
if (hasAny(diff.added, COMMAND_FORCES_SNAPSHOT_UPDATE_PATTERN)) {
|
|
185
|
+
addRisk('command_forces_snapshot_update', 'medium', classification.path, riskDetail(classification.path, 'adds snapshot update behavior to a validation script.'));
|
|
186
|
+
}
|
|
187
|
+
if (hasAny(diff.added, COMMAND_HIDES_FAILURE_PATTERN)) {
|
|
188
|
+
addRisk('command_hides_failure', 'critical', classification.path, riskDetail(classification.path, 'adds shell behavior that can hide validation failure.'));
|
|
189
|
+
}
|
|
190
|
+
if (hasAny(diff.added, TEST_SELECTION_PATTERN)) {
|
|
191
|
+
addRisk('test_selection_narrowed', 'medium', classification.path, riskDetail(classification.path, 'adds a test-selection filter; confirm coverage still matches the change.'));
|
|
192
|
+
}
|
|
49
193
|
}
|
|
50
194
|
}
|
|
51
195
|
return risks;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mustflow",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.16.0",
|
|
4
4
|
"description": "Agent workflow documents and CLI for mustflow repository roots.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT-0",
|
|
@@ -70,9 +70,9 @@
|
|
|
70
70
|
"workflow"
|
|
71
71
|
],
|
|
72
72
|
"devDependencies": {
|
|
73
|
-
"@types/node": "^25.
|
|
73
|
+
"@types/node": "^25.8.0",
|
|
74
74
|
"@types/sql.js": "^1.4.11",
|
|
75
|
-
"fast-check": "^4.
|
|
75
|
+
"fast-check": "^4.8.0",
|
|
76
76
|
"typescript": "^6.0.3"
|
|
77
77
|
},
|
|
78
78
|
"dependencies": {
|
|
@@ -326,6 +326,7 @@
|
|
|
326
326
|
"source",
|
|
327
327
|
"verification_plan_id",
|
|
328
328
|
"changed_file_count",
|
|
329
|
+
"criteria",
|
|
329
330
|
"matched_intents",
|
|
330
331
|
"ran_intents",
|
|
331
332
|
"passed_intents",
|
|
@@ -337,6 +338,14 @@
|
|
|
337
338
|
"scope_diff_risk_count",
|
|
338
339
|
"repeated_failure_count",
|
|
339
340
|
"validation_ratchet_risk_count",
|
|
341
|
+
"repro_evidence_risk_count",
|
|
342
|
+
"external_evidence_risk_count",
|
|
343
|
+
"write_drift_risk_count",
|
|
344
|
+
"receipt_binding_risk_count",
|
|
345
|
+
"stale_receipt_count",
|
|
346
|
+
"plan_mismatch_count",
|
|
347
|
+
"risks",
|
|
348
|
+
"receipt_binding",
|
|
340
349
|
"latest_run_status"
|
|
341
350
|
],
|
|
342
351
|
"properties": {
|
|
@@ -346,6 +355,7 @@
|
|
|
346
355
|
"pattern": "^sha256:[0-9a-f]{64}$"
|
|
347
356
|
},
|
|
348
357
|
"changed_file_count": { "type": ["integer", "null"] },
|
|
358
|
+
"criteria": { "$ref": "#/$defs/completionVerdictCriteria" },
|
|
349
359
|
"matched_intents": { "type": "integer" },
|
|
350
360
|
"ran_intents": { "type": "integer" },
|
|
351
361
|
"passed_intents": { "type": "integer" },
|
|
@@ -357,6 +367,14 @@
|
|
|
357
367
|
"scope_diff_risk_count": { "type": "integer" },
|
|
358
368
|
"repeated_failure_count": { "type": "integer" },
|
|
359
369
|
"validation_ratchet_risk_count": { "type": "integer" },
|
|
370
|
+
"repro_evidence_risk_count": { "type": "integer" },
|
|
371
|
+
"external_evidence_risk_count": { "type": "integer" },
|
|
372
|
+
"write_drift_risk_count": { "type": "integer" },
|
|
373
|
+
"receipt_binding_risk_count": { "type": "integer" },
|
|
374
|
+
"stale_receipt_count": { "type": "integer" },
|
|
375
|
+
"plan_mismatch_count": { "type": "integer" },
|
|
376
|
+
"risks": { "$ref": "#/$defs/completionVerdictRisks" },
|
|
377
|
+
"receipt_binding": { "$ref": "#/$defs/completionVerdictReceiptBinding" },
|
|
360
378
|
"latest_run_status": { "type": ["string", "null"] }
|
|
361
379
|
}
|
|
362
380
|
},
|
|
@@ -374,6 +392,71 @@
|
|
|
374
392
|
}
|
|
375
393
|
}
|
|
376
394
|
},
|
|
395
|
+
"completionVerdictCriteria": {
|
|
396
|
+
"type": "object",
|
|
397
|
+
"additionalProperties": false,
|
|
398
|
+
"required": ["total", "covered", "partially_covered", "uncovered", "blocked", "contradicted"],
|
|
399
|
+
"properties": {
|
|
400
|
+
"total": { "type": "integer" },
|
|
401
|
+
"covered": { "type": "integer" },
|
|
402
|
+
"partially_covered": { "type": "integer" },
|
|
403
|
+
"uncovered": { "type": "integer" },
|
|
404
|
+
"blocked": { "type": "integer" },
|
|
405
|
+
"contradicted": { "type": "integer" }
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
"completionVerdictRisks": {
|
|
409
|
+
"type": "object",
|
|
410
|
+
"additionalProperties": false,
|
|
411
|
+
"required": [
|
|
412
|
+
"source_anchor",
|
|
413
|
+
"scope_diff",
|
|
414
|
+
"repeated_failure",
|
|
415
|
+
"validation_ratchet",
|
|
416
|
+
"repro_evidence",
|
|
417
|
+
"external_evidence",
|
|
418
|
+
"write_drift",
|
|
419
|
+
"receipt_binding",
|
|
420
|
+
"stale_receipt",
|
|
421
|
+
"plan_mismatch"
|
|
422
|
+
],
|
|
423
|
+
"properties": {
|
|
424
|
+
"source_anchor": { "type": "integer" },
|
|
425
|
+
"scope_diff": { "type": "integer" },
|
|
426
|
+
"repeated_failure": { "type": "integer" },
|
|
427
|
+
"validation_ratchet": { "type": "integer" },
|
|
428
|
+
"repro_evidence": { "type": "integer" },
|
|
429
|
+
"external_evidence": { "type": "integer" },
|
|
430
|
+
"write_drift": { "type": "integer" },
|
|
431
|
+
"receipt_binding": { "type": "integer" },
|
|
432
|
+
"stale_receipt": { "type": "integer" },
|
|
433
|
+
"plan_mismatch": { "type": "integer" }
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
"completionVerdictReceiptBinding": {
|
|
437
|
+
"type": "object",
|
|
438
|
+
"additionalProperties": false,
|
|
439
|
+
"required": [
|
|
440
|
+
"plan_bound_count",
|
|
441
|
+
"plan_unbound_count",
|
|
442
|
+
"fingerprint_bound_count",
|
|
443
|
+
"fingerprint_unbound_count",
|
|
444
|
+
"current_state_bound_count",
|
|
445
|
+
"current_state_unavailable_count",
|
|
446
|
+
"stale_count",
|
|
447
|
+
"plan_mismatch_count"
|
|
448
|
+
],
|
|
449
|
+
"properties": {
|
|
450
|
+
"plan_bound_count": { "type": "integer" },
|
|
451
|
+
"plan_unbound_count": { "type": "integer" },
|
|
452
|
+
"fingerprint_bound_count": { "type": "integer" },
|
|
453
|
+
"fingerprint_unbound_count": { "type": "integer" },
|
|
454
|
+
"current_state_bound_count": { "type": "integer" },
|
|
455
|
+
"current_state_unavailable_count": { "type": "integer" },
|
|
456
|
+
"stale_count": { "type": "integer" },
|
|
457
|
+
"plan_mismatch_count": { "type": "integer" }
|
|
458
|
+
}
|
|
459
|
+
},
|
|
377
460
|
"harnessReport": {
|
|
378
461
|
"type": "object",
|
|
379
462
|
"additionalProperties": false,
|
|
@@ -506,7 +506,8 @@
|
|
|
506
506
|
"runnableCount",
|
|
507
507
|
"skippedCount",
|
|
508
508
|
"requirements",
|
|
509
|
-
"decisionGraph"
|
|
509
|
+
"decisionGraph",
|
|
510
|
+
"readModel"
|
|
510
511
|
],
|
|
511
512
|
"properties": {
|
|
512
513
|
"planSource": { "type": ["string", "null"] },
|
|
@@ -523,11 +524,182 @@
|
|
|
523
524
|
},
|
|
524
525
|
"decisionGraph": {
|
|
525
526
|
"$ref": "#/$defs/verificationDecisionGraph"
|
|
527
|
+
},
|
|
528
|
+
"readModel": {
|
|
529
|
+
"$ref": "#/$defs/localVerificationReadModel"
|
|
526
530
|
}
|
|
527
531
|
}
|
|
528
532
|
}
|
|
529
533
|
}
|
|
530
534
|
},
|
|
535
|
+
"localVerificationReadModel": {
|
|
536
|
+
"type": "object",
|
|
537
|
+
"additionalProperties": false,
|
|
538
|
+
"required": [
|
|
539
|
+
"source",
|
|
540
|
+
"authority",
|
|
541
|
+
"commandAuthority",
|
|
542
|
+
"grantsCommandAuthority",
|
|
543
|
+
"status",
|
|
544
|
+
"databasePath",
|
|
545
|
+
"indexFresh",
|
|
546
|
+
"stalePaths",
|
|
547
|
+
"planId",
|
|
548
|
+
"uncoveredCriteria",
|
|
549
|
+
"severeRisks",
|
|
550
|
+
"nonPassingReceipts",
|
|
551
|
+
"repeatedFailureFingerprints",
|
|
552
|
+
"validationWeakeningSignals",
|
|
553
|
+
"refreshHint"
|
|
554
|
+
],
|
|
555
|
+
"properties": {
|
|
556
|
+
"source": { "const": "local_index" },
|
|
557
|
+
"authority": { "const": "evidence_only" },
|
|
558
|
+
"commandAuthority": { "const": ".mustflow/config/commands.toml" },
|
|
559
|
+
"grantsCommandAuthority": { "const": false },
|
|
560
|
+
"status": { "enum": ["fresh", "missing", "stale", "unreadable"] },
|
|
561
|
+
"databasePath": { "type": "string" },
|
|
562
|
+
"indexFresh": { "type": "boolean" },
|
|
563
|
+
"stalePaths": {
|
|
564
|
+
"type": "array",
|
|
565
|
+
"items": { "type": "string" }
|
|
566
|
+
},
|
|
567
|
+
"planId": {
|
|
568
|
+
"type": ["string", "null"],
|
|
569
|
+
"pattern": "^sha256:[0-9a-f]{64}$"
|
|
570
|
+
},
|
|
571
|
+
"uncoveredCriteria": {
|
|
572
|
+
"type": "array",
|
|
573
|
+
"items": { "$ref": "#/$defs/localUncoveredCriterion" }
|
|
574
|
+
},
|
|
575
|
+
"severeRisks": {
|
|
576
|
+
"type": "array",
|
|
577
|
+
"items": { "$ref": "#/$defs/localSevereVerificationRisk" }
|
|
578
|
+
},
|
|
579
|
+
"nonPassingReceipts": {
|
|
580
|
+
"type": "array",
|
|
581
|
+
"items": { "$ref": "#/$defs/localNonPassingVerificationReceipt" }
|
|
582
|
+
},
|
|
583
|
+
"repeatedFailureFingerprints": {
|
|
584
|
+
"type": "array",
|
|
585
|
+
"items": { "$ref": "#/$defs/localRepeatedFailureFingerprint" }
|
|
586
|
+
},
|
|
587
|
+
"validationWeakeningSignals": {
|
|
588
|
+
"type": "array",
|
|
589
|
+
"items": { "$ref": "#/$defs/localValidationWeakeningSignal" }
|
|
590
|
+
},
|
|
591
|
+
"refreshHint": { "type": ["string", "null"] }
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
"localUncoveredCriterion": {
|
|
595
|
+
"type": "object",
|
|
596
|
+
"additionalProperties": false,
|
|
597
|
+
"required": [
|
|
598
|
+
"criterionId",
|
|
599
|
+
"source",
|
|
600
|
+
"reason",
|
|
601
|
+
"surface",
|
|
602
|
+
"pathHash",
|
|
603
|
+
"coverageStatus",
|
|
604
|
+
"receiptCount",
|
|
605
|
+
"gapCount",
|
|
606
|
+
"riskCount"
|
|
607
|
+
],
|
|
608
|
+
"properties": {
|
|
609
|
+
"criterionId": { "type": "string" },
|
|
610
|
+
"source": { "type": "string" },
|
|
611
|
+
"reason": { "type": ["string", "null"] },
|
|
612
|
+
"surface": { "type": ["string", "null"] },
|
|
613
|
+
"pathHash": { "type": ["string", "null"] },
|
|
614
|
+
"coverageStatus": { "type": ["string", "null"] },
|
|
615
|
+
"receiptCount": { "type": "integer" },
|
|
616
|
+
"gapCount": { "type": "integer" },
|
|
617
|
+
"riskCount": { "type": "integer" }
|
|
618
|
+
}
|
|
619
|
+
},
|
|
620
|
+
"localSevereVerificationRisk": {
|
|
621
|
+
"type": "object",
|
|
622
|
+
"additionalProperties": false,
|
|
623
|
+
"required": ["sourcePath", "ordinal", "code", "severity", "detailHash"],
|
|
624
|
+
"properties": {
|
|
625
|
+
"sourcePath": { "type": "string" },
|
|
626
|
+
"ordinal": { "type": "integer" },
|
|
627
|
+
"code": { "type": "string" },
|
|
628
|
+
"severity": { "type": "string" },
|
|
629
|
+
"detailHash": { "type": "string" }
|
|
630
|
+
}
|
|
631
|
+
},
|
|
632
|
+
"localNonPassingVerificationReceipt": {
|
|
633
|
+
"type": "object",
|
|
634
|
+
"additionalProperties": false,
|
|
635
|
+
"required": [
|
|
636
|
+
"receiptHash",
|
|
637
|
+
"planId",
|
|
638
|
+
"intent",
|
|
639
|
+
"status",
|
|
640
|
+
"commandFingerprint",
|
|
641
|
+
"contractFingerprint",
|
|
642
|
+
"currentStateHash",
|
|
643
|
+
"writeDriftStatus"
|
|
644
|
+
],
|
|
645
|
+
"properties": {
|
|
646
|
+
"receiptHash": { "type": "string" },
|
|
647
|
+
"planId": { "type": "string" },
|
|
648
|
+
"intent": { "type": ["string", "null"] },
|
|
649
|
+
"status": { "type": "string" },
|
|
650
|
+
"commandFingerprint": { "type": ["string", "null"] },
|
|
651
|
+
"contractFingerprint": { "type": ["string", "null"] },
|
|
652
|
+
"currentStateHash": { "type": ["string", "null"] },
|
|
653
|
+
"writeDriftStatus": { "type": ["string", "null"] }
|
|
654
|
+
}
|
|
655
|
+
},
|
|
656
|
+
"localRepeatedFailureFingerprint": {
|
|
657
|
+
"type": "object",
|
|
658
|
+
"additionalProperties": false,
|
|
659
|
+
"required": [
|
|
660
|
+
"sourcePath",
|
|
661
|
+
"fingerprint",
|
|
662
|
+
"verificationPlanId",
|
|
663
|
+
"status",
|
|
664
|
+
"failedIntents",
|
|
665
|
+
"primaryReason",
|
|
666
|
+
"failedIntentsHash",
|
|
667
|
+
"riskCodesHash",
|
|
668
|
+
"affectedSurfacesHash",
|
|
669
|
+
"seenCount",
|
|
670
|
+
"requiresNewEvidence"
|
|
671
|
+
],
|
|
672
|
+
"properties": {
|
|
673
|
+
"sourcePath": { "type": "string" },
|
|
674
|
+
"fingerprint": { "type": "string" },
|
|
675
|
+
"verificationPlanId": { "type": ["string", "null"] },
|
|
676
|
+
"status": { "type": "string" },
|
|
677
|
+
"failedIntents": {
|
|
678
|
+
"type": "array",
|
|
679
|
+
"items": { "type": "string" }
|
|
680
|
+
},
|
|
681
|
+
"primaryReason": { "type": ["string", "null"] },
|
|
682
|
+
"failedIntentsHash": { "type": ["string", "null"] },
|
|
683
|
+
"riskCodesHash": { "type": ["string", "null"] },
|
|
684
|
+
"affectedSurfacesHash": { "type": ["string", "null"] },
|
|
685
|
+
"seenCount": { "type": "integer" },
|
|
686
|
+
"requiresNewEvidence": { "type": "boolean" }
|
|
687
|
+
}
|
|
688
|
+
},
|
|
689
|
+
"localValidationWeakeningSignal": {
|
|
690
|
+
"type": "object",
|
|
691
|
+
"additionalProperties": false,
|
|
692
|
+
"required": ["signalId", "planId", "code", "severity", "pathHash", "beforeHash", "afterHash"],
|
|
693
|
+
"properties": {
|
|
694
|
+
"signalId": { "type": "string" },
|
|
695
|
+
"planId": { "type": ["string", "null"] },
|
|
696
|
+
"code": { "type": "string" },
|
|
697
|
+
"severity": { "type": "string" },
|
|
698
|
+
"pathHash": { "type": "string" },
|
|
699
|
+
"beforeHash": { "type": ["string", "null"] },
|
|
700
|
+
"afterHash": { "type": ["string", "null"] }
|
|
701
|
+
}
|
|
702
|
+
},
|
|
531
703
|
"verificationDecisionGraph": {
|
|
532
704
|
"type": "object",
|
|
533
705
|
"additionalProperties": false,
|