@nimiplatform/nimi-coding 0.4.0 → 0.5.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 (47) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +47 -35
  3. package/README.zh-CN.md +48 -36
  4. package/cli/commands/authority.mjs +80 -125
  5. package/cli/constants.mjs +1 -1
  6. package/cli/dev/check-authority-surface.mjs +17 -8
  7. package/cli/dev/check-product-conformance.mjs +4 -32
  8. package/cli/lib/authority/audit.mjs +262 -185
  9. package/cli/lib/authority/change-candidates.mjs +807 -0
  10. package/cli/lib/authority/closed-sets.mjs +7 -8
  11. package/cli/lib/authority/compile.mjs +6 -24
  12. package/cli/lib/authority/compiled-snapshot.mjs +143 -0
  13. package/cli/lib/authority/consumers.mjs +69 -19
  14. package/cli/lib/authority/diagnostics.mjs +7 -1
  15. package/cli/lib/authority/diff.mjs +77 -54
  16. package/cli/lib/authority/format.mjs +4 -32
  17. package/cli/lib/authority/git-snapshot.mjs +34 -379
  18. package/cli/lib/authority/graph.mjs +10 -10
  19. package/cli/lib/authority/impact.mjs +17 -144
  20. package/cli/lib/authority/review.mjs +33 -43
  21. package/cli/lib/authority/sarif.mjs +1 -8
  22. package/cli/lib/authority/source-yaml.mjs +0 -4
  23. package/cli/lib/authority/surface.mjs +19 -1
  24. package/cli/lib/authority/terms.mjs +10 -9
  25. package/cli/lib/authority/validate.mjs +6 -14
  26. package/cli/lib/entrypoints.mjs +2 -1
  27. package/contracts/authority-source.schema.yaml +0 -6
  28. package/contracts/authority-verifier-bindings.schema.yaml +21 -10
  29. package/methodology/authority-authoring.yaml +9 -26
  30. package/methodology/core.yaml +5 -3
  31. package/package.json +1 -1
  32. package/spec/authority-audit.yaml +56 -52
  33. package/spec/authority-authoring.yaml +3 -16
  34. package/spec/authority-change-candidates.yaml +146 -0
  35. package/spec/authority-consumers.yaml +7 -0
  36. package/spec/authority-discovery.yaml +2 -2
  37. package/spec/authority-graph.yaml +1 -1
  38. package/spec/authority-impact.yaml +16 -39
  39. package/spec/authority-review.yaml +29 -47
  40. package/spec/authority-terms.yaml +5 -1
  41. package/spec/product-scope.yaml +8 -7
  42. package/cli/lib/authority/evidence-snapshot.mjs +0 -1
  43. package/cli/lib/authority/evidence.mjs +0 -1432
  44. package/contracts/authority-evidence-bindings.schema.yaml +0 -53
  45. package/contracts/authority-evidence-probe-results.schema.yaml +0 -38
  46. package/contracts/authority-impact-dispositions.schema.yaml +0 -13
  47. package/spec/authority-evidence.yaml +0 -200
@@ -6,39 +6,36 @@ import {
6
6
  refuseAnchorsOutputBudget,
7
7
  } from "../lib/authority/anchors.mjs";
8
8
  import { auditAuthorityPath } from "../lib/authority/audit.mjs";
9
+ import {
10
+ CHANGE_CANDIDATE_CHANNELS,
11
+ changeCandidatesAuthorityPaths,
12
+ } from "../lib/authority/change-candidates.mjs";
9
13
  import { checkAuthorityPath, compileAuthorityPath } from "../lib/authority/compile.mjs";
10
14
  import { closedSetsAuthorityRepository } from "../lib/authority/closed-sets.mjs";
11
15
  import { consumersAuthorityRepository } from "../lib/authority/consumers.mjs";
12
16
  import { diffAuthorityPaths } from "../lib/authority/diff.mjs";
13
17
  import { discoverAuthorityPath } from "../lib/authority/discover.mjs";
14
- import { evidenceAuthorityRepository } from "../lib/authority/evidence.mjs";
15
18
  import { AuthorityInputError, formatAuthorityFile } from "../lib/authority/format.mjs";
16
19
  import { pathAuthorityPath, refsAuthorityPath, subgraphAuthorityPath } from "../lib/authority/graph.mjs";
17
20
  import { impactAuthorityPaths } from "../lib/authority/impact.mjs";
18
21
  import { contextAuthorityPath, queryAuthorityPath } from "../lib/authority/query.mjs";
19
22
  import { reviewAuthorityRepository } from "../lib/authority/review.mjs";
20
23
  import { authorityAuditResultToSarif } from "../lib/authority/sarif.mjs";
21
- import { loadAuthorityContract } from "../lib/authority/source-yaml.mjs";
22
24
  import { AUTHORING_SURFACE, AUTHORITY_SURFACE, integerFlagAdmission, booleanFlagAdmission } from "../lib/authority/surface.mjs";
23
25
  import { AUTHORITY_TERM_GRAMMAR_HELP, termsAuthorityRepository } from "../lib/authority/terms.mjs";
24
26
 
25
- const AUTHORITY_SOURCE_CONTRACT = await loadAuthorityContract();
26
- const ADMITTED_AUTHORITY_FORMATS = new Set([
27
- AUTHORITY_SOURCE_CONTRACT.format,
28
- AUTHORITY_SOURCE_CONTRACT.migration_inputs?.format,
29
- ].filter((format) => typeof format === "string"));
30
-
31
27
  // The single source for the authority command list; the root help renders it
32
28
  // rather than keeping a second copy that drifts.
33
29
  export const AUTHORITY_COMMAND_USAGE = [
34
- "nimicoding authority fmt <file> [--check] [--upgrade] [--json]",
35
- "nimicoding authority check <path> [--scope-bindings <file>] [--require-format <format>] [--json]",
30
+ "nimicoding authority fmt <file> [--check] [--json]",
31
+ "nimicoding authority check <path> [--scope-bindings <file>] [--json]",
36
32
  "nimicoding authority compile <path> [--json]",
37
33
  "nimicoding authority anchors <repository-path> --spec <corpus-path> [--scope-bindings <file>] [--embedded-paths] --max-units <positive-safe-integer> --max-anchors <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
38
34
  "nimicoding authority consumers <repository-path> --spec <corpus-path> [--external-root <scheme>=<path>]... --max-files <positive-safe-integer> --max-refs <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
39
35
  "nimicoding authority closed-sets <repository-path> --spec <corpus-path> --max-units <positive-safe-integer> --max-sets <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
40
36
  "nimicoding authority terms <repository-path> --spec <corpus-path> [--term <t>] [--code] --max-units <positive-safe-integer> --max-terms <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
41
37
  "nimicoding authority discover <path> <query> [--kind <definition|rule>] [--owner <exact-owner>] [--scope <exact-scope>] [--lifecycle <active|removed>] --max-candidates <positive-safe-integer> --max-snippet-terms <positive-safe-integer> --max-bytes <positive-safe-integer> [--preview-direction <incoming|outgoing|both> --relations <comma-separated-relation-types> --max-edges <positive-safe-integer>] [--json]",
38
+ "nimicoding authority change-candidates <before-path> <after-path> --channels <comma-separated-closed-set> --max-candidates <positive-safe-integer> --max-reasons <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
42
39
  "nimicoding authority query <path> <id> --max-bytes <positive-integer> [--json]",
43
40
  "nimicoding authority context <path> <id> --max-units <positive-integer> --max-bytes <positive-integer> [--json]",
44
41
  "nimicoding authority refs <path> <id> --direction <incoming|outgoing|both> --relations <comma-separated-relation-types> --max-units <positive-safe-integer> --max-edges <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
@@ -46,9 +43,8 @@ export const AUTHORITY_COMMAND_USAGE = [
46
43
  "nimicoding authority subgraph <path> <id> --direction <incoming|outgoing|both> --relations <comma-separated-relation-types> --depth <positive-safe-integer> --max-units <positive-safe-integer> --max-edges <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
47
44
  "nimicoding authority audit <path> --bindings <file> --max-units <positive-safe-integer> --max-edges <positive-safe-integer> --max-bytes <positive-safe-integer> [--json|--sarif]",
48
45
  "nimicoding authority diff <before-path> <after-path> --max-bytes <positive-integer> [--json]",
49
- "nimicoding authority impact <before-path> <after-path> --dispositions <file> --max-bytes <positive-integer> [--json]",
50
- "nimicoding authority review <repository-path> --base <git-ref> --bindings <file> --dispositions <file> --max-units <positive-safe-integer> --max-edges <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
51
- "nimicoding authority evidence <repository-path> --bindings <tracked-.nimi/config-path> [--probe-results <.nimi/local-path>] --max-units <positive-safe-integer> --max-bindings <positive-safe-integer> --max-locators <positive-safe-integer> --max-edges <positive-safe-integer> --max-input-bytes <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
46
+ "nimicoding authority impact <before-path> <after-path> --max-bytes <positive-integer> [--json]",
47
+ "nimicoding authority review <repository-path> --base <git-ref> --bindings <file> --max-units <positive-safe-integer> --max-edges <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
52
48
  ];
53
49
 
54
50
  const USAGE = [
@@ -62,26 +58,27 @@ const USAGE = [
62
58
  const AUTHORITY_IDENTIFIER = /^[a-z](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z](?:[a-z0-9-]*[a-z0-9])?)+$/;
63
59
 
64
60
  function parseOptions(subcommand, args) {
65
- const options = { json: false, sarif: false, check: false, upgrade: false, embeddedPaths: false, code: false, path: null, repositoryPath: null, spec: null, term: null, requireFormat: null, base: null, id: null, fromId: null, toId: null, query: null, beforePath: null, afterPath: null, dispositions: null, bindings: null, scopeBindings: null, probeResults: null, kind: null, owner: null, scope: null, lifecycle: null, direction: null, previewDirection: null, traversal: null, relations: null, depth: null, maxHops: null, maxCandidates: null, maxSnippetTerms: null, maxUnits: null, maxTerms: null, maxSets: null, maxAnchors: null, maxFiles: null, maxRefs: null, maxBindings: null, maxLocators: null, maxEdges: null, maxInputBytes: null, maxBytes: null, externalRoots: [] };
61
+ const options = { json: false, sarif: false, check: false, embeddedPaths: false, code: false, path: null, repositoryPath: null, spec: null, term: null, base: null, id: null, fromId: null, toId: null, query: null, beforePath: null, afterPath: null, bindings: null, scopeBindings: null, kind: null, owner: null, scope: null, lifecycle: null, direction: null, previewDirection: null, traversal: null, relations: null, channels: null, depth: null, maxHops: null, maxCandidates: null, maxReasons: null, maxSnippetTerms: null, maxUnits: null, maxTerms: null, maxSets: null, maxAnchors: null, maxFiles: null, maxRefs: null, maxEdges: null, maxBytes: null, externalRoots: [] };
66
62
  const graphCommands = ["refs", "path", "subgraph"];
67
63
  const reportIntegerAdmission = integerFlagAdmission();
68
64
  const reportIntegerCommands = (flag) => reportIntegerAdmission[flag] ?? [];
65
+ const surfaceValueCommands = (flag) => Object.entries(AUTHORITY_SURFACE)
66
+ .filter(([, entry]) => entry.valueFlags.includes(flag))
67
+ .map(([command]) => command);
69
68
  const integerOptions = {
70
- "--max-candidates": ["maxCandidates", ["discover"]],
69
+ "--max-candidates": ["maxCandidates", ["discover", "change-candidates"]],
70
+ "--max-reasons": ["maxReasons", ["change-candidates"]],
71
71
  "--max-snippet-terms": ["maxSnippetTerms", ["discover"]],
72
72
  // The report products' integer flags come from the enforced surface table,
73
73
  // so the parser and the product contracts cannot drift apart silently.
74
- "--max-units": ["maxUnits", ["context", "audit", "review", "evidence", ...reportIntegerCommands("--max-units"), ...graphCommands]],
74
+ "--max-units": ["maxUnits", ["context", "review", ...reportIntegerCommands("--max-units"), ...graphCommands]],
75
75
  "--max-terms": ["maxTerms", reportIntegerCommands("--max-terms")],
76
76
  "--max-sets": ["maxSets", reportIntegerCommands("--max-sets")],
77
77
  "--max-anchors": ["maxAnchors", reportIntegerCommands("--max-anchors")],
78
78
  "--max-files": ["maxFiles", reportIntegerCommands("--max-files")],
79
79
  "--max-refs": ["maxRefs", reportIntegerCommands("--max-refs")],
80
- "--max-bindings": ["maxBindings", ["evidence"]],
81
- "--max-locators": ["maxLocators", ["evidence"]],
82
- "--max-edges": ["maxEdges", ["audit", "review", "evidence", "discover", ...graphCommands]],
83
- "--max-input-bytes": ["maxInputBytes", ["evidence"]],
84
- "--max-bytes": ["maxBytes", ["discover", "query", "context", "audit", "diff", "impact", "review", "evidence", "anchors", "consumers", "terms", "closed-sets", ...graphCommands]],
80
+ "--max-edges": ["maxEdges", ["review", "discover", ...reportIntegerCommands("--max-edges"), ...graphCommands]],
81
+ "--max-bytes": ["maxBytes", ["discover", "change-candidates", "query", "context", "diff", "impact", "review", ...reportIntegerCommands("--max-bytes"), ...graphCommands]],
85
82
  "--max-hops": ["maxHops", ["path"]],
86
83
  "--depth": ["depth", ["subgraph"]],
87
84
  };
@@ -94,14 +91,13 @@ function parseOptions(subcommand, args) {
94
91
  continue;
95
92
  }
96
93
  if (!terminated && arg.startsWith("--")) {
97
- if (arg === "--json" && !options.json) {
94
+ if (arg === "--json" && !options.json && (AUTHORITY_SURFACE[subcommand]?.booleanFlags ?? ["json"]).includes("json")) {
98
95
  if (options.sarif) return { ok: false, error: "authority audit requires --json and --sarif to be mutually exclusive" };
99
96
  options.json = true;
100
- } else if (arg === "--sarif" && subcommand === "audit" && !options.sarif) {
97
+ } else if (arg === "--sarif" && AUTHORITY_SURFACE[subcommand]?.booleanFlags.includes("sarif") && !options.sarif) {
101
98
  if (options.json) return { ok: false, error: "authority audit requires --json and --sarif to be mutually exclusive" };
102
99
  options.sarif = true;
103
100
  } else if (arg === "--check" && AUTHORING_SURFACE[subcommand]?.booleanFlags.includes("check") && !options.check) options.check = true;
104
- else if (arg === "--upgrade" && AUTHORING_SURFACE[subcommand]?.booleanFlags.includes("upgrade") && !options.upgrade) options.upgrade = true;
105
101
  else if (arg === "--embedded-paths" && booleanFlagAdmission("embedded-paths").includes(subcommand) && !options.embeddedPaths) options.embeddedPaths = true;
106
102
  else if (arg === "--code" && booleanFlagAdmission("code").includes(subcommand) && !options.code) options.code = true;
107
103
  else if (arg === "--external-root" && (AUTHORITY_SURFACE[subcommand]?.repeatableFlags ?? []).includes("external-root")) {
@@ -156,6 +152,18 @@ function parseOptions(subcommand, args) {
156
152
  if (!["directed", "incidence"].includes(value)) return { ok: false, error: "authority path requires --traversal directed or incidence" };
157
153
  options.traversal = value;
158
154
  index += 1;
155
+ } else if (arg === "--channels" && subcommand === "change-candidates" && options.channels === null) {
156
+ const value = args[index + 1];
157
+ const channels = value?.split(",") ?? [];
158
+ if (
159
+ channels.length === 0
160
+ || channels.some((entry) => entry.length === 0 || !CHANGE_CANDIDATE_CHANNELS.includes(entry))
161
+ || new Set(channels).size !== channels.length
162
+ ) {
163
+ return { ok: false, error: `authority change-candidates requires a non-empty unique closed --channels set containing only ${CHANGE_CANDIDATE_CHANNELS.join(", ")}` };
164
+ }
165
+ options.channels = channels;
166
+ index += 1;
159
167
  } else if (arg === "--relations" && [...graphCommands, "discover"].includes(subcommand) && options.relations === null) {
160
168
  const value = args[index + 1];
161
169
  const relations = value?.split(",") ?? [];
@@ -164,12 +172,7 @@ function parseOptions(subcommand, args) {
164
172
  }
165
173
  options.relations = relations.sort();
166
174
  index += 1;
167
- } else if (arg === "--dispositions" && ["impact", "review"].includes(subcommand) && options.dispositions === null) {
168
- const value = args[index + 1];
169
- if (!value || value.startsWith("--")) return { ok: false, error: `authority ${subcommand} requires --dispositions followed by one file` };
170
- options.dispositions = value;
171
- index += 1;
172
- } else if (arg === "--bindings" && ["audit", "review", "evidence"].includes(subcommand) && options.bindings === null) {
175
+ } else if (arg === "--bindings" && ["review", ...surfaceValueCommands("bindings")].includes(subcommand) && options.bindings === null) {
173
176
  const value = args[index + 1];
174
177
  if (!value || value.startsWith("--")) return { ok: false, error: `authority ${subcommand} requires --bindings followed by one file` };
175
178
  options.bindings = value;
@@ -179,13 +182,6 @@ function parseOptions(subcommand, args) {
179
182
  if (!value || value.startsWith("--")) return { ok: false, error: `authority ${subcommand} requires --scope-bindings followed by one file` };
180
183
  options.scopeBindings = value;
181
184
  index += 1;
182
- } else if (arg === "--require-format" && subcommand === "check" && options.requireFormat === null) {
183
- const value = args[index + 1];
184
- if (!ADMITTED_AUTHORITY_FORMATS.has(value)) {
185
- return { ok: false, error: `authority check requires --require-format followed by an admitted source format: ${[...ADMITTED_AUTHORITY_FORMATS].join(" or ")}` };
186
- }
187
- options.requireFormat = value;
188
- index += 1;
189
185
  } else if (arg === "--spec" && ["anchors", "consumers", "terms", "closed-sets"].includes(subcommand) && options.spec === null) {
190
186
  const value = args[index + 1];
191
187
  if (!value || value.startsWith("--")) return { ok: false, error: `authority ${subcommand} requires --spec followed by one corpus path` };
@@ -196,11 +192,6 @@ function parseOptions(subcommand, args) {
196
192
  if (!value || value.startsWith("--") || /[\0\r\n]/u.test(value)) return { ok: false, error: "authority terms requires --term followed by one non-empty single-line term" };
197
193
  options.term = value;
198
194
  index += 1;
199
- } else if (arg === "--probe-results" && subcommand === "evidence" && options.probeResults === null) {
200
- const value = args[index + 1];
201
- if (!value || value.startsWith("--")) return { ok: false, error: "authority evidence requires --probe-results followed by one repository-relative file" };
202
- options.probeResults = value;
203
- index += 1;
204
195
  } else if (arg === "--base" && subcommand === "review" && options.base === null) {
205
196
  const value = args[index + 1];
206
197
  if (!value || value.startsWith("-")) return { ok: false, error: "authority review requires --base followed by one non-option Git ref" };
@@ -211,17 +202,17 @@ function parseOptions(subcommand, args) {
211
202
  }
212
203
  positionals.push(arg);
213
204
  }
214
- const expected = subcommand === "path" ? 3 : ["discover", "query", "context", "refs", "subgraph", "diff", "impact"].includes(subcommand) ? 2 : 1;
205
+ const expected = subcommand === "path" ? 3 : ["discover", "change-candidates", "query", "context", "refs", "subgraph", "diff", "impact"].includes(subcommand) ? 2 : 1;
215
206
  if (positionals.length !== expected) {
216
207
  const requirement = subcommand === "discover"
217
208
  ? "one path and one query"
218
- : ["diff", "impact"].includes(subcommand)
209
+ : ["change-candidates", "diff", "impact"].includes(subcommand)
219
210
  ? "one before path and one after path"
220
- : subcommand === "path" ? "one path, one exact from ID, and one exact to ID" : ["review", "evidence", "anchors", "consumers", "terms", "closed-sets"].includes(subcommand) ? "one repository path" : expected === 1 ? "one path" : "one path and one exact ID";
211
+ : subcommand === "path" ? "one path, one exact from ID, and one exact to ID" : ["review", "anchors", "consumers", "terms", "closed-sets"].includes(subcommand) ? "one repository path" : expected === 1 ? "one path" : "one path and one exact ID";
221
212
  return { ok: false, error: `authority ${subcommand} requires exactly ${requirement}` };
222
213
  }
223
- if (["diff", "impact"].includes(subcommand)) [options.beforePath, options.afterPath] = positionals;
224
- else if (["review", "evidence", "anchors", "consumers", "terms", "closed-sets"].includes(subcommand)) [options.repositoryPath] = positionals;
214
+ if (["change-candidates", "diff", "impact"].includes(subcommand)) [options.beforePath, options.afterPath] = positionals;
215
+ else if (["review", "anchors", "consumers", "terms", "closed-sets"].includes(subcommand)) [options.repositoryPath] = positionals;
225
216
  else if (subcommand === "path") [options.path, options.fromId, options.toId] = positionals;
226
217
  else [options.path, options.id] = positionals;
227
218
  if (subcommand === "discover") {
@@ -235,29 +226,24 @@ function parseOptions(subcommand, args) {
235
226
  const provided = previewOptions.filter((value) => value !== null).length;
236
227
  if (provided !== 0 && provided !== previewOptions.length) return { ok: false, error: "authority discover requires --preview-direction, --relations, and --max-edges together" };
237
228
  }
229
+ if (subcommand === "change-candidates" && options.channels === null) return { ok: false, error: "authority change-candidates requires --channels followed by a non-empty closed comma-separated set" };
230
+ if (subcommand === "change-candidates" && options.maxCandidates === null) return { ok: false, error: "authority change-candidates requires --max-candidates followed by a positive integer" };
231
+ if (subcommand === "change-candidates" && options.maxReasons === null) return { ok: false, error: "authority change-candidates requires --max-reasons followed by a positive integer" };
238
232
  if (["context", "anchors", "terms", "closed-sets", ...graphCommands].includes(subcommand) && options.maxUnits === null) return { ok: false, error: `authority ${subcommand} requires --max-units followed by a positive integer` };
239
233
  if (graphCommands.includes(subcommand) && options.maxEdges === null) return { ok: false, error: `authority ${subcommand} requires --max-edges followed by a positive integer` };
240
- if (["discover", "query", "context", "audit", "diff", "impact", "review", "evidence", "anchors", "consumers", "terms", "closed-sets", ...graphCommands].includes(subcommand) && options.maxBytes === null) return { ok: false, error: `authority ${subcommand} requires --max-bytes followed by a positive integer` };
234
+ if (["discover", "change-candidates", "query", "context", "audit", "diff", "impact", "review", "anchors", "consumers", "terms", "closed-sets", ...graphCommands].includes(subcommand) && options.maxBytes === null) return { ok: false, error: `authority ${subcommand} requires --max-bytes followed by a positive integer` };
241
235
  if (["refs", "subgraph"].includes(subcommand) && options.direction === null) return { ok: false, error: `authority ${subcommand} requires --direction` };
242
236
  if (subcommand === "path" && options.traversal === null) return { ok: false, error: "authority path requires --traversal" };
243
237
  if (graphCommands.includes(subcommand) && options.relations === null) return { ok: false, error: `authority ${subcommand} requires --relations` };
244
238
  if (subcommand === "path" && options.maxHops === null) return { ok: false, error: "authority path requires --max-hops followed by a positive integer" };
245
239
  if (subcommand === "subgraph" && options.depth === null) return { ok: false, error: "authority subgraph requires --depth followed by a positive integer" };
246
- if (subcommand === "impact" && options.dispositions === null) return { ok: false, error: "authority impact requires --dispositions followed by one file" };
247
240
  if (subcommand === "audit" && options.bindings === null) return { ok: false, error: "authority audit requires --bindings followed by one file" };
248
241
  if (subcommand === "audit" && options.maxUnits === null) return { ok: false, error: "authority audit requires --max-units followed by a positive integer" };
249
242
  if (subcommand === "audit" && options.maxEdges === null) return { ok: false, error: "authority audit requires --max-edges followed by a positive integer" };
250
243
  if (subcommand === "review" && options.base === null) return { ok: false, error: "authority review requires --base followed by one Git ref" };
251
244
  if (subcommand === "review" && options.bindings === null) return { ok: false, error: "authority review requires --bindings followed by one file" };
252
- if (subcommand === "review" && options.dispositions === null) return { ok: false, error: "authority review requires --dispositions followed by one file" };
253
245
  if (subcommand === "review" && options.maxUnits === null) return { ok: false, error: "authority review requires --max-units followed by a positive integer" };
254
246
  if (subcommand === "review" && options.maxEdges === null) return { ok: false, error: "authority review requires --max-edges followed by a positive integer" };
255
- if (subcommand === "evidence" && options.bindings === null) return { ok: false, error: "authority evidence requires --bindings followed by one repository-relative file" };
256
- if (subcommand === "evidence" && options.maxUnits === null) return { ok: false, error: "authority evidence requires --max-units followed by a positive integer" };
257
- if (subcommand === "evidence" && options.maxBindings === null) return { ok: false, error: "authority evidence requires --max-bindings followed by a positive integer" };
258
- if (subcommand === "evidence" && options.maxLocators === null) return { ok: false, error: "authority evidence requires --max-locators followed by a positive integer" };
259
- if (subcommand === "evidence" && options.maxEdges === null) return { ok: false, error: "authority evidence requires --max-edges followed by a positive integer" };
260
- if (subcommand === "evidence" && options.maxInputBytes === null) return { ok: false, error: "authority evidence requires --max-input-bytes followed by a positive integer" };
261
247
  if (subcommand === "anchors" && options.spec === null) return { ok: false, error: "authority anchors requires --spec followed by one corpus path" };
262
248
  if (subcommand === "anchors" && options.maxAnchors === null) return { ok: false, error: "authority anchors requires --max-anchors followed by a positive integer" };
263
249
  if (subcommand === "consumers" && options.spec === null) return { ok: false, error: "authority consumers requires --spec followed by one corpus path" };
@@ -284,19 +270,17 @@ function humanAuditLocation(location) {
284
270
  function formatAuditHuman(report) {
285
271
  const audit = report.audit;
286
272
  const operationStatus = audit?.operationStatus ?? report.semantic_status;
287
- const policyStatus = audit?.policyStatus ?? "indeterminate";
288
- const complete = audit?.complete ?? false;
273
+ const blockingStatus = audit?.blockingStatus ?? "not_evaluated";
289
274
  const lines = [
290
- `nimicoding authority audit: operation=${operationStatus}; policy=${policyStatus}; complete=${complete}`,
275
+ `nimicoding authority audit: operation=${operationStatus}; blocking=${blockingStatus}`,
291
276
  `files: ${report.summary.files}; units: ${report.summary.units}; diagnostics: ${report.summary.diagnostics}`,
292
277
  `audit bytes: ${report.audit_bytes}`,
293
278
  ];
294
279
  if (audit) {
295
- lines.push(`bindings: required=${audit.counts.bindings.required}; configured=${audit.counts.bindings.configured}; evaluated=${audit.counts.bindings.evaluated}`);
296
- lines.push(`returned: observations=${audit.counts.returned.observations}; findings=${audit.counts.returned.findings}; gaps=${audit.counts.returned.gaps}`);
280
+ lines.push(`bindings: configured=${audit.counts.bindings.configured}; evaluated=${audit.counts.bindings.evaluated}`);
281
+ lines.push(`returned: findings=${audit.counts.returned.findings}`);
297
282
  lines.push(`traversal: units=${audit.counts.traversal.units}; edges=${audit.counts.traversal.edges}`);
298
283
  for (const finding of audit.findings) lines.push(`finding ${finding.code} ${finding.fingerprint} at ${humanAuditLocation(finding.primaryLocation)}: ${finding.message}`);
299
- for (const gap of audit.gaps) lines.push(`gap ${gap.code} ${gap.fingerprint} at ${humanAuditLocation(gap.primaryLocation)}: ${gap.message}`);
300
284
  } else lines.push("counts: unavailable (audit refused before evaluation)");
301
285
  for (const diagnostic of report.diagnostics) lines.push(humanDiagnostic(diagnostic));
302
286
  return `${lines.join("\n")}\n`;
@@ -310,6 +294,7 @@ function outputReport(report, json) {
310
294
  if (report.changed !== undefined) process.stdout.write(`changed: ${report.changed}\n`);
311
295
  if (report.packet_bytes !== undefined) process.stdout.write(`packet bytes: ${report.packet_bytes}\n`);
312
296
  if (report.discovery_bytes !== undefined) process.stdout.write(`discovery bytes: ${report.discovery_bytes}\n`);
297
+ if (report.candidate_bytes !== undefined) process.stdout.write(`change-candidate bytes: ${report.candidate_bytes}\n`);
313
298
  if (report.payload_bytes !== undefined) process.stdout.write(`semantic payload bytes: ${report.payload_bytes}\n`);
314
299
  if (report.graph_bytes !== undefined) process.stdout.write(`graph bytes: ${report.graph_bytes}\n`);
315
300
  if (report.packet) {
@@ -321,6 +306,12 @@ function outputReport(report, json) {
321
306
  process.stdout.write(`candidates: ${report.discovery.candidates.map((candidate) => candidate.id).join(", ")}\n`);
322
307
  if (report.discovery.relationPreview) process.stdout.write(`relation preview: units=${report.discovery.relationPreview.counts.units}; edges=${report.discovery.relationPreview.counts.edges}; complete=${report.discovery.relationPreview.complete}\n`);
323
308
  }
309
+ if (report.change_candidates) {
310
+ const product = report.change_candidates;
311
+ process.stdout.write(`requested channels: ${product.requestedChannels.join(", ")}\n`);
312
+ process.stdout.write(`changed roots: ${product.counts.changedRoots}; eligible survivors: ${product.counts.eligibleAfterUnits}; recalled candidates: ${product.counts.candidates}; reasons: ${product.counts.reasons}\n`);
313
+ process.stdout.write(`candidates: ${product.candidates.map((candidate) => candidate.id).join(", ")}\n`);
314
+ }
324
315
  if (report.diff) process.stdout.write(`semantic changes: ${report.diff.summary.changes}\n`);
325
316
  if (report.impact) process.stdout.write(`impacted units: ${report.impact.impactedUnits.map((unit) => unit.id).join(", ")}\n`);
326
317
  if (report.graph) {
@@ -348,19 +339,18 @@ function outputAuditReport(report, output) {
348
339
  function formatReviewHuman(report) {
349
340
  const review = report.review;
350
341
  const lines = [
351
- `nimicoding authority review: operation=${review?.operationStatus ?? "refused"}; policy=${review?.policyStatus ?? "indeterminate"}; complete=${review?.complete ?? false}`,
342
+ `nimicoding authority review: operation=${review?.operationStatus ?? "refused"}; analysis-complete=${review?.analysisComplete ?? false}; configured-audit-blocking=${review?.audit?.blockingStatus ?? "not_evaluated"}`,
352
343
  ];
353
344
  if (review) {
354
345
  const base = review.snapshots.base;
355
346
  const worktree = review.snapshots.worktree;
356
- const unresolved = review.impact.obligations.filter((obligation) => obligation.disposition === null).length;
357
347
  lines.push(`base: commit=${base.commitOid}; identity=${base.contentIdentity}; files=${base.counts.files}; units=${base.counts.units}`);
358
348
  lines.push(`worktree: identity=${worktree.contentIdentity}; files=${worktree.counts.files}; units=${worktree.counts.units}`);
359
- lines.push(`semantic changes: ${review.diff.summary.changes}; impacted units: ${review.impact.impactedUnits.length}; unresolved obligations: ${unresolved}`);
360
- lines.push(`current audit: policy=${review.audit.policyStatus}; findings=${review.audit.findings.length}; gaps=${review.audit.gaps.length}`);
349
+ lines.push(`semantic changes: ${review.diff.summary.changes}; impacted units: ${review.impact.impactedUnits.length}; declared obligations: ${review.impact.obligations.length}`);
350
+ lines.push(`current configured audit: blocking=${review.audit.blockingStatus}; findings=${review.audit.findings.length}`);
351
+ lines.push(`business semantics: ${review.businessSemanticStatus}; implementation conformance: ${review.implementationConformanceStatus}`);
361
352
  lines.push(`review bytes: ${report.review_bytes}`);
362
353
  for (const finding of review.audit.findings) lines.push(`finding ${finding.code} ${finding.fingerprint} at ${humanAuditLocation(finding.primaryLocation)}: ${finding.message}`);
363
- for (const gap of review.audit.gaps) lines.push(`gap ${gap.code} ${gap.fingerprint} at ${humanAuditLocation(gap.primaryLocation)}: ${gap.message}`);
364
354
  } else {
365
355
  lines.push("review unavailable; partial=false");
366
356
  }
@@ -373,32 +363,6 @@ function outputReviewReport(report, json) {
373
363
  else process.stdout.write(formatReviewHuman(report));
374
364
  }
375
365
 
376
- function formatEvidenceHuman(report) {
377
- const evidence = report.evidence;
378
- const lines = [
379
- `nimicoding authority evidence: operation=${evidence?.operationStatus ?? "refused"}; evidence=${evidence?.evidenceStatus ?? "indeterminate"}; complete=${evidence?.complete ?? false}; conformance=${evidence?.conformanceStatus ?? "not_evaluated"}`,
380
- ];
381
- if (evidence) {
382
- lines.push(`snapshot: head=${evidence.identities.headCommitOid}; authority=${evidence.identities.authorityContentIdentity}; repository-input=${evidence.identities.repositoryInputIdentity}`);
383
- lines.push(`bindings: required=${evidence.counts.bindings.required}; configured=${evidence.counts.bindings.configured}; evaluated=${evidence.counts.bindings.evaluated}`);
384
- lines.push(`returned: observations=${evidence.counts.returned.observations}; findings=${evidence.counts.returned.findings}; gaps=${evidence.counts.returned.gaps}`);
385
- lines.push(`evidence bytes: ${report.evidence_bytes}`);
386
- for (const binding of evidence.bindings) {
387
- lines.push(`binding ${binding.id}: required=${binding.required}; reachability=${binding.packageProbe.reachability}; execution=${binding.packageProbe.execution}; conformance=${binding.packageProbe.conformance}`);
388
- if (binding.externalProbe) lines.push(` external ${binding.externalProbe.id}@${binding.externalProbe.version}: ${binding.externalProbe.status}${binding.externalProbe.reportedOutcome ? `/${binding.externalProbe.reportedOutcome}` : ""}; package-attestation=false`);
389
- }
390
- for (const finding of evidence.findings) lines.push(`finding ${finding.code} ${finding.fingerprint} at ${humanAuditLocation(finding.primaryLocation)}: ${finding.message}`);
391
- for (const gap of evidence.gaps) lines.push(`gap ${gap.code} ${gap.fingerprint} at ${humanAuditLocation(gap.primaryLocation)}: ${gap.message}`);
392
- } else lines.push("evidence unavailable; partial=false");
393
- for (const diagnostic of report.diagnostics) lines.push(humanDiagnostic(diagnostic));
394
- return `${lines.join("\n")}\n`;
395
- }
396
-
397
- function outputEvidenceReport(report, json) {
398
- if (json) process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
399
- else process.stdout.write(formatEvidenceHuman(report));
400
- }
401
-
402
366
  function formatAnchorsReport(report, json) {
403
367
  if (json) return `${JSON.stringify(report, null, 2)}\n`;
404
368
  const lines = [
@@ -544,7 +508,7 @@ export async function runAuthority(args) {
544
508
  process.stdout.write(`${USAGE}\n`);
545
509
  return 0;
546
510
  }
547
- if (!["fmt", "check", "compile", "anchors", "consumers", "terms", "closed-sets", "discover", "query", "context", "refs", "path", "subgraph", "audit", "diff", "impact", "review", "evidence"].includes(subcommand)) {
511
+ if (!["fmt", "check", "compile", "anchors", "consumers", "terms", "closed-sets", "discover", "change-candidates", "query", "context", "refs", "path", "subgraph", "audit", "diff", "impact", "review"].includes(subcommand)) {
548
512
  process.stderr.write(`nimicoding authority refused unknown subcommand: ${subcommand}\n${USAGE}\n`);
549
513
  return 2;
550
514
  }
@@ -555,15 +519,14 @@ export async function runAuthority(args) {
555
519
  }
556
520
  try {
557
521
  if (subcommand === "fmt") {
558
- const result = await formatAuthorityFile(parsed.options.path, { check: parsed.options.check, upgrade: parsed.options.upgrade });
559
- const report = makeReport("fmt", result, "not_evaluated", { changed: result.changed, check: parsed.options.check, upgrade: parsed.options.upgrade });
522
+ const result = await formatAuthorityFile(parsed.options.path, { check: parsed.options.check });
523
+ const report = makeReport("fmt", result, "not_evaluated", { changed: result.changed, check: parsed.options.check });
560
524
  outputReport(report, parsed.options.json);
561
525
  return result.ok ? 0 : 1;
562
526
  }
563
527
  if (subcommand === "check") {
564
528
  const result = await checkAuthorityPath(parsed.options.path, {
565
529
  scopeBindings: parsed.options.scopeBindings,
566
- requireFormat: parsed.options.requireFormat,
567
530
  });
568
531
  const report = makeReport("check", result, result.ok ? "valid" : "invalid");
569
532
  outputReport(report, parsed.options.json);
@@ -592,6 +555,21 @@ export async function runAuthority(args) {
592
555
  outputReport(report, parsed.options.json);
593
556
  return result.ok ? 0 : 1;
594
557
  }
558
+ if (subcommand === "change-candidates") {
559
+ const result = await changeCandidatesAuthorityPaths(parsed.options.beforePath, parsed.options.afterPath, {
560
+ channels: parsed.options.channels,
561
+ maxCandidates: parsed.options.maxCandidates,
562
+ maxReasons: parsed.options.maxReasons,
563
+ maxBytes: parsed.options.maxBytes,
564
+ });
565
+ const report = makeReport("change-candidates", result, result.ok ? "completed" : "refused", {
566
+ candidate_bytes: result.candidateBytes,
567
+ change_candidates: result.changeCandidates,
568
+ partial: result.partial,
569
+ });
570
+ outputReport(report, parsed.options.json);
571
+ return result.ok ? 0 : 1;
572
+ }
595
573
  if (subcommand === "query") {
596
574
  const result = await queryAuthorityPath(parsed.options.path, parsed.options.id, { maxBytes: parsed.options.maxBytes });
597
575
  const report = makeReport("query", result, result.ok ? "valid" : "invalid", { packet_bytes: result.packetBytes, packet: result.packet });
@@ -635,17 +613,17 @@ export async function runAuthority(args) {
635
613
  });
636
614
  outputAuditReport(report, parsed.options.json ? "json" : "human");
637
615
  }
638
- return result.audit?.operationStatus === "completed" && result.audit.policyStatus === "passed" ? 0 : 1;
616
+ return result.audit?.operationStatus === "completed" && result.audit.blockingStatus === "clear" ? 0 : 1;
639
617
  }
640
618
  if (subcommand === "diff") {
641
619
  const result = await diffAuthorityPaths(parsed.options.beforePath, parsed.options.afterPath, { maxBytes: parsed.options.maxBytes });
642
- const report = makeReport("diff", result, result.ok ? "valid" : "invalid", { payload_bytes: result.payloadBytes, diff: result.diff });
620
+ const report = makeReport("diff", result, result.ok ? "valid" : "invalid", { payload_bytes: result.payloadBytes, diff: result.diff, partial: result.partial });
643
621
  outputReport(report, parsed.options.json);
644
622
  return result.ok ? 0 : 1;
645
623
  }
646
624
  if (subcommand === "impact") {
647
- const result = await impactAuthorityPaths(parsed.options.beforePath, parsed.options.afterPath, parsed.options.dispositions, { maxBytes: parsed.options.maxBytes });
648
- const report = makeReport("impact", result, result.ok ? "valid" : "invalid", { payload_bytes: result.payloadBytes, diff: result.diff, impact: result.impact });
625
+ const result = await impactAuthorityPaths(parsed.options.beforePath, parsed.options.afterPath, { maxBytes: parsed.options.maxBytes });
626
+ const report = makeReport("impact", result, result.ok ? "valid" : "invalid", { payload_bytes: result.payloadBytes, diff: result.diff, impact: result.impact, partial: result.partial });
649
627
  outputReport(report, parsed.options.json);
650
628
  return result.ok ? 0 : 1;
651
629
  }
@@ -803,33 +781,10 @@ export async function runAuthority(args) {
803
781
  outputClosedSetsReport(report, parsed.options.json);
804
782
  return result.ok ? 0 : 1;
805
783
  }
806
- if (subcommand === "evidence") {
807
- const result = await evidenceAuthorityRepository(
808
- parsed.options.repositoryPath,
809
- parsed.options.bindings,
810
- parsed.options.probeResults,
811
- {
812
- maxUnits: parsed.options.maxUnits,
813
- maxBindings: parsed.options.maxBindings,
814
- maxLocators: parsed.options.maxLocators,
815
- maxEdges: parsed.options.maxEdges,
816
- maxInputBytes: parsed.options.maxInputBytes,
817
- maxBytes: parsed.options.maxBytes,
818
- },
819
- );
820
- const report = makeReport("evidence", result, result.evidence?.operationStatus ?? "refused", {
821
- evidence_bytes: result.evidenceBytes,
822
- evidence: result.evidence,
823
- partial: result.partial,
824
- });
825
- outputEvidenceReport(report, parsed.options.json);
826
- return result.evidence?.complete && result.evidence.evidenceStatus === "available" ? 0 : 1;
827
- }
828
784
  const result = await reviewAuthorityRepository(
829
785
  parsed.options.repositoryPath,
830
786
  parsed.options.base,
831
787
  parsed.options.bindings,
832
- parsed.options.dispositions,
833
788
  { maxUnits: parsed.options.maxUnits, maxEdges: parsed.options.maxEdges, maxBytes: parsed.options.maxBytes },
834
789
  );
835
790
  const report = makeReport("review", result, result.review?.operationStatus ?? "refused", {
@@ -838,7 +793,7 @@ export async function runAuthority(args) {
838
793
  partial: result.partial,
839
794
  });
840
795
  outputReviewReport(report, parsed.options.json);
841
- return result.review?.complete && result.review.policyStatus === "passed" ? 0 : 1;
796
+ return result.review?.analysisComplete === true && result.review.audit.blockingStatus === "clear" ? 0 : 1;
842
797
  } catch (error) {
843
798
  const message = error instanceof AuthorityInputError ? error.message : `authority ${subcommand} could not evaluate input: ${error.message}`;
844
799
  process.stderr.write(`${message}\n`);
package/cli/constants.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export const VERSION = "0.4.0";
1
+ export const VERSION = "0.5.0";
2
2
  export const PACKAGE_NAME = "@nimiplatform/nimi-coding";
3
3
  export const LOCAL_GITIGNORE_ENTRIES = [".nimi/local/"];
4
4
  export const AUTHORITY_GITATTRIBUTES_ENTRIES = [
@@ -12,12 +12,18 @@ import { fileURLToPath } from "node:url";
12
12
 
13
13
  import YAML from "yaml";
14
14
 
15
+ import { AUTHORITY_COMMAND_USAGE } from "../commands/authority.mjs";
15
16
  import { PNPM_BUILTIN_SUBCOMMANDS } from "../lib/authority/anchors.mjs";
16
- import { AUTHORING_SURFACE, AUTHORITY_SURFACE, REPOSITORY_BOUND_COMMANDS } from "../lib/authority/surface.mjs";
17
+ import {
18
+ AUTHORING_SURFACE,
19
+ AUTHORITY_SURFACE,
20
+ ENFORCED_AUTHORITY_COMMANDS,
21
+ } from "../lib/authority/surface.mjs";
17
22
 
18
23
  const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
19
24
 
20
25
  const CONTRACT_FILES = {
26
+ audit: "spec/authority-audit.yaml",
21
27
  anchors: "spec/authority-anchors.yaml",
22
28
  consumers: "spec/authority-consumers.yaml",
23
29
  terms: "spec/authority-terms.yaml",
@@ -42,7 +48,7 @@ export function checkAuthoritySurface(root = packageRoot) {
42
48
  const failures = [];
43
49
  const contractIds = new Map();
44
50
 
45
- for (const command of REPOSITORY_BOUND_COMMANDS) {
51
+ for (const command of ENFORCED_AUTHORITY_COMMANDS) {
46
52
  const relative = CONTRACT_FILES[command];
47
53
  if (relative === undefined) {
48
54
  failures.push(`command ${command} is in the enforced surface but has no product contract file`);
@@ -109,6 +115,13 @@ export function checkAuthoritySurface(root = packageRoot) {
109
115
  `${command} command line flags ${JSON.stringify(commandFlags)} != declared surface ${JSON.stringify(surfaceFlags)}`,
110
116
  );
111
117
  }
118
+ const usagePrefix = `nimicoding authority ${command} `;
119
+ const helpUsages = AUTHORITY_COMMAND_USAGE.filter((usage) => usage.startsWith(usagePrefix));
120
+ if (helpUsages.length !== 1 || helpUsages[0] !== contract.command) {
121
+ failures.push(
122
+ `${command} help usage ${JSON.stringify(helpUsages)} != contract command ${JSON.stringify(contract.command)}`,
123
+ );
124
+ }
112
125
  }
113
126
 
114
127
  // fmt and the source schema: the authoring contract has to describe the
@@ -135,10 +148,6 @@ export function checkAuthoritySurface(root = packageRoot) {
135
148
  if (authoring.fields?.format !== source.format) {
136
149
  failures.push(`authoring format ${authoring.fields?.format} != source schema format ${source.format}`);
137
150
  }
138
- const authoringMigration = authoring.fields?.migration_inputs?.format;
139
- if (authoringMigration !== source.migration_inputs?.format) {
140
- failures.push(`migration input format: authoring ${authoringMigration} != source schema ${source.migration_inputs?.format}`);
141
- }
142
151
  if (!sameSet(authoring.profiles?.yaml?.container_fields ?? [], source.yaml_document?.allowed_fields ?? [])) {
143
152
  failures.push(
144
153
  `container fields: authoring ${JSON.stringify(sorted(authoring.profiles?.yaml?.container_fields ?? []))} `
@@ -152,7 +161,7 @@ export function checkAuthoritySurface(root = packageRoot) {
152
161
 
153
162
  // The other direction: a contract that no enforced command claims.
154
163
  for (const [command, relative] of Object.entries(CONTRACT_FILES)) {
155
- if (!REPOSITORY_BOUND_COMMANDS.includes(command)) {
164
+ if (!ENFORCED_AUTHORITY_COMMANDS.includes(command)) {
156
165
  failures.push(`${relative} declares command ${command}, which the enforced surface does not admit`);
157
166
  }
158
167
  }
@@ -168,6 +177,6 @@ if (import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith
168
177
  for (const failure of failures) process.stderr.write(`authority-surface: ${failure}\n`);
169
178
  if (failures.length > 0) process.exit(1);
170
179
  process.stdout.write(
171
- `authority-surface: ${REPOSITORY_BOUND_COMMANDS.length} repository-bound commands match their product contracts\n`,
180
+ `authority-surface: ${ENFORCED_AUTHORITY_COMMANDS.length} public command surfaces match their product contracts\n`,
172
181
  );
173
182
  }
@@ -335,18 +335,6 @@ function createClassification(contracts) {
335
335
  return leaves;
336
336
  }
337
337
 
338
- function addAuthoringClassification(leaves, authoring) {
339
- const values = authoring?.commands?.check?.require_format?.admitted_values;
340
- if (!Array.isArray(values) || values.length === 0) throw new Error("authoring contract declares no admitted check formats");
341
- values.forEach((value, index) => leaves.set(`authoring:commands.check.require_format.admitted_values[${index}]`, {
342
- contract: "authoring",
343
- path: `commands.check.require_format.admitted_values[${index}]`,
344
- value,
345
- status: "unmapped",
346
- details: [],
347
- }));
348
- }
349
-
350
338
  function classify(leaves, contract, leafPath, matches, detail) {
351
339
  const leaf = leaves.get(`${contract}:${leafPath}`);
352
340
  if (!leaf) throw new Error(`behavioural check names no classified leaf: ${contract}:${leafPath}`);
@@ -431,7 +419,7 @@ async function exerciseParserSurface(cliPath, fixture, contracts, leaves) {
431
419
  return validReports;
432
420
  }
433
421
 
434
- async function exerciseRequiredCombinations(cliPath, fixture, contracts, authoring, leaves) {
422
+ async function exerciseRequiredCombinations(cliPath, fixture, contracts, leaves) {
435
423
  const codeWithoutTerm = await runCli(cliPath, fixture.root, [
436
424
  ...requiredArgs("terms", fixture).filter((entry) => entry !== "--json"),
437
425
  "--code", "--json",
@@ -442,20 +430,6 @@ async function exerciseRequiredCombinations(cliPath, fixture, contracts, authori
442
430
  classify(leaves, "terms", leafPath, refusedCode, `required --code/--term combination exited ${codeWithoutTerm.code}`);
443
431
  }
444
432
 
445
- const admitted = authoring.commands.check.require_format.admitted_values;
446
- const invalidFormat = await runCli(cliPath, fixture.root, [
447
- "authority", "check", fixture.spec, "--require-format", "nimicoding.authority/invalid", "--json",
448
- ]);
449
- const formatRefusal = invalidFormat.code === 2
450
- && invalidFormat.stdout === ""
451
- && admitted.every((format) => invalidFormat.stderr.includes(format));
452
- admitted.forEach((_, index) => classify(
453
- leaves,
454
- "authoring",
455
- `commands.check.require_format.admitted_values[${index}]`,
456
- formatRefusal,
457
- `invalid --require-format exited ${invalidFormat.code} and named admitted formats`,
458
- ));
459
433
  }
460
434
 
461
435
  async function independentFixtureOracle(fixture) {
@@ -479,7 +453,7 @@ async function independentFixtureOracle(fixture) {
479
453
  units: 3,
480
454
  files: 6,
481
455
  refs: 2,
482
- terms: 2,
456
+ terms: 5,
483
457
  sets: 2,
484
458
  consumersInputBytes: nonCorpusBytes,
485
459
  termsInputBytes: authorityBytes + nonCorpusBytes,
@@ -682,7 +656,7 @@ async function exerciseCoreClosure(cliPath, fixture, contracts, leaves, validRep
682
656
  consumers: validReports.consumers.report?.summary?.filesScanned === 6
683
657
  && validReports.consumers.report?.summary?.references === 2,
684
658
  terms: validReports.terms.report?.summary?.units === 3
685
- && validReports.terms.report?.summary?.distinctTerms === 2,
659
+ && validReports.terms.report?.summary?.distinctTerms === 5,
686
660
  "closed-sets": validReports["closed-sets"].report?.summary?.sets?.total === 2,
687
661
  };
688
662
  for (const command of Object.keys(contracts)) {
@@ -841,13 +815,11 @@ async function runConformanceLane({ contractDirectory, cliPath, lane }) {
841
815
  for (const [command, file] of Object.entries(PRODUCT_CONTRACTS)) {
842
816
  contracts[command] = YAML.parse(await readFile(path.join(contractDirectory, file), "utf8"));
843
817
  }
844
- const authoring = YAML.parse(await readFile(path.join(contractDirectory, "authority-authoring.yaml"), "utf8"));
845
818
  const leaves = createClassification(contracts);
846
- addAuthoringClassification(leaves, authoring);
847
819
  const fixture = await createRepositoryFixture();
848
820
  try {
849
821
  const validReports = await exerciseParserSurface(cliPath, fixture, contracts, leaves);
850
- await exerciseRequiredCombinations(cliPath, fixture, contracts, authoring, leaves);
822
+ await exerciseRequiredCombinations(cliPath, fixture, contracts, leaves);
851
823
  await exerciseCompleteAndBudgets(cliPath, fixture, contracts, leaves, validReports);
852
824
  await exerciseGrammar(cliPath, fixture, leaves, validReports);
853
825
  await addAnchorFinding(fixture);