@nimiplatform/nimi-coding 0.1.0 → 0.2.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/README.md +19 -20
- package/adapters/oh-my-codex/README.md +8 -9
- package/cli/commands/audit-sweep.mjs +10 -10
- package/cli/commands/classify-spec-tree.mjs +5 -0
- package/cli/commands/closeout.mjs +3 -0
- package/cli/commands/generate-spec-derived-docs.mjs +20 -0
- package/cli/commands/generate-spec-migration-plan.mjs +30 -0
- package/cli/commands/start.mjs +5 -1
- package/cli/commands/surface-validator-command.mjs +49 -0
- package/cli/commands/sweep-design.mjs +295 -0
- package/cli/commands/sweep.mjs +22 -0
- package/cli/commands/sync.mjs +132 -0
- package/cli/commands/topic-formatters.mjs +8 -8
- package/cli/commands/validate-ai-governance.mjs +167 -46
- package/cli/commands/validate-domain-admission.mjs +5 -0
- package/cli/commands/validate-guidance-bodies.mjs +5 -0
- package/cli/commands/validate-placement.mjs +5 -0
- package/cli/commands/validate-projection-edges.mjs +5 -0
- package/cli/commands/validate-spec-audit.mjs +5 -1
- package/cli/commands/validate-table-family.mjs +5 -0
- package/cli/commands/validate-tracked-output-admission.mjs +5 -0
- package/cli/constants.mjs +5 -49
- package/cli/help.mjs +33 -11
- package/cli/index.mjs +20 -2
- package/cli/lib/audit-sweep-runtime/admissions.mjs +38 -29
- package/cli/lib/audit-sweep-runtime/audit-validity.mjs +8 -0
- package/cli/lib/audit-sweep-runtime/chunks.mjs +11 -11
- package/cli/lib/audit-sweep-runtime/closeout.mjs +8 -8
- package/cli/lib/audit-sweep-runtime/codex-auditor-evidence.mjs +3 -3
- package/cli/lib/audit-sweep-runtime/codex-auditor.mjs +10 -10
- package/cli/lib/audit-sweep-runtime/common.mjs +7 -7
- package/cli/lib/audit-sweep-runtime/format.mjs +3 -3
- package/cli/lib/audit-sweep-runtime/ingest.mjs +8 -8
- package/cli/lib/audit-sweep-runtime/inventory-spec-chunks.mjs +24 -27
- package/cli/lib/audit-sweep-runtime/inventory.mjs +58 -18
- package/cli/lib/audit-sweep-runtime/ledger.mjs +1 -1
- package/cli/lib/audit-sweep-runtime/p0p1-profile.mjs +2 -2
- package/cli/lib/audit-sweep-runtime/remediation.mjs +6 -6
- package/cli/lib/audit-sweep-runtime/rerun.mjs +6 -6
- package/cli/lib/audit-sweep-runtime/status.mjs +1 -1
- package/cli/lib/audit-sweep-runtime/validators.mjs +2 -2
- package/cli/lib/authority-convergence.mjs +397 -2
- package/cli/lib/blueprint-audit.mjs +5 -5
- package/cli/lib/closeout.mjs +126 -3
- package/cli/lib/contracts.mjs +21 -17
- package/cli/lib/handoff.mjs +29 -11
- package/cli/lib/high-risk-admission.mjs +60 -11
- package/cli/lib/high-risk-decision.mjs +31 -2
- package/cli/lib/high-risk-ingest.mjs +5 -1
- package/cli/lib/high-risk-review.mjs +5 -1
- package/cli/lib/internal/contracts-parse.mjs +195 -24
- package/cli/lib/internal/contracts-validators.mjs +3 -2
- package/cli/lib/internal/doctor-bootstrap-surface.mjs +82 -35
- package/cli/lib/internal/doctor-delegated-surface.mjs +1 -1
- package/cli/lib/internal/doctor-finalize.mjs +12 -8
- package/cli/lib/internal/doctor-inspectors.mjs +34 -1
- package/cli/lib/internal/governance/ai/ai-context-budget-core.mjs +74 -12
- package/cli/lib/internal/governance/ai/ai-structure-budget-core.mjs +24 -6
- package/cli/lib/internal/governance/ai/check-agents-freshness.mjs +18 -23
- package/cli/lib/internal/surface-taxonomy-validators.mjs +931 -0
- package/cli/lib/internal/validators-spec.mjs +229 -20
- package/cli/lib/sweep-design-runtime/common.mjs +246 -0
- package/cli/lib/sweep-design-runtime/engine.mjs +733 -0
- package/cli/lib/sweep-design-runtime/fix-topic.mjs +414 -0
- package/cli/lib/sweep-design-runtime/lifecycle.mjs +54 -0
- package/cli/lib/sweep-design-runtime/results.mjs +324 -0
- package/cli/lib/sweep-design.mjs +8 -0
- package/cli/lib/sync.mjs +143 -0
- package/cli/lib/topic-artifacts.mjs +186 -0
- package/cli/lib/topic-authority-coverage.mjs +73 -0
- package/cli/lib/topic-closeout.mjs +560 -0
- package/cli/lib/topic-common.mjs +404 -0
- package/cli/lib/topic-decisions.mjs +332 -0
- package/cli/lib/topic-draft-packets.mjs +126 -7
- package/cli/lib/topic-execution.mjs +515 -0
- package/cli/lib/topic-goal.mjs +112 -33
- package/cli/lib/topic-ledger.mjs +281 -0
- package/cli/lib/topic-lifecycle-artifacts.mjs +173 -0
- package/cli/lib/topic-root-validation.mjs +288 -0
- package/cli/lib/topic-runner-commands.mjs +174 -0
- package/cli/lib/topic-runner-deferral.mjs +532 -0
- package/cli/lib/topic-runner-stale-gates.mjs +114 -0
- package/cli/lib/topic-runner-validation.mjs +138 -0
- package/cli/lib/topic-runner.mjs +109 -154
- package/cli/lib/topic-scaffold.mjs +252 -0
- package/cli/lib/topic-waves.mjs +403 -0
- package/cli/lib/topic.mjs +81 -93
- package/cli/lib/value-helpers.mjs +6 -1
- package/cli/seeds/bootstrap.mjs +96 -20
- package/cli/seeds/seed-policy.yaml +67 -0
- package/config/bootstrap.yaml +1 -1
- package/config/skill-manifest.yaml +4 -2
- package/config/spec-generation-inputs.yaml +41 -19
- package/contracts/audit-remediation-map.schema.yaml +1 -0
- package/contracts/audit-sweep-result.yaml +4 -0
- package/contracts/domain-admission.schema.yaml +56 -0
- package/contracts/migration-inventory.schema.yaml +80 -0
- package/contracts/negative-fixtures.yaml +91 -0
- package/contracts/placement-contract.schema.yaml +163 -0
- package/contracts/projection-edge.schema.yaml +130 -0
- package/contracts/shared-enums.yaml +68 -0
- package/contracts/spec-generation-audit.schema.yaml +19 -4
- package/contracts/spec-generation-inputs.schema.yaml +130 -29
- package/contracts/spec-reconstruction-result.yaml +9 -5
- package/contracts/surface-taxonomy.schema.yaml +201 -0
- package/contracts/sweep-design-result.yaml +349 -0
- package/contracts/table-family.schema.yaml +114 -0
- package/contracts/topic-goal.schema.yaml +10 -1
- package/contracts/tracked-output-admission.schema.yaml +70 -0
- package/contracts/workflow-consumer.schema.yaml +112 -0
- package/methodology/audit-sweep-p0p1-recall.yaml +1 -1
- package/methodology/spec-reconstruction.yaml +53 -30
- package/package.json +5 -4
- package/spec/_meta/command-gating-matrix.yaml +33 -0
- package/spec/_meta/generate-drift-migration-checklist.yaml +44 -62
- package/spec/_meta/governance-routing-cutover-checklist.yaml +3 -3
- package/spec/_meta/phase2-impacted-surface-matrix.yaml +14 -14
- package/spec/_meta/spec-authority-cutover-readiness.yaml +3 -5
- package/spec/_meta/spec-tree-model.yaml +104 -36
- package/spec/bootstrap-state.yaml +36 -36
- package/spec/product-scope.yaml +13 -10
|
@@ -27,22 +27,24 @@ export {
|
|
|
27
27
|
parseHighRiskSchemaContract,
|
|
28
28
|
} from "./contracts-parse-high-risk.mjs";
|
|
29
29
|
|
|
30
|
-
const SPEC_TREE_PROFILE_ENUM = ["minimal", "standard", "mature"];
|
|
30
|
+
const SPEC_TREE_PROFILE_ENUM = ["minimal", "standard", "mature", "surface_taxonomy_v1"];
|
|
31
31
|
const AUTHORITY_MODE_ENUM = [
|
|
32
32
|
"external_authority_active",
|
|
33
33
|
"external_blueprint_active",
|
|
34
34
|
"canonical_cutover_ready",
|
|
35
35
|
"canonical_active",
|
|
36
|
+
"surface_class_validated",
|
|
36
37
|
];
|
|
37
38
|
const BLUEPRINT_MODE_ENUM = [
|
|
38
39
|
"none",
|
|
39
40
|
"repo_spec_blueprint",
|
|
40
41
|
"custom_blueprint",
|
|
41
42
|
];
|
|
42
|
-
const SPEC_GENERATION_MODE_ENUM = ["mixed"];
|
|
43
|
+
const SPEC_GENERATION_MODE_ENUM = ["mixed", "class_filtered"];
|
|
43
44
|
const SPEC_GENERATION_ACCEPTANCE_MODE_ENUM = [
|
|
44
45
|
"canonical_tree_validity_without_blueprint",
|
|
45
46
|
"semantic_and_structural_parity_when_blueprint_exists",
|
|
47
|
+
"placement_validity_before_generation",
|
|
46
48
|
];
|
|
47
49
|
const SPEC_GENERATION_ORDER_ENUM = [
|
|
48
50
|
"index",
|
|
@@ -50,6 +52,12 @@ const SPEC_GENERATION_ORDER_ENUM = [
|
|
|
50
52
|
"kernel_tables",
|
|
51
53
|
"generated_views",
|
|
52
54
|
"thin_guides",
|
|
55
|
+
"classify_inputs",
|
|
56
|
+
"validate_placement",
|
|
57
|
+
"product_authority",
|
|
58
|
+
"product_authority_tables",
|
|
59
|
+
"derived_views",
|
|
60
|
+
"generation_audit",
|
|
53
61
|
];
|
|
54
62
|
const TOPIC_LIFECYCLE_REPORT_MARKDOWN = /^\.nimi\/topics\/(?:proposal|ongoing|pending|closed)\/\d{4}-\d{2}-\d{2}-[a-z0-9]+(?:-[a-z0-9]+)*\/[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*\.md$/;
|
|
55
63
|
|
|
@@ -61,6 +69,13 @@ function toStringOrNull(value) {
|
|
|
61
69
|
return typeof value === "string" ? value : null;
|
|
62
70
|
}
|
|
63
71
|
|
|
72
|
+
function toStringList(value) {
|
|
73
|
+
if (typeof value === "string") {
|
|
74
|
+
return [value];
|
|
75
|
+
}
|
|
76
|
+
return toStringArray(value);
|
|
77
|
+
}
|
|
78
|
+
|
|
64
79
|
function normalizePathClass(entry) {
|
|
65
80
|
if (!isPlainObject(entry)) {
|
|
66
81
|
return null;
|
|
@@ -100,13 +115,48 @@ function normalizeGeneratedPipeline(entry) {
|
|
|
100
115
|
return {
|
|
101
116
|
id: toStringOrNull(entry.id),
|
|
102
117
|
ownerSurface: toStringOrNull(entry.owner_surface),
|
|
103
|
-
inputRoots: toStringArray(entry.input_roots)
|
|
118
|
+
inputRoots: toStringArray(entry.input_roots).length > 0
|
|
119
|
+
? toStringArray(entry.input_roots)
|
|
120
|
+
: [
|
|
121
|
+
...toStringList(entry.input_contract_ref),
|
|
122
|
+
...toStringList(entry.input_config_ref),
|
|
123
|
+
],
|
|
104
124
|
outputRoots: toStringArray(entry.output_roots),
|
|
105
125
|
generateCommand: toStringOrNull(entry.generate_command),
|
|
106
126
|
driftCheckCommand: toStringOrNull(entry.drift_check_command),
|
|
107
127
|
};
|
|
108
128
|
}
|
|
109
129
|
|
|
130
|
+
function normalizeV2SpecDomain(entry) {
|
|
131
|
+
if (!isPlainObject(entry)) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
const root = toStringOrNull(entry.root);
|
|
135
|
+
return {
|
|
136
|
+
id: toStringOrNull(entry.id),
|
|
137
|
+
root,
|
|
138
|
+
normativeRoot: root ? `${root}/kernel` : null,
|
|
139
|
+
tablesRoot: root ? `${root}/kernel/tables` : null,
|
|
140
|
+
generatedRoot: null,
|
|
141
|
+
guidePaths: [],
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function normalizeV2PathClass(entry) {
|
|
146
|
+
if (!isPlainObject(entry)) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
id: toStringOrNull(entry.id),
|
|
151
|
+
pathPatterns: toStringArray(entry.path_patterns),
|
|
152
|
+
excludedPathPatterns: toStringArray(entry.excluded_path_patterns),
|
|
153
|
+
allowedExtensions: toStringArray(entry.allowed_extensions),
|
|
154
|
+
generatorRefs: [],
|
|
155
|
+
mustReferenceNormativeIds: entry.must_reference_product_authority_ids === true,
|
|
156
|
+
normative: ["product_authority", "product_authority_table"].includes(entry.id),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
110
160
|
function pathStartsWithRoot(targetPath, root) {
|
|
111
161
|
if (typeof targetPath !== "string" || typeof root !== "string") {
|
|
112
162
|
return false;
|
|
@@ -140,8 +190,19 @@ function topicLifecycleMarkdownPathsAreSortable(paths) {
|
|
|
140
190
|
});
|
|
141
191
|
}
|
|
142
192
|
|
|
193
|
+
function isAllowedV2AuthorityDocsRoot(root) {
|
|
194
|
+
if (typeof root !== "string" || root.length === 0) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
if (root === "." || root === "./" || root === "/" || root === "**" || root.includes("..")) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
return root === ".nimi/spec" || root.startsWith(".nimi/spec/");
|
|
201
|
+
}
|
|
202
|
+
|
|
143
203
|
export function parseSpecReconstructionContract(text) {
|
|
144
204
|
const parsed = parseYamlText(text);
|
|
205
|
+
const version = parsed?.version ?? null;
|
|
145
206
|
const summaryRequiredFields = toStringArray(parsed?.summary_required_fields);
|
|
146
207
|
const summaryStatusEnum = toStringArray(parsed?.summary_status_enum);
|
|
147
208
|
const completionRequirements = toStringArray(parsed?.completion_requirements);
|
|
@@ -156,20 +217,35 @@ export function parseSpecReconstructionContract(text) {
|
|
|
156
217
|
}
|
|
157
218
|
: null;
|
|
158
219
|
|
|
220
|
+
const expectedSummaryFields = version === 2
|
|
221
|
+
? [...SPEC_RECONSTRUCTION_SUMMARY_REQUIRED_FIELDS.slice(0, 2), "placement_report_ref", ...SPEC_RECONSTRUCTION_SUMMARY_REQUIRED_FIELDS.slice(2)]
|
|
222
|
+
: SPEC_RECONSTRUCTION_SUMMARY_REQUIRED_FIELDS;
|
|
223
|
+
const expectedProfileRef = version === 2 ? ".nimi/contracts/surface-taxonomy.schema.yaml" : SPEC_TREE_MODEL_REF;
|
|
224
|
+
const expectedInputsRef = version === 2 ? ".nimi/config/spec-generation-inputs.yaml" : SPEC_GENERATION_INPUTS_REF;
|
|
225
|
+
const expectedAuditContractRef = version === 2 ? ".nimi/contracts/spec-generation-audit.schema.yaml" : SPEC_GENERATION_AUDIT_CONTRACT_REF;
|
|
226
|
+
const expectedAuditRef = version === 2 ? ".nimi/local/state/spec-generation/spec-generation-audit.yaml" : SPEC_GENERATION_AUDIT_REF;
|
|
227
|
+
const fileClassRequirement = version === 2
|
|
228
|
+
? "declared_surface_class_constraints_valid"
|
|
229
|
+
: "declared_file_class_constraints_valid";
|
|
230
|
+
const auditRequirement = version === 2
|
|
231
|
+
? "spec_generation_audit_present_and_valid_as_local_state"
|
|
232
|
+
: "spec_generation_audit_present_and_valid";
|
|
233
|
+
|
|
159
234
|
return {
|
|
160
|
-
ok:
|
|
235
|
+
ok: [1, 2].includes(version)
|
|
236
|
+
&& arraysEqual(summaryRequiredFields, expectedSummaryFields)
|
|
161
237
|
&& arraysEqual(summaryStatusEnum, SPEC_RECONSTRUCTION_SUMMARY_STATUS)
|
|
162
238
|
&& completionRequirements.includes("canonical_tree_ready")
|
|
163
239
|
&& completionRequirements.includes("declared_profile_required_files_valid")
|
|
164
|
-
&& completionRequirements.includes(
|
|
165
|
-
&& completionRequirements.includes(
|
|
240
|
+
&& completionRequirements.includes(fileClassRequirement)
|
|
241
|
+
&& completionRequirements.includes(auditRequirement)
|
|
166
242
|
&& completionRequirements.includes("required_canonical_files_have_matching_audit_entries")
|
|
167
243
|
&& completionRequirements.includes("unresolved_gaps_must_remain_explicit")
|
|
168
244
|
&& completionRequirements.includes("semantic_and_structural_parity_when_blueprint_exists")
|
|
169
|
-
&& canonicalTreeCompletion?.profileRef ===
|
|
170
|
-
&& canonicalTreeCompletion?.generationInputsRef ===
|
|
171
|
-
&& canonicalTreeCompletion?.auditContractRef ===
|
|
172
|
-
&& canonicalTreeCompletion?.auditRef ===
|
|
245
|
+
&& canonicalTreeCompletion?.profileRef === expectedProfileRef
|
|
246
|
+
&& canonicalTreeCompletion?.generationInputsRef === expectedInputsRef
|
|
247
|
+
&& canonicalTreeCompletion?.auditContractRef === expectedAuditContractRef
|
|
248
|
+
&& canonicalTreeCompletion?.auditRef === expectedAuditRef
|
|
173
249
|
&& canonicalTreeCompletion?.requiredTreeState === "canonical_tree_ready"
|
|
174
250
|
&& canonicalTreeCompletion?.requiredFilesValid === true,
|
|
175
251
|
canonicalTreeCompletion,
|
|
@@ -181,24 +257,36 @@ export function parseSpecReconstructionContract(text) {
|
|
|
181
257
|
|
|
182
258
|
export function parseSpecGenerationAuditContract(text) {
|
|
183
259
|
const parsed = parseYamlText(text);
|
|
260
|
+
const version = parsed?.version ?? null;
|
|
184
261
|
const requiredTopLevelFields = toStringArray(parsed?.required_top_level_fields);
|
|
185
262
|
const requiredFileEntryFields = toStringArray(parsed?.required_file_entry_fields);
|
|
186
263
|
const sourceBasisEnum = toStringArray(parsed?.source_basis_enum);
|
|
187
264
|
const coverageStatusEnum = toStringArray(parsed?.coverage_status_enum);
|
|
188
265
|
const hardConstraints = toStringArray(parsed?.hard_constraints);
|
|
189
266
|
|
|
267
|
+
const expectedTopLevelFields = version === 2
|
|
268
|
+
? [...SPEC_GENERATION_AUDIT_REQUIRED_TOP_LEVEL_FIELDS.slice(0, 4), "placement_report_ref", ...SPEC_GENERATION_AUDIT_REQUIRED_TOP_LEVEL_FIELDS.slice(4)]
|
|
269
|
+
: SPEC_GENERATION_AUDIT_REQUIRED_TOP_LEVEL_FIELDS;
|
|
270
|
+
const expectedFileEntryFields = version === 2
|
|
271
|
+
? ["canonical_path", "surface_class", "source_refs", "source_basis", "coverage_status", "unresolved_items"]
|
|
272
|
+
: SPEC_GENERATION_AUDIT_FILE_REQUIRED_FIELDS;
|
|
273
|
+
const expectedTargetRef = version === 2 ? ".nimi/local/state/spec-generation/spec-generation-audit.yaml" : SPEC_GENERATION_AUDIT_REF;
|
|
274
|
+
const sourceConstraint = version === 2
|
|
275
|
+
? "source_refs_must_stay_within_class_filtered_declared_inputs"
|
|
276
|
+
: "source_refs_must_stay_within_declared_inputs_or_optional_benchmark_root";
|
|
277
|
+
|
|
190
278
|
return {
|
|
191
|
-
ok:
|
|
279
|
+
ok: [1, 2].includes(version)
|
|
192
280
|
&& String(parsed?.audit_contract?.id ?? "") === "canonical_spec_generation_audit"
|
|
193
|
-
&& String(parsed?.audit_contract?.target_ref ?? "") ===
|
|
194
|
-
&& arraysEqual(requiredTopLevelFields,
|
|
195
|
-
&& arraysEqual(requiredFileEntryFields,
|
|
281
|
+
&& String(parsed?.audit_contract?.target_ref ?? "") === expectedTargetRef
|
|
282
|
+
&& arraysEqual(requiredTopLevelFields, expectedTopLevelFields)
|
|
283
|
+
&& arraysEqual(requiredFileEntryFields, expectedFileEntryFields)
|
|
196
284
|
&& arraysEqual(sourceBasisEnum, SPEC_GENERATION_AUDIT_SOURCE_BASIS_ENUM)
|
|
197
285
|
&& arraysEqual(coverageStatusEnum, SPEC_GENERATION_AUDIT_COVERAGE_STATUS_ENUM)
|
|
198
286
|
&& hardConstraints.includes("every_generated_canonical_file_requires_a_matching_audit_entry")
|
|
199
287
|
&& hardConstraints.includes("required_canonical_files_must_not_be_placeholder_not_allowed")
|
|
200
288
|
&& hardConstraints.includes("unresolved_or_inferred_content_must_be_explicit")
|
|
201
|
-
&& hardConstraints.includes(
|
|
289
|
+
&& hardConstraints.includes(sourceConstraint)
|
|
202
290
|
&& hardConstraints.includes("no_empty_success_looking_audit_entries"),
|
|
203
291
|
requiredTopLevelFields,
|
|
204
292
|
requiredFileEntryFields,
|
|
@@ -215,6 +303,26 @@ export function parseSpecGenerationInputsContract(text) {
|
|
|
215
303
|
const generationOrderEnum = toStringArray(parsed?.generation_order_enum);
|
|
216
304
|
const hardConstraints = toStringArray(parsed?.hard_constraints);
|
|
217
305
|
|
|
306
|
+
if (parsed?.version === 2) {
|
|
307
|
+
return {
|
|
308
|
+
ok: String(contract?.id ?? "") === "canonical_spec_generation_inputs"
|
|
309
|
+
&& String(contract?.owner ?? "") === "nimi-coding"
|
|
310
|
+
&& requiredFields.includes("mode")
|
|
311
|
+
&& requiredFields.includes("canonical_target_root")
|
|
312
|
+
&& requiredFields.includes("docs_inputs")
|
|
313
|
+
&& requiredFields.includes("forbidden_source_classes")
|
|
314
|
+
&& arraysEqual(toStringArray(parsed?.mode_enum), ["class_filtered"])
|
|
315
|
+
&& SPEC_GENERATION_ACCEPTANCE_MODE_ENUM.includes(toStringArray(parsed?.acceptance_mode_enum)[0])
|
|
316
|
+
&& generationOrderEnum.includes("classify_inputs")
|
|
317
|
+
&& generationOrderEnum.includes("validate_placement")
|
|
318
|
+
&& hardConstraints.includes("docs_roots_blanket_ingestion_is_forbidden")
|
|
319
|
+
&& hardConstraints.includes("each_input_file_must_have_an_accepted_surface_class_before_render"),
|
|
320
|
+
requiredFields,
|
|
321
|
+
generationOrderEnum,
|
|
322
|
+
hardConstraints,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
218
326
|
return {
|
|
219
327
|
ok: parsed?.version === 1
|
|
220
328
|
&& String(contract?.id ?? "") === "canonical_spec_generation_inputs"
|
|
@@ -242,6 +350,12 @@ export function parseSpecGenerationInputsConfig(text) {
|
|
|
242
350
|
const codeRoots = toStringArray(config?.code_roots);
|
|
243
351
|
const docsRoots = toStringArray(config?.docs_roots);
|
|
244
352
|
const structureRoots = toStringArray(config?.structure_roots);
|
|
353
|
+
const codeInputs = Array.isArray(config?.code_inputs) ? config.code_inputs : [];
|
|
354
|
+
const docsInputs = Array.isArray(config?.docs_inputs) ? config.docs_inputs : [];
|
|
355
|
+
const structureInputs = Array.isArray(config?.structure_inputs) ? config.structure_inputs : [];
|
|
356
|
+
const topicInputs = Array.isArray(config?.topic_inputs) ? config.topic_inputs : [];
|
|
357
|
+
const localInputs = Array.isArray(config?.local_inputs) ? config.local_inputs : [];
|
|
358
|
+
const forbiddenSourceClasses = toStringArray(config?.forbidden_source_classes);
|
|
245
359
|
const humanNotePaths = toStringArray(config?.human_note_paths);
|
|
246
360
|
const benchmarkBlueprintRoot = typeof config?.benchmark_blueprint_root === "string"
|
|
247
361
|
? config.benchmark_blueprint_root
|
|
@@ -251,6 +365,46 @@ export function parseSpecGenerationInputsConfig(text) {
|
|
|
251
365
|
const generationOrder = toStringArray(config?.generation_order);
|
|
252
366
|
const inferenceRules = toStringArray(config?.inference_rules);
|
|
253
367
|
|
|
368
|
+
if (parsed?.version === 2) {
|
|
369
|
+
const legacyRootsAbsent = !("code_roots" in config)
|
|
370
|
+
&& !("docs_roots" in config)
|
|
371
|
+
&& !("structure_roots" in config);
|
|
372
|
+
return {
|
|
373
|
+
ok: String(parsed?.contract_ref ?? "") === ".nimi/contracts/spec-generation-inputs.schema.yaml"
|
|
374
|
+
&& mode === "class_filtered"
|
|
375
|
+
&& canonicalTargetRoot === ".nimi/spec"
|
|
376
|
+
&& legacyRootsAbsent
|
|
377
|
+
&& Array.isArray(codeInputs)
|
|
378
|
+
&& docsInputs.length > 0
|
|
379
|
+
&& docsInputs.every((entry) => isPlainObject(entry)
|
|
380
|
+
&& typeof entry.root === "string"
|
|
381
|
+
&& isAllowedV2AuthorityDocsRoot(entry.root)
|
|
382
|
+
&& Array.isArray(entry.allowed_surface_classes)
|
|
383
|
+
&& Array.isArray(entry.forbidden_surface_classes)
|
|
384
|
+
&& entry.use_as_authority === true)
|
|
385
|
+
&& Array.isArray(structureInputs)
|
|
386
|
+
&& Array.isArray(topicInputs)
|
|
387
|
+
&& Array.isArray(localInputs)
|
|
388
|
+
&& forbiddenSourceClasses.length > 0
|
|
389
|
+
&& generationOrder.includes("classify_inputs")
|
|
390
|
+
&& generationOrder.includes("validate_placement")
|
|
391
|
+
&& inferenceRules.includes("no_blanket_docs_roots")
|
|
392
|
+
&& SPEC_GENERATION_ACCEPTANCE_MODE_ENUM.includes(acceptanceMode)
|
|
393
|
+
&& topicLifecycleMarkdownPathsAreSortable(humanNotePaths),
|
|
394
|
+
mode,
|
|
395
|
+
canonicalTargetRoot,
|
|
396
|
+
codeRoots: codeInputs.map((entry) => String(entry.root ?? "")).filter(Boolean),
|
|
397
|
+
docsRoots: docsInputs.map((entry) => String(entry.root ?? "")).filter(Boolean),
|
|
398
|
+
structureRoots: structureInputs.map((entry) => String(entry.root ?? "")).filter(Boolean),
|
|
399
|
+
humanNotePaths,
|
|
400
|
+
benchmarkBlueprintRoot,
|
|
401
|
+
benchmarkMode: benchmarkMode ?? "none",
|
|
402
|
+
acceptanceMode,
|
|
403
|
+
generationOrder,
|
|
404
|
+
inferenceRules,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
254
408
|
return {
|
|
255
409
|
ok: parsed?.version === 1
|
|
256
410
|
&& String(parsed?.contract_ref ?? "") === SPEC_GENERATION_INPUTS_CONTRACT_REF
|
|
@@ -289,24 +443,40 @@ export function parseSpecTreeModel(text) {
|
|
|
289
443
|
const parsed = parseYamlText(text);
|
|
290
444
|
const model = parsed?.spec_tree_model;
|
|
291
445
|
const profile = toStringOrNull(model?.profile);
|
|
292
|
-
const canonicalRoot = toStringOrNull(model?.canonical_root);
|
|
446
|
+
const canonicalRoot = toStringOrNull(model?.canonical_root) ?? toStringOrNull(model?.product_authority_root);
|
|
293
447
|
const authorityMode = normalizeAuthorityModeValue(toStringOrNull(model?.authority_mode));
|
|
448
|
+
const isV2 = parsed?.version === 2;
|
|
294
449
|
const domains = Array.isArray(model?.domains)
|
|
295
|
-
? model.domains.map(normalizeSpecDomain).filter(Boolean)
|
|
450
|
+
? model.domains.map(isV2 ? normalizeV2SpecDomain : normalizeSpecDomain).filter(Boolean)
|
|
296
451
|
: [];
|
|
297
|
-
const
|
|
298
|
-
|
|
452
|
+
const normativeClassSource = isV2 ? model?.product_authority_classes : model?.normative_classes;
|
|
453
|
+
const derivedClassSource = isV2
|
|
454
|
+
? (Array.isArray(model?.non_authority_classes) ? model.non_authority_classes.filter((entry) => entry?.id === "derived_view") : [])
|
|
455
|
+
: model?.derived_classes;
|
|
456
|
+
const normativeClasses = Array.isArray(normativeClassSource)
|
|
457
|
+
? normativeClassSource.map(isV2 ? normalizeV2PathClass : normalizePathClass).filter(Boolean)
|
|
299
458
|
: [];
|
|
300
|
-
const derivedClasses = Array.isArray(
|
|
301
|
-
?
|
|
459
|
+
const derivedClasses = Array.isArray(derivedClassSource)
|
|
460
|
+
? derivedClassSource.map(isV2 ? (entry) => ({
|
|
461
|
+
id: toStringOrNull(entry.id),
|
|
462
|
+
pathPatterns: toStringArray(entry.allowed_roots),
|
|
463
|
+
excludedPathPatterns: [],
|
|
464
|
+
allowedExtensions: [],
|
|
465
|
+
generatorRefs: [],
|
|
466
|
+
mustReferenceNormativeIds: false,
|
|
467
|
+
normative: false,
|
|
468
|
+
}) : normalizePathClass).filter(Boolean)
|
|
302
469
|
: [];
|
|
303
470
|
const guidanceClasses = Array.isArray(model?.guidance_classes)
|
|
304
|
-
? model.guidance_classes.map(normalizePathClass).filter(Boolean)
|
|
471
|
+
? model.guidance_classes.map(isV2 ? normalizeV2PathClass : normalizePathClass).filter(Boolean)
|
|
305
472
|
: [];
|
|
306
473
|
const requiredFilesByProfile = SPEC_TREE_PROFILE_ENUM.reduce((acc, currentProfile) => {
|
|
307
474
|
acc[currentProfile] = toStringArray(model?.required_files?.[currentProfile]);
|
|
308
475
|
return acc;
|
|
309
476
|
}, {});
|
|
477
|
+
if (isV2 && requiredFilesByProfile.surface_taxonomy_v1.length === 0) {
|
|
478
|
+
requiredFilesByProfile.surface_taxonomy_v1 = requiredFilesByProfile.minimal;
|
|
479
|
+
}
|
|
310
480
|
const generatedPipelines = Array.isArray(model?.generated_pipelines)
|
|
311
481
|
? model.generated_pipelines.map(normalizeGeneratedPipeline).filter(Boolean)
|
|
312
482
|
: [];
|
|
@@ -333,7 +503,8 @@ export function parseSpecTreeModel(text) {
|
|
|
333
503
|
&& (!domain.generatedRoot || pathStartsWithRoot(domain.generatedRoot, domain.normativeRoot))
|
|
334
504
|
&& domain.guidePaths.every((guidePath) => pathStartsWithRoot(guidePath, domain.root))
|
|
335
505
|
));
|
|
336
|
-
const
|
|
506
|
+
const profilesToCheck = isV2 ? [profile] : SPEC_TREE_PROFILE_ENUM.filter((entry) => entry !== "surface_taxonomy_v1");
|
|
507
|
+
const requiredFilesValid = profilesToCheck.every((currentProfile) => (
|
|
337
508
|
requiredFilesByProfile[currentProfile].length > 0
|
|
338
509
|
&& requiredFilesByProfile[currentProfile].every((requiredPath) => pathStartsWithRoot(requiredPath, canonicalRoot))
|
|
339
510
|
));
|
|
@@ -356,7 +527,7 @@ export function parseSpecTreeModel(text) {
|
|
|
356
527
|
);
|
|
357
528
|
|
|
358
529
|
return {
|
|
359
|
-
ok: parsed?.version
|
|
530
|
+
ok: [1, 2].includes(parsed?.version)
|
|
360
531
|
&& profileValid
|
|
361
532
|
&& canonicalRootValid
|
|
362
533
|
&& authorityModeValid
|
|
@@ -91,10 +91,11 @@ export function validateSpecReconstructionSummary(summary, contract, verifiedAt)
|
|
|
91
91
|
};
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
const expectedAuditRef = contract.canonicalTreeCompletion?.auditRef ?? SPEC_GENERATION_AUDIT_REF;
|
|
95
|
+
if (typeof summary.audit_ref !== "string" || summary.audit_ref !== expectedAuditRef) {
|
|
95
96
|
return {
|
|
96
97
|
ok: false,
|
|
97
|
-
reason: `spec_reconstruction summary.audit_ref must be \`${
|
|
98
|
+
reason: `spec_reconstruction summary.audit_ref must be \`${expectedAuditRef}\``,
|
|
98
99
|
};
|
|
99
100
|
}
|
|
100
101
|
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
COMMAND_GATING_MATRIX_REF,
|
|
6
6
|
LOCAL_GITIGNORE_ENTRIES,
|
|
7
7
|
PACKAGE_NAME,
|
|
8
|
-
REQUIRED_BOOTSTRAP_FILES,
|
|
9
8
|
REQUIRED_LOCAL_DIRS,
|
|
10
9
|
SPEC_GENERATION_AUDIT_CONTRACT_REF,
|
|
11
10
|
SPEC_GENERATION_AUDIT_REF,
|
|
@@ -13,6 +12,7 @@ import {
|
|
|
13
12
|
SPEC_GENERATION_INPUTS_REF,
|
|
14
13
|
SPEC_TREE_MODEL_REF,
|
|
15
14
|
} from "../../constants.mjs";
|
|
15
|
+
import { getBootstrapSeedEntries } from "../../seeds/bootstrap.mjs";
|
|
16
16
|
import { inspectBootstrapCompatibility } from "../bootstrap.mjs";
|
|
17
17
|
import {
|
|
18
18
|
findCommandGatingRule,
|
|
@@ -68,11 +68,22 @@ export async function inspectDoctorBootstrapSurface(projectRoot) {
|
|
|
68
68
|
|
|
69
69
|
checks.push(buildCheck("nimi_root", true, ".nimi directory exists"));
|
|
70
70
|
|
|
71
|
+
const seedEntries = await getBootstrapSeedEntries();
|
|
71
72
|
const missingBootstrapFiles = [];
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
const driftedPackageCanonicalFiles = [];
|
|
74
|
+
for (const entry of seedEntries) {
|
|
75
|
+
const absolutePath = path.join(projectRoot, entry.outputRelativePath);
|
|
76
|
+
const info = await pathExists(absolutePath);
|
|
74
77
|
if (!info || !info.isFile()) {
|
|
75
|
-
missingBootstrapFiles.push(
|
|
78
|
+
missingBootstrapFiles.push(entry.outputRelativePath);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (entry.ownership !== "package_canonical") {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const actual = await readTextIfFile(absolutePath);
|
|
85
|
+
if (actual !== entry.content) {
|
|
86
|
+
driftedPackageCanonicalFiles.push(entry.outputRelativePath);
|
|
76
87
|
}
|
|
77
88
|
}
|
|
78
89
|
checks.push(
|
|
@@ -80,10 +91,18 @@ export async function inspectDoctorBootstrapSurface(projectRoot) {
|
|
|
80
91
|
"bootstrap_seed_files",
|
|
81
92
|
missingBootstrapFiles.length === 0,
|
|
82
93
|
missingBootstrapFiles.length === 0
|
|
83
|
-
? `All
|
|
84
|
-
: `Missing
|
|
94
|
+
? `All bootstrap seed files declared by the seed projection policy are present (${seedEntries.length}/${seedEntries.length})`
|
|
95
|
+
: `Missing bootstrap seed files declared by the seed projection policy: ${missingBootstrapFiles.join(", ")}`,
|
|
85
96
|
),
|
|
86
97
|
);
|
|
98
|
+
checks.push({
|
|
99
|
+
id: "bootstrap_seed_package_canonical_in_sync",
|
|
100
|
+
ok: true,
|
|
101
|
+
severity: driftedPackageCanonicalFiles.length === 0 ? "ok" : "warn",
|
|
102
|
+
detail: driftedPackageCanonicalFiles.length === 0
|
|
103
|
+
? "All package_canonical seed files match package source byte-for-byte"
|
|
104
|
+
: `package_canonical seed files diverge from package source; run \`nimicoding sync --apply\` to refresh, or \`nimicoding sync --check\` for the authoritative gate: ${driftedPackageCanonicalFiles.join(", ")}`,
|
|
105
|
+
});
|
|
87
106
|
|
|
88
107
|
const missingLocalDirs = [];
|
|
89
108
|
for (const relativePath of REQUIRED_LOCAL_DIRS) {
|
|
@@ -149,15 +168,23 @@ export async function inspectDoctorBootstrapSurface(projectRoot) {
|
|
|
149
168
|
: "bootstrap.yaml declares an unsupported bootstrap contract id or version",
|
|
150
169
|
});
|
|
151
170
|
|
|
171
|
+
const specGenerationInputsContract = await loadSpecGenerationInputsContract(projectRoot);
|
|
172
|
+
const specGenerationAuditContract = await loadSpecGenerationAuditContract(projectRoot);
|
|
173
|
+
const specGenerationInputs = await loadSpecGenerationInputsConfig(projectRoot);
|
|
174
|
+
const usesV2SurfaceModel = specGenerationInputs.ok && specGenerationInputs.mode === "class_filtered";
|
|
175
|
+
|
|
152
176
|
const bootstrapStateText = await readTextIfFile(path.join(projectRoot, ".nimi", "spec", "bootstrap-state.yaml"));
|
|
153
177
|
const bootstrapStateContract = inspectBootstrapStateContract(bootstrapStateText);
|
|
154
178
|
checks.push(
|
|
155
179
|
buildCheck(
|
|
156
180
|
"bootstrap_state_contract",
|
|
157
|
-
bootstrapStateContract.ok,
|
|
158
|
-
|
|
181
|
+
usesV2SurfaceModel || bootstrapStateContract.ok,
|
|
182
|
+
usesV2SurfaceModel
|
|
183
|
+
? "v2 host-local surface model does not require .nimi/spec/bootstrap-state.yaml"
|
|
184
|
+
: bootstrapStateContract.ok
|
|
159
185
|
? `bootstrap-state.yaml matches the ${bootstrapStateContract.treeState} tree-state contract`
|
|
160
186
|
: "bootstrap-state.yaml is missing required lifecycle fields or declares an unsupported lifecycle state",
|
|
187
|
+
usesV2SurfaceModel ? "info" : undefined,
|
|
161
188
|
),
|
|
162
189
|
);
|
|
163
190
|
|
|
@@ -166,10 +193,13 @@ export async function inspectDoctorBootstrapSurface(projectRoot) {
|
|
|
166
193
|
checks.push(
|
|
167
194
|
buildCheck(
|
|
168
195
|
"standalone_completion_truth",
|
|
169
|
-
completionTruth.ok,
|
|
170
|
-
|
|
196
|
+
usesV2SurfaceModel || completionTruth.ok,
|
|
197
|
+
usesV2SurfaceModel
|
|
198
|
+
? "v2 host-local surface model does not require .nimi/spec/product-scope.yaml"
|
|
199
|
+
: completionTruth.ok
|
|
171
200
|
? `Product scope declares standalone completion profile ${completionTruth.completionProfile}`
|
|
172
201
|
: "product-scope.yaml is missing or drifted from the package-owned standalone completion truth",
|
|
202
|
+
usesV2SurfaceModel ? "info" : undefined,
|
|
173
203
|
),
|
|
174
204
|
);
|
|
175
205
|
|
|
@@ -177,14 +207,16 @@ export async function inspectDoctorBootstrapSurface(projectRoot) {
|
|
|
177
207
|
checks.push(
|
|
178
208
|
buildCheck(
|
|
179
209
|
"spec_tree_model_contract",
|
|
180
|
-
specTreeModel.ok,
|
|
181
|
-
|
|
210
|
+
usesV2SurfaceModel || specTreeModel.ok,
|
|
211
|
+
usesV2SurfaceModel
|
|
212
|
+
? ".nimi/contracts/surface-taxonomy.schema.yaml supplies the v2 spec surface profile"
|
|
213
|
+
: specTreeModel.ok
|
|
182
214
|
? `${SPEC_TREE_MODEL_REF} declares canonical root ${specTreeModel.canonicalRoot} with profile ${specTreeModel.profile}`
|
|
183
215
|
: `${SPEC_TREE_MODEL_REF} is missing or malformed`,
|
|
216
|
+
usesV2SurfaceModel ? "ok" : undefined,
|
|
184
217
|
),
|
|
185
218
|
);
|
|
186
219
|
|
|
187
|
-
const specGenerationInputsContract = await loadSpecGenerationInputsContract(projectRoot);
|
|
188
220
|
checks.push(
|
|
189
221
|
buildCheck(
|
|
190
222
|
"spec_generation_inputs_contract",
|
|
@@ -195,7 +227,6 @@ export async function inspectDoctorBootstrapSurface(projectRoot) {
|
|
|
195
227
|
),
|
|
196
228
|
);
|
|
197
229
|
|
|
198
|
-
const specGenerationAuditContract = await loadSpecGenerationAuditContract(projectRoot);
|
|
199
230
|
checks.push(
|
|
200
231
|
buildCheck(
|
|
201
232
|
"spec_generation_audit_contract",
|
|
@@ -206,13 +237,12 @@ export async function inspectDoctorBootstrapSurface(projectRoot) {
|
|
|
206
237
|
),
|
|
207
238
|
);
|
|
208
239
|
|
|
209
|
-
const specGenerationInputs = await loadSpecGenerationInputsConfig(projectRoot);
|
|
210
240
|
checks.push(
|
|
211
241
|
buildCheck(
|
|
212
242
|
"spec_generation_inputs_config",
|
|
213
243
|
specGenerationInputs.ok,
|
|
214
244
|
specGenerationInputs.ok
|
|
215
|
-
? `${SPEC_GENERATION_INPUTS_REF} declares
|
|
245
|
+
? `${SPEC_GENERATION_INPUTS_REF} declares ${specGenerationInputs.mode} canonical spec generation inputs`
|
|
216
246
|
: `${SPEC_GENERATION_INPUTS_REF} is missing or malformed`,
|
|
217
247
|
),
|
|
218
248
|
);
|
|
@@ -221,21 +251,29 @@ export async function inspectDoctorBootstrapSurface(projectRoot) {
|
|
|
221
251
|
checks.push(
|
|
222
252
|
buildCheck(
|
|
223
253
|
"command_gating_matrix_contract",
|
|
224
|
-
commandGatingMatrix.ok,
|
|
225
|
-
|
|
254
|
+
usesV2SurfaceModel || commandGatingMatrix.ok,
|
|
255
|
+
usesV2SurfaceModel
|
|
256
|
+
? "v2 workflow gates are provided by project-local .nimi/contracts and .nimi/methodology"
|
|
257
|
+
: commandGatingMatrix.ok
|
|
226
258
|
? `${COMMAND_GATING_MATRIX_REF} declares ${commandGatingMatrix.entries.length} command gating rules`
|
|
227
259
|
: `${COMMAND_GATING_MATRIX_REF} is missing or malformed`,
|
|
260
|
+
usesV2SurfaceModel ? "ok" : undefined,
|
|
228
261
|
),
|
|
229
262
|
);
|
|
230
263
|
|
|
231
264
|
const blueprintReference = await loadBlueprintReference(projectRoot);
|
|
232
|
-
const blueprintReferenceExpected =
|
|
265
|
+
const blueprintReferenceExpected = usesV2SurfaceModel
|
|
266
|
+
? specGenerationInputs.benchmarkMode !== "none" || typeof specGenerationInputs.benchmarkBlueprintRoot === "string"
|
|
267
|
+
: bootstrapStateContract.blueprintMode !== "none";
|
|
268
|
+
const expectedBlueprintCanonicalRoot = usesV2SurfaceModel
|
|
269
|
+
? specGenerationInputs.canonicalTargetRoot
|
|
270
|
+
: specTreeModel.canonicalRoot;
|
|
233
271
|
const blueprintReferenceAligned = !blueprintReferenceExpected
|
|
234
272
|
? !blueprintReference.present
|
|
235
273
|
: blueprintReference.present
|
|
236
274
|
&& blueprintReference.ok
|
|
237
|
-
&& blueprintReference.mode === bootstrapStateContract.blueprintMode
|
|
238
|
-
&& blueprintReference.canonicalTargetRoot ===
|
|
275
|
+
&& (usesV2SurfaceModel || blueprintReference.mode === bootstrapStateContract.blueprintMode)
|
|
276
|
+
&& blueprintReference.canonicalTargetRoot === expectedBlueprintCanonicalRoot;
|
|
239
277
|
checks.push(
|
|
240
278
|
buildCheck(
|
|
241
279
|
"blueprint_reference_contract",
|
|
@@ -257,7 +295,7 @@ export async function inspectDoctorBootstrapSurface(projectRoot) {
|
|
|
257
295
|
available: benchmarkAvailable,
|
|
258
296
|
ready: benchmarkAvailable && Boolean(
|
|
259
297
|
specGenerationInputs.ok
|
|
260
|
-
&& specTreeModel.ok
|
|
298
|
+
&& (usesV2SurfaceModel || specTreeModel.ok)
|
|
261
299
|
&& (
|
|
262
300
|
specGenerationInputs.benchmarkMode === "none"
|
|
263
301
|
? !blueprintReference.present
|
|
@@ -295,32 +333,38 @@ export async function inspectDoctorBootstrapSurface(projectRoot) {
|
|
|
295
333
|
: `Canonical tree required files are still missing: ${canonicalTree.missing.join(", ")}`,
|
|
296
334
|
});
|
|
297
335
|
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
);
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
336
|
+
const specGenerationAuditRef = usesV2SurfaceModel
|
|
337
|
+
? ".nimi/local/state/spec-generation/spec-generation-audit.yaml"
|
|
338
|
+
: SPEC_GENERATION_AUDIT_REF;
|
|
339
|
+
const specGenerationAuditInfo = await pathExists(path.join(projectRoot, specGenerationAuditRef));
|
|
340
|
+
let specGenerationAudit = emptySpecGenerationAudit();
|
|
341
|
+
specGenerationAudit.auditPath = specGenerationAuditRef;
|
|
342
|
+
if (specGenerationAuditInfo?.isFile()) {
|
|
343
|
+
const specGenerationAuditReport = await validateSpecAudit(
|
|
344
|
+
path.join(projectRoot, specGenerationAuditRef),
|
|
345
|
+
{ projectRoot },
|
|
346
|
+
);
|
|
347
|
+
specGenerationAudit = {
|
|
305
348
|
present: true,
|
|
306
349
|
ok: specGenerationAuditReport.ok,
|
|
307
|
-
auditPath:
|
|
350
|
+
auditPath: specGenerationAuditRef,
|
|
308
351
|
validator: "validate-spec-audit",
|
|
309
352
|
summary: specGenerationAuditReport.summary ?? null,
|
|
310
353
|
reason: specGenerationAuditReport.ok
|
|
311
354
|
? "Spec generation audit is present and structurally valid"
|
|
312
355
|
: specGenerationAuditReport.errors.join("; "),
|
|
313
356
|
};
|
|
357
|
+
}
|
|
314
358
|
const specGenerationAuditCheckSeverity = !specGenerationAudit.present
|
|
315
|
-
? canonicalTree.requiredFilesValid ? "
|
|
359
|
+
? canonicalTree.requiredFilesValid ? "warn" : "info"
|
|
316
360
|
: specGenerationAudit.ok ? "ok" : canonicalTree.requiredFilesValid ? "error" : "warn";
|
|
317
361
|
checks.push({
|
|
318
362
|
id: "spec_generation_audit",
|
|
319
|
-
ok: !specGenerationAudit.present ?
|
|
363
|
+
ok: !specGenerationAudit.present ? true : specGenerationAudit.ok,
|
|
320
364
|
severity: specGenerationAuditCheckSeverity,
|
|
321
365
|
detail: !specGenerationAudit.present
|
|
322
366
|
? canonicalTree.requiredFilesValid
|
|
323
|
-
? "Canonical tree is ready
|
|
367
|
+
? "Canonical tree is ready and spec generation audit is missing; run the audit workflow when file-level generation evidence is required"
|
|
324
368
|
: specGenerationAudit.reason
|
|
325
369
|
: specGenerationAudit.ok
|
|
326
370
|
? specGenerationAudit.reason
|
|
@@ -331,10 +375,13 @@ export async function inspectDoctorBootstrapSurface(projectRoot) {
|
|
|
331
375
|
checks.push(
|
|
332
376
|
buildCheck(
|
|
333
377
|
"high_risk_closeout_gate",
|
|
334
|
-
Boolean(highRiskCloseoutGate?.completedRequires?.tree_state === "canonical_tree_ready"),
|
|
335
|
-
|
|
378
|
+
usesV2SurfaceModel || Boolean(highRiskCloseoutGate?.completedRequires?.tree_state === "canonical_tree_ready"),
|
|
379
|
+
usesV2SurfaceModel
|
|
380
|
+
? "v2 high-risk closeout gates are contract-driven by project-local .nimi/contracts"
|
|
381
|
+
: highRiskCloseoutGate
|
|
336
382
|
? "Command gating matrix includes high_risk_execution closeout readiness"
|
|
337
383
|
: "command gating matrix is missing closeout gating for high_risk_execution",
|
|
384
|
+
usesV2SurfaceModel ? "ok" : undefined,
|
|
338
385
|
),
|
|
339
386
|
);
|
|
340
387
|
|
|
@@ -147,7 +147,7 @@ export async function inspectDoctorDelegatedSurface(projectRoot) {
|
|
|
147
147
|
const missingHandoffPaths = [];
|
|
148
148
|
for (const relativePath of handoffRequiredContext) {
|
|
149
149
|
const info = await pathExists(path.join(projectRoot, relativePath));
|
|
150
|
-
if (!info) {
|
|
150
|
+
if (!info && relativePath !== ".nimi/spec") {
|
|
151
151
|
missingHandoffPaths.push(relativePath);
|
|
152
152
|
}
|
|
153
153
|
}
|