mustflow 2.16.0 → 2.18.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 (36) hide show
  1. package/README.md +5 -5
  2. package/dist/cli/commands/classify.js +13 -3
  3. package/dist/cli/commands/dashboard.js +2 -1
  4. package/dist/cli/commands/impact.js +13 -3
  5. package/dist/cli/commands/run.js +86 -11
  6. package/dist/cli/commands/upgrade.js +3 -1
  7. package/dist/cli/commands/verify.js +9 -1
  8. package/dist/cli/commands/version.js +1 -1
  9. package/dist/cli/i18n/en.js +8 -1
  10. package/dist/cli/i18n/es.js +8 -1
  11. package/dist/cli/i18n/fr.js +8 -1
  12. package/dist/cli/i18n/hi.js +8 -1
  13. package/dist/cli/i18n/ko.js +7 -0
  14. package/dist/cli/i18n/zh.js +7 -0
  15. package/dist/cli/lib/git-changes.js +25 -2
  16. package/dist/cli/lib/local-index/constants.js +4 -1
  17. package/dist/cli/lib/local-index/index.js +22 -5
  18. package/dist/cli/lib/npm-version-check.js +71 -1
  19. package/dist/cli/lib/repo-map.js +81 -28
  20. package/dist/cli/lib/run-plan.js +25 -2
  21. package/dist/cli/lib/validation/index.js +2 -1
  22. package/dist/core/check-issues.js +2 -0
  23. package/dist/core/command-contract-rules.js +104 -2
  24. package/dist/core/command-contract-validation.js +14 -2
  25. package/dist/core/command-intent-eligibility.js +9 -1
  26. package/dist/core/command-output-limits.js +5 -0
  27. package/dist/core/contract-lint.js +10 -1
  28. package/package.json +1 -1
  29. package/schemas/README.md +3 -3
  30. package/schemas/change-verification-report.schema.json +2 -1
  31. package/schemas/contract-lint-report.schema.json +2 -1
  32. package/schemas/explain-report.schema.json +1 -0
  33. package/schemas/latest-run-pointer.schema.json +1 -0
  34. package/schemas/verify-report.schema.json +1 -0
  35. package/schemas/verify-run-manifest.schema.json +1 -0
  36. package/templates/default/manifest.toml +1 -1
@@ -1,7 +1,8 @@
1
1
  import { COMMAND_LIFECYCLES, COMMAND_RUN_POLICIES, LONG_RUNNING_LIFECYCLES, isRecord, } from './config-loading.js';
2
2
  import { COMMAND_ENV_POLICIES } from './command-env.js';
3
3
  import { COMMAND_EFFECT_CONCURRENCY, COMMAND_EFFECT_MODES, COMMAND_EFFECT_TYPES, validateCommandEffectLockWarnings, validateCommandEffects, } from './command-effects.js';
4
- import { commandIntentHasBlockedShellBackgroundPattern, commandIntentHasCommandSource, commandIntentNameIsSafe, } from './command-contract-rules.js';
4
+ import { commandIntentBlockedCommandPattern, commandIntentHasBlockedShellBackgroundPattern, commandIntentHasCommandSource, commandIntentNameIsSafe, } from './command-contract-rules.js';
5
+ import { MAX_COMMAND_OUTPUT_BYTES, commandMaxOutputBytesLimitMessage } from './command-output-limits.js';
5
6
  function commandContractIssue(message) {
6
7
  return { message };
7
8
  }
@@ -46,6 +47,12 @@ function validatePositiveIntegerField(table, key, label, issues) {
46
47
  issues.push(commandContractIssue(`${label} must be a positive integer`));
47
48
  }
48
49
  }
50
+ function validateMaxOutputBytesField(table, key, label, issues) {
51
+ validatePositiveIntegerField(table, key, label, issues);
52
+ if (isPositiveInteger(table[key]) && Number(table[key]) > MAX_COMMAND_OUTPUT_BYTES) {
53
+ issues.push(commandContractIssue(commandMaxOutputBytesLimitMessage(label)));
54
+ }
55
+ }
49
56
  function validateAllowedStringField(table, key, label, allowedValues, issues) {
50
57
  if (!hasOwn(table, key)) {
51
58
  return;
@@ -70,7 +77,7 @@ function validateCommandDefaults(commandsToml, issues) {
70
77
  validateAllowedStringField(defaults, 'env_policy', '[commands.defaults].env_policy', COMMAND_ENV_POLICIES, issues);
71
78
  validateStringArrayField(defaults, 'env_allowlist', '[commands.defaults].env_allowlist', issues);
72
79
  validatePositiveIntegerField(defaults, 'default_timeout_seconds', '[commands.defaults].default_timeout_seconds', issues);
73
- validatePositiveIntegerField(defaults, 'max_output_bytes', '[commands.defaults].max_output_bytes', issues);
80
+ validateMaxOutputBytesField(defaults, 'max_output_bytes', '[commands.defaults].max_output_bytes', issues);
74
81
  validatePositiveIntegerField(defaults, 'kill_after_seconds', '[commands.defaults].kill_after_seconds', issues);
75
82
  }
76
83
  function validateCommandResources(commandsToml, issues) {
@@ -148,6 +155,7 @@ function validateCommandIntent(intentName, intent, issues) {
148
155
  validateAllowedStringField(intent, 'run_policy', `[commands.intents.${intentName}].run_policy`, COMMAND_RUN_POLICIES, issues);
149
156
  validateAllowedStringField(intent, 'env_policy', `[commands.intents.${intentName}].env_policy`, COMMAND_ENV_POLICIES, issues);
150
157
  validateStringArrayField(intent, 'env_allowlist', `[commands.intents.${intentName}].env_allowlist`, issues);
158
+ validateMaxOutputBytesField(intent, 'max_output_bytes', `[commands.intents.${intentName}].max_output_bytes`, issues);
151
159
  validateCommandIntentSelection(intentName, intent, issues);
152
160
  if (intent.status !== 'configured') {
153
161
  return;
@@ -178,6 +186,10 @@ function validateCommandIntent(intentName, intent, issues) {
178
186
  if (commandIntentHasBlockedShellBackgroundPattern(intent)) {
179
187
  issues.push(commandContractIssue(`Shell intent ${intentName} contains a blocked long-running or background pattern`));
180
188
  }
189
+ const blockedCommandPattern = commandIntentBlockedCommandPattern(intent);
190
+ if (blockedCommandPattern?.code === 'long_running_command_pattern') {
191
+ issues.push(commandContractIssue(`Intent ${intentName} contains a blocked long-running or background command pattern`));
192
+ }
181
193
  if (hasOwn(intent, 'success_exit_codes')) {
182
194
  const value = intent.success_exit_codes;
183
195
  if (!Array.isArray(value) || value.length === 0 || value.some((entry) => !Number.isInteger(entry))) {
@@ -1,5 +1,5 @@
1
1
  import { isRecord, readString } from './config-loading.js';
2
- import { commandIntentHasBlockedShellBackgroundPattern, commandIntentHasCommandSource, commandIntentNameIsSafe, } from './command-contract-rules.js';
2
+ import { commandIntentBlockedCommandPattern, commandIntentHasBlockedShellBackgroundPattern, commandIntentHasCommandSource, commandIntentNameIsSafe, } from './command-contract-rules.js';
3
3
  export function evaluateCommandIntentEligibility(intentName, rawIntent) {
4
4
  if (!commandIntentNameIsSafe(intentName)) {
5
5
  return {
@@ -68,6 +68,14 @@ export function evaluateCommandIntentEligibility(intentName, rawIntent) {
68
68
  detail: 'Shell command contains a blocked long-running or background pattern.',
69
69
  };
70
70
  }
71
+ const blockedPattern = commandIntentBlockedCommandPattern(rawIntent);
72
+ if (blockedPattern?.code === 'long_running_command_pattern') {
73
+ return {
74
+ ok: false,
75
+ code: 'blocked_long_running_command_pattern',
76
+ detail: blockedPattern.detail,
77
+ };
78
+ }
71
79
  return {
72
80
  ok: true,
73
81
  code: 'ok',
@@ -0,0 +1,5 @@
1
+ export const DEFAULT_COMMAND_MAX_OUTPUT_BYTES = 1_048_576;
2
+ export const MAX_COMMAND_OUTPUT_BYTES = 16 * 1024 * 1024;
3
+ export function commandMaxOutputBytesLimitMessage(label) {
4
+ return `${label} must be less than or equal to ${MAX_COMMAND_OUTPUT_BYTES}`;
5
+ }
@@ -2,7 +2,8 @@ import { existsSync, readFileSync } from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { COMMAND_LIFECYCLES, COMMAND_RUN_POLICIES, LONG_RUNNING_LIFECYCLES, isRecord, readPositiveInteger, readString, readStringArray, } from './config-loading.js';
4
4
  import { evaluateCommandIntentEligibility, } from './command-intent-eligibility.js';
5
- import { commandIntentHasBlockedShellBackgroundPattern, commandIntentHasCommandSource, commandIntentNameIsSafe, } from './command-contract-rules.js';
5
+ import { commandIntentBlockedCommandPattern, commandIntentHasBlockedShellBackgroundPattern, commandIntentHasCommandSource, commandIntentNameIsSafe, } from './command-contract-rules.js';
6
+ import { MAX_COMMAND_OUTPUT_BYTES } from './command-output-limits.js';
6
7
  import { commandEffectsConflict, normalizeCommandEffects } from './command-effects.js';
7
8
  import { listChangeClassificationValidationReasons } from './change-classification.js';
8
9
  import { parseSkillIndexRoutes } from './skill-route-alignment.js';
@@ -309,6 +310,10 @@ function lintIntent(name, value, issues) {
309
310
  if (lifecycle === 'oneshot' && readPositiveInteger(value, 'timeout_seconds') === undefined) {
310
311
  pushIssue(issues, 'error', 'oneshot_missing_timeout', name, `Oneshot intent ${name} must define timeout_seconds.`);
311
312
  }
313
+ const maxOutputBytes = readPositiveInteger(value, 'max_output_bytes');
314
+ if (maxOutputBytes !== undefined && maxOutputBytes > MAX_COMMAND_OUTPUT_BYTES) {
315
+ pushIssue(issues, 'error', 'max_output_bytes_exceeds_limit', name, `Intent ${name} max_output_bytes must be less than or equal to ${MAX_COMMAND_OUTPUT_BYTES}.`);
316
+ }
312
317
  if (lifecycle === 'oneshot' && readString(value, 'stdin') !== 'closed') {
313
318
  pushIssue(issues, 'error', 'oneshot_stdin_not_closed', name, `Oneshot intent ${name} must set stdin to closed.`);
314
319
  }
@@ -321,6 +326,10 @@ function lintIntent(name, value, issues) {
321
326
  if (commandIntentHasBlockedShellBackgroundPattern(value)) {
322
327
  pushIssue(issues, 'error', 'shell_background_pattern', name, `Shell intent ${name} contains a blocked long-running or background pattern.`);
323
328
  }
329
+ const blockedCommandPattern = commandIntentBlockedCommandPattern(value);
330
+ if (blockedCommandPattern?.code === 'long_running_command_pattern') {
331
+ pushIssue(issues, 'error', 'long_running_command_pattern', name, `Intent ${name} contains a blocked long-running or background command pattern.`);
332
+ }
324
333
  if (!successExitCodesAreValid(value)) {
325
334
  pushIssue(issues, 'error', 'invalid_success_exit_codes', name, `Intent ${name} success_exit_codes must be an integer array.`);
326
335
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mustflow",
3
- "version": "2.16.0",
3
+ "version": "2.18.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
@@ -35,10 +35,10 @@ Current schemas:
35
35
  `mf explain verify --reason <event> --json`, `mf explain retention --json`, `mf explain skills --json`,
36
36
  and `mf explain surface --json`. Verify explanations include the shared `decisionGraph` evidence model.
37
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
38
+ explicit execution aggregate, evidence-based completion verdict, and evidence model with a
39
+ conservative coverage matrix for the selected receipts and skipped checks
40
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
41
+ the same execution aggregate, completion verdict, evidence model, and coverage matrix as the verify report
42
42
  - `change-verification-report.schema.json`: output of `mf verify --reason <event> --plan-only --json` and
43
43
  `mf verify --from-classification <classify-report.json> --plan-only --json`, including the `decision_graph` that links
44
44
  changed surfaces, classification reasons, command candidates, eligibility, selected or not-selected state,
@@ -251,7 +251,8 @@
251
251
  "missing_timeout",
252
252
  "missing_command_source",
253
253
  "unsafe_intent_name",
254
- "blocked_shell_background_pattern"
254
+ "blocked_shell_background_pattern",
255
+ "blocked_long_running_command_pattern"
255
256
  ]
256
257
  },
257
258
  "verificationCandidate": {
@@ -139,7 +139,8 @@
139
139
  "missing_timeout",
140
140
  "missing_command_source",
141
141
  "unsafe_intent_name",
142
- "blocked_shell_background_pattern"
142
+ "blocked_shell_background_pattern",
143
+ "blocked_long_running_command_pattern"
143
144
  ]
144
145
  },
145
146
  "runnable": { "type": "boolean" },
@@ -829,6 +829,7 @@
829
829
  "missing_command_source",
830
830
  "unsafe_intent_name",
831
831
  "blocked_shell_background_pattern",
832
+ "blocked_long_running_command_pattern",
832
833
  null
833
834
  ]
834
835
  },
@@ -33,6 +33,7 @@
33
33
  "type": "string",
34
34
  "pattern": "^sha256:[0-9a-f]{64}$"
35
35
  },
36
+ "execution_status": { "enum": ["passed", "partial", "failed", "blocked"] },
36
37
  "status": { "enum": ["passed", "partial", "failed", "blocked"] },
37
38
  "completion_verdict": { "$ref": "#/$defs/completionVerdict" },
38
39
  "evidence_model": { "$ref": "#/$defs/evidenceModel" },
@@ -34,6 +34,7 @@
34
34
  "type": "string",
35
35
  "pattern": "^sha256:[0-9a-f]{64}$"
36
36
  },
37
+ "execution_status": { "enum": ["passed", "partial", "failed", "blocked"] },
37
38
  "status": { "enum": ["passed", "partial", "failed", "blocked"] },
38
39
  "completion_verdict": { "$ref": "#/$defs/completionVerdict" },
39
40
  "evidence_model": { "$ref": "#/$defs/evidenceModel" },
@@ -30,6 +30,7 @@
30
30
  "type": "string",
31
31
  "pattern": "^sha256:[0-9a-f]{64}$"
32
32
  },
33
+ "execution_status": { "enum": ["passed", "partial", "failed", "blocked"] },
33
34
  "status": { "enum": ["passed", "partial", "failed", "blocked"] },
34
35
  "completion_verdict": { "$ref": "#/$defs/completionVerdict" },
35
36
  "evidence_model": { "$ref": "#/$defs/evidenceModel" },
@@ -1,6 +1,6 @@
1
1
  id = "default"
2
2
  name = "default"
3
- version = "2.16.0"
3
+ version = "2.18.0"
4
4
  description = "Minimal workflow for LLM agents to read, edit, and verify their work in a repository."
5
5
  common_root = "common"
6
6
  locales_root = "locales"