@nimiplatform/nimi-coding 0.3.0 → 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.
- package/CHANGELOG.md +27 -0
- package/CONTRIBUTING.md +3 -3
- package/README.md +285 -41
- package/README.zh-CN.md +294 -31
- package/bin/nimicoding.mjs +1 -1
- package/cli/commands/authority.mjs +849 -0
- package/cli/commands/clear.mjs +27 -7
- package/cli/commands/doctor.mjs +10 -1
- package/cli/commands/start.mjs +23 -27
- package/cli/commands/sync.mjs +21 -101
- package/cli/constants.mjs +7 -31
- package/cli/dev/check-authority-surface.mjs +173 -0
- package/cli/dev/check-product-conformance.mjs +917 -0
- package/cli/help.mjs +10 -17
- package/cli/index.mjs +6 -47
- package/cli/lib/authority/anchors.mjs +491 -0
- package/cli/lib/authority/audit.mjs +490 -0
- package/cli/lib/authority/closed-sets.mjs +646 -0
- package/cli/lib/authority/compile.mjs +110 -0
- package/cli/lib/authority/consumers.mjs +470 -0
- package/cli/lib/authority/diagnostics.mjs +87 -0
- package/cli/lib/authority/diff.mjs +287 -0
- package/cli/lib/authority/discover.mjs +447 -0
- package/cli/lib/authority/evidence-snapshot.mjs +1 -0
- package/cli/lib/authority/evidence.mjs +1432 -0
- package/cli/lib/authority/format.mjs +197 -0
- package/cli/lib/authority/git-snapshot.mjs +1065 -0
- package/cli/lib/authority/graph.mjs +351 -0
- package/cli/lib/authority/impact.mjs +252 -0
- package/cli/lib/authority/lexicon.mjs +55 -0
- package/cli/lib/authority/query.mjs +162 -0
- package/cli/lib/authority/repository-corpus.mjs +37 -0
- package/cli/lib/authority/repository-inventory.mjs +34 -0
- package/cli/lib/authority/review.mjs +222 -0
- package/cli/lib/authority/sarif.mjs +181 -0
- package/cli/lib/authority/scope-bindings.mjs +225 -0
- package/cli/lib/authority/source-map.mjs +105 -0
- package/cli/lib/authority/source-markdown.mjs +167 -0
- package/cli/lib/authority/source-yaml.mjs +312 -0
- package/cli/lib/authority/surface.mjs +79 -0
- package/cli/lib/authority/terms.mjs +421 -0
- package/cli/lib/authority/tracked-text.mjs +70 -0
- package/cli/lib/authority/validate.mjs +308 -0
- package/cli/lib/bootstrap.mjs +55 -71
- package/cli/lib/doctor.mjs +40 -123
- package/cli/lib/entrypoints.mjs +103 -105
- package/cli/lib/fs-helpers.mjs +139 -15
- package/cli/lib/internal/governance/config.mjs +0 -36
- package/cli/lib/shared.mjs +8 -21
- package/cli/lib/sync.mjs +81 -92
- package/cli/lib/value-helpers.mjs +0 -26
- package/cli/lib/yaml-helpers.mjs +1 -124
- package/cli/seeds/bootstrap.mjs +31 -87
- package/cli/seeds/seed-policy.yaml +9 -63
- package/contracts/authority-evidence-bindings.schema.yaml +53 -0
- package/contracts/authority-evidence-probe-results.schema.yaml +38 -0
- package/contracts/authority-impact-dispositions.schema.yaml +13 -0
- package/contracts/authority-scope-bindings.schema.yaml +15 -0
- package/contracts/authority-source.schema.yaml +64 -0
- package/contracts/authority-verifier-bindings.schema.yaml +20 -0
- package/methodology/authority-authoring.yaml +144 -0
- package/methodology/core.yaml +31 -26
- package/package.json +10 -6
- package/spec/authority-anchors.yaml +99 -0
- package/spec/authority-audit.yaml +85 -0
- package/spec/authority-authoring.yaml +150 -0
- package/spec/authority-closed-sets.yaml +65 -0
- package/spec/authority-consumers.yaml +70 -0
- package/spec/authority-context.yaml +50 -0
- package/spec/authority-discovery.yaml +119 -0
- package/spec/authority-evidence.yaml +200 -0
- package/spec/authority-graph.yaml +59 -0
- package/spec/authority-impact.yaml +84 -0
- package/spec/authority-review.yaml +89 -0
- package/spec/authority-terms.yaml +59 -0
- package/spec/product-scope.yaml +36 -23
- package/cli/commands/blueprint-audit.mjs +0 -91
- package/cli/commands/classify-spec-tree.mjs +0 -5
- package/cli/commands/generate-spec-derived-docs.mjs +0 -151
- package/cli/commands/generate-spec-migration-plan.mjs +0 -30
- package/cli/commands/surface-validator-command.mjs +0 -49
- package/cli/commands/validate-domain-admission.mjs +0 -5
- package/cli/commands/validate-guidance-bodies.mjs +0 -5
- package/cli/commands/validate-placement.mjs +0 -5
- package/cli/commands/validate-projection-edges.mjs +0 -5
- package/cli/commands/validate-spec-audit.mjs +0 -27
- package/cli/commands/validate-spec-governance.mjs +0 -124
- package/cli/commands/validate-spec-tree.mjs +0 -27
- package/cli/commands/validate-table-family.mjs +0 -5
- package/cli/commands/validate-tracked-output-admission.mjs +0 -5
- package/cli/lib/blueprint-audit.mjs +0 -363
- package/cli/lib/contracts.mjs +0 -21
- package/cli/lib/internal/contracts-loaders.mjs +0 -47
- package/cli/lib/internal/contracts-parse.mjs +0 -192
- package/cli/lib/internal/governance/runner.mjs +0 -35
- package/cli/lib/internal/surface-taxonomy-validators.mjs +0 -1147
- package/cli/lib/internal/validators-shared.mjs +0 -12
- package/cli/lib/internal/validators-spec-helpers.mjs +0 -52
- package/cli/lib/internal/validators-spec.mjs +0 -380
- package/cli/lib/validators.mjs +0 -30
- package/config/bootstrap.yaml +0 -6
- package/config/spec-generation-inputs.yaml +0 -41
- package/contracts/domain-admission.schema.yaml +0 -56
- package/contracts/migration-inventory.schema.yaml +0 -15
- package/contracts/negative-fixtures.yaml +0 -26
- package/contracts/placement-contract.schema.yaml +0 -127
- package/contracts/projection-edge.schema.yaml +0 -46
- package/contracts/shared-enums.yaml +0 -63
- package/contracts/spec-generation-audit.schema.yaml +0 -46
- package/contracts/spec-generation-inputs.schema.yaml +0 -43
- package/contracts/spec-layout.schema.yaml +0 -32
- package/contracts/surface-taxonomy.schema.yaml +0 -139
- package/contracts/table-family.schema.yaml +0 -133
- package/contracts/tracked-output-admission.schema.yaml +0 -17
- package/methodology/four-closure-policy.yaml +0 -20
- package/methodology/role-separation-policy.yaml +0 -22
- package/methodology/spec-reconstruction.yaml +0 -69
- 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
|
-
}
|
package/cli/lib/validators.mjs
DELETED
|
@@ -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
|
-
}
|
package/config/bootstrap.yaml
DELETED
|
@@ -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
|