mustflow 2.69.0 → 2.74.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 (48) hide show
  1. package/README.md +19 -5
  2. package/dist/cli/commands/api.js +17 -0
  3. package/dist/cli/commands/evidence.js +71 -0
  4. package/dist/cli/commands/script-pack.js +124 -0
  5. package/dist/cli/commands/verify.js +50 -15
  6. package/dist/cli/commands/workspace.js +2 -0
  7. package/dist/cli/i18n/en.js +38 -0
  8. package/dist/cli/i18n/es.js +38 -0
  9. package/dist/cli/i18n/fr.js +38 -0
  10. package/dist/cli/i18n/hi.js +38 -0
  11. package/dist/cli/i18n/ko.js +38 -0
  12. package/dist/cli/i18n/zh.js +38 -0
  13. package/dist/cli/index.js +1 -0
  14. package/dist/cli/lib/agent-context.js +179 -10
  15. package/dist/cli/lib/command-registry.js +6 -0
  16. package/dist/cli/lib/dashboard-export.js +1 -0
  17. package/dist/cli/lib/script-pack-registry.js +27 -0
  18. package/dist/cli/script-packs/core-text-budget.js +241 -0
  19. package/dist/core/change-verification.js +10 -0
  20. package/dist/core/completion-verdict.js +14 -1
  21. package/dist/core/complexity-budget.js +206 -0
  22. package/dist/core/conflict-ledger.js +122 -0
  23. package/dist/core/failure-replay-capsule.js +213 -0
  24. package/dist/core/public-json-contracts.js +27 -0
  25. package/dist/core/risk-priced-evidence.js +213 -0
  26. package/dist/core/script-check-result.js +1 -0
  27. package/dist/core/text-budget.js +262 -0
  28. package/dist/core/verification-evidence.js +61 -13
  29. package/package.json +1 -1
  30. package/schemas/README.md +23 -11
  31. package/schemas/change-verification-report.schema.json +29 -0
  32. package/schemas/context-report.schema.json +58 -2
  33. package/schemas/dashboard-export.schema.json +42 -1
  34. package/schemas/diff-risk.schema.json +6 -0
  35. package/schemas/evidence-report.schema.json +45 -0
  36. package/schemas/latest-run-pointer.schema.json +50 -1
  37. package/schemas/script-pack-catalog.schema.json +68 -0
  38. package/schemas/text-budget-report.schema.json +131 -0
  39. package/schemas/verification-plan.schema.json +32 -0
  40. package/schemas/verify-report.schema.json +360 -1
  41. package/schemas/verify-run-manifest.schema.json +50 -1
  42. package/schemas/workspace-verification-plan.schema.json +32 -0
  43. package/templates/default/i18n.toml +8 -2
  44. package/templates/default/locales/en/.mustflow/skills/INDEX.md +3 -2
  45. package/templates/default/locales/en/.mustflow/skills/adapter-boundary/SKILL.md +19 -2
  46. package/templates/default/locales/en/.mustflow/skills/design-implementation-handoff/SKILL.md +250 -0
  47. package/templates/default/locales/en/.mustflow/skills/routes.toml +7 -1
  48. package/templates/default/manifest.toml +8 -1
package/README.md CHANGED
@@ -123,8 +123,16 @@ mustflow installs and validates an agent workflow for user projects.
123
123
  - Reports host adapter compatibility with `mf adapters status` without generating host-specific files or treating them as command authority.
124
124
  - Classifies changed files, public surfaces, and validation reasons with `mf classify`.
125
125
  - Inspects changed files for quality-gaming patterns such as line stuffing, suppressions, test bypass markers, type escapes, and placeholder implementations with `mf quality check`.
126
- - Prints execution-free verification plans with `mf verify --plan-only --json`, including a machine-readable verification decision graph and read-only local-index lock explanations when available.
126
+ - Prints execution-free verification plans with `mf verify --plan-only --json`, including a risk-priced evidence assessment, machine-readable verification decision graph, and read-only local-index lock explanations when available.
127
+ - Reports a read-only complexity budget in `mf api diff-risk --changed --json`, `mf verify`
128
+ evidence, and dashboard exports so agents justify new dependencies, helper-style surfaces,
129
+ config/schema churn, and broad structural changes before treating added complexity as free.
130
+ - Lists and runs bundled utility scripts through `mf script-pack`, including `core/text-budget` for
131
+ exact file and JSON-field length budgets, so future checks do not sprawl into top-level commands.
132
+ - Prints context trust metadata in `mf context --json` and prompt-cache bundles so agents can distinguish binding instructions, command contracts, contextual hints, generated evidence, and volatile runtime data before using them.
127
133
  - Runs only allowed one-shot commands within a timeout via `mf run <intent>` or `mf verify` when the selected intent is runnable.
134
+ - Records blockers, contradictions, verification gaps, and remaining risks as a structured conflict ledger in verify, evidence, and dashboard reports.
135
+ - Stores bounded failure replay capsules for failed `mf verify` runs so future agents can reproduce the intent, receipt, command fingerprint, and changed-file state without copying raw command output.
128
136
  - Writes command receipts under `.mustflow/state/runs/run-*` and atomically updates `.mustflow/state/runs/latest.json`.
129
137
  - Generates a concise repository navigation map, `REPO_MAP.md`, with `mf map`.
130
138
  - Indexes and searches mustflow docs, skills, skill routes, command rules, command-effect locks, file fingerprints, and opt-in source anchor metadata with SQLite via `mf index` and `mf search`. The local SQLite file is a rebuildable lookup cache, not a memory store, audit log, command transcript store, command-authority source, or source-content database.
@@ -251,7 +259,7 @@ mf run mustflow_update_apply
251
259
  | `mf contract-lint` | Inspect `.mustflow/config/commands.toml` for command-contract errors and warnings without running commands. Add `--suggest` to print non-runnable candidate snippets from existing command files. |
252
260
  | `mf onboard commands` | Suggest review-only command-intent snippets from package.json, Makefile, or justfile without writing files or granting command authority. |
253
261
  | `mf next` | Inspect install state, changed files, verification coverage, and command-contract gaps, then print the next safe mustflow action without running commands. |
254
- | `mf evidence` | Summarize changed-file verification requirements, latest evidence, receipts, remaining risks, and gaps without running commands. |
262
+ | `mf evidence` | Summarize changed-file verification requirements, risk-priced evidence assessment, latest failure replay capsule, conflict ledger, receipts, remaining risks, and gaps without running commands. |
255
263
  | `mf workspace status` | Inspect configured workspace roots and nested repository contract readiness without granting parent-to-child command authority. |
256
264
  | `mf workspace command-catalog` | Aggregate per-repository command intent availability with safe `mf run` entrypoints and no raw command strings. |
257
265
  | `mf workspace verify --changed --plan-only` | Aggregate per-repository changed-file verification plans without running commands or granting parent-to-child command authority. |
@@ -260,7 +268,7 @@ mf run mustflow_update_apply
260
268
  | `mf api command-catalog --json` | Print command intent availability and safe `mf run` entrypoints without exposing raw command strings. |
261
269
  | `mf api verification-plan --changed --json` | Print a stable, read-only verification plan for changed files without executing commands. |
262
270
  | `mf api latest-evidence --json` | Print bounded latest run or verify evidence without raw command output. |
263
- | `mf api diff-risk --changed --json` | Print a compact changed-file risk, verification summary, and read-only residual correction signals. |
271
+ | `mf api diff-risk --changed --json` | Print a compact changed-file risk, verification summary, read-only complexity budget, and residual correction signals. |
264
272
  | `mf api health --json` | Print a compact workspace health report for quick agent gating. |
265
273
  | `mf api locks --json` | Print active `mf run` locks for multi-session coordination. |
266
274
  | `mf api serve --stdio` | Serve the same read-only API reports as newline-delimited JSON responses over stdin/stdout. |
@@ -269,12 +277,15 @@ mf run mustflow_update_apply
269
277
  | `mf docs review comment <path>` | Add multiline review guidance to an existing queue entry. |
270
278
  | `mf docs review approve <path>` | Mark review complete and hide the document from the default queue. |
271
279
  | `mf handoff validate <path>` | Validate a restricted work-item or handoff JSON record without writing files. |
272
- | `mf context --json` | Print read order, command rules, available capabilities, and recent run summary as JSON. |
280
+ | `mf context --json` | Print read order, command rules, context trust metadata, available capabilities, prompt-cache bundles, and recent run summary as JSON. |
273
281
  | `mf skill route` | Resolve compact skill route candidates from task text, paths, and reasons before reading selected skill documents. |
274
282
  | `mf map --stdout` | Print the current mustflow root map to stdout. |
275
283
  | `mf map --write` | Create or update `REPO_MAP.md`. |
276
284
  | `mf quality check` | Inspect changed files for quality-gaming patterns without writing files. |
277
285
  | `mf quality check --all` | Inspect every tracked text file for quality-gaming patterns. |
286
+ | `mf script-pack list` | List bundled script packs and script refs such as `core/text-budget`. |
287
+ | `mf script-pack run core/text-budget check <path...> --max <count>` | Check exact text length budgets for files using grapheme counts by default. |
288
+ | `mf script-pack run core/text-budget check package.json --json-pointer /description --max <count> --json` | Check a JSON string field and print the stable report schema. |
278
289
  | `mf run <intent>` | Run an allowed one-shot command. |
279
290
  | `mf run <intent> --wait` | Wait for conflicting active run locks before executing the command. |
280
291
  | `mf run <intent> --dry-run --json` | Preview whether an intent is runnable and what command metadata would be used, without executing it. |
@@ -300,6 +311,9 @@ mf run mustflow_update_apply
300
311
 
301
312
  Automation and agents should use `--json` output or `mf api serve --stdio` JSONL responses instead of parsing human-facing text. Published JSON Schemas for stable outputs live in `schemas/`.
302
313
 
314
+ `core/text-budget` counts `line` units by splitting text on line breaks; a trailing line break
315
+ therefore contributes an empty final line.
316
+
303
317
  ## Command execution policy
304
318
 
305
319
  Runnable work is declared in `.mustflow/config/commands.toml` so agents do not guess commands.
@@ -318,7 +332,7 @@ Development servers, watch modes, browser UIs, interactive commands, and backgro
318
332
 
319
333
  Command environments remove the project-local `node_modules/.bin` path from `PATH` by default. If an intent needs a project dependency binary such as `eslint`, `tsc`, or `vitest`, declare it through the package manager, for example `npm exec eslint -- ...`, `pnpm exec tsc -- --noEmit`, `bun x eslint ...`, or `yarn exec eslint ...`. `mf check --strict` warns when an agent-runnable intent uses a bare executable name that appears under the project-local `.bin` directory, except for names listed in `defaults.allow_project_local_bin_bare_executables`. `mf run` may resolve those allowed names directly from the local `.bin` directory without exposing every local binary through `PATH`. The installed template allows `mf` and `mustflow` by default. Intent-level `allow_env_inheritance_risks = true` is available when a command intentionally uses `env_policy = "inherit"`.
320
334
 
321
- Use `mf verify --reason <event> --plan-only --json` to inspect matching verification intents, command eligibility, remaining gaps, and missing runnable coverage without executing commands. Use `mf run <intent> --dry-run --json` to inspect one resolved command intent without spawning a process or writing a run receipt. Plan-only verification includes a `decision_graph` that connects changed surfaces, classification reasons, command candidates, eligibility checks, effects, and gaps. When `.mustflow/cache/mustflow.sqlite` is fresh, scheduled entries also include read-only `effectGraph` metadata for write locks and lock conflicts. These graph rows are marked `explanation_only` and never grant command authority; `.mustflow/config/commands.toml` remains the only runnable command source.
335
+ Use `mf verify --reason <event> --plan-only --json` to inspect matching verification intents, command eligibility, risk-priced evidence requirements, remaining gaps, and missing runnable coverage without executing commands. Use `mf run <intent> --dry-run --json` to inspect one resolved command intent without spawning a process or writing a run receipt. Plan-only verification includes a `decision_graph` that connects changed surfaces, classification reasons, command candidates, eligibility checks, effects, and gaps. When `.mustflow/cache/mustflow.sqlite` is fresh, scheduled entries also include read-only `effectGraph` metadata for write locks and lock conflicts. These graph rows are marked `explanation_only` and never grant command authority; `.mustflow/config/commands.toml` remains the only runnable command source.
322
336
 
323
337
  Each executed command run writes a run record under `.mustflow/state/runs/run-*` and atomically updates `.mustflow/state/runs/latest.json`. The record includes the intent name, working directory, timeout, exit code, timeout status, and the tail of stdout and stderr.
324
338
 
@@ -7,6 +7,7 @@ import { runApiServe } from './api/serve.js';
7
7
  import { createRecommendedNextCommands } from './api/workspace-recommendations.js';
8
8
  import { listActiveRunLocks } from '../../core/active-run-locks.js';
9
9
  import { createChangeVerificationReport, } from '../../core/change-verification.js';
10
+ import { createComplexityBudgetReport, } from '../../core/complexity-budget.js';
10
11
  import { readUtf8FileInsideWithoutSymlinks } from '../../core/safe-filesystem.js';
11
12
  import { createVerificationPlanId } from '../../core/verification-plan-id.js';
12
13
  import { printUsageError, renderHelp } from '../lib/cli-output.js';
@@ -422,6 +423,7 @@ function createUnavailableVerificationPlanOutput(mustflowRoot, classification, i
422
423
  requirements: [],
423
424
  candidates: [],
424
425
  gaps: [],
426
+ risk_assessment: null,
425
427
  schedule: null,
426
428
  test_selection: null,
427
429
  execution_policy: createVerificationPlanExecutionPolicy(),
@@ -479,6 +481,7 @@ function createVerificationPlanOutput() {
479
481
  surfaces: gap.surfaces,
480
482
  detail: gap.detail,
481
483
  })),
484
+ risk_assessment: report.risk_assessment,
482
485
  schedule: toVerificationPlanSchedule(report),
483
486
  test_selection: toVerificationPlanTestSelection(report),
484
487
  execution_policy: createVerificationPlanExecutionPolicy(),
@@ -614,6 +617,14 @@ function getRiskLevel(classification, report) {
614
617
  if (classification.summary.fileCount === 0) {
615
618
  return 'none';
616
619
  }
620
+ if (report.risk_assessment.level === 'critical') {
621
+ return 'high';
622
+ }
623
+ if (report.risk_assessment.level === 'high' ||
624
+ report.risk_assessment.level === 'medium' ||
625
+ report.risk_assessment.level === 'low') {
626
+ return report.risk_assessment.level;
627
+ }
617
628
  const reasons = new Set(classification.summary.validationReasons);
618
629
  if (report.gaps.length > 0 ||
619
630
  classification.summary.publicSurfaceCount > 0 ||
@@ -794,6 +805,7 @@ function createDiffRiskOutput() {
794
805
  update_policies: [],
795
806
  drift_checks: [],
796
807
  required_verification: [],
808
+ complexity_budget: null,
797
809
  residual_corrections: createResidualCorrections(mustflowRoot, null),
798
810
  gap_count: 0,
799
811
  gaps: [],
@@ -820,6 +832,10 @@ function createDiffRiskOutput() {
820
832
  }
821
833
  const requiredVerification = report ? report.schedule.entries.map((entry) => entry.intent) : [];
822
834
  const verificationPlanId = report && contract ? createVerificationPlanId(report, contract) : null;
835
+ const complexityBudget = createComplexityBudgetReport({
836
+ files: classification.files,
837
+ summary: classification.summary,
838
+ });
823
839
  return {
824
840
  schema_version: API_DIFF_RISK_SCHEMA_VERSION,
825
841
  command: 'api diff-risk',
@@ -834,6 +850,7 @@ function createDiffRiskOutput() {
834
850
  update_policies: classification.summary.updatePolicies,
835
851
  drift_checks: classification.summary.driftChecks,
836
852
  required_verification: requiredVerification,
853
+ complexity_budget: complexityBudget,
837
854
  residual_corrections: createResidualCorrections(mustflowRoot, verificationPlanId),
838
855
  gap_count: report?.gaps.length ?? 0,
839
856
  gaps: report?.gaps.map((gap) => ({
@@ -234,6 +234,7 @@ function createPlanFromReport(report, contract, classification, latest) {
234
234
  changed_files: classification.files,
235
235
  validation_reasons: classification.summary.validationReasons,
236
236
  selected_intents: report.schedule.entries.map((entry) => entry.intent),
237
+ risk_assessment: report.risk_assessment,
237
238
  requirement_count: requirements.length,
238
239
  gap_count: gaps.length,
239
240
  requirements,
@@ -278,6 +279,7 @@ function unavailablePlan(source, error, classification = null) {
278
279
  changed_files: classification?.files ?? [],
279
280
  validation_reasons: classification?.summary.validationReasons ?? [],
280
281
  selected_intents: [],
282
+ risk_assessment: fallbackRiskAssessment('Plan unavailable; risk assessment could not be computed.'),
281
283
  requirement_count: 0,
282
284
  gap_count: 0,
283
285
  requirements: [],
@@ -304,6 +306,7 @@ function createPlanFromFile(mustflowRoot, planPath, latest) {
304
306
  const schedule = isRecord(parsed.schedule) ? parsed.schedule : null;
305
307
  const testSelection = isRecord(parsed.test_selection) ? parsed.test_selection : null;
306
308
  const classification = isRecord(parsed.classification) ? parsed.classification : null;
309
+ const riskAssessment = readRiskAssessment(parsed.risk_assessment);
307
310
  const selectedIntents = schedule
308
311
  ? uniqueSorted([
309
312
  ...readStringArrayField(schedule, 'selected_intents'),
@@ -324,6 +327,7 @@ function createPlanFromFile(mustflowRoot, planPath, latest) {
324
327
  changed_files: classification ? readStringArrayField(classification, 'files') : readStringArrayField(parsed, 'files'),
325
328
  validation_reasons: classification ? readStringArrayField(classification, 'validation_reasons') : [],
326
329
  selected_intents: selectedIntents,
330
+ risk_assessment: riskAssessment ?? fallbackRiskAssessment('Plan file does not include risk_assessment.'),
327
331
  requirement_count: requirementReports.length,
328
332
  gap_count: gaps.length,
329
333
  requirements: requirementReports,
@@ -371,6 +375,62 @@ function readLatestRequirement(record) {
371
375
  skipped_intents: readStringArrayField(record, 'skipped_intents'),
372
376
  };
373
377
  }
378
+ function isRiskLevel(value) {
379
+ return value === 'low' || value === 'medium' || value === 'high' || value === 'critical';
380
+ }
381
+ function readRiskAssessment(value) {
382
+ if (!isRecord(value)) {
383
+ return null;
384
+ }
385
+ const level = readString(value, 'level');
386
+ if (value.schema_version !== '1' || value.source !== 'change_classification_and_command_contract' || !isRiskLevel(level)) {
387
+ return null;
388
+ }
389
+ return {
390
+ schema_version: '1',
391
+ source: 'change_classification_and_command_contract',
392
+ level,
393
+ reasons: readStringArrayField(value, 'reasons'),
394
+ required_evidence: readStringArrayField(value, 'required_evidence'),
395
+ blocking_gaps: readStringArrayField(value, 'blocking_gaps'),
396
+ rollback_required: readBooleanField(value, 'rollback_required') ?? false,
397
+ human_approval_required: readBooleanField(value, 'human_approval_required') ?? false,
398
+ manual_review_required: readBooleanField(value, 'manual_review_required') ?? false,
399
+ };
400
+ }
401
+ function readFailureReplayCapsule(value) {
402
+ if (!isRecord(value)) {
403
+ return null;
404
+ }
405
+ if (value.schema_version !== '1' ||
406
+ value.source !== 'mf_verify_failure' ||
407
+ value.authority !== 'replay_supporting_evidence') {
408
+ return null;
409
+ }
410
+ return value;
411
+ }
412
+ function readConflictLedger(value) {
413
+ if (!isRecord(value)) {
414
+ return null;
415
+ }
416
+ if (value.schema_version !== '1' || value.source !== 'verification_evidence_model' || !Array.isArray(value.items)) {
417
+ return null;
418
+ }
419
+ return value;
420
+ }
421
+ function fallbackRiskAssessment(reason) {
422
+ return {
423
+ schema_version: '1',
424
+ source: 'change_classification_and_command_contract',
425
+ level: 'low',
426
+ reasons: [reason],
427
+ required_evidence: ['changed_file_review'],
428
+ blocking_gaps: [],
429
+ rollback_required: false,
430
+ human_approval_required: false,
431
+ manual_review_required: false,
432
+ };
433
+ }
374
434
  function createLatestReport(mustflowRoot, expectedPlanId, requested) {
375
435
  if (!requested) {
376
436
  return latestNotRequested();
@@ -392,6 +452,11 @@ function createLatestReport(mustflowRoot, expectedPlanId, requested) {
392
452
  }
393
453
  const evidenceModel = isRecord(parsed.evidence_model) ? parsed.evidence_model : null;
394
454
  const completionVerdict = isRecord(parsed.completion_verdict) ? parsed.completion_verdict : null;
455
+ const latestRiskAssessment = readRiskAssessment(parsed.risk_assessment) ?? (evidenceModel ? readRiskAssessment(evidenceModel.risk_assessment) : null);
456
+ const failureReplayCapsule = readFailureReplayCapsule(parsed.failure_replay_capsule) ??
457
+ (evidenceModel ? readFailureReplayCapsule(evidenceModel.failure_replay_capsule) : null);
458
+ const conflictLedger = readConflictLedger(parsed.conflict_ledger) ??
459
+ (evidenceModel ? readConflictLedger(evidenceModel.conflict_ledger) : null);
395
460
  const requirements = evidenceModel ? recordArray(evidenceModel.requirements).map(readLatestRequirement) : [];
396
461
  const receipts = evidenceModel ? recordArray(evidenceModel.receipts).map(readLatestReceipt) : [];
397
462
  const skippedChecks = evidenceModel ? recordArray(evidenceModel.skipped_checks).map(toGapReport) : [];
@@ -408,6 +473,9 @@ function createLatestReport(mustflowRoot, expectedPlanId, requested) {
408
473
  receipt_count: receipts.length,
409
474
  skipped_check_count: skippedChecks.length,
410
475
  remaining_risk_count: remainingRisks.length,
476
+ risk_assessment: latestRiskAssessment,
477
+ failure_replay_capsule: failureReplayCapsule,
478
+ conflict_ledger: conflictLedger,
411
479
  requirements,
412
480
  receipts,
413
481
  skipped_checks: skippedChecks,
@@ -428,6 +496,9 @@ function latestEmpty(status) {
428
496
  receipt_count: null,
429
497
  skipped_check_count: 0,
430
498
  remaining_risk_count: 0,
499
+ risk_assessment: null,
500
+ failure_replay_capsule: null,
501
+ conflict_ledger: null,
431
502
  requirements: [],
432
503
  receipts: [],
433
504
  skipped_checks: [],
@@ -0,0 +1,124 @@
1
+ import { printUsageError, renderHelp } from '../lib/cli-output.js';
2
+ import { t } from '../lib/i18n.js';
3
+ import { formatCliOptionParseError, hasCliOptionToken, hasParsedCliOption, parseCliOptions, } from '../lib/option-parser.js';
4
+ import { resolveMustflowRoot } from '../lib/project-root.js';
5
+ import { findScriptPackScript, SCRIPT_PACKS, } from '../lib/script-pack-registry.js';
6
+ const SCRIPT_PACK_LIST_OPTIONS = [{ name: '--json', kind: 'boolean' }];
7
+ function createCatalogReport(mustflowRoot, packs) {
8
+ return {
9
+ schema_version: '1',
10
+ command: 'script-pack',
11
+ action: 'list',
12
+ ok: true,
13
+ mustflow_root: mustflowRoot,
14
+ packs: packs.map((pack) => ({
15
+ id: pack.id,
16
+ summary_key: pack.summaryKey,
17
+ scripts: pack.scripts.map((script) => ({
18
+ id: script.id,
19
+ ref: script.ref,
20
+ usage: script.usage,
21
+ summary_key: script.summaryKey,
22
+ actions: script.actions,
23
+ report_schema_file: script.reportSchemaFile,
24
+ })),
25
+ })),
26
+ issues: [],
27
+ };
28
+ }
29
+ export function getScriptPackHelp(lang = 'en') {
30
+ return renderHelp({
31
+ usage: 'mf script-pack <list|run> [options]',
32
+ summary: t(lang, 'scriptPack.help.summary'),
33
+ options: [
34
+ { label: '--json', description: t(lang, 'cli.option.json') },
35
+ { label: '-h, --help', description: t(lang, 'cli.option.help') },
36
+ ],
37
+ examples: [
38
+ 'mf script-pack list',
39
+ 'mf script-pack list --json',
40
+ 'mf script-pack run core/text-budget check README.md --max 5000',
41
+ 'mf script-pack run core/text-budget --help',
42
+ ],
43
+ exitCodes: [
44
+ { label: '0', description: t(lang, 'scriptPack.help.exit.ok') },
45
+ { label: '1', description: t(lang, 'scriptPack.help.exit.fail') },
46
+ ],
47
+ }, lang);
48
+ }
49
+ function renderCatalogSummary(report, lang) {
50
+ const lines = [t(lang, 'scriptPack.title')];
51
+ for (const pack of report.packs) {
52
+ lines.push(`- ${pack.id}: ${t(lang, pack.summary_key)}`);
53
+ for (const script of pack.scripts) {
54
+ const schema = script.report_schema_file ?? t(lang, 'value.none');
55
+ lines.push(` - ${script.ref}: ${t(lang, script.summary_key)} ` +
56
+ `(${t(lang, 'scriptPack.label.actions')}: ${script.actions.join(', ')}, ` +
57
+ `${t(lang, 'scriptPack.label.schema')}: ${schema})`);
58
+ }
59
+ }
60
+ return lines.join('\n');
61
+ }
62
+ function parseListOptions(args, lang) {
63
+ const parsed = parseCliOptions(args, SCRIPT_PACK_LIST_OPTIONS, { allowPositionals: false });
64
+ const json = hasParsedCliOption(parsed, '--json');
65
+ if (parsed.error) {
66
+ return { json, error: formatCliOptionParseError(parsed.error, lang) };
67
+ }
68
+ return { json };
69
+ }
70
+ async function runScriptPackList(args, reporter, lang) {
71
+ if (hasCliOptionToken(args, '--help', ['-h'])) {
72
+ reporter.stdout(getScriptPackHelp(lang));
73
+ return 0;
74
+ }
75
+ const options = parseListOptions(args, lang);
76
+ if (options.error) {
77
+ printUsageError(reporter, options.error, 'mf script-pack --help', getScriptPackHelp(lang), lang);
78
+ return 1;
79
+ }
80
+ const report = createCatalogReport(resolveMustflowRoot(), SCRIPT_PACKS);
81
+ if (options.json) {
82
+ reporter.stdout(JSON.stringify(report, null, 2));
83
+ return 0;
84
+ }
85
+ reporter.stdout(renderCatalogSummary(report, lang));
86
+ return 0;
87
+ }
88
+ async function runScriptPackScript(args, reporter, lang) {
89
+ const [scriptRef, ...scriptArgs] = args;
90
+ if (args.length === 1 && hasCliOptionToken(args, '--help', ['-h'])) {
91
+ reporter.stdout(getScriptPackHelp(lang));
92
+ return 0;
93
+ }
94
+ if (!scriptRef) {
95
+ printUsageError(reporter, t(lang, 'scriptPack.error.missingScript'), 'mf script-pack --help', getScriptPackHelp(lang), lang);
96
+ return 1;
97
+ }
98
+ const script = findScriptPackScript(scriptRef);
99
+ if (!script) {
100
+ printUsageError(reporter, t(lang, 'scriptPack.error.unknownScript', { script: scriptRef }), 'mf script-pack list', getScriptPackHelp(lang), lang);
101
+ return 1;
102
+ }
103
+ const runner = await script.loadRunner();
104
+ return runner(scriptArgs, reporter, lang);
105
+ }
106
+ export async function runScriptPack(args, reporter, lang = 'en') {
107
+ const [action, ...rest] = args;
108
+ if ((!action || action !== 'run') && hasCliOptionToken(args, '--help', ['-h'])) {
109
+ reporter.stdout(getScriptPackHelp(lang));
110
+ return 0;
111
+ }
112
+ if (!action) {
113
+ printUsageError(reporter, t(lang, 'scriptPack.error.missingAction'), 'mf script-pack --help', getScriptPackHelp(lang), lang);
114
+ return 1;
115
+ }
116
+ if (action === 'list') {
117
+ return runScriptPackList(rest, reporter, lang);
118
+ }
119
+ if (action === 'run') {
120
+ return runScriptPackScript(rest, reporter, lang);
121
+ }
122
+ printUsageError(reporter, t(lang, 'scriptPack.error.unknownAction', { action }), 'mf script-pack --help', getScriptPackHelp(lang), lang);
123
+ return 1;
124
+ }
@@ -5,11 +5,13 @@ import { createCorrelationId } from '../../core/correlation-id.js';
5
5
  import { readUtf8FileInsideWithoutSymlinks, writeJsonFileInsideWithoutSymlinks } from '../../core/safe-filesystem.js';
6
6
  import { createVerifyCompletionVerdict, } from '../../core/completion-verdict.js';
7
7
  import { createExternalEvidenceRisks, } from '../../core/external-evidence.js';
8
+ import { createFailureReplayCapsule, } from '../../core/failure-replay-capsule.js';
8
9
  import { createRepeatedFailureRisks, createVerificationFailureFingerprint, updateRepeatedFailureState, } from '../../core/repeated-failure.js';
9
10
  import { createVerificationPlanId } from '../../core/verification-plan-id.js';
10
11
  import { countReproEvidenceVerdictEffects, createReproEvidenceRisks, } from '../../core/repro-evidence.js';
11
12
  import { createVerifyEvidenceModel } from '../../core/verification-evidence.js';
12
13
  import { createScopeDiffRisks } from '../../core/scope-risk.js';
14
+ import { riskPricedEvidenceRiskCount, } from '../../core/risk-priced-evidence.js';
13
15
  import { countValidationRatchetVerdictEffects, createValidationRatchetRisks, } from '../../core/validation-ratchet.js';
14
16
  import { finishRunWriteBatchTracking, startRunWriteBatchTracking, } from '../../core/run-write-drift.js';
15
17
  import { createCommandEnv } from '../../core/command-env.js';
@@ -486,6 +488,16 @@ function createCompletionVerdictForResults(input) {
486
488
  const receiptBinding = createReceiptBindingEvidence(input.results, input.verificationPlanId);
487
489
  const receiptBindingRiskCount = receiptBinding.plan_unbound_count + receiptBinding.fingerprint_unbound_count;
488
490
  const repeatedFailureBlockerCount = input.repeatedFailureRisks.filter((risk) => risk.verdict_effect === 'blocker').length;
491
+ const writeDriftRiskCount = countUndeclaredWriteDrift(input.results);
492
+ const specificReviewRiskCount = input.sourceAnchorRiskCount +
493
+ input.scopeDiffRiskCount +
494
+ input.validationRatchetRiskCount +
495
+ input.reproEvidenceRiskCount +
496
+ input.externalEvidenceRiskCount +
497
+ writeDriftRiskCount +
498
+ receiptBindingRiskCount +
499
+ receiptBinding.stale_count;
500
+ const genericRiskPricedEvidenceRiskCount = specificReviewRiskCount === 0 ? riskPricedEvidenceRiskCount(input.report.risk_assessment) : 0;
489
501
  return createVerifyCompletionVerdict({
490
502
  verificationPlanId: input.verificationPlanId,
491
503
  matchedIntents: input.summary.matched,
@@ -505,7 +517,8 @@ function createCompletionVerdictForResults(input) {
505
517
  reproEvidenceContradictionCount: input.reproEvidenceContradictionCount,
506
518
  reproEvidenceUnverifiedCount: input.reproEvidenceUnverifiedCount,
507
519
  externalEvidenceRiskCount: input.externalEvidenceRiskCount,
508
- writeDriftRiskCount: countUndeclaredWriteDrift(input.results),
520
+ riskPricedEvidenceRiskCount: genericRiskPricedEvidenceRiskCount,
521
+ writeDriftRiskCount,
509
522
  receiptBindingRiskCount,
510
523
  staleReceiptCount: receiptBinding.stale_count,
511
524
  planMismatchCount: receiptBinding.plan_mismatch_count,
@@ -642,6 +655,15 @@ function writeVerifyRunReceipts(projectRoot, output, report, sourceAnchorRisks,
642
655
  results,
643
656
  }),
644
657
  });
658
+ const failureReplayCapsule = createFailureReplayCapsule({
659
+ projectRoot,
660
+ verificationPlanId: output.verification_plan_id,
661
+ status: output.status,
662
+ reasons: output.reasons,
663
+ report,
664
+ results,
665
+ failureFingerprint,
666
+ });
645
667
  const repeatedFailureSummary = updateRepeatedFailureState({
646
668
  projectRoot,
647
669
  failureFingerprint,
@@ -670,28 +692,32 @@ function writeVerifyRunReceipts(projectRoot, output, report, sourceAnchorRisks,
670
692
  reproEvidenceUnverifiedCount: reproEvidenceVerdictEffects.unverified,
671
693
  externalEvidenceRiskCount: externalEvidenceRisks.length,
672
694
  });
695
+ const evidenceModel = createVerifyEvidenceModel({
696
+ report,
697
+ results,
698
+ verificationPlanId: output.verification_plan_id,
699
+ verdict: completionVerdict,
700
+ sourceAnchorRisks,
701
+ scopeDiffRisks,
702
+ repeatedFailureRisks: finalRepeatedFailureRisks,
703
+ validationRatchetRisks,
704
+ reproEvidence,
705
+ reproEvidenceRisks,
706
+ externalChecks,
707
+ externalEvidenceRisks,
708
+ failureReplayCapsule,
709
+ });
673
710
  const outputWithReceiptPaths = {
674
711
  ...output,
675
712
  completion_verdict: completionVerdict,
713
+ evidence_model: evidenceModel,
714
+ conflict_ledger: evidenceModel.conflict_ledger,
676
715
  failure_fingerprint: failureFingerprint,
716
+ failure_replay_capsule: failureReplayCapsule,
677
717
  repeated_failure_summary: repeatedFailureSummary,
678
718
  run_dir: statePaths.runDir,
679
719
  manifest_path: statePaths.manifestPath,
680
720
  results,
681
- evidence_model: createVerifyEvidenceModel({
682
- report,
683
- results,
684
- verificationPlanId: output.verification_plan_id,
685
- verdict: completionVerdict,
686
- sourceAnchorRisks,
687
- scopeDiffRisks,
688
- repeatedFailureRisks: finalRepeatedFailureRisks,
689
- validationRatchetRisks,
690
- reproEvidence,
691
- reproEvidenceRisks,
692
- externalChecks,
693
- externalEvidenceRisks,
694
- }),
695
721
  };
696
722
  const manifest = {
697
723
  schema_version: '1',
@@ -703,9 +729,12 @@ function writeVerifyRunReceipts(projectRoot, output, report, sourceAnchorRisks,
703
729
  verification_plan_id: outputWithReceiptPaths.verification_plan_id,
704
730
  execution_status: outputWithReceiptPaths.execution_status,
705
731
  status: outputWithReceiptPaths.status,
732
+ risk_assessment: outputWithReceiptPaths.risk_assessment,
706
733
  completion_verdict: outputWithReceiptPaths.completion_verdict,
707
734
  evidence_model: outputWithReceiptPaths.evidence_model,
735
+ conflict_ledger: outputWithReceiptPaths.conflict_ledger,
708
736
  failure_fingerprint: outputWithReceiptPaths.failure_fingerprint,
737
+ failure_replay_capsule: outputWithReceiptPaths.failure_replay_capsule,
709
738
  repeated_failure_summary: outputWithReceiptPaths.repeated_failure_summary,
710
739
  summary: outputWithReceiptPaths.summary,
711
740
  ...(outputWithReceiptPaths.repro_evidence ? { repro_evidence: outputWithReceiptPaths.repro_evidence } : {}),
@@ -724,9 +753,12 @@ function writeVerifyRunReceipts(projectRoot, output, report, sourceAnchorRisks,
724
753
  verification_plan_id: outputWithReceiptPaths.verification_plan_id,
725
754
  execution_status: outputWithReceiptPaths.execution_status,
726
755
  status: outputWithReceiptPaths.status,
756
+ risk_assessment: outputWithReceiptPaths.risk_assessment,
727
757
  completion_verdict: outputWithReceiptPaths.completion_verdict,
728
758
  evidence_model: outputWithReceiptPaths.evidence_model,
759
+ conflict_ledger: outputWithReceiptPaths.conflict_ledger,
729
760
  failure_fingerprint: outputWithReceiptPaths.failure_fingerprint,
761
+ failure_replay_capsule: outputWithReceiptPaths.failure_replay_capsule,
730
762
  repeated_failure_summary: outputWithReceiptPaths.repeated_failure_summary,
731
763
  summary: outputWithReceiptPaths.summary,
732
764
  ...(outputWithReceiptPaths.repro_evidence ? { repro_evidence: outputWithReceiptPaths.repro_evidence } : {}),
@@ -814,9 +846,12 @@ async function createVerifyOutput(input, planSource, projectRoot, lang, reproEvi
814
846
  verification_plan_id: verificationPlanId,
815
847
  execution_status: status,
816
848
  status,
849
+ risk_assessment: report.risk_assessment,
817
850
  completion_verdict: completionVerdict,
818
851
  evidence_model: evidenceModel,
852
+ conflict_ledger: evidenceModel.conflict_ledger,
819
853
  failure_fingerprint: failureFingerprint,
854
+ failure_replay_capsule: null,
820
855
  repeated_failure_summary: null,
821
856
  summary,
822
857
  ...(parallelismReport ? { parallelism: parallelismReport } : {}),
@@ -312,6 +312,7 @@ function createUnavailableVerificationRepository(repository, commandSurface, sta
312
312
  changed_file_count: classification ? classification.summary.fileCount : null,
313
313
  changed_files: classification ? classification.files : [],
314
314
  verification_plan_id: null,
315
+ risk_assessment: null,
315
316
  requirement_count: 0,
316
317
  candidate_count: 0,
317
318
  selected_intent_count: 0,
@@ -367,6 +368,7 @@ function createVerificationRepository(projectRoot, repository) {
367
368
  changed_file_count: classification.summary.fileCount,
368
369
  changed_files: classification.files,
369
370
  verification_plan_id: createVerificationPlanId(report, contract),
371
+ risk_assessment: report.risk_assessment,
370
372
  requirement_count: report.requirements.length,
371
373
  candidate_count: report.candidates.length,
372
374
  selected_intent_count: report.schedule.entries.length,
@@ -40,6 +40,7 @@ export const enMessages = {
40
40
  "command.map.summary": "Generate REPO_MAP.md",
41
41
  "command.lineEndings.summary": "Inspect and normalize line-ending policy",
42
42
  "command.quality.summary": "Inspect changed files for quality-gaming patterns",
43
+ "command.scriptPack.summary": "List and run bundled mustflow script packs",
43
44
  "command.run.summary": "Run a configured oneshot command",
44
45
  "command.context.summary": "Print machine-readable agent context",
45
46
  "command.tech.summary": "Manage technology preferences for agents",
@@ -182,6 +183,7 @@ export const enMessages = {
182
183
  "label.results": "Results",
183
184
  "label.comment": "Comment",
184
185
  "label.mode": "Mode",
186
+ "label.status": "Status",
185
187
  "adapters.help.summary": "Inspect repository-visible host adapter files without generating or authorizing adapter surfaces.",
186
188
  "adapters.help.exit.ok": "Adapter compatibility was inspected and printed",
187
189
  "adapters.help.exit.fail": "The command received invalid input",
@@ -747,6 +749,42 @@ Read these files before working:
747
749
  "quality.clean": "No quality-gaming risks found.",
748
750
  "quality.error.missingAction": "Specify a quality action: check",
749
751
  "quality.error.unknownAction": "Unknown quality action: {action}",
752
+ "scriptPack.help.summary": "List and run bundled mustflow script-pack utilities through one stable command namespace.",
753
+ "scriptPack.help.exit.ok": "The script-pack command completed successfully",
754
+ "scriptPack.help.exit.fail": "The script-pack command received invalid input or the selected script failed",
755
+ "scriptPack.title": "mustflow script packs",
756
+ "scriptPack.pack.core.summary": "Core built-in utility scripts",
757
+ "scriptPack.script.textBudget.summary": "Check exact text length budgets for files or JSON string fields",
758
+ "scriptPack.label.script": "Script",
759
+ "scriptPack.label.actions": "actions",
760
+ "scriptPack.label.schema": "schema",
761
+ "scriptPack.error.missingAction": "Specify a script-pack action: list or run",
762
+ "scriptPack.error.unknownAction": "Unknown script-pack action: {action}",
763
+ "scriptPack.error.missingScript": "Specify a script ref such as core/text-budget",
764
+ "scriptPack.error.unknownScript": "Unknown script-pack script: {script}",
765
+ "textBudget.help.summary": "Check exact text length budgets for files or JSON string fields using grapheme counts by default.",
766
+ "textBudget.help.option.min": "Require at least this many units",
767
+ "textBudget.help.option.max": "Require at most this many units",
768
+ "textBudget.help.option.exact": "Require exactly this many units; cannot be combined with --min or --max",
769
+ "textBudget.help.option.unit": "Counting unit: grapheme, code-point, utf16, utf8-byte, word, or line",
770
+ "textBudget.help.option.jsonPointer": "Read a string field from each JSON file before counting",
771
+ "textBudget.help.exit.ok": "Every checked target is within the declared budget",
772
+ "textBudget.help.exit.fail": "A budget violation, read error, JSON error, or invalid input was found",
773
+ "textBudget.title": "mustflow text budget",
774
+ "textBudget.label.budget": "Budget",
775
+ "textBudget.label.checkedTargets": "Checked targets",
776
+ "textBudget.label.findings": "Findings",
777
+ "textBudget.label.metrics": "Metrics",
778
+ "textBudget.label.issues": "Issues",
779
+ "textBudget.clean": "All text budgets passed.",
780
+ "textBudget.error.missingAction": "Specify a text-budget action: check",
781
+ "textBudget.error.unknownAction": "Unknown text-budget action: {action}",
782
+ "textBudget.error.missingPath": "Provide at least one path to check",
783
+ "textBudget.error.missingBudget": "Declare at least one text budget with --min, --max, or --exact",
784
+ "textBudget.error.exactConflict": "Cannot combine --exact with --min or --max",
785
+ "textBudget.error.invalidNumber": "{option} must be a non-negative safe integer: {value}",
786
+ "textBudget.error.invalidUnit": "Unknown text-budget unit: {unit}. Use one of: {allowed}",
787
+ "textBudget.error.minGreaterThanMax": "--min must be less than or equal to --max",
750
788
  "run.help.summary": "Run a configured oneshot command from .mustflow/config/commands.toml.",
751
789
  "run.help.option.dryRun": "Print a non-executing command plan",
752
790
  "run.help.option.planOnly": "Alias for --dry-run",