@nimiplatform/nimi-coding 0.3.1 → 0.4.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 (118) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/CONTRIBUTING.md +3 -3
  3. package/README.md +285 -41
  4. package/README.zh-CN.md +294 -31
  5. package/bin/nimicoding.mjs +1 -1
  6. package/cli/commands/authority.mjs +849 -0
  7. package/cli/commands/clear.mjs +27 -7
  8. package/cli/commands/doctor.mjs +10 -1
  9. package/cli/commands/start.mjs +23 -27
  10. package/cli/commands/sync.mjs +21 -101
  11. package/cli/constants.mjs +7 -31
  12. package/cli/dev/check-authority-surface.mjs +173 -0
  13. package/cli/dev/check-product-conformance.mjs +917 -0
  14. package/cli/help.mjs +10 -17
  15. package/cli/index.mjs +6 -47
  16. package/cli/lib/authority/anchors.mjs +491 -0
  17. package/cli/lib/authority/audit.mjs +490 -0
  18. package/cli/lib/authority/closed-sets.mjs +646 -0
  19. package/cli/lib/authority/compile.mjs +110 -0
  20. package/cli/lib/authority/consumers.mjs +470 -0
  21. package/cli/lib/authority/diagnostics.mjs +87 -0
  22. package/cli/lib/authority/diff.mjs +287 -0
  23. package/cli/lib/authority/discover.mjs +447 -0
  24. package/cli/lib/authority/evidence-snapshot.mjs +1 -0
  25. package/cli/lib/authority/evidence.mjs +1432 -0
  26. package/cli/lib/authority/format.mjs +197 -0
  27. package/cli/lib/authority/git-snapshot.mjs +1065 -0
  28. package/cli/lib/authority/graph.mjs +351 -0
  29. package/cli/lib/authority/impact.mjs +252 -0
  30. package/cli/lib/authority/lexicon.mjs +55 -0
  31. package/cli/lib/authority/query.mjs +162 -0
  32. package/cli/lib/authority/repository-corpus.mjs +37 -0
  33. package/cli/lib/authority/repository-inventory.mjs +34 -0
  34. package/cli/lib/authority/review.mjs +222 -0
  35. package/cli/lib/authority/sarif.mjs +181 -0
  36. package/cli/lib/authority/scope-bindings.mjs +225 -0
  37. package/cli/lib/authority/source-map.mjs +105 -0
  38. package/cli/lib/authority/source-markdown.mjs +167 -0
  39. package/cli/lib/authority/source-yaml.mjs +312 -0
  40. package/cli/lib/authority/surface.mjs +79 -0
  41. package/cli/lib/authority/terms.mjs +421 -0
  42. package/cli/lib/authority/tracked-text.mjs +70 -0
  43. package/cli/lib/authority/validate.mjs +308 -0
  44. package/cli/lib/bootstrap.mjs +55 -71
  45. package/cli/lib/doctor.mjs +40 -123
  46. package/cli/lib/entrypoints.mjs +103 -105
  47. package/cli/lib/fs-helpers.mjs +139 -15
  48. package/cli/lib/internal/governance/config.mjs +0 -36
  49. package/cli/lib/shared.mjs +8 -21
  50. package/cli/lib/sync.mjs +81 -92
  51. package/cli/lib/value-helpers.mjs +0 -26
  52. package/cli/lib/yaml-helpers.mjs +1 -124
  53. package/cli/seeds/bootstrap.mjs +31 -87
  54. package/cli/seeds/seed-policy.yaml +9 -63
  55. package/contracts/authority-evidence-bindings.schema.yaml +53 -0
  56. package/contracts/authority-evidence-probe-results.schema.yaml +38 -0
  57. package/contracts/authority-impact-dispositions.schema.yaml +13 -0
  58. package/contracts/authority-scope-bindings.schema.yaml +15 -0
  59. package/contracts/authority-source.schema.yaml +64 -0
  60. package/contracts/authority-verifier-bindings.schema.yaml +20 -0
  61. package/methodology/authority-authoring.yaml +144 -0
  62. package/methodology/core.yaml +31 -26
  63. package/package.json +10 -6
  64. package/spec/authority-anchors.yaml +99 -0
  65. package/spec/authority-audit.yaml +85 -0
  66. package/spec/authority-authoring.yaml +150 -0
  67. package/spec/authority-closed-sets.yaml +65 -0
  68. package/spec/authority-consumers.yaml +70 -0
  69. package/spec/authority-context.yaml +50 -0
  70. package/spec/authority-discovery.yaml +119 -0
  71. package/spec/authority-evidence.yaml +200 -0
  72. package/spec/authority-graph.yaml +59 -0
  73. package/spec/authority-impact.yaml +84 -0
  74. package/spec/authority-review.yaml +89 -0
  75. package/spec/authority-terms.yaml +59 -0
  76. package/spec/product-scope.yaml +36 -23
  77. package/cli/commands/blueprint-audit.mjs +0 -91
  78. package/cli/commands/classify-spec-tree.mjs +0 -5
  79. package/cli/commands/generate-spec-derived-docs.mjs +0 -151
  80. package/cli/commands/generate-spec-migration-plan.mjs +0 -30
  81. package/cli/commands/surface-validator-command.mjs +0 -49
  82. package/cli/commands/validate-domain-admission.mjs +0 -5
  83. package/cli/commands/validate-guidance-bodies.mjs +0 -5
  84. package/cli/commands/validate-placement.mjs +0 -5
  85. package/cli/commands/validate-projection-edges.mjs +0 -5
  86. package/cli/commands/validate-spec-audit.mjs +0 -27
  87. package/cli/commands/validate-spec-governance.mjs +0 -144
  88. package/cli/commands/validate-spec-tree.mjs +0 -27
  89. package/cli/commands/validate-table-family.mjs +0 -5
  90. package/cli/commands/validate-tracked-output-admission.mjs +0 -5
  91. package/cli/lib/blueprint-audit.mjs +0 -363
  92. package/cli/lib/contracts.mjs +0 -21
  93. package/cli/lib/internal/contracts-loaders.mjs +0 -47
  94. package/cli/lib/internal/contracts-parse.mjs +0 -192
  95. package/cli/lib/internal/governance/runner.mjs +0 -35
  96. package/cli/lib/internal/surface-taxonomy-validators.mjs +0 -1147
  97. package/cli/lib/internal/validators-shared.mjs +0 -12
  98. package/cli/lib/internal/validators-spec-helpers.mjs +0 -52
  99. package/cli/lib/internal/validators-spec.mjs +0 -380
  100. package/cli/lib/validators.mjs +0 -30
  101. package/config/bootstrap.yaml +0 -6
  102. package/config/spec-generation-inputs.yaml +0 -41
  103. package/contracts/domain-admission.schema.yaml +0 -56
  104. package/contracts/migration-inventory.schema.yaml +0 -15
  105. package/contracts/negative-fixtures.yaml +0 -26
  106. package/contracts/placement-contract.schema.yaml +0 -127
  107. package/contracts/projection-edge.schema.yaml +0 -46
  108. package/contracts/shared-enums.yaml +0 -63
  109. package/contracts/spec-generation-audit.schema.yaml +0 -46
  110. package/contracts/spec-generation-inputs.schema.yaml +0 -43
  111. package/contracts/spec-layout.schema.yaml +0 -32
  112. package/contracts/surface-taxonomy.schema.yaml +0 -139
  113. package/contracts/table-family.schema.yaml +0 -133
  114. package/contracts/tracked-output-admission.schema.yaml +0 -17
  115. package/methodology/four-closure-policy.yaml +0 -20
  116. package/methodology/role-separation-policy.yaml +0 -22
  117. package/methodology/spec-reconstruction.yaml +0 -69
  118. package/spec/_meta/spec-tree-model.yaml +0 -44
@@ -1,12 +0,0 @@
1
- export const VALIDATOR_CLI_RESULT_CONTRACT = "validator-cli-result.v1";
2
-
3
- export const VALIDATOR_NATIVE_REFUSAL_CODES = {
4
- SPEC_TREE_MISSING: "SPEC_TREE_MISSING",
5
- SPEC_TREE_INVALID: "SPEC_TREE_INVALID",
6
- SPEC_AUDIT_MISSING: "SPEC_AUDIT_MISSING",
7
- SPEC_AUDIT_INVALID: "SPEC_AUDIT_INVALID",
8
- };
9
-
10
- export function makeValidatorRefusal(code, message) {
11
- return { code, message };
12
- }
@@ -1,52 +0,0 @@
1
- import { readdir } from "node:fs/promises";
2
- import path from "node:path";
3
-
4
- export async function collectTreeFiles(rootPath) {
5
- async function walk(currentPath) {
6
- let entries;
7
- try {
8
- entries = await readdir(currentPath, { withFileTypes: true });
9
- } catch {
10
- return [];
11
- }
12
-
13
- const files = [];
14
- for (const entry of entries) {
15
- const childPath = path.join(currentPath, entry.name);
16
- if (entry.isDirectory()) {
17
- files.push(...await walk(childPath));
18
- } else if (entry.isFile()) {
19
- files.push(path.relative(rootPath, childPath).split(path.sep).join(path.posix.sep));
20
- }
21
- }
22
- return files.sort();
23
- }
24
-
25
- return walk(rootPath);
26
- }
27
- export function isSourceRefWithinDeclaredRoots(sourceRef, declaredInputs) {
28
- const roots = [
29
- ...declaredInputs.code_roots,
30
- ...declaredInputs.docs_roots,
31
- ...declaredInputs.structure_roots,
32
- ...(declaredInputs.benchmark_blueprint_root ? [declaredInputs.benchmark_blueprint_root] : []),
33
- ];
34
-
35
- return roots.some((root) => (
36
- root === "."
37
- ? !path.posix.isAbsolute(sourceRef)
38
- : sourceRef === root || sourceRef.startsWith(`${root}/`)
39
- ));
40
- }
41
-
42
- export function isDeclaredInputsCompatibleWithConfig(declaredInputs, generationInputs, blueprintReference) {
43
- const blueprintRoot = blueprintReference.present ? blueprintReference.root : null;
44
- const rootsAlign = (declaredRoots, configuredRoots) =>
45
- declaredRoots.every((entry) => configuredRoots.includes(entry));
46
-
47
- return rootsAlign(declaredInputs.code_roots, generationInputs.codeRoots ?? [])
48
- && rootsAlign(declaredInputs.docs_roots, generationInputs.docsRoots ?? [])
49
- && rootsAlign(declaredInputs.structure_roots, generationInputs.structureRoots ?? [])
50
- && declaredInputs.human_note_paths.length === 0
51
- && declaredInputs.benchmark_blueprint_root === blueprintRoot;
52
- }
@@ -1,380 +0,0 @@
1
- import path from "node:path";
2
-
3
- import {
4
- SPEC_GENERATION_AUDIT_CONTRACT_REF,
5
- SPEC_GENERATION_AUDIT_COVERAGE_STATUS_ENUM,
6
- SPEC_GENERATION_AUDIT_SOURCE_BASIS_ENUM,
7
- } from "../../constants.mjs";
8
- import {
9
- loadBlueprintReference,
10
- loadSpecGenerationAuditContract,
11
- loadSpecGenerationInputsConfig,
12
- } from "../contracts.mjs";
13
- import { readTextIfFile } from "../fs-helpers.mjs";
14
- import { isPlainObject } from "../value-helpers.mjs";
15
- import { parseYamlText } from "../yaml-helpers.mjs";
16
- import {
17
- makeValidatorRefusal,
18
- VALIDATOR_NATIVE_REFUSAL_CODES,
19
- } from "./validators-shared.mjs";
20
- import {
21
- validateDomainAdmission,
22
- validateGuidanceBodies,
23
- validatePlacement,
24
- validateProjectionEdges,
25
- validateTableFamily,
26
- } from "./surface-taxonomy-validators.mjs";
27
- import {
28
- collectTreeFiles,
29
- isDeclaredInputsCompatibleWithConfig,
30
- isSourceRefWithinDeclaredRoots,
31
- } from "./validators-spec-helpers.mjs";
32
-
33
- async function loadRequiredFiles(projectRoot) {
34
- const text = await readTextIfFile(path.join(projectRoot, ".nimi", "methodology", "spec-reconstruction.yaml"));
35
- const parsed = parseYamlText(text);
36
- return Array.isArray(parsed?.reconstruction?.target_tree_shape?.minimal_required_outputs)
37
- ? parsed.reconstruction.target_tree_shape.minimal_required_outputs.map(String)
38
- : [];
39
- }
40
- function normalizeAuditFileClass(entry) {
41
- return typeof entry.surface_class === "string"
42
- ? entry.surface_class
43
- : String(entry.file_class ?? "");
44
- }
45
-
46
- function toPortableProjectPath(projectRoot, absolutePath) {
47
- return path.relative(projectRoot, absolutePath).split(path.sep).join(path.posix.sep);
48
- }
49
-
50
- function invalidTree(errors, warnings, message) {
51
- return {
52
- ok: false,
53
- errors,
54
- warnings,
55
- refusal: makeValidatorRefusal(VALIDATOR_NATIVE_REFUSAL_CODES.SPEC_TREE_INVALID, message),
56
- };
57
- }
58
-
59
- export async function validateSpecTree(rootPath, options = {}) {
60
- const projectRoot = options.projectRoot ?? process.cwd();
61
- const generationInputs = await loadSpecGenerationInputsConfig(projectRoot);
62
- const errors = [];
63
- const warnings = [];
64
-
65
- if (!generationInputs.ok || generationInputs.mode !== "class_filtered") {
66
- return invalidTree(
67
- [`invalid spec generation inputs config: ${generationInputs.path}`],
68
- warnings,
69
- "spec tree validation requires class-filtered generation inputs",
70
- );
71
- }
72
-
73
- const targetRoot = path.resolve(rootPath);
74
- const expectedRoot = path.resolve(projectRoot, generationInputs.canonicalTargetRoot);
75
- if (targetRoot !== expectedRoot) {
76
- errors.push(`spec tree root mismatch: expected ${expectedRoot} but received ${targetRoot}`);
77
- }
78
-
79
- const files = await collectTreeFiles(targetRoot);
80
- if (files.length === 0) {
81
- return {
82
- ok: false,
83
- errors: errors.length > 0 ? errors : [`missing spec tree root: ${targetRoot}`],
84
- warnings,
85
- refusal: makeValidatorRefusal(
86
- VALIDATOR_NATIVE_REFUSAL_CODES.SPEC_TREE_MISSING,
87
- "spec tree root is missing or empty",
88
- ),
89
- };
90
- }
91
-
92
- const canonicalRoot = generationInputs.canonicalTargetRoot;
93
- const requiredFiles = await loadRequiredFiles(projectRoot);
94
- const missingRequired = requiredFiles
95
- .map((entry) => path.posix.relative(canonicalRoot, entry))
96
- .filter((entry) => !files.includes(entry));
97
- if (missingRequired.length > 0) {
98
- errors.push(`missing required canonical files: ${missingRequired.join(", ")}`);
99
- }
100
-
101
- const rootRef = path.relative(projectRoot, targetRoot).split(path.sep).join(path.posix.sep) || ".";
102
- const reports = await Promise.all([
103
- validatePlacement(projectRoot, { rootRef }),
104
- validateDomainAdmission(projectRoot, { rootRef }),
105
- validateTableFamily(projectRoot, { rootRef }),
106
- validateProjectionEdges(projectRoot, { rootRef }),
107
- validateGuidanceBodies(projectRoot, { rootRef }),
108
- ]);
109
- for (const report of reports) {
110
- for (const error of report.errors ?? []) {
111
- if (!errors.includes(error)) errors.push(error);
112
- }
113
- for (const warning of report.warnings ?? []) {
114
- if (!warnings.includes(warning)) warnings.push(warning);
115
- }
116
- }
117
-
118
- return {
119
- ok: errors.length === 0,
120
- errors,
121
- warnings,
122
- refusal: errors.length === 0
123
- ? null
124
- : makeValidatorRefusal(
125
- VALIDATOR_NATIVE_REFUSAL_CODES.SPEC_TREE_INVALID,
126
- `spec tree is invalid: ${path.basename(targetRoot)}`,
127
- ),
128
- summary: {
129
- profile: "surface_taxonomy_v2",
130
- canonicalRoot,
131
- totalFiles: files.length,
132
- requiredFiles: requiredFiles.length,
133
- missingRequired,
134
- classifiedFiles: reports[0]?.summary?.total ?? files.length,
135
- unexpectedFiles: [],
136
- conflictingFiles: [],
137
- },
138
- };
139
- }
140
-
141
- export async function validateSpecAudit(auditPath, options = {}) {
142
- const projectRoot = options.projectRoot ?? process.cwd();
143
- const generationInputs = await loadSpecGenerationInputsConfig(projectRoot);
144
- const blueprintReference = await loadBlueprintReference(projectRoot);
145
- const auditContract = await loadSpecGenerationAuditContract(projectRoot);
146
- const errors = [];
147
- const warnings = [];
148
-
149
- if (!generationInputs.ok || generationInputs.mode !== "class_filtered") {
150
- return {
151
- ok: false,
152
- errors: [`invalid spec generation inputs config: ${generationInputs.path}`],
153
- warnings,
154
- refusal: makeValidatorRefusal(
155
- VALIDATOR_NATIVE_REFUSAL_CODES.SPEC_AUDIT_INVALID,
156
- "spec audit validation requires class-filtered generation inputs",
157
- ),
158
- };
159
- }
160
- if (!auditContract.ok) {
161
- return {
162
- ok: false,
163
- errors: [`invalid spec generation audit contract: ${auditContract.path}`],
164
- warnings,
165
- refusal: makeValidatorRefusal(
166
- VALIDATOR_NATIVE_REFUSAL_CODES.SPEC_AUDIT_INVALID,
167
- "spec audit validation requires a valid audit contract",
168
- ),
169
- };
170
- }
171
- if (!blueprintReference.ok) {
172
- errors.push(`invalid blueprint reference: ${blueprintReference.path}`);
173
- }
174
-
175
- const absoluteAuditPath = path.resolve(auditPath);
176
- const auditText = await readTextIfFile(absoluteAuditPath);
177
- if (auditText === null) {
178
- return {
179
- ok: false,
180
- errors: [`missing file: ${absoluteAuditPath}`],
181
- warnings,
182
- refusal: makeValidatorRefusal(
183
- VALIDATOR_NATIVE_REFUSAL_CODES.SPEC_AUDIT_MISSING,
184
- "spec generation audit artifact is missing",
185
- ),
186
- };
187
- }
188
-
189
- const parsed = parseYamlText(auditText);
190
- const audit = parsed?.spec_generation_audit;
191
- if (!isPlainObject(parsed) || !isPlainObject(audit)) {
192
- return {
193
- ok: false,
194
- errors: [`invalid YAML document: ${absoluteAuditPath}`],
195
- warnings,
196
- refusal: makeValidatorRefusal(
197
- VALIDATOR_NATIVE_REFUSAL_CODES.SPEC_AUDIT_INVALID,
198
- "spec generation audit artifact is not valid YAML",
199
- ),
200
- };
201
- }
202
-
203
- if (parsed.version !== 2) errors.push("spec generation audit version must be 2");
204
- if (String(parsed.contract_ref ?? "") !== SPEC_GENERATION_AUDIT_CONTRACT_REF) {
205
- errors.push(`spec generation audit contract_ref must be ${SPEC_GENERATION_AUDIT_CONTRACT_REF}`);
206
- }
207
-
208
- const missingTopLevelFields = auditContract.requiredTopLevelFields.filter((field) => !(field in audit));
209
- if (missingTopLevelFields.length > 0) {
210
- errors.push(`spec generation audit is missing required fields: ${missingTopLevelFields.join(", ")}`);
211
- }
212
-
213
- const declaredInputs = {
214
- code_roots: Array.isArray(audit?.input_roots?.code_roots) ? audit.input_roots.code_roots.map(String) : [],
215
- docs_roots: Array.isArray(audit?.input_roots?.docs_roots) ? audit.input_roots.docs_roots.map(String) : [],
216
- structure_roots: Array.isArray(audit?.input_roots?.structure_roots) ? audit.input_roots.structure_roots.map(String) : [],
217
- human_note_paths: Array.isArray(audit?.input_roots?.human_note_paths) ? audit.input_roots.human_note_paths.map(String) : [],
218
- benchmark_blueprint_root: audit?.input_roots?.benchmark_blueprint_root === null
219
- ? null
220
- : typeof audit?.input_roots?.benchmark_blueprint_root === "string"
221
- ? audit.input_roots.benchmark_blueprint_root
222
- : null,
223
- };
224
-
225
- if (String(audit.generation_mode ?? "") !== "class_filtered") {
226
- errors.push("spec generation audit generation_mode must be class_filtered");
227
- }
228
- const canonicalRoot = generationInputs.canonicalTargetRoot;
229
- if (String(audit.canonical_target_root ?? "") !== canonicalRoot) {
230
- errors.push(`spec generation audit canonical_target_root must be ${canonicalRoot}`);
231
- }
232
- if (String(audit.declared_profile ?? "") !== "surface_taxonomy_v2") {
233
- errors.push("spec generation audit declared_profile must be surface_taxonomy_v2");
234
- }
235
- if (!isDeclaredInputsCompatibleWithConfig(declaredInputs, generationInputs, blueprintReference)) {
236
- errors.push("spec generation audit input_roots must match declared generation inputs and optional blueprint root");
237
- }
238
-
239
- const treeFiles = await collectTreeFiles(path.resolve(projectRoot, canonicalRoot));
240
- if (treeFiles.length === 0) {
241
- errors.push(`missing spec tree root: ${path.resolve(projectRoot, canonicalRoot)}`);
242
- }
243
- const auditedFiles = treeFiles.filter((entry) => !entry.startsWith("_meta/"));
244
- const fileEntries = Array.isArray(audit.files) ? audit.files : [];
245
- if (!Array.isArray(audit.files)) errors.push("spec generation audit files must be an array");
246
-
247
- const fileEntryRefs = audit.file_entry_refs === undefined
248
- ? []
249
- : Array.isArray(audit.file_entry_refs)
250
- ? audit.file_entry_refs.map(String)
251
- : null;
252
- if (fileEntryRefs === null) {
253
- errors.push("spec generation audit file_entry_refs must be an array when present");
254
- }
255
-
256
- const referencedFileEntries = [];
257
- for (const entryRef of fileEntryRefs ?? []) {
258
- const expectedPrefix = ".nimi/local/state/spec-generation/spec-generation-audit/";
259
- const absoluteEntryRef = path.resolve(projectRoot, entryRef);
260
- const relativeEntryRef = toPortableProjectPath(projectRoot, absoluteEntryRef);
261
- if (!entryRef || path.isAbsolute(entryRef) || relativeEntryRef !== entryRef || !entryRef.startsWith(expectedPrefix)) {
262
- errors.push(`spec generation audit file_entry_ref must stay under ${expectedPrefix}: ${entryRef}`);
263
- continue;
264
- }
265
- const entryText = await readTextIfFile(absoluteEntryRef);
266
- const entryParsed = parseYamlText(entryText);
267
- const entryPayload = entryParsed?.spec_generation_audit_file_entries;
268
- if (!isPlainObject(entryParsed) || !isPlainObject(entryPayload) || entryParsed.version !== 2) {
269
- errors.push(`spec generation audit file_entry_ref is invalid: ${entryRef}`);
270
- continue;
271
- }
272
- if (String(entryParsed.contract_ref ?? "") !== SPEC_GENERATION_AUDIT_CONTRACT_REF) {
273
- errors.push(`spec generation audit file_entry_ref contract_ref is invalid: ${entryRef}`);
274
- }
275
- if (String(entryPayload.parent_ref ?? "") !== toPortableProjectPath(projectRoot, absoluteAuditPath)) {
276
- errors.push(`spec generation audit file_entry_ref parent_ref is invalid: ${entryRef}`);
277
- }
278
- if (!Array.isArray(entryPayload.files)) {
279
- errors.push(`spec generation audit file_entry_ref files must be an array: ${entryRef}`);
280
- continue;
281
- }
282
- referencedFileEntries.push(...entryPayload.files);
283
- }
284
-
285
- const auditEntryByRelativePath = new Map();
286
- for (const entry of [...fileEntries, ...referencedFileEntries]) {
287
- if (!isPlainObject(entry)) {
288
- errors.push("spec generation audit file entries must be mappings");
289
- continue;
290
- }
291
- const missingFields = auditContract.requiredFileEntryFields.filter((field) => !(field in entry));
292
- if (missingFields.length > 0) {
293
- errors.push(`spec generation audit file entry is missing required fields: ${missingFields.join(", ")}`);
294
- continue;
295
- }
296
- const canonicalPath = String(entry.canonical_path ?? "");
297
- if (!canonicalPath.startsWith(`${canonicalRoot}/`)) {
298
- errors.push(`spec generation audit canonical_path must stay under ${canonicalRoot}: ${canonicalPath}`);
299
- continue;
300
- }
301
- const relativePath = path.posix.relative(canonicalRoot, canonicalPath);
302
- if (relativePath.startsWith("_meta/")) {
303
- errors.push(`spec generation audit must not record _meta files: ${canonicalPath}`);
304
- continue;
305
- }
306
- if (auditEntryByRelativePath.has(relativePath)) {
307
- errors.push(`duplicate spec generation audit entry: ${canonicalPath}`);
308
- continue;
309
- }
310
-
311
- if (!Array.isArray(entry.source_refs) || entry.source_refs.length === 0) {
312
- errors.push(`spec generation audit source_refs must be non-empty for ${canonicalPath}`);
313
- } else {
314
- const invalidRefs = entry.source_refs.filter((ref) => !isSourceRefWithinDeclaredRoots(ref, declaredInputs));
315
- if (invalidRefs.length > 0) {
316
- errors.push(`spec generation audit source_refs escape declared inputs for ${canonicalPath}: ${invalidRefs.join(", ")}`);
317
- }
318
- }
319
- if (!SPEC_GENERATION_AUDIT_SOURCE_BASIS_ENUM.includes(String(entry.source_basis ?? ""))) {
320
- errors.push(`spec generation audit source_basis is invalid for ${canonicalPath}`);
321
- }
322
- if (!SPEC_GENERATION_AUDIT_COVERAGE_STATUS_ENUM.includes(String(entry.coverage_status ?? ""))) {
323
- errors.push(`spec generation audit coverage_status is invalid for ${canonicalPath}`);
324
- }
325
- if (!Array.isArray(entry.unresolved_items) || entry.unresolved_items.some((item) => typeof item !== "string")) {
326
- errors.push(`spec generation audit unresolved_items must be strings for ${canonicalPath}`);
327
- }
328
- if ((entry.source_basis !== "grounded" || entry.coverage_status !== "complete")
329
- && (!Array.isArray(entry.unresolved_items) || entry.unresolved_items.length === 0)) {
330
- errors.push(`inferred or partial files must declare unresolved_items for ${canonicalPath}`);
331
- }
332
- auditEntryByRelativePath.set(relativePath, entry);
333
- }
334
-
335
- const missingAuditEntries = auditedFiles.filter((entry) => !auditEntryByRelativePath.has(entry));
336
- if (missingAuditEntries.length > 0) {
337
- errors.push(`spec generation audit is missing canonical files: ${missingAuditEntries.join(", ")}`);
338
- }
339
- for (const relativePath of auditEntryByRelativePath.keys()) {
340
- if (!auditedFiles.includes(relativePath)) {
341
- errors.push(`spec generation audit entry points to a non-existent canonical file: ${relativePath}`);
342
- }
343
- }
344
-
345
- const requiredFiles = (await loadRequiredFiles(projectRoot))
346
- .map((entry) => path.posix.relative(canonicalRoot, entry))
347
- .filter((entry) => !entry.startsWith("_meta/"));
348
- for (const requiredFile of requiredFiles) {
349
- const entry = auditEntryByRelativePath.get(requiredFile);
350
- if (!entry) errors.push(`required canonical file is missing an audit entry: ${requiredFile}`);
351
- else if (entry.coverage_status === "placeholder_not_allowed") {
352
- errors.push(`required canonical file must not be placeholder_not_allowed: ${requiredFile}`);
353
- }
354
- }
355
-
356
- const values = [...auditEntryByRelativePath.values()];
357
- return {
358
- ok: errors.length === 0,
359
- errors,
360
- warnings,
361
- refusal: errors.length === 0
362
- ? null
363
- : makeValidatorRefusal(
364
- VALIDATOR_NATIVE_REFUSAL_CODES.SPEC_AUDIT_INVALID,
365
- `spec generation audit is invalid: ${path.basename(absoluteAuditPath)}`,
366
- ),
367
- summary: {
368
- canonicalRoot,
369
- declaredProfile: "surface_taxonomy_v2",
370
- auditedFiles: values.length,
371
- requiredAuditedFiles: requiredFiles.length,
372
- missingAuditEntries,
373
- completeFiles: values.filter((entry) => entry.coverage_status === "complete").length,
374
- partialFiles: values.filter((entry) => entry.coverage_status === "partial").length,
375
- placeholderFiles: values.filter((entry) => entry.coverage_status === "placeholder_not_allowed").length,
376
- unresolvedFiles: values.filter((entry) => entry.unresolved_items?.length > 0).length,
377
- inferredFiles: values.filter((entry) => entry.source_basis !== "grounded").length,
378
- },
379
- };
380
- }
@@ -1,30 +0,0 @@
1
- import {
2
- VALIDATOR_CLI_RESULT_CONTRACT,
3
- VALIDATOR_NATIVE_REFUSAL_CODES,
4
- } from "./internal/validators-shared.mjs";
5
- import {
6
- validateSpecAudit as validateSpecAuditInternal,
7
- validateSpecTree as validateSpecTreeInternal,
8
- } from "./internal/validators-spec.mjs";
9
-
10
- export { VALIDATOR_NATIVE_REFUSAL_CODES };
11
-
12
- export function validateSpecTree(rootPath, options = {}) {
13
- return validateSpecTreeInternal(rootPath, options);
14
- }
15
- export function validateSpecAudit(auditPath, options = {}) {
16
- return validateSpecAuditInternal(auditPath, options);
17
- }
18
-
19
- export function buildValidatorCliReport(validator, filePath, report) {
20
- return {
21
- contract: VALIDATOR_CLI_RESULT_CONTRACT,
22
- validator,
23
- target_ref: filePath,
24
- ok: Boolean(report.ok),
25
- refusal: report.refusal || null,
26
- errors: report.errors || [],
27
- warnings: report.warnings || [],
28
- ...(report.summary ? { summary: report.summary } : {}),
29
- };
30
- }
@@ -1,6 +0,0 @@
1
- version: 2
2
- initialized_by: "@nimiplatform/nimi-coding"
3
- cli_version: "0.3.1"
4
- bootstrap_contract: "nimicoding.bootstrap"
5
- bootstrap_contract_version: 2
6
- profile: governance
@@ -1,41 +0,0 @@
1
- version: 3
2
- contract_ref: .nimi/contracts/spec-generation-inputs.schema.yaml
3
- spec_generation_inputs:
4
- mode: class_filtered
5
- canonical_target_root: .nimi/spec
6
- code_inputs: []
7
- docs_inputs:
8
- - root: .nimi/spec
9
- class_filter: product_authority_only
10
- allowed_surface_classes: [product_authority, product_authority_table, thin_guidance, host_projection_anchor, support_registry]
11
- forbidden_surface_classes: [derived_view, tracked_derived_projection, spec_generation_state, audit_evidence_state, operational_local_artifact, nimicoding_managed_projection, methodology_authority]
12
- owner: nimi_host
13
- use_as_authority: true
14
- projection_edge_ref: null
15
- structure_inputs: []
16
- local_inputs:
17
- - root: .nimi/local
18
- owner: external_tooling
19
- allowed_surface_classes: [spec_generation_state, audit_evidence_state, operational_local_artifact]
20
- use_as_authority: false
21
- forbidden_source_classes:
22
- - derived_view
23
- - tracked_derived_projection
24
- - spec_generation_state
25
- - audit_evidence_state
26
- - operational_local_artifact
27
- - nimicoding_managed_projection
28
- generation_order:
29
- - classify_inputs
30
- - validate_placement
31
- - product_authority
32
- - product_authority_tables
33
- - thin_guides
34
- - derived_views
35
- - generation_audit
36
- inference_rules:
37
- - no_blanket_docs_roots
38
- - no_generated_as_authority
39
- - no_derived_views_written_to_local
40
- - no_audit_or_operational_state_as_authority
41
- acceptance_mode: placement_validity_before_generation
@@ -1,56 +0,0 @@
1
- version: 2
2
- contract:
3
- id: nimicoding.domain-admission.v2
4
- owner: nimi-coding
5
- purpose: Require explicit admission for every host spec domain before it is product authority.
6
- required_top_level_fields:
7
- - version
8
- - domain_admissions
9
- - domain_authority_class_enum
10
- - semantic_constraints
11
- domain_admission_required_fields:
12
- - domain_id
13
- - domain_root
14
- - authority_class
15
- - owner
16
- - admitted_by
17
- - allowed_surface_classes
18
- - forbidden_surface_classes
19
- - validation_commands
20
- - migration_disposition_when_unadmitted
21
- shared_enum_ref: shared-enums.yaml
22
- domain_admissions:
23
- - domain_id: project
24
- domain_root: .nimi/spec/project
25
- authority_class: active_product
26
- owner: project
27
- admitted_by: nimicoding_init_default_project_domain
28
- allowed_surface_classes: [product_authority, product_authority_table, thin_guidance, support_registry]
29
- forbidden_surface_classes: [derived_view, spec_generation_state, audit_evidence_state, operational_local_artifact, methodology_authority]
30
- validation_commands: [pnpm exec nimicoding validate-spec-tree -- .nimi/spec]
31
- migration_disposition_when_unadmitted: block
32
- domain_authority_class_enum:
33
- - active_product
34
- - package_projection_anchor_only
35
- - migration_input_only
36
- - excluded_from_spec
37
- migration_disposition_when_unadmitted_enum:
38
- - move_local
39
- - move_package
40
- - split
41
- - delete
42
- - block
43
- domain_rules:
44
- - domain_id: project
45
- required_authority_class: active_product
46
- admission_required: true
47
- semantic_constraints:
48
- - directory_presence_does_not_admit_product_authority
49
- - unadmitted_domain_must_not_be_used_as_implementation_authority
50
- - product_domain_admission_must_name_validation_commands
51
- - package_projection_domains_must_not_define_product_rules
52
- - active_product_domain_does_not_admit_generated_evidence_or_state_surfaces
53
- - product_authority_tables_must_declare_table_family
54
- - thin_guidance_must_not_carry_rule_body
55
- - package_default_domain_admission_must_remain_generic_project_skeleton
56
- - product_hosts_must_own_domain_specific_admission_as_host_profile_override
@@ -1,15 +0,0 @@
1
- version: 2
2
- contract:
3
- id: nimicoding.spec-migration-inventory.v2
4
- owner: nimi-coding
5
- purpose: Describe a non-mutating migration plan for spec surface cleanup.
6
- required_entry_fields: [source_path, current_inferred_class, target_class, disposition, target_root, owner, required_confirmation, ambiguity, evidence, validation_commands]
7
- target_class_enum: [product_authority, product_authority_table, thin_guidance, host_instruction, tracked_derived_projection, derived_view, spec_generation_state, audit_evidence_state, operational_local_artifact, methodology_authority, nimicoding_managed_projection, host_projection_anchor, support_registry]
8
- disposition_enum: [keep, move_package, move_local, split, rewrite, delete, block]
9
- required_confirmation_enum: [none, product_semantic_fork, owner_ambiguity, package_boundary_ambiguity, destructive_deletion]
10
- plan_fields: [contract, ok, version, root, inventory, disposition_enum, target_class_enum, semantic_constraints, summary, enum_validation, required_confirmations, groups, migration_groups, mutation_policy]
11
- semantic_constraints:
12
- - inventory_and_plan_commands_must_not_modify_source_files
13
- - unresolved_semantic_forks_remain_blocking
14
- - migration_groups_are_descriptive_and_do_not_schedule_work
15
- - emitted_plans_are_local_non_authority_state
@@ -1,26 +0,0 @@
1
- version: 2
2
- contract:
3
- id: nimicoding.spec-surface-negative-fixtures.v2
4
- owner: nimi-coding
5
- fixtures:
6
- - id: generated-view-under-authority
7
- setup: .nimi/spec/project/kernel/generated/overview.md
8
- expected_failure: derived_view_under_product_authority_root
9
- - id: generation-state-under-authority
10
- setup: .nimi/spec/_meta/spec-generation-audit.yaml
11
- expected_failure: spec_generation_state_under_spec
12
- - id: package-contract-promoted-to-spec
13
- setup: .nimi/contracts/surface-taxonomy.schema.yaml copied under .nimi/spec/project/kernel
14
- expected_failure: package_methodology_under_host_spec
15
- - id: unknown-domain
16
- setup: .nimi/spec/unadmitted/kernel/index.md
17
- expected_failure: missing_domain_admission
18
- - id: table-without-family
19
- setup: .nimi/spec/project/kernel/tables/rules.yaml without table_family
20
- expected_failure: missing_table_family
21
- - id: guidance-with-rule-body
22
- setup: .nimi/spec/project.md containing normative rule body
23
- expected_failure: guidance_defines_rule_body
24
- semantic_constraints:
25
- - every_fail_closed_rule_requires_a_negative_fixture
26
- - fixtures_must_name_the_exact_refusal_code