mustflow 1.31.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.
Files changed (66) hide show
  1. package/README.md +23 -9
  2. package/dist/cli/commands/classify.js +61 -6
  3. package/dist/cli/commands/contract-lint.js +13 -4
  4. package/dist/cli/commands/dashboard.js +77 -2
  5. package/dist/cli/commands/explain-verify.js +11 -1
  6. package/dist/cli/commands/index.js +14 -0
  7. package/dist/cli/commands/run.js +4 -1
  8. package/dist/cli/commands/verify.js +986 -43
  9. package/dist/cli/i18n/en.js +61 -10
  10. package/dist/cli/i18n/es.js +61 -10
  11. package/dist/cli/i18n/fr.js +61 -10
  12. package/dist/cli/i18n/hi.js +61 -10
  13. package/dist/cli/i18n/ko.js +61 -10
  14. package/dist/cli/i18n/zh.js +61 -10
  15. package/dist/cli/lib/dashboard-export.js +62 -12
  16. package/dist/cli/lib/dashboard-html/client-script.js +1936 -0
  17. package/dist/cli/lib/dashboard-html/locale-bootstrap.js +8 -0
  18. package/dist/cli/lib/dashboard-html/styles.js +572 -0
  19. package/dist/cli/lib/dashboard-html/template.js +134 -0
  20. package/dist/cli/lib/dashboard-html/types.js +1 -0
  21. package/dist/cli/lib/dashboard-html.js +1 -1907
  22. package/dist/cli/lib/dashboard-locale.js +37 -0
  23. package/dist/cli/lib/local-index/constants.js +48 -0
  24. package/dist/cli/lib/local-index/index.js +2951 -0
  25. package/dist/cli/lib/local-index/sql.js +15 -0
  26. package/dist/cli/lib/local-index/types.js +1 -0
  27. package/dist/cli/lib/local-index.js +1 -1911
  28. package/dist/cli/lib/run-plan.js +76 -1
  29. package/dist/cli/lib/templates.js +18 -1
  30. package/dist/cli/lib/validation/command-intents.js +11 -0
  31. package/dist/cli/lib/validation/constants.js +238 -0
  32. package/dist/cli/lib/validation/index.js +1384 -0
  33. package/dist/cli/lib/validation/primitives.js +198 -0
  34. package/dist/cli/lib/validation/test-selection.js +95 -0
  35. package/dist/cli/lib/validation/types.js +1 -0
  36. package/dist/cli/lib/validation.js +1 -1770
  37. package/dist/core/check-issues.js +6 -0
  38. package/dist/core/completion-verdict.js +341 -0
  39. package/dist/core/contract-lint.js +221 -6
  40. package/dist/core/external-evidence.js +9 -0
  41. package/dist/core/public-json-contracts.js +21 -0
  42. package/dist/core/repeated-failure.js +179 -0
  43. package/dist/core/repro-evidence.js +134 -0
  44. package/dist/core/scope-risk.js +64 -0
  45. package/dist/core/skill-route-alignment.js +20 -0
  46. package/dist/core/source-anchor-status.js +4 -1
  47. package/dist/core/test-selection.js +3 -0
  48. package/dist/core/validation-ratchet.js +196 -0
  49. package/dist/core/verification-evidence.js +249 -0
  50. package/examples/README.md +12 -4
  51. package/package.json +3 -3
  52. package/schemas/README.md +13 -3
  53. package/schemas/change-verification-report.schema.json +16 -2
  54. package/schemas/commands.schema.json +4 -0
  55. package/schemas/contract-lint-report.schema.json +29 -0
  56. package/schemas/dashboard-export.schema.json +310 -0
  57. package/schemas/explain-report.schema.json +173 -1
  58. package/schemas/latest-run-pointer.schema.json +601 -0
  59. package/schemas/run-receipt.schema.json +4 -0
  60. package/schemas/test-selection.schema.json +81 -0
  61. package/schemas/verify-report.schema.json +578 -1
  62. package/schemas/verify-run-manifest.schema.json +627 -0
  63. package/templates/default/i18n.toml +1 -1
  64. package/templates/default/locales/en/.mustflow/skills/INDEX.md +124 -29
  65. package/templates/default/locales/en/.mustflow/skills/routes.toml +289 -0
  66. package/templates/default/manifest.toml +29 -2
@@ -0,0 +1,249 @@
1
+ function uniqueSorted(values) {
2
+ return [...new Set([...values].filter((value) => typeof value === 'string' && value.length > 0))].sort((left, right) => left.localeCompare(right));
3
+ }
4
+ function receiptEntries(results) {
5
+ return results
6
+ .filter((result) => !result.skipped)
7
+ .map((result) => ({
8
+ intent: result.intent,
9
+ status: result.status,
10
+ skipped: false,
11
+ verification_plan_id: result.verification_plan_id,
12
+ receipt_path: result.receipt_path,
13
+ receipt_sha256: result.receipt_sha256,
14
+ }));
15
+ }
16
+ function skippedCheckEntries(results) {
17
+ return results
18
+ .filter((result) => result.skipped)
19
+ .map((result) => ({
20
+ intent: result.intent,
21
+ reason: result.reason,
22
+ detail: result.detail,
23
+ }));
24
+ }
25
+ function resultForIntent(results, intent) {
26
+ return results.find((result) => result.intent === intent && !result.skipped) ?? null;
27
+ }
28
+ function requirementOutcome(input) {
29
+ if (input.selectedIntents.some((intent) => {
30
+ const result = resultForIntent(input.results, intent);
31
+ return result?.status === 'failed' || result?.status === 'timed_out' || result?.status === 'start_failed';
32
+ })) {
33
+ return 'contradicted';
34
+ }
35
+ if (input.gapCount > 0 || (input.selectedIntents.length === 0 && input.skippedIntents.length > 0)) {
36
+ return 'blocked';
37
+ }
38
+ if (input.selectedIntents.length === 0) {
39
+ return 'unverified';
40
+ }
41
+ if (input.skippedIntents.length > 0) {
42
+ return 'partially_verified';
43
+ }
44
+ return input.selectedIntents.every((intent) => resultForIntent(input.results, intent)?.status === 'passed')
45
+ ? 'verified'
46
+ : 'unverified';
47
+ }
48
+ function explanationFromVerdict(verdict) {
49
+ return {
50
+ verified_by: verdict.status === 'verified' ? [verdict.primary_reason] : [],
51
+ downgraded_by: verdict.status === 'partially_verified' || verdict.status === 'unverified' ? verdict.limitations : [],
52
+ blocked_by: verdict.status === 'blocked' ? verdict.blockers : [],
53
+ contradicted_by: verdict.status === 'contradicted' ? verdict.contradictions : [],
54
+ };
55
+ }
56
+ function coverageStatusFromOutcome(outcome) {
57
+ if (outcome === 'verified') {
58
+ return 'covered';
59
+ }
60
+ if (outcome === 'partially_verified') {
61
+ return 'partially_covered';
62
+ }
63
+ if (outcome === 'unverified') {
64
+ return 'uncovered';
65
+ }
66
+ return outcome;
67
+ }
68
+ function receiptPathsForIntents(receipts, intents) {
69
+ const selected = new Set(intents);
70
+ return uniqueSorted(receipts
71
+ .filter((receipt) => receipt.intent !== null && selected.has(receipt.intent))
72
+ .map((receipt) => receipt.receipt_path));
73
+ }
74
+ function coverageMatrixFromRequirements(input) {
75
+ return input.requirements.map((requirement, index) => {
76
+ const matchingGaps = input.source === 'dashboard_snapshot'
77
+ ? input.gaps
78
+ : input.gaps.filter((gap) => gap.reason === requirement.reason);
79
+ const matchingSourceAnchorRisks = input.sourceAnchorRisks.filter((risk) => requirement.files.includes(risk.path));
80
+ const status = coverageStatusFromOutcome(requirement.outcome);
81
+ return {
82
+ criterion_id: `${input.source}:${index + 1}:${requirement.reason}`,
83
+ source: input.source,
84
+ statement: `Verification requirement: ${requirement.reason}`,
85
+ status: matchingSourceAnchorRisks.length > 0 && status === 'covered' ? 'partially_covered' : status,
86
+ requirement_reason: requirement.reason,
87
+ evidence: {
88
+ intents: uniqueSorted([...requirement.selected_intents, ...requirement.skipped_intents]),
89
+ receipt_paths: receiptPathsForIntents(input.receipts, requirement.selected_intents),
90
+ gap_reasons: uniqueSorted(matchingGaps.map((gap) => gap.detail)),
91
+ source_anchor_ids: uniqueSorted(matchingSourceAnchorRisks.map((risk) => risk.anchorId)),
92
+ },
93
+ };
94
+ });
95
+ }
96
+ function sourceAnchorRemainingRisks(sourceAnchorRisks) {
97
+ return sourceAnchorRisks.map((risk) => ({
98
+ code: 'source_anchor_invariant_review_required',
99
+ severity: 'high',
100
+ detail: `${risk.anchorId} in ${risk.path}:${risk.lineStart} is ${risk.status}; review its invariant before marking the task verified.`,
101
+ }));
102
+ }
103
+ function scopeDiffRemainingRisks(scopeDiffRisks) {
104
+ return scopeDiffRisks.map((risk) => ({
105
+ code: risk.code,
106
+ severity: risk.severity,
107
+ detail: risk.detail,
108
+ }));
109
+ }
110
+ function repeatedFailureRemainingRisks(repeatedFailureRisks) {
111
+ return repeatedFailureRisks.map((risk) => ({
112
+ code: risk.code,
113
+ severity: risk.severity,
114
+ detail: risk.detail,
115
+ }));
116
+ }
117
+ function validationRatchetRemainingRisks(validationRatchetRisks) {
118
+ return validationRatchetRisks.map((risk) => ({
119
+ code: risk.code,
120
+ severity: risk.severity,
121
+ detail: risk.detail,
122
+ }));
123
+ }
124
+ function reproEvidenceRemainingRisks(reproEvidenceRisks) {
125
+ return reproEvidenceRisks.map((risk) => ({
126
+ code: risk.code,
127
+ severity: risk.severity,
128
+ detail: risk.detail,
129
+ }));
130
+ }
131
+ function externalEvidenceRemainingRisks(externalEvidenceRisks) {
132
+ return externalEvidenceRisks.map((risk) => ({
133
+ code: risk.code,
134
+ severity: risk.severity,
135
+ detail: risk.detail,
136
+ }));
137
+ }
138
+ export function createVerifyEvidenceModel(input) {
139
+ const requirements = input.report.requirements.map((requirement) => {
140
+ const candidates = input.report.candidates.filter((candidate) => candidate.reason === requirement.reason);
141
+ const selectedIntents = uniqueSorted(candidates
142
+ .filter((candidate) => candidate.selectionState === 'selected')
143
+ .map((candidate) => candidate.intent));
144
+ const skippedIntents = uniqueSorted(candidates
145
+ .filter((candidate) => candidate.status !== 'runnable')
146
+ .map((candidate) => candidate.intent));
147
+ const gapCount = input.report.gaps.filter((gap) => gap.reason === requirement.reason).length;
148
+ return {
149
+ reason: requirement.reason,
150
+ files: [...requirement.files],
151
+ surfaces: [...requirement.surfaces],
152
+ candidate_intents: uniqueSorted(candidates.map((candidate) => candidate.intent)),
153
+ selected_intents: selectedIntents,
154
+ skipped_intents: skippedIntents,
155
+ gap_count: gapCount,
156
+ outcome: requirementOutcome({
157
+ selectedIntents,
158
+ skippedIntents,
159
+ gapCount,
160
+ results: input.results,
161
+ }),
162
+ };
163
+ });
164
+ const receipts = receiptEntries(input.results);
165
+ const sourceAnchorRisks = input.sourceAnchorRisks ?? [];
166
+ const scopeDiffRisks = input.scopeDiffRisks ?? [];
167
+ const repeatedFailureRisks = input.repeatedFailureRisks ?? [];
168
+ const validationRatchetRisks = input.validationRatchetRisks ?? [];
169
+ const reproEvidence = input.reproEvidence ?? null;
170
+ const reproEvidenceRisks = input.reproEvidenceRisks ?? [];
171
+ const externalChecks = input.externalChecks ?? [];
172
+ const externalEvidenceRisks = input.externalEvidenceRisks ?? [];
173
+ const gaps = input.report.gaps.map((gap) => ({
174
+ reason: gap.reason,
175
+ intent: null,
176
+ status: 'missing',
177
+ detail: gap.detail,
178
+ files: [...gap.files],
179
+ surfaces: [...gap.surfaces],
180
+ }));
181
+ return {
182
+ schema_version: '1',
183
+ source: 'mf_verify',
184
+ verification_plan_id: input.verificationPlanId,
185
+ requirements,
186
+ coverage_matrix: coverageMatrixFromRequirements({
187
+ source: 'verification_requirement',
188
+ requirements,
189
+ receipts,
190
+ gaps,
191
+ sourceAnchorRisks,
192
+ }),
193
+ receipts,
194
+ skipped_checks: skippedCheckEntries(input.results),
195
+ gaps,
196
+ remaining_risks: [
197
+ ...sourceAnchorRemainingRisks(sourceAnchorRisks),
198
+ ...scopeDiffRemainingRisks(scopeDiffRisks),
199
+ ...repeatedFailureRemainingRisks(repeatedFailureRisks),
200
+ ...validationRatchetRemainingRisks(validationRatchetRisks),
201
+ ...reproEvidenceRemainingRisks(reproEvidenceRisks),
202
+ ...externalEvidenceRemainingRisks(externalEvidenceRisks),
203
+ ],
204
+ ...(reproEvidence ? { repro_evidence: reproEvidence } : {}),
205
+ ...(externalChecks.length > 0 ? { external_checks: externalChecks } : {}),
206
+ explanation: explanationFromVerdict(input.verdict),
207
+ };
208
+ }
209
+ export function createDashboardEvidenceModel(input) {
210
+ const requirements = input.changedSurfaces.length > 0 || input.runnableIntents.length > 0
211
+ ? [
212
+ {
213
+ reason: 'dashboard_snapshot',
214
+ files: [],
215
+ surfaces: [...input.changedSurfaces],
216
+ candidate_intents: uniqueSorted(input.runnableIntents),
217
+ selected_intents: [],
218
+ skipped_intents: uniqueSorted(input.skippedChecks.map((check) => check.intent)),
219
+ gap_count: input.gaps.length,
220
+ outcome: input.verdict.status,
221
+ },
222
+ ]
223
+ : [];
224
+ const receipts = input.latestReceipt ? [input.latestReceipt] : [];
225
+ const sourceAnchorRisks = input.sourceAnchorRisks ?? [];
226
+ const scopeDiffRisks = input.scopeDiffRisks ?? [];
227
+ return {
228
+ schema_version: '1',
229
+ source: 'dashboard_export',
230
+ verification_plan_id: null,
231
+ requirements,
232
+ coverage_matrix: coverageMatrixFromRequirements({
233
+ source: 'dashboard_snapshot',
234
+ requirements,
235
+ receipts,
236
+ gaps: input.gaps,
237
+ sourceAnchorRisks,
238
+ }),
239
+ receipts,
240
+ skipped_checks: input.skippedChecks,
241
+ gaps: input.gaps,
242
+ remaining_risks: [
243
+ ...input.remainingRisks,
244
+ ...sourceAnchorRemainingRisks(sourceAnchorRisks),
245
+ ...scopeDiffRemainingRisks(scopeDiffRisks),
246
+ ],
247
+ explanation: explanationFromVerdict(input.verdict),
248
+ };
249
+ }
@@ -4,10 +4,18 @@ This directory contains human-readable project examples.
4
4
 
5
5
  These examples are not test fixtures and are not copied by `mf init`. They illustrate the structure of a project before and after integrating mustflow, while excluding generated or template-owned files from the example content.
6
6
 
7
- Current examples:
7
+ ## Choose an Example
8
+
9
+ If you are new to mustflow:
8
10
 
9
- - `docs-only/`: A documentation-focused repository with explicit checks for missing code.
10
- - `host-instruction-conflicts/`: A repository where host-specific instructions overlap with mustflow rules.
11
11
  - `minimal-js/`: A small JavaScript package before and after adding mustflow.
12
+
13
+ If you are building or integrating an AI coding tool:
14
+
15
+ - `host-instruction-conflicts/`: A repository where host-specific instructions overlap with mustflow rules.
12
16
  - `missing-command-contracts/`: A repository with package scripts that are not yet defined as mustflow command intents.
13
- - `nested-repos/`: Parent and child repositories with separate local contracts.
17
+
18
+ If you are studying repository structure:
19
+
20
+ - `docs-only/`: A documentation-focused repository with explicit checks for missing code.
21
+ - `nested-repos/`: Parent and child repositories with separate local contracts.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mustflow",
3
- "version": "1.31.0",
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.6.2",
73
+ "@types/node": "^25.8.0",
74
74
  "@types/sql.js": "^1.4.11",
75
- "fast-check": "^4.7.0",
75
+ "fast-check": "^4.8.0",
76
76
  "typescript": "^6.0.3"
77
77
  },
78
78
  "dependencies": {
package/schemas/README.md CHANGED
@@ -12,15 +12,21 @@ Current schemas:
12
12
  including bounded declared-write drift metadata, a safe latest-run performance summary, and optional
13
13
  structured phase timings and selection summaries
14
14
  - `commands.schema.json`: parsed `.mustflow/config/commands.toml`
15
+ - `test-selection.schema.json`: parsed optional `.mustflow/config/test-selection.toml`
15
16
  - `contract-lint-report.schema.json`: output of `mf contract-lint --json`
16
17
  - `dashboard-export.schema.json`: bounded static export written by `mf dashboard --export-json <path>`,
17
- including output policy, redaction and truncation metadata, and the dashboard harness report
18
+ including output policy, redaction and truncation metadata, the dashboard harness report, and
19
+ the evidence-based completion verdict, evidence model, and conservative coverage matrix for the
20
+ exported snapshot
18
21
  - `classify-report.schema.json`: output of `mf classify --changed --json` and
19
22
  `mf classify <path...> --json`
20
23
  - `impact-report.schema.json`: output of `mf impact --changed --json` and
21
24
  `mf impact <path...> --json`
22
25
  - `line-endings-report.schema.json`: output of `mf line-endings check --json` and
23
26
  `mf line-endings normalize --json`
27
+ - `latest-run-pointer.schema.json`: `.mustflow/state/runs/latest.json` when `mf verify` writes a
28
+ pointer to the latest verify run bundle, including the verify completion verdict, evidence model,
29
+ and coverage matrix
24
30
  - `handoff-validation-report.schema.json`: output of
25
31
  `mf handoff validate <path> --json`
26
32
  - `version-sources-report.schema.json`: output of `mf version-sources --json`
@@ -28,9 +34,13 @@ Current schemas:
28
34
  - `explain-report.schema.json`: output of `mf explain authority --json`, `mf explain command --json`,
29
35
  `mf explain verify --reason <event> --json`, `mf explain retention --json`, `mf explain skills --json`,
30
36
  and `mf explain surface --json`. Verify explanations include the shared `decisionGraph` evidence model.
31
- - `verify-report.schema.json`: output of `mf verify --reason <event> --json`
37
+ - `verify-report.schema.json`: output of `mf verify --reason <event> --json`, including an
38
+ evidence-based completion verdict and evidence model with a conservative coverage matrix for the
39
+ selected receipts and skipped checks
40
+ - `verify-run-manifest.schema.json`: `.mustflow/state/runs/verify-latest/manifest.json`, including
41
+ the same completion verdict, evidence model, and coverage matrix as the verify report
32
42
  - `change-verification-report.schema.json`: output of `mf verify --reason <event> --plan-only --json` and
33
- `mf verify --from-plan <classify-report.json> --plan-only --json`, including the `decision_graph` that links
43
+ `mf verify --from-classification <classify-report.json> --plan-only --json`, including the `decision_graph` that links
34
44
  changed surfaces, classification reasons, command candidates, eligibility, selected or not-selected state,
35
45
  effects, and gaps.
36
46
  Local-index command-effect graphs are explanation-only and cannot grant command authority.
@@ -13,7 +13,8 @@
13
13
  "candidates",
14
14
  "gaps",
15
15
  "schedule",
16
- "decision_graph"
16
+ "decision_graph",
17
+ "test_selection"
17
18
  ],
18
19
  "properties": {
19
20
  "schema_version": {
@@ -28,6 +29,10 @@
28
29
  "classification_summary": {
29
30
  "$ref": "#/$defs/classificationSummary"
30
31
  },
32
+ "verification_plan_id": {
33
+ "type": "string",
34
+ "pattern": "^sha256:[0-9a-f]{64}$"
35
+ },
31
36
  "requirements": {
32
37
  "type": "array",
33
38
  "items": {
@@ -252,7 +257,16 @@
252
257
  "verificationCandidate": {
253
258
  "type": "object",
254
259
  "additionalProperties": false,
255
- "required": ["reason", "intent", "status", "skipReason", "detail"],
260
+ "required": [
261
+ "reason",
262
+ "intent",
263
+ "status",
264
+ "skipReason",
265
+ "detail",
266
+ "candidateState",
267
+ "eligibilityState",
268
+ "selectionState"
269
+ ],
256
270
  "properties": {
257
271
  "reason": {
258
272
  "type": "string"
@@ -147,6 +147,10 @@
147
147
  },
148
148
  "env_policy": { "$ref": "#/$defs/envPolicy" },
149
149
  "env_allowlist": { "$ref": "#/$defs/stringArray" },
150
+ "manual_start_hint": { "type": "string" },
151
+ "health_check_url": { "type": "string" },
152
+ "stop_instruction": { "type": "string" },
153
+ "related_oneshot_checks": { "$ref": "#/$defs/stringArray" },
150
154
  "effects": {
151
155
  "type": "array",
152
156
  "items": { "$ref": "#/$defs/effect" }
@@ -55,6 +55,35 @@
55
55
  "type": "array",
56
56
  "items": { "type": "string" }
57
57
  },
58
+ "suggestions": {
59
+ "type": "array",
60
+ "items": {
61
+ "type": "object",
62
+ "additionalProperties": false,
63
+ "required": [
64
+ "sourceFile",
65
+ "sourceKind",
66
+ "sourceName",
67
+ "commandHint",
68
+ "suggestedIntent",
69
+ "status",
70
+ "reason",
71
+ "snippet"
72
+ ],
73
+ "properties": {
74
+ "sourceFile": { "type": "string" },
75
+ "sourceKind": {
76
+ "enum": ["package_script", "make_target", "just_recipe"]
77
+ },
78
+ "sourceName": { "type": "string" },
79
+ "commandHint": { "type": "string" },
80
+ "suggestedIntent": { "type": "string" },
81
+ "status": { "const": "unknown" },
82
+ "reason": { "type": "string" },
83
+ "snippet": { "type": "string" }
84
+ }
85
+ }
86
+ },
58
87
  "coverage": {
59
88
  "type": "object",
60
89
  "additionalProperties": false,