mustflow 2.85.4 → 2.99.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 (78) hide show
  1. package/dist/cli/commands/script-pack.js +10 -0
  2. package/dist/cli/i18n/en.js +183 -0
  3. package/dist/cli/i18n/es.js +183 -0
  4. package/dist/cli/i18n/fr.js +183 -0
  5. package/dist/cli/i18n/hi.js +183 -0
  6. package/dist/cli/i18n/ko.js +183 -0
  7. package/dist/cli/i18n/zh.js +183 -0
  8. package/dist/cli/lib/script-pack-registry.js +284 -1
  9. package/dist/cli/script-packs/code-change-impact.js +6 -0
  10. package/dist/cli/script-packs/code-import-cycle.js +193 -0
  11. package/dist/cli/script-packs/docs-link-integrity.js +145 -0
  12. package/dist/cli/script-packs/repo-approval-gate.js +100 -0
  13. package/dist/cli/script-packs/repo-git-ignore-audit.js +119 -0
  14. package/dist/cli/script-packs/repo-manifest-lock-drift.js +122 -0
  15. package/dist/cli/script-packs/repo-merge-conflict-scan.js +123 -0
  16. package/dist/cli/script-packs/repo-skill-route-audit.js +86 -0
  17. package/dist/cli/script-packs/repo-version-source.js +92 -0
  18. package/dist/cli/script-packs/test-performance-report.js +247 -0
  19. package/dist/cli/script-packs/test-regression-selector.js +167 -0
  20. package/dist/core/change-impact.js +23 -51
  21. package/dist/core/change-surface-classification.js +198 -0
  22. package/dist/core/docs-link-integrity.js +443 -0
  23. package/dist/core/import-cycle.js +152 -0
  24. package/dist/core/public-json-contracts.js +116 -0
  25. package/dist/core/repo-approval-gate.js +116 -0
  26. package/dist/core/repo-git-ignore-audit.js +302 -0
  27. package/dist/core/repo-manifest-lock-drift.js +321 -0
  28. package/dist/core/repo-merge-conflict-scan.js +335 -0
  29. package/dist/core/repo-version-source.js +82 -0
  30. package/dist/core/script-pack-suggestions.js +77 -1
  31. package/dist/core/skill-route-audit.js +354 -0
  32. package/dist/core/test-performance-report.js +697 -0
  33. package/dist/core/test-regression-selector.js +335 -0
  34. package/package.json +1 -1
  35. package/schemas/README.md +40 -2
  36. package/schemas/change-impact-report.schema.json +35 -1
  37. package/schemas/import-cycle-report.schema.json +157 -0
  38. package/schemas/link-integrity-report.schema.json +176 -0
  39. package/schemas/repo-approval-gate-report.schema.json +115 -0
  40. package/schemas/repo-git-ignore-audit-report.schema.json +201 -0
  41. package/schemas/repo-manifest-lock-drift-report.schema.json +202 -0
  42. package/schemas/repo-merge-conflict-scan-report.schema.json +169 -0
  43. package/schemas/repo-version-source-report.schema.json +127 -0
  44. package/schemas/skill-route-audit-report.schema.json +144 -0
  45. package/schemas/test-performance-report.schema.json +319 -0
  46. package/schemas/test-regression-selector-report.schema.json +187 -0
  47. package/templates/default/i18n.toml +66 -18
  48. package/templates/default/locales/en/.mustflow/skills/INDEX.md +45 -8
  49. package/templates/default/locales/en/.mustflow/skills/api-access-control-review/SKILL.md +48 -27
  50. package/templates/default/locales/en/.mustflow/skills/api-failure-triage/SKILL.md +270 -0
  51. package/templates/default/locales/en/.mustflow/skills/auth-flow-triage/SKILL.md +192 -0
  52. package/templates/default/locales/en/.mustflow/skills/auth-permission-change/SKILL.md +59 -13
  53. package/templates/default/locales/en/.mustflow/skills/backend-log-evidence-review/SKILL.md +14 -5
  54. package/templates/default/locales/en/.mustflow/skills/cache-integrity-review/SKILL.md +30 -15
  55. package/templates/default/locales/en/.mustflow/skills/change-blast-radius-review/SKILL.md +45 -32
  56. package/templates/default/locales/en/.mustflow/skills/ci-pipeline-triage/SKILL.md +200 -0
  57. package/templates/default/locales/en/.mustflow/skills/clarifying-question-gate/SKILL.md +87 -13
  58. package/templates/default/locales/en/.mustflow/skills/docker-runtime-triage/SKILL.md +191 -0
  59. package/templates/default/locales/en/.mustflow/skills/go-code-change/SKILL.md +18 -13
  60. package/templates/default/locales/en/.mustflow/skills/line-ending-hygiene/SKILL.md +18 -10
  61. package/templates/default/locales/en/.mustflow/skills/llm-hallucination-control-review/SKILL.md +4 -1
  62. package/templates/default/locales/en/.mustflow/skills/motion-system-contract-review/SKILL.md +155 -0
  63. package/templates/default/locales/en/.mustflow/skills/next-action-menu/SKILL.md +177 -0
  64. package/templates/default/locales/en/.mustflow/skills/observability-debuggability-review/SKILL.md +15 -7
  65. package/templates/default/locales/en/.mustflow/skills/payment-integrity-review/SKILL.md +59 -35
  66. package/templates/default/locales/en/.mustflow/skills/powershell-code-change/SKILL.md +16 -6
  67. package/templates/default/locales/en/.mustflow/skills/prompt-contract-quality-review/SKILL.md +4 -1
  68. package/templates/default/locales/en/.mustflow/skills/python-code-change/SKILL.md +19 -10
  69. package/templates/default/locales/en/.mustflow/skills/rag-pipeline-triage/SKILL.md +206 -0
  70. package/templates/default/locales/en/.mustflow/skills/routes.toml +54 -0
  71. package/templates/default/locales/en/.mustflow/skills/rust-code-change/SKILL.md +10 -4
  72. package/templates/default/locales/en/.mustflow/skills/search-index-integrity-review/SKILL.md +181 -0
  73. package/templates/default/locales/en/.mustflow/skills/service-boundary-architecture/SKILL.md +37 -23
  74. package/templates/default/locales/en/.mustflow/skills/test-suite-performance-review/SKILL.md +9 -0
  75. package/templates/default/locales/en/.mustflow/skills/typescript-code-change/SKILL.md +14 -9
  76. package/templates/default/locales/en/.mustflow/skills/vector-search-integrity-review/SKILL.md +209 -0
  77. package/templates/default/locales/en/.mustflow/skills/version-freshness-check/SKILL.md +16 -14
  78. package/templates/default/manifest.toml +64 -1
@@ -0,0 +1,335 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { createHash } from 'node:crypto';
3
+ import { existsSync } from 'node:fs';
4
+ import path from 'node:path';
5
+ import { classifyChangeSurface, normalizeChangePath, selectorFallbackForChangedPath, statusFromGitNameStatus, } from './change-surface-classification.js';
6
+ import { inspectDependencyGraph } from './dependency-graph.js';
7
+ export const TEST_REGRESSION_SELECTOR_PACK_ID = 'test';
8
+ export const TEST_REGRESSION_SELECTOR_SCRIPT_ID = 'regression-selector';
9
+ export const TEST_REGRESSION_SELECTOR_SCRIPT_REF = `${TEST_REGRESSION_SELECTOR_PACK_ID}/${TEST_REGRESSION_SELECTOR_SCRIPT_ID}`;
10
+ const DEFAULT_BASE_REF = 'HEAD';
11
+ const DEFAULT_MAX_FILES = 200;
12
+ const DEFAULT_MAX_TESTS = 100;
13
+ function normalizeRelativePath(value) {
14
+ return normalizeChangePath(value);
15
+ }
16
+ function sha256Tagged(value) {
17
+ return `sha256:${createHash('sha256').update(value).digest('hex')}`;
18
+ }
19
+ function runGit(root, args) {
20
+ const result = spawnSync('git', [...args], {
21
+ cwd: root,
22
+ encoding: 'utf8',
23
+ stdio: ['ignore', 'pipe', 'pipe'],
24
+ windowsHide: true,
25
+ maxBuffer: 16 * 1024 * 1024,
26
+ });
27
+ return {
28
+ ok: result.status === 0,
29
+ stdout: result.stdout ?? '',
30
+ stderr: result.stderr ?? '',
31
+ status: result.status,
32
+ };
33
+ }
34
+ function isInsideGitWorktree(root) {
35
+ const result = runGit(root, ['rev-parse', '--is-inside-work-tree']);
36
+ return result.ok && result.stdout.trim() === 'true';
37
+ }
38
+ function makeFinding(code, severity, pathValue, message) {
39
+ return { code, severity, path: pathValue, message };
40
+ }
41
+ function statusFromCode(code) {
42
+ return statusFromGitNameStatus(code);
43
+ }
44
+ function surfaceForPath(relativePath) {
45
+ return classifyChangeSurface(relativePath, { includeDocsSite: true });
46
+ }
47
+ function parseNameStatus(stdout) {
48
+ const files = [];
49
+ for (const line of stdout.split(/\r?\n/u)) {
50
+ if (!line.trim()) {
51
+ continue;
52
+ }
53
+ const [statusCode = 'M', firstPath = '', secondPath = ''] = line.split('\t');
54
+ const currentPath = normalizeRelativePath(secondPath || firstPath);
55
+ const previousPath = secondPath ? normalizeRelativePath(firstPath) : null;
56
+ files.push({
57
+ path: currentPath,
58
+ previous_path: previousPath,
59
+ status: statusFromCode(statusCode),
60
+ surface: surfaceForPath(currentPath),
61
+ });
62
+ }
63
+ return files;
64
+ }
65
+ function addUntrackedFiles(root, paths, files) {
66
+ const untracked = runGit(root, ['ls-files', '--others', '--exclude-standard', '--', ...paths]);
67
+ if (!untracked.ok) {
68
+ return;
69
+ }
70
+ const known = new Set(files.map((file) => file.path));
71
+ for (const line of untracked.stdout.split(/\r?\n/u)) {
72
+ const relativePath = normalizeRelativePath(line.trim());
73
+ if (!relativePath || relativePath === '.' || known.has(relativePath)) {
74
+ continue;
75
+ }
76
+ files.push({
77
+ path: relativePath,
78
+ previous_path: null,
79
+ status: 'untracked',
80
+ surface: surfaceForPath(relativePath),
81
+ });
82
+ }
83
+ }
84
+ function collectChangedFiles(root, policy, findings, issues) {
85
+ if (!isInsideGitWorktree(root)) {
86
+ const message = 'Git worktree is unavailable; regression-selector cannot inspect changed files.';
87
+ findings.push(makeFinding('test_regression_selector_git_unavailable', 'low', '.', message));
88
+ issues.push(message);
89
+ return [];
90
+ }
91
+ const diffArgs = policy.head_ref
92
+ ? ['diff', '--name-status', '--diff-filter=ACMRTD', policy.base_ref, policy.head_ref, '--', ...policy.path_filters]
93
+ : ['diff', '--name-status', '--diff-filter=ACMRTD', policy.base_ref, '--', ...policy.path_filters];
94
+ const diff = runGit(root, diffArgs);
95
+ if (!diff.ok) {
96
+ const detail = diff.stderr.trim() || diff.stdout.trim() || `git diff exited with ${diff.status}`;
97
+ findings.push(makeFinding('test_regression_selector_invalid_ref', 'high', '.', detail));
98
+ issues.push(detail);
99
+ return [];
100
+ }
101
+ const files = parseNameStatus(diff.stdout);
102
+ if (!policy.head_ref) {
103
+ addUntrackedFiles(root, policy.path_filters, files);
104
+ }
105
+ files.sort((left, right) => left.path.localeCompare(right.path));
106
+ if (files.length > policy.max_files) {
107
+ const message = `Regression selector matched ${files.length} files; max_files is ${policy.max_files}.`;
108
+ findings.push(makeFinding('test_regression_selector_max_files_exceeded', 'high', '.', message));
109
+ issues.push(message);
110
+ }
111
+ return files.slice(0, policy.max_files);
112
+ }
113
+ function siblingBase(relativePath) {
114
+ const normalized = normalizeRelativePath(relativePath);
115
+ const directory = path.posix.dirname(normalized);
116
+ const parsed = path.posix.parse(normalized);
117
+ return directory === '.' ? parsed.name : `${directory}/${parsed.name}`;
118
+ }
119
+ function testCandidatesFor(relativePath) {
120
+ const base = siblingBase(relativePath);
121
+ const name = path.posix.basename(base);
122
+ return [
123
+ `${base}.test.ts`,
124
+ `${base}.test.js`,
125
+ `${base}.spec.ts`,
126
+ `${base}.spec.js`,
127
+ `tests/${name}.test.js`,
128
+ `tests/${name}.test.ts`,
129
+ `tests/cli/${name}.test.js`,
130
+ ];
131
+ }
132
+ function addTestCandidate(root, candidates, pathValue, reason, sourcePath, confidence) {
133
+ const normalized = normalizeRelativePath(pathValue);
134
+ const existing = candidates.get(normalized);
135
+ if (existing) {
136
+ const reasons = [...new Set([...existing.reasons, reason])];
137
+ const sourcePaths = [...new Set([...existing.source_paths, sourcePath])];
138
+ const primaryReason = confidence > existing.confidence ? reason : existing.reason;
139
+ const primarySourcePath = confidence > existing.confidence ? sourcePath : existing.source_path;
140
+ candidates.set(normalized, {
141
+ ...existing,
142
+ reason: primaryReason,
143
+ reasons,
144
+ source_path: primarySourcePath,
145
+ source_paths: sourcePaths,
146
+ confidence: Math.max(existing.confidence, confidence),
147
+ });
148
+ return;
149
+ }
150
+ candidates.set(normalized, {
151
+ path: normalized,
152
+ exists: existsSync(path.join(root, ...normalized.split('/'))),
153
+ reason,
154
+ reasons: [reason],
155
+ source_path: sourcePath,
156
+ source_paths: [sourcePath],
157
+ confidence,
158
+ });
159
+ }
160
+ function addChangedFileCandidates(root, changedFiles, candidates) {
161
+ for (const changedFile of changedFiles) {
162
+ if (changedFile.surface === 'test' && changedFile.status !== 'deleted') {
163
+ addTestCandidate(root, candidates, changedFile.path, 'changed_test', changedFile.path, 1);
164
+ }
165
+ if (changedFile.surface === 'source' && changedFile.status !== 'deleted') {
166
+ for (const candidatePath of testCandidatesFor(changedFile.path)) {
167
+ addTestCandidate(root, candidates, candidatePath, 'sibling_test', changedFile.path, 0.7);
168
+ }
169
+ }
170
+ }
171
+ }
172
+ function addImporterCandidates(root, changedFiles, candidates, issues) {
173
+ const sourcePaths = changedFiles
174
+ .filter((file) => file.surface === 'source' && file.status !== 'deleted')
175
+ .map((file) => file.path);
176
+ if (sourcePaths.length === 0) {
177
+ return;
178
+ }
179
+ const dependencyReport = inspectDependencyGraph(root, {
180
+ paths: sourcePaths,
181
+ maxDepth: 1,
182
+ maxFiles: Math.max(sourcePaths.length, 50),
183
+ maxFileBytes: 256 * 1024,
184
+ maxNodes: 200,
185
+ maxEdges: 300,
186
+ });
187
+ if (!dependencyReport.ok) {
188
+ for (const issue of dependencyReport.issues) {
189
+ issues.push(`dependency-graph: ${issue}`);
190
+ }
191
+ }
192
+ const changedPathSet = new Set(sourcePaths);
193
+ for (const edge of dependencyReport.edges) {
194
+ if (!changedPathSet.has(edge.target_path) || changedPathSet.has(edge.source_path)) {
195
+ continue;
196
+ }
197
+ for (const candidatePath of testCandidatesFor(edge.source_path)) {
198
+ addTestCandidate(root, candidates, candidatePath, 'importer_sibling_test', edge.target_path, 0.6);
199
+ }
200
+ }
201
+ }
202
+ function fallbackForChangedFile(changedFile) {
203
+ return selectorFallbackForChangedPath(changedFile);
204
+ }
205
+ function chooseRecommendedIntent(selectedTests, fallbacks, changedFiles) {
206
+ if (fallbacks.some((fallback) => fallback.recommended_intent === 'test')) {
207
+ return 'test';
208
+ }
209
+ if (fallbacks.some((fallback) => fallback.recommended_intent === 'test_release')) {
210
+ return 'test_release';
211
+ }
212
+ if (fallbacks.length > 0) {
213
+ return 'test_related';
214
+ }
215
+ if (selectedTests.length > 0 || changedFiles.some((file) => file.surface === 'source' || file.surface === 'test')) {
216
+ return 'test_related_cached';
217
+ }
218
+ return 'changes_status';
219
+ }
220
+ function confidenceFor(selectedTests, fallbacks, changedFiles) {
221
+ if (fallbacks.length > 0) {
222
+ return 'low';
223
+ }
224
+ if (selectedTests.length === 0 || changedFiles.length === 0) {
225
+ return 'low';
226
+ }
227
+ if (changedFiles.every((file) => file.surface === 'test')) {
228
+ return 'high';
229
+ }
230
+ return 'medium';
231
+ }
232
+ function selectionStatus(selectedTests, fallbacks, changedFiles) {
233
+ if (fallbacks.length > 0) {
234
+ return 'fallback';
235
+ }
236
+ if (changedFiles.length === 0 || selectedTests.length === 0) {
237
+ return 'empty';
238
+ }
239
+ return 'selected';
240
+ }
241
+ function createRunHint(selectedTests, recommendedIntent) {
242
+ if (recommendedIntent === 'test_related_cached' && selectedTests.length > 0) {
243
+ return `MUSTFLOW_TEST_CHANGED_FILES=${selectedTests.map((test) => test.path).join(',')} mf run test_related_cached`;
244
+ }
245
+ if (recommendedIntent === 'test_related' || recommendedIntent === 'test_release' || recommendedIntent === 'test') {
246
+ return `mf run ${recommendedIntent}`;
247
+ }
248
+ return null;
249
+ }
250
+ export function createTestRegressionSelectorReport(projectRoot, options = {}) {
251
+ const root = path.resolve(projectRoot);
252
+ const policy = {
253
+ base_ref: options.baseRef ?? DEFAULT_BASE_REF,
254
+ head_ref: options.headRef ?? null,
255
+ compare_worktree: options.headRef === undefined || options.headRef === null,
256
+ max_files: options.maxFiles ?? DEFAULT_MAX_FILES,
257
+ max_tests: options.maxTests ?? DEFAULT_MAX_TESTS,
258
+ path_filters: (options.paths ?? []).map(normalizeRelativePath),
259
+ };
260
+ const findings = [];
261
+ const issues = [];
262
+ const changedFiles = collectChangedFiles(root, policy, findings, issues);
263
+ const candidates = new Map();
264
+ addChangedFileCandidates(root, changedFiles, candidates);
265
+ addImporterCandidates(root, changedFiles, candidates, issues);
266
+ const fallbackMap = new Map();
267
+ for (const changedFile of changedFiles) {
268
+ const fallback = fallbackForChangedFile(changedFile);
269
+ if (fallback) {
270
+ fallbackMap.set(`${fallback.reason}:${fallback.path}`, fallback);
271
+ }
272
+ }
273
+ if (findings.some((finding) => finding.code === 'test_regression_selector_max_files_exceeded')) {
274
+ fallbackMap.set('fallback_truncated:.', {
275
+ reason: 'fallback_truncated',
276
+ path: '.',
277
+ message: 'Changed-file input was truncated by max_files.',
278
+ recommended_intent: 'test',
279
+ });
280
+ }
281
+ if (changedFiles.length === 0) {
282
+ fallbackMap.set('no_changed_files:.', {
283
+ reason: 'no_changed_files',
284
+ path: '.',
285
+ message: 'No changed files were detected for regression selection.',
286
+ recommended_intent: 'changes_status',
287
+ });
288
+ }
289
+ const sortedCandidates = [...candidates.values()].sort((left, right) => Number(right.exists) - Number(left.exists) || right.confidence - left.confidence || left.path.localeCompare(right.path));
290
+ const selectedTests = sortedCandidates.filter((candidate) => candidate.exists).slice(0, policy.max_tests);
291
+ const missingCandidates = sortedCandidates.filter((candidate) => !candidate.exists).slice(0, policy.max_tests);
292
+ const truncated = sortedCandidates.filter((candidate) => candidate.exists).length > selectedTests.length;
293
+ if (truncated) {
294
+ const message = `Regression selector found more than ${policy.max_tests} existing test candidates.`;
295
+ findings.push(makeFinding('test_regression_selector_max_tests_exceeded', 'medium', '.', message));
296
+ issues.push(message);
297
+ }
298
+ const fallbacks = [...fallbackMap.values()].sort((left, right) => left.recommended_intent.localeCompare(right.recommended_intent) || left.path.localeCompare(right.path));
299
+ const recommendedIntent = chooseRecommendedIntent(selectedTests, fallbacks, changedFiles);
300
+ const selectorStatus = selectionStatus(selectedTests, fallbacks, changedFiles);
301
+ const status = findings.some((finding) => finding.severity === 'high' || finding.severity === 'critical')
302
+ ? 'failed'
303
+ : 'passed';
304
+ const summary = {
305
+ changed_file_count: changedFiles.length,
306
+ selected_test_count: selectedTests.length,
307
+ missing_candidate_count: missingCandidates.length,
308
+ fallback_count: fallbacks.length,
309
+ selection_status: selectorStatus,
310
+ confidence: confidenceFor(selectedTests, fallbacks, changedFiles),
311
+ recommended_intent: recommendedIntent,
312
+ };
313
+ return {
314
+ schema_version: '1',
315
+ command: 'script-pack',
316
+ pack_id: TEST_REGRESSION_SELECTOR_PACK_ID,
317
+ script_id: TEST_REGRESSION_SELECTOR_SCRIPT_ID,
318
+ script_ref: TEST_REGRESSION_SELECTOR_SCRIPT_REF,
319
+ action: 'select',
320
+ status,
321
+ ok: status === 'passed',
322
+ mustflow_root: root,
323
+ policy,
324
+ input_hash: sha256Tagged(JSON.stringify({ policy, changedFiles, selectedTests, fallbacks })),
325
+ summary,
326
+ changed_files: changedFiles,
327
+ selected_tests: selectedTests,
328
+ missing_candidates: missingCandidates,
329
+ fallbacks,
330
+ run_hint: createRunHint(selectedTests, recommendedIntent),
331
+ truncated,
332
+ findings,
333
+ issues,
334
+ };
335
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mustflow",
3
- "version": "2.85.4",
3
+ "version": "2.99.0",
4
4
  "description": "Agent workflow documents and CLI for mustflow repository roots.",
5
5
  "type": "module",
6
6
  "license": "MIT-0",
package/schemas/README.md CHANGED
@@ -73,10 +73,15 @@ Current schemas:
73
73
  `mf script-pack run code/dependency-graph scan <path...> --json`, containing bounded relative
74
74
  TypeScript and JavaScript import graph nodes, edges, cycle hints, target flags, depth, importer
75
75
  counts, policy limits, and stable input-limit finding codes
76
+ - `import-cycle-report.schema.json`: output of
77
+ `mf script-pack run code/import-cycle check <path...> --json`, containing bounded relative
78
+ TypeScript and JavaScript import cycles with exact cycle paths, import line evidence, policy
79
+ limits, and stable cycle or input-limit finding codes
76
80
  - `change-impact-report.schema.json`: output of
77
81
  `mf script-pack run code/change-impact analyze [path...] --json`, containing git-diff changed
78
- files, surface classifications, bounded impact candidates, optional script-pack hints,
79
- verification intent hints, policy limits, and stable git or input-limit finding codes
82
+ files, surface classifications, bounded impact candidates, script-pack hints with related
83
+ verification intent and selected-test fallback evidence, verification intent hints, policy limits,
84
+ and stable git or input-limit finding codes
80
85
  - `code-symbol-read-report.schema.json`: output of
81
86
  `mf script-pack run code/symbol-read read <path> --start-line <line> --json`, containing a
82
87
  focused source snippet selected by source anchor, outline symbol line, or explicit line range with
@@ -95,6 +100,18 @@ Current schemas:
95
100
  `mf script-pack run docs/reference-drift check [path...] --json`, containing checked
96
101
  documentation references to `mf` commands, script-pack refs, schema files, repository paths,
97
102
  and stable stale-reference finding codes
103
+ - `link-integrity-report.schema.json`: output of
104
+ `mf script-pack run docs/link-integrity check [path...] --json`, containing checked Markdown
105
+ and MDX inline links, local file and anchor resolution status, skipped external URL metadata,
106
+ and stable missing-link finding codes
107
+ - `test-performance-report.schema.json`: output of
108
+ `mf script-pack run test/performance-report summarize --json`, containing retained run
109
+ performance evidence, slow intent summaries, timeout-pressure findings, selected-test fallback
110
+ signals, and phase bottleneck summaries from `.mustflow/state`
111
+ - `test-regression-selector-report.schema.json`: output of
112
+ `mf script-pack run test/regression-selector select --json`, containing changed files, likely
113
+ regression-test candidates with selection evidence, conservative fallback reasons, confidence,
114
+ and recommended verification intent hints
98
115
  - `config-chain-report.schema.json`: output of
99
116
  `mf script-pack run repo/config-chain inspect <path...> --json`, containing nearby package,
100
117
  TypeScript, ESLint, Prettier, Vite, Vitest, Tailwind, Jest, Playwright, and mustflow config files
@@ -114,6 +131,27 @@ Current schemas:
114
131
  - `generated-boundary-report.schema.json`: output of
115
132
  `mf script-pack run repo/generated-boundary check <path...> --json`, containing candidate path
116
133
  classifications for generated, ignored, protected, vendor, and cache boundaries before or after edits
134
+ - `repo-merge-conflict-scan-report.schema.json`: output of
135
+ `mf script-pack run repo/merge-conflict-scan check [path...] --json`, containing checked files,
136
+ redaction-safe Git merge conflict marker locations, marker summaries, and stable conflict-marker
137
+ finding codes
138
+ - `repo-git-ignore-audit-report.schema.json`: output of
139
+ `mf script-pack run repo/git-ignore-audit audit [path...] --json`, containing Git ignore source
140
+ metadata, explicit path visibility status, ignore-rule evidence, tracked-path caveats, and stable
141
+ ignored-path finding codes without reading ignored file content
142
+ - `repo-manifest-lock-drift-report.schema.json`: output of
143
+ `mf script-pack run repo/manifest-lock-drift check [path...] --json`, containing manifest-lock
144
+ metadata, locked file existence, hash comparison status, and stable drift finding codes without
145
+ rewriting `.mustflow/config/manifest.lock.toml`
146
+ - `skill-route-audit-report.schema.json`: output of
147
+ `mf script-pack run repo/skill-route-audit audit --json`, containing source skill, route metadata,
148
+ skill index, default template skill copy, manifest profile, and i18n metadata drift findings
149
+ - `repo-version-source-report.schema.json`: output of
150
+ `mf script-pack run repo/version-source inspect --json`, containing detected version sources,
151
+ release-versioning preference status, source authority counts, and missing-source findings
152
+ - `repo-approval-gate-report.schema.json`: output of
153
+ `mf script-pack run repo/approval-gate check --action <type> --json`, containing approval policy
154
+ decisions, required-action findings, and unreadable policy issues
117
155
  - `related-files-report.schema.json`: output of
118
156
  `mf script-pack run repo/related-files map <path...> --json`, containing conservative related-file
119
157
  candidates from direct imports, importers, same-basename siblings, parent configuration files, and
@@ -49,6 +49,32 @@
49
49
  "surface": {
50
50
  "enum": ["source", "test", "docs", "schema", "config", "package", "template", "workflow", "i18n", "unknown"]
51
51
  },
52
+ "selectorReason": {
53
+ "enum": [
54
+ "changed_test",
55
+ "sibling_test",
56
+ "importer_sibling_test",
57
+ "fallback_lockfile",
58
+ "fallback_package_metadata",
59
+ "fallback_template",
60
+ "fallback_compiler_or_runner_config",
61
+ "fallback_shared_test_fixture",
62
+ "fallback_migration_or_database",
63
+ "fallback_generated_contract",
64
+ "fallback_ci_workflow",
65
+ "fallback_mustflow_workflow",
66
+ "fallback_deleted",
67
+ "fallback_renamed",
68
+ "fallback_type_changed",
69
+ "fallback_package_or_template",
70
+ "fallback_schema",
71
+ "fallback_workflow_or_config",
72
+ "fallback_deleted_or_renamed",
73
+ "fallback_unknown",
74
+ "fallback_truncated",
75
+ "no_changed_files"
76
+ ]
77
+ },
52
78
  "policy": {
53
79
  "type": "object",
54
80
  "additionalProperties": false,
@@ -115,7 +141,15 @@
115
141
  "script_ref": { "type": "string" },
116
142
  "command": { "type": "string" },
117
143
  "reason": { "type": "string" },
118
- "confidence": { "type": "number", "minimum": 0, "maximum": 1 }
144
+ "confidence": { "type": "number", "minimum": 0, "maximum": 1 },
145
+ "related_intents": {
146
+ "type": "array",
147
+ "items": { "type": "string" }
148
+ },
149
+ "expected_fallback_reasons": {
150
+ "type": "array",
151
+ "items": { "$ref": "#/$defs/selectorReason" }
152
+ }
119
153
  }
120
154
  },
121
155
  "verificationHint": {
@@ -0,0 +1,157 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://mustflow.github.io/schemas/import-cycle-report.schema.json",
4
+ "title": "mustflow import-cycle report",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schema_version",
9
+ "command",
10
+ "pack_id",
11
+ "script_id",
12
+ "script_ref",
13
+ "action",
14
+ "status",
15
+ "ok",
16
+ "mustflow_root",
17
+ "policy",
18
+ "input_hash",
19
+ "targets",
20
+ "graph",
21
+ "cycles",
22
+ "truncated",
23
+ "findings",
24
+ "issues"
25
+ ],
26
+ "properties": {
27
+ "schema_version": { "const": "1" },
28
+ "command": { "const": "script-pack" },
29
+ "pack_id": { "const": "code" },
30
+ "script_id": { "const": "import-cycle" },
31
+ "script_ref": { "const": "code/import-cycle" },
32
+ "action": { "const": "check" },
33
+ "status": { "enum": ["passed", "failed", "error"] },
34
+ "ok": { "type": "boolean" },
35
+ "mustflow_root": { "type": "string" },
36
+ "policy": { "$ref": "#/$defs/policy" },
37
+ "input_hash": { "$ref": "#/$defs/sha256" },
38
+ "targets": { "type": "array", "items": { "$ref": "#/$defs/target" } },
39
+ "graph": { "$ref": "#/$defs/graph" },
40
+ "cycles": { "type": "array", "items": { "$ref": "#/$defs/cycle" } },
41
+ "truncated": { "type": "boolean" },
42
+ "findings": { "type": "array", "items": { "$ref": "#/$defs/finding" } },
43
+ "issues": { "type": "array", "items": { "type": "string" } }
44
+ },
45
+ "$defs": {
46
+ "sha256": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
47
+ "stringArray": { "type": "array", "items": { "type": "string" } },
48
+ "language": {
49
+ "enum": [
50
+ "typescript",
51
+ "tsx",
52
+ "javascript",
53
+ "jsx",
54
+ "javascript-module",
55
+ "javascript-commonjs",
56
+ "json",
57
+ "other"
58
+ ]
59
+ },
60
+ "targetKind": { "enum": ["file", "directory", "missing", "other", "unknown"] },
61
+ "edgeKind": { "enum": ["static_import", "static_export", "dynamic_import", "require"] },
62
+ "policy": {
63
+ "type": "object",
64
+ "additionalProperties": false,
65
+ "required": [
66
+ "max_file_bytes",
67
+ "max_files",
68
+ "max_depth",
69
+ "max_nodes",
70
+ "max_edges",
71
+ "extensions",
72
+ "ignored_directories",
73
+ "max_cycles"
74
+ ],
75
+ "properties": {
76
+ "max_file_bytes": { "type": "integer", "minimum": 1 },
77
+ "max_files": { "type": "integer", "minimum": 1 },
78
+ "max_depth": { "type": "integer", "minimum": 1 },
79
+ "max_nodes": { "type": "integer", "minimum": 1 },
80
+ "max_edges": { "type": "integer", "minimum": 1 },
81
+ "extensions": { "$ref": "#/$defs/stringArray" },
82
+ "ignored_directories": { "$ref": "#/$defs/stringArray" },
83
+ "max_cycles": { "type": "integer", "minimum": 1 }
84
+ }
85
+ },
86
+ "target": {
87
+ "type": "object",
88
+ "additionalProperties": false,
89
+ "required": ["input", "path", "exists", "kind", "language"],
90
+ "properties": {
91
+ "input": { "type": "string" },
92
+ "path": { "type": "string" },
93
+ "exists": { "type": ["boolean", "null"] },
94
+ "kind": { "$ref": "#/$defs/targetKind" },
95
+ "language": { "$ref": "#/$defs/language" }
96
+ }
97
+ },
98
+ "graph": {
99
+ "type": "object",
100
+ "additionalProperties": false,
101
+ "required": ["script_ref", "status", "node_count", "edge_count", "cycle_hint_count", "truncated"],
102
+ "properties": {
103
+ "script_ref": { "const": "code/dependency-graph" },
104
+ "status": { "enum": ["passed", "failed", "error"] },
105
+ "node_count": { "type": "integer", "minimum": 0 },
106
+ "edge_count": { "type": "integer", "minimum": 0 },
107
+ "cycle_hint_count": { "type": "integer", "minimum": 0 },
108
+ "truncated": { "type": "boolean" }
109
+ }
110
+ },
111
+ "edge": {
112
+ "type": "object",
113
+ "additionalProperties": false,
114
+ "required": ["source_path", "target_path", "specifier", "line", "kind"],
115
+ "properties": {
116
+ "source_path": { "type": "string" },
117
+ "target_path": { "type": "string" },
118
+ "specifier": { "type": "string" },
119
+ "line": { "type": "integer", "minimum": 1 },
120
+ "kind": { "$ref": "#/$defs/edgeKind" }
121
+ }
122
+ },
123
+ "cycle": {
124
+ "type": "object",
125
+ "additionalProperties": false,
126
+ "required": ["cycle_id", "path_count", "paths", "edges"],
127
+ "properties": {
128
+ "cycle_id": { "type": "string", "pattern": "^cycle:[a-f0-9]{12}$" },
129
+ "path_count": { "type": "integer", "minimum": 0 },
130
+ "paths": { "$ref": "#/$defs/stringArray" },
131
+ "edges": { "type": "array", "items": { "$ref": "#/$defs/edge" } }
132
+ }
133
+ },
134
+ "finding": {
135
+ "type": "object",
136
+ "additionalProperties": false,
137
+ "required": ["code", "severity", "message", "path"],
138
+ "properties": {
139
+ "code": {
140
+ "enum": [
141
+ "dependency_graph_path_outside_root",
142
+ "dependency_graph_unreadable_path",
143
+ "dependency_graph_max_files_exceeded",
144
+ "dependency_graph_max_nodes_exceeded",
145
+ "dependency_graph_max_edges_exceeded",
146
+ "import_cycle_detected",
147
+ "import_cycle_max_cycles_exceeded"
148
+ ]
149
+ },
150
+ "severity": { "enum": ["low", "medium", "high", "critical"] },
151
+ "message": { "type": "string" },
152
+ "path": { "type": "string" },
153
+ "cycle_id": { "type": "string", "pattern": "^cycle:[a-f0-9]{12}$" }
154
+ }
155
+ }
156
+ }
157
+ }