mustflow 2.84.0 → 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 (105) hide show
  1. package/README.md +11 -2
  2. package/dist/cli/commands/script-pack.js +14 -0
  3. package/dist/cli/i18n/en.js +262 -0
  4. package/dist/cli/i18n/es.js +262 -0
  5. package/dist/cli/i18n/fr.js +262 -0
  6. package/dist/cli/i18n/hi.js +262 -0
  7. package/dist/cli/i18n/ko.js +262 -0
  8. package/dist/cli/i18n/zh.js +262 -0
  9. package/dist/cli/lib/repo-map.js +27 -6
  10. package/dist/cli/lib/run-root-trust.js +15 -1
  11. package/dist/cli/lib/script-pack-registry.js +397 -0
  12. package/dist/cli/lib/validation/index.js +2 -2
  13. package/dist/cli/lib/validation/primitives.js +4 -1
  14. package/dist/cli/script-packs/code-change-impact.js +178 -0
  15. package/dist/cli/script-packs/code-dependency-graph.js +181 -0
  16. package/dist/cli/script-packs/code-import-cycle.js +193 -0
  17. package/dist/cli/script-packs/docs-link-integrity.js +145 -0
  18. package/dist/cli/script-packs/repo-approval-gate.js +100 -0
  19. package/dist/cli/script-packs/repo-env-contract.js +156 -0
  20. package/dist/cli/script-packs/repo-git-ignore-audit.js +119 -0
  21. package/dist/cli/script-packs/repo-manifest-lock-drift.js +122 -0
  22. package/dist/cli/script-packs/repo-merge-conflict-scan.js +123 -0
  23. package/dist/cli/script-packs/repo-secret-risk-scan.js +147 -0
  24. package/dist/cli/script-packs/repo-skill-route-audit.js +86 -0
  25. package/dist/cli/script-packs/repo-version-source.js +92 -0
  26. package/dist/cli/script-packs/test-performance-report.js +247 -0
  27. package/dist/cli/script-packs/test-regression-selector.js +167 -0
  28. package/dist/core/change-impact.js +355 -0
  29. package/dist/core/change-surface-classification.js +198 -0
  30. package/dist/core/change-verification.js +32 -5
  31. package/dist/core/config-loading.js +121 -4
  32. package/dist/core/dependency-graph.js +490 -0
  33. package/dist/core/docs-link-integrity.js +443 -0
  34. package/dist/core/env-contract.js +450 -0
  35. package/dist/core/import-cycle.js +152 -0
  36. package/dist/core/line-endings.js +26 -13
  37. package/dist/core/public-json-contracts.js +167 -0
  38. package/dist/core/repo-approval-gate.js +116 -0
  39. package/dist/core/repo-git-ignore-audit.js +302 -0
  40. package/dist/core/repo-manifest-lock-drift.js +321 -0
  41. package/dist/core/repo-merge-conflict-scan.js +335 -0
  42. package/dist/core/repo-version-source.js +82 -0
  43. package/dist/core/route-outline.js +57 -5
  44. package/dist/core/script-pack-suggestions.js +97 -1
  45. package/dist/core/secret-risk-scan.js +440 -0
  46. package/dist/core/skill-route-audit.js +354 -0
  47. package/dist/core/test-performance-report.js +697 -0
  48. package/dist/core/test-regression-selector.js +335 -0
  49. package/package.json +1 -1
  50. package/schemas/README.md +54 -0
  51. package/schemas/change-impact-report.schema.json +184 -0
  52. package/schemas/commands.schema.json +12 -0
  53. package/schemas/dependency-graph-report.schema.json +149 -0
  54. package/schemas/env-contract-report.schema.json +203 -0
  55. package/schemas/import-cycle-report.schema.json +157 -0
  56. package/schemas/link-integrity-report.schema.json +176 -0
  57. package/schemas/repo-approval-gate-report.schema.json +115 -0
  58. package/schemas/repo-git-ignore-audit-report.schema.json +201 -0
  59. package/schemas/repo-manifest-lock-drift-report.schema.json +202 -0
  60. package/schemas/repo-merge-conflict-scan-report.schema.json +169 -0
  61. package/schemas/repo-version-source-report.schema.json +127 -0
  62. package/schemas/secret-risk-scan-report.schema.json +152 -0
  63. package/schemas/skill-route-audit-report.schema.json +144 -0
  64. package/schemas/test-performance-report.schema.json +319 -0
  65. package/schemas/test-regression-selector-report.schema.json +187 -0
  66. package/templates/default/i18n.toml +80 -26
  67. package/templates/default/locales/en/.mustflow/skills/INDEX.md +51 -9
  68. package/templates/default/locales/en/.mustflow/skills/api-access-control-review/SKILL.md +48 -27
  69. package/templates/default/locales/en/.mustflow/skills/api-failure-triage/SKILL.md +270 -0
  70. package/templates/default/locales/en/.mustflow/skills/architecture-deepening-review/SKILL.md +28 -11
  71. package/templates/default/locales/en/.mustflow/skills/astro-code-change/SKILL.md +71 -27
  72. package/templates/default/locales/en/.mustflow/skills/auth-flow-triage/SKILL.md +192 -0
  73. package/templates/default/locales/en/.mustflow/skills/auth-permission-change/SKILL.md +59 -13
  74. package/templates/default/locales/en/.mustflow/skills/backend-log-evidence-review/SKILL.md +14 -5
  75. package/templates/default/locales/en/.mustflow/skills/cache-integrity-review/SKILL.md +30 -15
  76. package/templates/default/locales/en/.mustflow/skills/change-blast-radius-review/SKILL.md +45 -32
  77. package/templates/default/locales/en/.mustflow/skills/ci-pipeline-triage/SKILL.md +200 -0
  78. package/templates/default/locales/en/.mustflow/skills/clarifying-question-gate/SKILL.md +87 -13
  79. package/templates/default/locales/en/.mustflow/skills/cross-agent-session-reference/SKILL.md +23 -8
  80. package/templates/default/locales/en/.mustflow/skills/dependency-upgrade-review/SKILL.md +3 -1
  81. package/templates/default/locales/en/.mustflow/skills/docker-runtime-triage/SKILL.md +191 -0
  82. package/templates/default/locales/en/.mustflow/skills/github-contribution-quality-gate/SKILL.md +48 -11
  83. package/templates/default/locales/en/.mustflow/skills/go-code-change/SKILL.md +18 -13
  84. package/templates/default/locales/en/.mustflow/skills/javascript-code-change/SKILL.md +15 -13
  85. package/templates/default/locales/en/.mustflow/skills/line-ending-hygiene/SKILL.md +18 -10
  86. package/templates/default/locales/en/.mustflow/skills/llm-hallucination-control-review/SKILL.md +4 -1
  87. package/templates/default/locales/en/.mustflow/skills/motion-system-contract-review/SKILL.md +155 -0
  88. package/templates/default/locales/en/.mustflow/skills/next-action-menu/SKILL.md +177 -0
  89. package/templates/default/locales/en/.mustflow/skills/node-code-change/SKILL.md +16 -14
  90. package/templates/default/locales/en/.mustflow/skills/observability-debuggability-review/SKILL.md +15 -7
  91. package/templates/default/locales/en/.mustflow/skills/payment-integrity-review/SKILL.md +59 -35
  92. package/templates/default/locales/en/.mustflow/skills/powershell-code-change/SKILL.md +16 -6
  93. package/templates/default/locales/en/.mustflow/skills/prompt-contract-quality-review/SKILL.md +4 -1
  94. package/templates/default/locales/en/.mustflow/skills/python-code-change/SKILL.md +19 -10
  95. package/templates/default/locales/en/.mustflow/skills/rag-pipeline-triage/SKILL.md +206 -0
  96. package/templates/default/locales/en/.mustflow/skills/routes.toml +69 -9
  97. package/templates/default/locales/en/.mustflow/skills/rust-code-change/SKILL.md +10 -4
  98. package/templates/default/locales/en/.mustflow/skills/search-index-integrity-review/SKILL.md +181 -0
  99. package/templates/default/locales/en/.mustflow/skills/security-privacy-review/SKILL.md +3 -1
  100. package/templates/default/locales/en/.mustflow/skills/service-boundary-architecture/SKILL.md +37 -23
  101. package/templates/default/locales/en/.mustflow/skills/test-suite-performance-review/SKILL.md +323 -0
  102. package/templates/default/locales/en/.mustflow/skills/typescript-code-change/SKILL.md +18 -10
  103. package/templates/default/locales/en/.mustflow/skills/vector-search-integrity-review/SKILL.md +209 -0
  104. package/templates/default/locales/en/.mustflow/skills/version-freshness-check/SKILL.md +16 -14
  105. package/templates/default/manifest.toml +71 -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.84.0",
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
@@ -69,6 +69,19 @@ Current schemas:
69
69
  file hashes, language metadata, symbol names, declaration kinds, line ranges, signatures, export
70
70
  flags, source-anchor navigation metadata, static return metadata, and stable input-limit finding
71
71
  codes
72
+ - `dependency-graph-report.schema.json`: output of
73
+ `mf script-pack run code/dependency-graph scan <path...> --json`, containing bounded relative
74
+ TypeScript and JavaScript import graph nodes, edges, cycle hints, target flags, depth, importer
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
80
+ - `change-impact-report.schema.json`: output of
81
+ `mf script-pack run code/change-impact analyze [path...] --json`, containing git-diff changed
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
72
85
  - `code-symbol-read-report.schema.json`: output of
73
86
  `mf script-pack run code/symbol-read read <path> --start-line <line> --json`, containing a
74
87
  focused source snippet selected by source anchor, outline symbol line, or explicit line range with
@@ -87,10 +100,30 @@ Current schemas:
87
100
  `mf script-pack run docs/reference-drift check [path...] --json`, containing checked
88
101
  documentation references to `mf` commands, script-pack refs, schema files, repository paths,
89
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
90
115
  - `config-chain-report.schema.json`: output of
91
116
  `mf script-pack run repo/config-chain inspect <path...> --json`, containing nearby package,
92
117
  TypeScript, ESLint, Prettier, Vite, Vitest, Tailwind, Jest, Playwright, and mustflow config files
93
118
  with static inheritance, reference, workspace, dynamic-config, path, and content-hash metadata
119
+ - `env-contract-report.schema.json`: output of
120
+ `mf script-pack run repo/env-contract scan [path...] --json`, containing code, CI, docs, config,
121
+ and env-example references for environment-variable contract drift without reading secret env files
122
+ or printing env values
123
+ - `secret-risk-scan-report.schema.json`: output of
124
+ `mf script-pack run repo/secret-risk-scan scan [path...] --json`, containing plausible
125
+ hardcoded-secret findings with detector names, paths, line numbers, and redacted fingerprints
126
+ without printing secret values
94
127
  - `text-budget-report.schema.json`: output of
95
128
  `mf script-pack run core/text-budget check <path...> --json`, containing
96
129
  exact text-budget metrics, input content hashes, policy metadata, findings, and JSON Pointer field
@@ -98,6 +131,27 @@ Current schemas:
98
131
  - `generated-boundary-report.schema.json`: output of
99
132
  `mf script-pack run repo/generated-boundary check <path...> --json`, containing candidate path
100
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
101
155
  - `related-files-report.schema.json`: output of
102
156
  `mf script-pack run repo/related-files map <path...> --json`, containing conservative related-file
103
157
  candidates from direct imports, importers, same-basename siblings, parent configuration files, and
@@ -0,0 +1,184 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://mustflow.github.io/schemas/change-impact-report.schema.json",
4
+ "title": "mustflow change-impact 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
+ "changed_files",
20
+ "impacts",
21
+ "script_hints",
22
+ "verification_hints",
23
+ "truncated",
24
+ "findings",
25
+ "issues"
26
+ ],
27
+ "properties": {
28
+ "schema_version": { "const": "1" },
29
+ "command": { "const": "script-pack" },
30
+ "pack_id": { "const": "code" },
31
+ "script_id": { "const": "change-impact" },
32
+ "script_ref": { "const": "code/change-impact" },
33
+ "action": { "const": "analyze" },
34
+ "status": { "enum": ["passed", "failed", "error"] },
35
+ "ok": { "type": "boolean" },
36
+ "mustflow_root": { "type": "string" },
37
+ "policy": { "$ref": "#/$defs/policy" },
38
+ "input_hash": { "$ref": "#/$defs/sha256" },
39
+ "changed_files": { "type": "array", "items": { "$ref": "#/$defs/changedFile" } },
40
+ "impacts": { "type": "array", "items": { "$ref": "#/$defs/impact" } },
41
+ "script_hints": { "type": "array", "items": { "$ref": "#/$defs/scriptHint" } },
42
+ "verification_hints": { "type": "array", "items": { "$ref": "#/$defs/verificationHint" } },
43
+ "truncated": { "type": "boolean" },
44
+ "findings": { "type": "array", "items": { "$ref": "#/$defs/finding" } },
45
+ "issues": { "type": "array", "items": { "type": "string" } }
46
+ },
47
+ "$defs": {
48
+ "sha256": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
49
+ "surface": {
50
+ "enum": ["source", "test", "docs", "schema", "config", "package", "template", "workflow", "i18n", "unknown"]
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
+ },
78
+ "policy": {
79
+ "type": "object",
80
+ "additionalProperties": false,
81
+ "required": [
82
+ "base_ref",
83
+ "head_ref",
84
+ "compare_worktree",
85
+ "max_files",
86
+ "max_impacts",
87
+ "max_file_bytes",
88
+ "path_filters"
89
+ ],
90
+ "properties": {
91
+ "base_ref": { "type": "string" },
92
+ "head_ref": { "type": ["string", "null"] },
93
+ "compare_worktree": { "type": "boolean" },
94
+ "max_files": { "type": "integer", "minimum": 1 },
95
+ "max_impacts": { "type": "integer", "minimum": 1 },
96
+ "max_file_bytes": { "type": "integer", "minimum": 1 },
97
+ "path_filters": { "type": "array", "items": { "type": "string" } }
98
+ }
99
+ },
100
+ "changedFile": {
101
+ "type": "object",
102
+ "additionalProperties": false,
103
+ "required": ["path", "previous_path", "status", "surface"],
104
+ "properties": {
105
+ "path": { "type": "string" },
106
+ "previous_path": { "type": ["string", "null"] },
107
+ "status": { "enum": ["added", "modified", "deleted", "renamed", "type_changed", "untracked"] },
108
+ "surface": { "$ref": "#/$defs/surface" }
109
+ }
110
+ },
111
+ "impact": {
112
+ "type": "object",
113
+ "additionalProperties": false,
114
+ "required": ["path", "exists", "surface", "relationship", "confidence", "reason", "source_path"],
115
+ "properties": {
116
+ "path": { "type": "string" },
117
+ "exists": { "type": "boolean" },
118
+ "surface": { "$ref": "#/$defs/surface" },
119
+ "relationship": {
120
+ "enum": [
121
+ "changed_file",
122
+ "imports_changed_file",
123
+ "sibling_test",
124
+ "sibling_source",
125
+ "contract_doc",
126
+ "contract_test",
127
+ "package_surface",
128
+ "workflow_surface"
129
+ ]
130
+ },
131
+ "confidence": { "type": "number", "minimum": 0, "maximum": 1 },
132
+ "reason": { "type": "string" },
133
+ "source_path": { "type": "string" }
134
+ }
135
+ },
136
+ "scriptHint": {
137
+ "type": "object",
138
+ "additionalProperties": false,
139
+ "required": ["script_ref", "command", "reason", "confidence"],
140
+ "properties": {
141
+ "script_ref": { "type": "string" },
142
+ "command": { "type": "string" },
143
+ "reason": { "type": "string" },
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
+ }
153
+ }
154
+ },
155
+ "verificationHint": {
156
+ "type": "object",
157
+ "additionalProperties": false,
158
+ "required": ["intent", "reason", "confidence"],
159
+ "properties": {
160
+ "intent": { "type": "string" },
161
+ "reason": { "type": "string" },
162
+ "confidence": { "type": "number", "minimum": 0, "maximum": 1 }
163
+ }
164
+ },
165
+ "finding": {
166
+ "type": "object",
167
+ "additionalProperties": false,
168
+ "required": ["code", "severity", "message", "path"],
169
+ "properties": {
170
+ "code": {
171
+ "enum": [
172
+ "change_impact_git_unavailable",
173
+ "change_impact_invalid_ref",
174
+ "change_impact_max_files_exceeded",
175
+ "change_impact_max_impacts_exceeded"
176
+ ]
177
+ },
178
+ "severity": { "enum": ["low", "medium", "high", "critical"] },
179
+ "message": { "type": "string" },
180
+ "path": { "type": "string" }
181
+ }
182
+ }
183
+ }
184
+ }
@@ -47,6 +47,18 @@
47
47
  "resources": {
48
48
  "type": "object",
49
49
  "additionalProperties": { "$ref": "#/$defs/resource" }
50
+ },
51
+ "include": {
52
+ "type": "object",
53
+ "additionalProperties": false,
54
+ "required": ["files"],
55
+ "properties": {
56
+ "files": {
57
+ "type": "array",
58
+ "items": { "type": "string", "pattern": "^commands/(?:[A-Za-z0-9._-]+/)*[A-Za-z0-9._-]+\\.toml$" },
59
+ "uniqueItems": true
60
+ }
61
+ }
50
62
  }
51
63
  },
52
64
  "$defs": {