mustflow 1.30.0 → 2.11.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 (82) hide show
  1. package/README.md +35 -11
  2. package/dist/cli/commands/classify.js +61 -6
  3. package/dist/cli/commands/contract-lint.js +13 -4
  4. package/dist/cli/commands/dashboard.js +6 -0
  5. package/dist/cli/commands/index.js +5 -0
  6. package/dist/cli/commands/run.js +224 -48
  7. package/dist/cli/commands/upgrade.js +65 -0
  8. package/dist/cli/commands/verify.js +550 -33
  9. package/dist/cli/i18n/en.js +73 -10
  10. package/dist/cli/i18n/es.js +73 -10
  11. package/dist/cli/i18n/fr.js +73 -10
  12. package/dist/cli/i18n/hi.js +73 -10
  13. package/dist/cli/i18n/ko.js +73 -10
  14. package/dist/cli/i18n/zh.js +73 -10
  15. package/dist/cli/index.js +27 -46
  16. package/dist/cli/lib/command-registry.js +5 -0
  17. package/dist/cli/lib/dashboard-export.js +62 -12
  18. package/dist/cli/lib/dashboard-html/client-script.js +1936 -0
  19. package/dist/cli/lib/dashboard-html/locale-bootstrap.js +8 -0
  20. package/dist/cli/lib/dashboard-html/styles.js +572 -0
  21. package/dist/cli/lib/dashboard-html/template.js +134 -0
  22. package/dist/cli/lib/dashboard-html/types.js +1 -0
  23. package/dist/cli/lib/dashboard-html.js +1 -1907
  24. package/dist/cli/lib/dashboard-locale.js +37 -0
  25. package/dist/cli/lib/local-index/constants.js +48 -0
  26. package/dist/cli/lib/local-index/index.js +2256 -0
  27. package/dist/cli/lib/local-index/sql.js +15 -0
  28. package/dist/cli/lib/local-index/types.js +1 -0
  29. package/dist/cli/lib/local-index.js +1 -1908
  30. package/dist/cli/lib/reporter.js +6 -0
  31. package/dist/cli/lib/run-plan.js +96 -4
  32. package/dist/cli/lib/templates.js +18 -1
  33. package/dist/cli/lib/validation/command-intents.js +11 -0
  34. package/dist/cli/lib/validation/constants.js +238 -0
  35. package/dist/cli/lib/validation/index.js +1384 -0
  36. package/dist/cli/lib/validation/primitives.js +198 -0
  37. package/dist/cli/lib/validation/test-selection.js +95 -0
  38. package/dist/cli/lib/validation/types.js +1 -0
  39. package/dist/cli/lib/validation.js +1 -1661
  40. package/dist/core/bounded-output.js +38 -0
  41. package/dist/core/change-classification.js +6 -2
  42. package/dist/core/change-verification.js +240 -6
  43. package/dist/core/check-issues.js +12 -0
  44. package/dist/core/command-contract-validation.js +20 -0
  45. package/dist/core/command-effects.js +13 -0
  46. package/dist/core/completion-verdict.js +209 -0
  47. package/dist/core/contract-lint.js +316 -7
  48. package/dist/core/dashboard-verification.js +8 -0
  49. package/dist/core/external-evidence.js +9 -0
  50. package/dist/core/public-json-contracts.js +28 -0
  51. package/dist/core/repeated-failure.js +17 -0
  52. package/dist/core/repro-evidence.js +53 -0
  53. package/dist/core/run-performance-history.js +307 -0
  54. package/dist/core/run-profile.js +87 -0
  55. package/dist/core/run-receipt.js +171 -4
  56. package/dist/core/run-write-drift.js +18 -2
  57. package/dist/core/scope-risk.js +64 -0
  58. package/dist/core/skill-route-alignment.js +110 -0
  59. package/dist/core/source-anchor-status.js +4 -1
  60. package/dist/core/test-selection.js +227 -0
  61. package/dist/core/validation-ratchet.js +52 -0
  62. package/dist/core/verification-decision-graph.js +67 -0
  63. package/dist/core/verification-evidence.js +249 -0
  64. package/dist/core/verification-scheduler.js +96 -2
  65. package/examples/README.md +12 -4
  66. package/package.json +1 -1
  67. package/schemas/README.md +18 -4
  68. package/schemas/change-verification-report.schema.json +169 -5
  69. package/schemas/commands.schema.json +51 -1
  70. package/schemas/contract-lint-report.schema.json +80 -0
  71. package/schemas/dashboard-export.schema.json +500 -0
  72. package/schemas/explain-report.schema.json +2 -0
  73. package/schemas/latest-run-pointer.schema.json +384 -0
  74. package/schemas/run-receipt.schema.json +113 -0
  75. package/schemas/test-selection.schema.json +81 -0
  76. package/schemas/verify-report.schema.json +361 -1
  77. package/schemas/verify-run-manifest.schema.json +410 -0
  78. package/templates/default/common/.mustflow/config/commands.toml +1 -1
  79. package/templates/default/i18n.toml +1 -1
  80. package/templates/default/locales/en/.mustflow/skills/INDEX.md +124 -29
  81. package/templates/default/locales/en/.mustflow/skills/routes.toml +289 -0
  82. package/templates/default/manifest.toml +29 -2
package/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  Languages: [English](README.md) · [한국어](docs/i18n/ko/README.md) · [中文](docs/i18n/zh/README.md) · [Español](docs/i18n/es/README.md) · [Français](docs/i18n/fr/README.md) · [हिन्दी](docs/i18n/hi/README.md)
4
4
 
5
- mustflow is a workflow CLI designed for LLM coding agents. It guides agents to enter a repository, understand the correct operating context, run only authorized commands, and verify their work without guessing.
5
+ mustflow is a repository-local work contract and verification CLI for LLM coding agents. It keeps agents inside explicit read, command, and verification boundaries without replacing the host agent's sandbox, approval, checkpoint, model, or tool policies.
6
6
 
7
- The core concept is straightforward: place `AGENTS.md` at the project root and keep detailed workflows under `.mustflow/`. Agents start from `AGENTS.md` and then follow the command contract, skills, project context, and verification rules in sequence.
7
+ The core concept is straightforward: place `AGENTS.md` at the project root and keep detailed workflows under `.mustflow/`. Agents start from `AGENTS.md`, then follow the repository command contract, skills, project context, and verification rules in sequence.
8
8
 
9
9
  - Documentation site: <https://0disoft.github.io/mustflow/>
10
10
  - Human-readable project examples: [`examples/`](examples/)
@@ -14,10 +14,24 @@ The core concept is straightforward: place `AGENTS.md` at the project root and k
14
14
  - Security: [SECURITY.md](https://github.com/0disoft/mustflow/blob/main/SECURITY.md)
15
15
  - Changelog: [CHANGELOG.md](https://github.com/0disoft/mustflow/blob/main/CHANGELOG.md)
16
16
 
17
+ ## Choose your path
18
+
19
+ - Use mustflow in your repository: start with [Quick start](#quick-start), then review the [no-guessing workflow](#no-guessing-workflow) and [`examples/minimal-js/`](examples/minimal-js/).
20
+ - Contribute to mustflow: read [CONTRIBUTING.md](CONTRIBUTING.md), then run only configured command intents from [`.mustflow/config/commands.toml`](.mustflow/config/commands.toml).
21
+ - Build an AI coding tool or agent harness: use `AGENTS.md` and `mf context --json` for repository context, then consume JSON output and schemas from `mf classify`, `mf verify`, `mf run`, `mf dashboard`, and [`schemas/`](schemas/).
22
+
17
23
  ## No-guessing workflow
18
24
 
19
25
  The initial mustflow path is deliberately narrow.
20
26
 
27
+ Authority stays narrow:
28
+
29
+ - Current user instructions define the task goal unless they are unsafe.
30
+ - Host safety, sandbox, and approval gates still apply.
31
+ - Repository work rules come from the nearest `AGENTS.md` and `.mustflow/config/*.toml`.
32
+ - Command execution authority comes only from `.mustflow/config/commands.toml`.
33
+ - Skills, context files, preferences, generated maps, search results, cache, and state files guide or explain work. They do not grant command permission.
34
+
21
35
  ```sh
22
36
  npm install -D mustflow
23
37
  npx mf init --yes
@@ -27,9 +41,9 @@ npx mf check --strict
27
41
  After changes to code, templates, schemas, or documentation, classify the changed paths and review the verification plan before running any commands.
28
42
 
29
43
  ```sh
30
- npx mf classify --changed --json > .mustflow/state/change-plan.json
31
- npx mf verify --from-plan .mustflow/state/change-plan.json --plan-only --json
32
- npx mf verify --from-plan .mustflow/state/change-plan.json --json
44
+ npx mf classify --changed --write .mustflow/state/change-classification.json
45
+ npx mf verify --from-classification .mustflow/state/change-classification.json --plan-only --json
46
+ npx mf verify --from-classification .mustflow/state/change-classification.json --json
33
47
  ```
34
48
 
35
49
  The plan is based on change classification and the `required_after` metadata in `.mustflow/config/commands.toml`. A command runs only if its declared intent is configured, one-shot, agent-allowed, closed-stdin, bounded by a timeout, and backed by an explicit command source.
@@ -239,9 +253,9 @@ If a project already has optional root Markdown files such as `README.md`, `PROJ
239
253
  npx mf init --yes
240
254
  npx mf doctor
241
255
  npx mf check --strict
242
- npx mf classify --changed --json > .mustflow/state/change-plan.json
243
- npx mf verify --from-plan .mustflow/state/change-plan.json --plan-only --json
244
- npx mf verify --from-plan .mustflow/state/change-plan.json --json
256
+ npx mf classify --changed --write .mustflow/state/change-classification.json
257
+ npx mf verify --from-classification .mustflow/state/change-classification.json --plan-only --json
258
+ npx mf verify --from-classification .mustflow/state/change-classification.json --json
245
259
  ```
246
260
 
247
261
  Create the optional local search index if search capabilities are needed. Run the normal command
@@ -268,6 +282,14 @@ npx mf update --dry-run
268
282
  npx mf update --apply
269
283
  ```
270
284
 
285
+ After updating the mustflow package, `mf upgrade` combines the package freshness check with the safe project-file update step. It does not install packages by itself; update npm, pnpm, or Bun first.
286
+
287
+ ```sh
288
+ bun update -g mustflow
289
+ mf upgrade --dry-run
290
+ mf upgrade
291
+ ```
292
+
271
293
  Agents should prefer the configured update intents so the repository receives a run receipt.
272
294
 
273
295
  ```sh
@@ -286,8 +308,8 @@ mf run mustflow_update_apply
286
308
  | `mf check` | Validate mustflow files, TOML configuration, and skill document shape. |
287
309
  | `mf check --strict` | Run additional safety checks for document identity, authority/lifecycle metadata, skill index/body alignment, skill metadata, command boundaries, version-source discovery, retention policy, output limits, raw logs, and secret-like context. |
288
310
  | `mf adapters status` | Inspect existing host-specific instruction and adapter files without generating adapter files or granting command authority. |
289
- | `mf classify --changed` | Classify changed paths, public surfaces, and validation reasons without modifying files. |
290
- | `mf contract-lint` | Inspect `.mustflow/config/commands.toml` for command-contract errors and warnings without running commands. |
311
+ | `mf classify --changed` | Classify changed paths, public surfaces, and validation reasons. Add `--write <path>` to save the classification report. |
312
+ | `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. |
291
313
  | `mf doctor` | Inspect the current mustflow root without writing files. |
292
314
  | `mf docs review list` | Show documents still waiting for prose review after agent edits. |
293
315
  | `mf docs review add <path>` | Add or refresh a document review queue entry. |
@@ -304,6 +326,8 @@ mf run mustflow_update_apply
304
326
  | `mf status` | Inspect installed state and changed or missing files. |
305
327
  | `mf update --dry-run` | Calculate a template update plan without writing files. |
306
328
  | `mf update --apply` | Apply template updates when nothing is blocked. |
329
+ | `mf upgrade` | Check package freshness, then apply safe bundled template updates when the package is current. |
330
+ | `mf upgrade --dry-run` | Check package freshness and print the safe project update plan without writing files. |
307
331
  | `mf help <topic>` | Show installed mustflow help. |
308
332
  | `mf dashboard` | Start a local inspection dashboard for status, verification recommendations, release/version-source status, template update readiness, latest run receipt, skill routes, safe preferences, and documentation review. Use `--export-json <path>` or `--export <path>` for a bounded static report. It does not execute commands or apply fixes. |
309
333
  | `mf version` | Print the installed mustflow package version. |
@@ -346,7 +370,7 @@ npx mf init --product-source-locale en --product-locale ko-KR
346
370
  npx mf init --set git.auto_commit=true
347
371
  ```
348
372
 
349
- - `--profile`: Project profile. The default is `minimal`. Profiles also select the installed skill surface: `minimal` installs core everyday coding skills, while `oss`, `team`, `product`, and `library` add opt-in skill groups without removing optional skill files from the package.
373
+ - `--profile`: Project profile. The default is `minimal`. Profiles also select the installed skill surface: `minimal` installs core everyday coding skills, `patterns` adds architecture-pattern procedures, and `oss`, `team`, `product`, and `library` add opt-in skill groups without removing optional skill files from the package.
350
374
  - `--locale`: Installed mustflow document language. The default template currently supports `en`, `ko`, `zh`, `es`, `fr`, and `hi`. The default template includes localized documents for all these locales.
351
375
  - `--agent-lang`: Default language for final agent reports.
352
376
  - `--interactive`: Choose init settings via prompts.
@@ -1,3 +1,5 @@
1
+ import { mkdirSync, writeFileSync } from 'node:fs';
2
+ import path from 'node:path';
1
3
  import { createChangeClassificationReport, } from '../../core/change-classification.js';
2
4
  import { printUsageError, renderHelp } from '../lib/cli-output.js';
3
5
  import { readGitChangedFiles } from '../lib/git-changes.js';
@@ -10,10 +12,15 @@ export function getClassifyHelp(lang = 'en') {
10
12
  summary: t(lang, 'classify.help.summary'),
11
13
  options: [
12
14
  { label: '--changed', description: t(lang, 'classify.help.option.changed') },
15
+ { label: '--write <path>', description: t(lang, 'classify.help.option.write') },
13
16
  { label: '--json', description: t(lang, 'cli.option.json') },
14
17
  { label: '-h, --help', description: t(lang, 'cli.option.help') },
15
18
  ],
16
- examples: ['mf classify --changed', 'mf classify README.md schemas/verify-report.schema.json --json'],
19
+ examples: [
20
+ 'mf classify --changed',
21
+ 'mf classify --changed --write .mustflow/state/change-classification.json',
22
+ 'mf classify README.md schemas/verify-report.schema.json --json',
23
+ ],
17
24
  exitCodes: [
18
25
  { label: '0', description: t(lang, 'classify.help.exit.ok') },
19
26
  { label: '1', description: t(lang, 'cli.common.invalidInput') },
@@ -24,7 +31,9 @@ function parseClassifyArgs(args) {
24
31
  const paths = [];
25
32
  let json = false;
26
33
  let changed = false;
27
- for (const arg of args) {
34
+ let writePath;
35
+ for (let index = 0; index < args.length; index += 1) {
36
+ const arg = args[index];
28
37
  if (arg === '--json') {
29
38
  json = true;
30
39
  continue;
@@ -33,12 +42,29 @@ function parseClassifyArgs(args) {
33
42
  changed = true;
34
43
  continue;
35
44
  }
45
+ if (arg === '--write') {
46
+ const value = args[index + 1];
47
+ if (!value || value.startsWith('-')) {
48
+ return { json, changed, writePath, paths, error: 'missing_write_value' };
49
+ }
50
+ writePath = value;
51
+ index += 1;
52
+ continue;
53
+ }
54
+ if (arg.startsWith('--write=')) {
55
+ const value = arg.slice('--write='.length);
56
+ if (value.length === 0) {
57
+ return { json, changed, writePath, paths, error: 'missing_write_value' };
58
+ }
59
+ writePath = value;
60
+ continue;
61
+ }
36
62
  if (arg.startsWith('-')) {
37
- return { json, changed, paths, error: arg };
63
+ return { json, changed, writePath, paths, error: arg };
38
64
  }
39
65
  paths.push(arg);
40
66
  }
41
- return { json, changed, paths };
67
+ return { json, changed, writePath, paths };
42
68
  }
43
69
  export function createClassifyOutput(projectRoot, source, paths) {
44
70
  const files = source === 'changed' ? readGitChangedFiles(projectRoot) : paths;
@@ -75,6 +101,19 @@ function renderClassifyOutput(output, lang) {
75
101
  }
76
102
  return lines.join('\n');
77
103
  }
104
+ function resolveWritePath(projectRoot, inputPath) {
105
+ const resolved = path.resolve(projectRoot, inputPath);
106
+ const relative = path.relative(projectRoot, resolved);
107
+ if (relative.startsWith('..') || path.isAbsolute(relative)) {
108
+ throw new Error('write_path_outside_root');
109
+ }
110
+ return resolved;
111
+ }
112
+ function writeClassifyOutput(projectRoot, inputPath, output) {
113
+ const outputPath = resolveWritePath(projectRoot, inputPath);
114
+ mkdirSync(path.dirname(outputPath), { recursive: true });
115
+ writeFileSync(outputPath, `${JSON.stringify(output, null, 2)}\n`, 'utf8');
116
+ }
78
117
  export function runClassify(args, reporter, lang = 'en') {
79
118
  if (args.includes('--help') || args.includes('-h')) {
80
119
  reporter.stdout(getClassifyHelp(lang));
@@ -82,7 +121,10 @@ export function runClassify(args, reporter, lang = 'en') {
82
121
  }
83
122
  const parsed = parseClassifyArgs(args);
84
123
  if (parsed.error) {
85
- printUsageError(reporter, t(lang, 'cli.error.unknownOption', { option: parsed.error }), 'mf classify --help', getClassifyHelp(lang), lang);
124
+ const message = parsed.error === 'missing_write_value'
125
+ ? t(lang, 'cli.error.missingValue', { option: '--write' })
126
+ : t(lang, 'cli.error.unknownOption', { option: parsed.error });
127
+ printUsageError(reporter, message, 'mf classify --help', getClassifyHelp(lang), lang);
86
128
  return 1;
87
129
  }
88
130
  if (parsed.changed && parsed.paths.length > 0) {
@@ -93,7 +135,20 @@ export function runClassify(args, reporter, lang = 'en') {
93
135
  printUsageError(reporter, t(lang, 'classify.error.missingInput'), 'mf classify --help', getClassifyHelp(lang), lang);
94
136
  return 1;
95
137
  }
96
- const output = createClassifyOutput(resolveMustflowRoot(), parsed.changed ? 'changed' : 'paths', parsed.paths);
138
+ const projectRoot = resolveMustflowRoot();
139
+ const output = createClassifyOutput(projectRoot, parsed.changed ? 'changed' : 'paths', parsed.paths);
140
+ if (parsed.writePath) {
141
+ try {
142
+ writeClassifyOutput(projectRoot, parsed.writePath, output);
143
+ }
144
+ catch (error) {
145
+ const message = error instanceof Error && error.message === 'write_path_outside_root'
146
+ ? t(lang, 'classify.error.write_path_outside_root')
147
+ : t(lang, 'cli.common.invalidInput');
148
+ printUsageError(reporter, message, 'mf classify --help', getClassifyHelp(lang), lang);
149
+ return 1;
150
+ }
151
+ }
97
152
  if (parsed.json) {
98
153
  reporter.stdout(JSON.stringify(output, null, 2));
99
154
  return 0;
@@ -14,10 +14,11 @@ export function getContractLintHelp(lang = 'en') {
14
14
  summary: t(lang, 'contractLint.help.summary'),
15
15
  options: [
16
16
  { label: '--coverage', description: t(lang, 'contractLint.help.option.coverage') },
17
+ { label: '--suggest', description: t(lang, 'contractLint.help.option.suggest') },
17
18
  { label: '--json', description: t(lang, 'cli.option.json') },
18
19
  { label: '-h, --help', description: t(lang, 'cli.option.help') },
19
20
  ],
20
- examples: ['mf contract-lint', 'mf contract-lint --coverage', 'mf contract-lint --coverage --json'],
21
+ examples: ['mf contract-lint', 'mf contract-lint --coverage', 'mf contract-lint --suggest', 'mf contract-lint --coverage --json'],
21
22
  exitCodes: [
22
23
  { label: '0', description: t(lang, 'contractLint.help.exit.ok') },
23
24
  { label: '1', description: t(lang, 'contractLint.help.exit.fail') },
@@ -32,13 +33,14 @@ function readPreferences(projectRoot) {
32
33
  const preferences = readTomlFile(preferencesPath);
33
34
  return isRecord(preferences) ? preferences : undefined;
34
35
  }
35
- function createContractLintOutput(projectRoot, coverage) {
36
+ function createContractLintOutput(projectRoot, coverage, suggest) {
36
37
  return {
37
38
  schema_version: CONTRACT_LINT_SCHEMA_VERSION,
38
39
  command: 'contract-lint',
39
40
  mustflow_root: projectRoot,
40
41
  report: lintCommandContract(readCommandContract(projectRoot), {
41
42
  coverage,
43
+ suggest,
42
44
  projectRoot,
43
45
  releaseVersioningEnabled: releaseVersioningIsEnabled(readPreferences(projectRoot)),
44
46
  }),
@@ -63,6 +65,13 @@ function renderContractLintOutput(output, lang) {
63
65
  if (output.report.coverage) {
64
66
  lines.push('', t(lang, 'contractLint.label.coverage'), `${t(lang, 'contractLint.label.classificationReasons')}: ${output.report.coverage.knownClassificationReasons.length}`, `${t(lang, 'contractLint.label.requiredAfterReasons')}: ${output.report.coverage.requiredAfterReasons.length}`, `${t(lang, 'contractLint.label.runnableReasons')}: ${output.report.coverage.runnableReasons.length}`, `${t(lang, 'contractLint.label.coverageFindings')}: ${output.report.coverage.findings.length}`);
65
67
  }
68
+ if (output.report.suggestions) {
69
+ lines.push('', t(lang, 'contractLint.label.suggestions'));
70
+ for (const suggestion of output.report.suggestions) {
71
+ lines.push(`- ${suggestion.sourceFile}:${suggestion.sourceName} -> ${suggestion.suggestedIntent}`);
72
+ lines.push(...suggestion.snippet.split('\n').map((line) => ` ${line}`));
73
+ }
74
+ }
66
75
  if (output.report.issues.length > 0) {
67
76
  lines.push('', t(lang, 'contractLint.label.issues'));
68
77
  for (const issue of output.report.issues) {
@@ -77,13 +86,13 @@ export function runContractLint(args, reporter, lang = 'en') {
77
86
  reporter.stdout(getContractLintHelp(lang));
78
87
  return 0;
79
88
  }
80
- const supported = new Set(['--coverage', '--json']);
89
+ const supported = new Set(['--coverage', '--suggest', '--json']);
81
90
  const unsupported = args.filter((arg) => !supported.has(arg));
82
91
  if (unsupported.length > 0) {
83
92
  printUsageError(reporter, t(lang, 'cli.error.unknownOption', { option: unsupported[0] }), 'mf contract-lint --help', getContractLintHelp(lang), lang);
84
93
  return 1;
85
94
  }
86
- const output = createContractLintOutput(resolveMustflowRoot(), args.includes('--coverage'));
95
+ const output = createContractLintOutput(resolveMustflowRoot(), args.includes('--coverage'), args.includes('--suggest'));
87
96
  if (args.includes('--json')) {
88
97
  reporter.stdout(JSON.stringify(output, null, 2));
89
98
  }
@@ -25,6 +25,7 @@ const DEFAULT_DASHBOARD_HOST = '127.0.0.1';
25
25
  const DEFAULT_DASHBOARD_PORT = 0;
26
26
  const MAX_REQUEST_BYTES = 64 * 1024;
27
27
  const LOCAL_DASHBOARD_HOSTS = new Set(['127.0.0.1', 'localhost', '::1']);
28
+ const DOC_REVIEW_BULK_PAYLOAD_FIELDS = ['paths', 'documents', 'entries'];
28
29
  const RELEASE_FILE_PATTERNS = [
29
30
  /^package\.json$/u,
30
31
  /^bun\.lockb?$/u,
@@ -296,6 +297,11 @@ function readDocReviewPayload(value) {
296
297
  throw new Error('Request body must be a JSON object.');
297
298
  }
298
299
  const payload = value;
300
+ for (const field of DOC_REVIEW_BULK_PAYLOAD_FIELDS) {
301
+ if (field in payload) {
302
+ throw new Error('Bulk documentation review updates require a separate confirmed flow.');
303
+ }
304
+ }
299
305
  const status = readRequiredStringField(payload, 'status');
300
306
  if (status !== 'approved' && status !== 'needs_human' && status !== 'ignored') {
301
307
  throw new Error('status must be approved, needs_human, or ignored.');
@@ -42,6 +42,11 @@ function renderIndexSummary(result, lang) {
42
42
  `skill_routes: ${result.skill_route_count}`,
43
43
  `${t(lang, 'label.commandIntents')}: ${result.command_intent_count}`,
44
44
  `command_effects: ${result.command_effect_count}`,
45
+ `verification_evidence_summaries: ${result.verification_evidence_summary_count}`,
46
+ `verification_receipt_summaries: ${result.verification_receipt_summary_count}`,
47
+ `verification_coverage_states: ${result.verification_coverage_state_count}`,
48
+ `verification_risk_signals: ${result.verification_risk_signal_count}`,
49
+ `failure_fingerprints: ${result.failure_fingerprint_count}`,
45
50
  `source_anchors: ${result.source_anchor_count}`,
46
51
  `index_mode: ${result.index_mode}`,
47
52
  `reused_existing: ${result.reused_existing ? 'yes' : 'no'}`,