@nimiplatform/nimi-coding 0.1.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 (186) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +348 -0
  3. package/adapters/README.md +25 -0
  4. package/adapters/claude/README.md +89 -0
  5. package/adapters/claude/profile.yaml +70 -0
  6. package/adapters/codex/README.md +53 -0
  7. package/adapters/codex/profile.yaml +78 -0
  8. package/adapters/oh-my-codex/README.md +185 -0
  9. package/adapters/oh-my-codex/profile.yaml +46 -0
  10. package/bin/nimicoding.mjs +6 -0
  11. package/cli/commands/admit-high-risk-decision.mjs +108 -0
  12. package/cli/commands/audit-sweep.mjs +341 -0
  13. package/cli/commands/blueprint-audit.mjs +91 -0
  14. package/cli/commands/clear.mjs +168 -0
  15. package/cli/commands/closeout.mjs +183 -0
  16. package/cli/commands/decide-high-risk-execution.mjs +124 -0
  17. package/cli/commands/doctor.mjs +53 -0
  18. package/cli/commands/generate-spec-derived-docs.mjs +131 -0
  19. package/cli/commands/handoff.mjs +123 -0
  20. package/cli/commands/ingest-high-risk-execution.mjs +95 -0
  21. package/cli/commands/review-high-risk-execution.mjs +95 -0
  22. package/cli/commands/start.mjs +717 -0
  23. package/cli/commands/topic-formatters.mjs +382 -0
  24. package/cli/commands/topic-goal.mjs +33 -0
  25. package/cli/commands/topic-options-shared.mjs +27 -0
  26. package/cli/commands/topic-options-workflow.mjs +767 -0
  27. package/cli/commands/topic-options.mjs +626 -0
  28. package/cli/commands/topic-runner.mjs +169 -0
  29. package/cli/commands/topic.mjs +795 -0
  30. package/cli/commands/validate-acceptance.mjs +5 -0
  31. package/cli/commands/validate-ai-governance.mjs +214 -0
  32. package/cli/commands/validate-execution-packet.mjs +5 -0
  33. package/cli/commands/validate-orchestration-state.mjs +5 -0
  34. package/cli/commands/validate-prompt.mjs +5 -0
  35. package/cli/commands/validate-spec-audit.mjs +27 -0
  36. package/cli/commands/validate-spec-governance.mjs +124 -0
  37. package/cli/commands/validate-spec-tree.mjs +27 -0
  38. package/cli/commands/validate-worker-output.mjs +5 -0
  39. package/cli/constants.mjs +489 -0
  40. package/cli/help.mjs +134 -0
  41. package/cli/index.mjs +103 -0
  42. package/cli/lib/adapter-profiles.mjs +403 -0
  43. package/cli/lib/audit-execution.mjs +52 -0
  44. package/cli/lib/audit-sweep-runtime/admissions.mjs +381 -0
  45. package/cli/lib/audit-sweep-runtime/audit-validity.mjs +333 -0
  46. package/cli/lib/audit-sweep-runtime/chunks.mjs +697 -0
  47. package/cli/lib/audit-sweep-runtime/closeout.mjs +144 -0
  48. package/cli/lib/audit-sweep-runtime/codex-auditor-evidence.mjs +639 -0
  49. package/cli/lib/audit-sweep-runtime/codex-auditor.mjs +515 -0
  50. package/cli/lib/audit-sweep-runtime/common.mjs +329 -0
  51. package/cli/lib/audit-sweep-runtime/coverage-quality.mjs +172 -0
  52. package/cli/lib/audit-sweep-runtime/evidence-assignment.mjs +152 -0
  53. package/cli/lib/audit-sweep-runtime/format.mjs +57 -0
  54. package/cli/lib/audit-sweep-runtime/ingest.mjs +486 -0
  55. package/cli/lib/audit-sweep-runtime/inventory-spec-chunks.mjs +198 -0
  56. package/cli/lib/audit-sweep-runtime/inventory.mjs +728 -0
  57. package/cli/lib/audit-sweep-runtime/ledger.mjs +315 -0
  58. package/cli/lib/audit-sweep-runtime/p0p1-profile.mjs +101 -0
  59. package/cli/lib/audit-sweep-runtime/remediation.mjs +349 -0
  60. package/cli/lib/audit-sweep-runtime/rerun.mjs +129 -0
  61. package/cli/lib/audit-sweep-runtime/risk-budget.mjs +300 -0
  62. package/cli/lib/audit-sweep-runtime/status.mjs +62 -0
  63. package/cli/lib/audit-sweep-runtime/validators-ledger.mjs +215 -0
  64. package/cli/lib/audit-sweep-runtime/validators.mjs +758 -0
  65. package/cli/lib/audit-sweep.mjs +18 -0
  66. package/cli/lib/authority-convergence.mjs +309 -0
  67. package/cli/lib/blueprint-audit.mjs +370 -0
  68. package/cli/lib/bootstrap.mjs +228 -0
  69. package/cli/lib/closeout.mjs +623 -0
  70. package/cli/lib/codex-sdk-runner.mjs +76 -0
  71. package/cli/lib/contracts.mjs +180 -0
  72. package/cli/lib/doctor.mjs +18 -0
  73. package/cli/lib/entrypoints.mjs +274 -0
  74. package/cli/lib/external-execution.mjs +101 -0
  75. package/cli/lib/fs-helpers.mjs +33 -0
  76. package/cli/lib/handoff.mjs +785 -0
  77. package/cli/lib/high-risk-admission.mjs +442 -0
  78. package/cli/lib/high-risk-decision.mjs +324 -0
  79. package/cli/lib/high-risk-ingest.mjs +317 -0
  80. package/cli/lib/high-risk-review.mjs +263 -0
  81. package/cli/lib/internal/contracts-loaders.mjs +132 -0
  82. package/cli/lib/internal/contracts-parse-high-risk.mjs +131 -0
  83. package/cli/lib/internal/contracts-parse.mjs +457 -0
  84. package/cli/lib/internal/contracts-validators.mjs +398 -0
  85. package/cli/lib/internal/doctor-bootstrap-surface.mjs +359 -0
  86. package/cli/lib/internal/doctor-delegated-surface.mjs +256 -0
  87. package/cli/lib/internal/doctor-finalize.mjs +385 -0
  88. package/cli/lib/internal/doctor-format.mjs +286 -0
  89. package/cli/lib/internal/doctor-inspectors.mjs +294 -0
  90. package/cli/lib/internal/doctor-state.mjs +205 -0
  91. package/cli/lib/internal/governance/ai/ai-context-budget-core.mjs +315 -0
  92. package/cli/lib/internal/governance/ai/ai-structure-budget-core.mjs +358 -0
  93. package/cli/lib/internal/governance/ai/check-agents-freshness.mjs +155 -0
  94. package/cli/lib/internal/governance/ai/check-high-risk-doc-metadata-core.mjs +173 -0
  95. package/cli/lib/internal/governance/config.mjs +150 -0
  96. package/cli/lib/internal/governance/runner.mjs +35 -0
  97. package/cli/lib/internal/governance/shared/read-yaml-with-fragments.mjs +49 -0
  98. package/cli/lib/internal/validators-artifacts.mjs +515 -0
  99. package/cli/lib/internal/validators-shared.mjs +28 -0
  100. package/cli/lib/internal/validators-spec-helpers.mjs +186 -0
  101. package/cli/lib/internal/validators-spec.mjs +410 -0
  102. package/cli/lib/shared.mjs +83 -0
  103. package/cli/lib/topic-draft-packets.mjs +48 -0
  104. package/cli/lib/topic-goal.mjs +361 -0
  105. package/cli/lib/topic-runner.mjs +772 -0
  106. package/cli/lib/topic.mjs +93 -0
  107. package/cli/lib/ui.mjs +178 -0
  108. package/cli/lib/validators.mjs +78 -0
  109. package/cli/lib/value-helpers.mjs +24 -0
  110. package/cli/lib/yaml-helpers.mjs +133 -0
  111. package/cli/nimicoding.mjs +1 -0
  112. package/cli/seeds/bootstrap.mjs +47 -0
  113. package/config/audit-execution-artifacts.yaml +20 -0
  114. package/config/bootstrap.yaml +6 -0
  115. package/config/external-execution-artifacts.yaml +16 -0
  116. package/config/host-adapter.yaml +30 -0
  117. package/config/host-profile.yaml +29 -0
  118. package/config/installer-evidence.yaml +31 -0
  119. package/config/skill-installer.yaml +23 -0
  120. package/config/skill-manifest.yaml +46 -0
  121. package/config/skills.yaml +30 -0
  122. package/config/spec-generation-inputs.yaml +25 -0
  123. package/contracts/acceptance.schema.yaml +16 -0
  124. package/contracts/admission-checklist.schema.yaml +15 -0
  125. package/contracts/audit-chunk.schema.yaml +110 -0
  126. package/contracts/audit-closeout.schema.yaml +51 -0
  127. package/contracts/audit-finding.schema.yaml +61 -0
  128. package/contracts/audit-ledger.schema.yaml +138 -0
  129. package/contracts/audit-plan.schema.yaml +123 -0
  130. package/contracts/audit-remediation-map.schema.yaml +51 -0
  131. package/contracts/audit-rerun.schema.yaml +31 -0
  132. package/contracts/audit-sweep-result.yaml +49 -0
  133. package/contracts/authority-convergence-audit.schema.yaml +19 -0
  134. package/contracts/closeout.schema.yaml +25 -0
  135. package/contracts/decision-review.schema.yaml +16 -0
  136. package/contracts/doc-spec-audit-result.yaml +19 -0
  137. package/contracts/execution-packet.schema.yaml +49 -0
  138. package/contracts/external-host-compatibility.yaml +22 -0
  139. package/contracts/forbidden-shortcuts.catalog.yaml +23 -0
  140. package/contracts/high-risk-admission.schema.yaml +23 -0
  141. package/contracts/high-risk-execution-result.yaml +20 -0
  142. package/contracts/orchestration-state.schema.yaml +41 -0
  143. package/contracts/overflow-continuation.schema.yaml +12 -0
  144. package/contracts/packet.schema.yaml +30 -0
  145. package/contracts/pending-note.schema.yaml +17 -0
  146. package/contracts/prompt.schema.yaml +12 -0
  147. package/contracts/remediation.schema.yaml +16 -0
  148. package/contracts/result.schema.yaml +24 -0
  149. package/contracts/spec-generation-audit.schema.yaml +31 -0
  150. package/contracts/spec-generation-inputs.schema.yaml +39 -0
  151. package/contracts/spec-reconstruction-result.yaml +37 -0
  152. package/contracts/topic-goal.schema.yaml +78 -0
  153. package/contracts/topic-run-ledger.schema.yaml +72 -0
  154. package/contracts/topic-step-decision.schema.yaml +45 -0
  155. package/contracts/topic.schema.yaml +65 -0
  156. package/contracts/true-close.schema.yaml +15 -0
  157. package/contracts/wave.schema.yaml +29 -0
  158. package/contracts/worker-output.schema.yaml +15 -0
  159. package/methodology/audit-sweep-p0p1-recall.yaml +45 -0
  160. package/methodology/authority-convergence-policy.yaml +42 -0
  161. package/methodology/core.yaml +25 -0
  162. package/methodology/four-closure-policy.yaml +28 -0
  163. package/methodology/overflow-continuation-policy.yaml +14 -0
  164. package/methodology/role-separation-policy.yaml +28 -0
  165. package/methodology/skill-exchange-projection.yaml +114 -0
  166. package/methodology/skill-handoff.yaml +34 -0
  167. package/methodology/skill-installer-result.yaml +27 -0
  168. package/methodology/skill-installer-summary-projection.yaml +181 -0
  169. package/methodology/skill-runtime.yaml +23 -0
  170. package/methodology/spec-reconstruction.yaml +63 -0
  171. package/methodology/spec-target-truth-profile.yaml +53 -0
  172. package/methodology/topic-lifecycle-report.yaml +144 -0
  173. package/methodology/topic-lifecycle.yaml +37 -0
  174. package/methodology/topic-naming-ontology.yaml +21 -0
  175. package/methodology/topic-ontology.yaml +38 -0
  176. package/methodology/topic-validation-policy.yaml +9 -0
  177. package/methodology/wave-dag-policy.yaml +14 -0
  178. package/package.json +50 -0
  179. package/spec/_meta/command-gating-matrix.yaml +110 -0
  180. package/spec/_meta/generate-drift-migration-checklist.yaml +155 -0
  181. package/spec/_meta/governance-routing-cutover-checklist.yaml +35 -0
  182. package/spec/_meta/phase2-impacted-surface-matrix.yaml +44 -0
  183. package/spec/_meta/spec-authority-cutover-readiness.yaml +104 -0
  184. package/spec/_meta/spec-tree-model.yaml +72 -0
  185. package/spec/bootstrap-state.yaml +99 -0
  186. package/spec/product-scope.yaml +56 -0
@@ -0,0 +1,457 @@
1
+ import {
2
+ BLUEPRINT_REFERENCE_REF,
3
+ SPEC_GENERATION_AUDIT_CONTRACT_REF,
4
+ SPEC_GENERATION_AUDIT_COVERAGE_STATUS_ENUM,
5
+ SPEC_GENERATION_AUDIT_FILE_REQUIRED_FIELDS,
6
+ SPEC_GENERATION_AUDIT_REF,
7
+ SPEC_GENERATION_AUDIT_REQUIRED_TOP_LEVEL_FIELDS,
8
+ SPEC_GENERATION_AUDIT_SOURCE_BASIS_ENUM,
9
+ SPEC_GENERATION_INPUTS_CONTRACT_REF,
10
+ SPEC_GENERATION_INPUTS_REF,
11
+ SPEC_RECONSTRUCTION_SUMMARY_REQUIRED_FIELDS,
12
+ SPEC_RECONSTRUCTION_SUMMARY_STATUS,
13
+ SPEC_TREE_MODEL_REF,
14
+ } from "../../constants.mjs";
15
+ import {
16
+ arraysEqual,
17
+ isPlainObject,
18
+ toStringArray,
19
+ } from "../value-helpers.mjs";
20
+ import { parseYamlText } from "../yaml-helpers.mjs";
21
+ export {
22
+ parseAuditSweepContract,
23
+ parseDocSpecAuditContract,
24
+ parseExternalHostCompatibilityContract,
25
+ parseHighRiskAdmissionContract,
26
+ parseHighRiskExecutionContract,
27
+ parseHighRiskSchemaContract,
28
+ } from "./contracts-parse-high-risk.mjs";
29
+
30
+ const SPEC_TREE_PROFILE_ENUM = ["minimal", "standard", "mature"];
31
+ const AUTHORITY_MODE_ENUM = [
32
+ "external_authority_active",
33
+ "external_blueprint_active",
34
+ "canonical_cutover_ready",
35
+ "canonical_active",
36
+ ];
37
+ const BLUEPRINT_MODE_ENUM = [
38
+ "none",
39
+ "repo_spec_blueprint",
40
+ "custom_blueprint",
41
+ ];
42
+ const SPEC_GENERATION_MODE_ENUM = ["mixed"];
43
+ const SPEC_GENERATION_ACCEPTANCE_MODE_ENUM = [
44
+ "canonical_tree_validity_without_blueprint",
45
+ "semantic_and_structural_parity_when_blueprint_exists",
46
+ ];
47
+ const SPEC_GENERATION_ORDER_ENUM = [
48
+ "index",
49
+ "kernel_markdown",
50
+ "kernel_tables",
51
+ "generated_views",
52
+ "thin_guides",
53
+ ];
54
+ 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
+
56
+ function normalizeAuthorityModeValue(value) {
57
+ return value === "external_blueprint_active" ? "external_authority_active" : value;
58
+ }
59
+
60
+ function toStringOrNull(value) {
61
+ return typeof value === "string" ? value : null;
62
+ }
63
+
64
+ function normalizePathClass(entry) {
65
+ if (!isPlainObject(entry)) {
66
+ return null;
67
+ }
68
+
69
+ return {
70
+ id: toStringOrNull(entry.id),
71
+ pathPatterns: toStringArray(entry.path_patterns),
72
+ excludedPathPatterns: toStringArray(entry.excluded_path_patterns),
73
+ allowedExtensions: toStringArray(entry.allowed_extensions),
74
+ generatorRefs: toStringArray(entry.generator_refs),
75
+ mustReferenceNormativeIds: entry.must_reference_normative_ids === true,
76
+ normative: entry.normative === true,
77
+ };
78
+ }
79
+
80
+ function normalizeSpecDomain(entry) {
81
+ if (!isPlainObject(entry)) {
82
+ return null;
83
+ }
84
+
85
+ return {
86
+ id: toStringOrNull(entry.id),
87
+ root: toStringOrNull(entry.root),
88
+ normativeRoot: toStringOrNull(entry.normative_root),
89
+ tablesRoot: toStringOrNull(entry.tables_root),
90
+ generatedRoot: toStringOrNull(entry.generated_root),
91
+ guidePaths: toStringArray(entry.guide_paths),
92
+ };
93
+ }
94
+
95
+ function normalizeGeneratedPipeline(entry) {
96
+ if (!isPlainObject(entry)) {
97
+ return null;
98
+ }
99
+
100
+ return {
101
+ id: toStringOrNull(entry.id),
102
+ ownerSurface: toStringOrNull(entry.owner_surface),
103
+ inputRoots: toStringArray(entry.input_roots),
104
+ outputRoots: toStringArray(entry.output_roots),
105
+ generateCommand: toStringOrNull(entry.generate_command),
106
+ driftCheckCommand: toStringOrNull(entry.drift_check_command),
107
+ };
108
+ }
109
+
110
+ function pathStartsWithRoot(targetPath, root) {
111
+ if (typeof targetPath !== "string" || typeof root !== "string") {
112
+ return false;
113
+ }
114
+
115
+ return targetPath === root || targetPath.startsWith(`${root}/`);
116
+ }
117
+
118
+ function isDatePrefixedTopicLifecycleMarkdownPath(value) {
119
+ if (typeof value !== "string") {
120
+ return false;
121
+ }
122
+ return TOPIC_LIFECYCLE_REPORT_MARKDOWN.test(value);
123
+ }
124
+
125
+ function topicLifecycleMarkdownPathsAreSortable(paths) {
126
+ return paths.every((entry) => {
127
+ if (typeof entry !== "string") {
128
+ return true;
129
+ }
130
+ if (entry.startsWith(".local/report/")) {
131
+ return false;
132
+ }
133
+ if (!entry.startsWith(".nimi/topics/")) {
134
+ return true;
135
+ }
136
+ if (!entry.endsWith(".md")) {
137
+ return true;
138
+ }
139
+ return isDatePrefixedTopicLifecycleMarkdownPath(entry);
140
+ });
141
+ }
142
+
143
+ export function parseSpecReconstructionContract(text) {
144
+ const parsed = parseYamlText(text);
145
+ const summaryRequiredFields = toStringArray(parsed?.summary_required_fields);
146
+ const summaryStatusEnum = toStringArray(parsed?.summary_status_enum);
147
+ const completionRequirements = toStringArray(parsed?.completion_requirements);
148
+ const canonicalTreeCompletion = isPlainObject(parsed?.canonical_tree_completion)
149
+ ? {
150
+ profileRef: toStringOrNull(parsed.canonical_tree_completion.profile_ref),
151
+ generationInputsRef: toStringOrNull(parsed.canonical_tree_completion.generation_inputs_ref),
152
+ auditContractRef: toStringOrNull(parsed.canonical_tree_completion.audit_contract_ref),
153
+ auditRef: toStringOrNull(parsed.canonical_tree_completion.audit_ref),
154
+ requiredTreeState: toStringOrNull(parsed.canonical_tree_completion.required_tree_state),
155
+ requiredFilesValid: parsed.canonical_tree_completion.required_files_valid === true,
156
+ }
157
+ : null;
158
+
159
+ return {
160
+ ok: arraysEqual(summaryRequiredFields, SPEC_RECONSTRUCTION_SUMMARY_REQUIRED_FIELDS)
161
+ && arraysEqual(summaryStatusEnum, SPEC_RECONSTRUCTION_SUMMARY_STATUS)
162
+ && completionRequirements.includes("canonical_tree_ready")
163
+ && completionRequirements.includes("declared_profile_required_files_valid")
164
+ && completionRequirements.includes("declared_file_class_constraints_valid")
165
+ && completionRequirements.includes("spec_generation_audit_present_and_valid")
166
+ && completionRequirements.includes("required_canonical_files_have_matching_audit_entries")
167
+ && completionRequirements.includes("unresolved_gaps_must_remain_explicit")
168
+ && completionRequirements.includes("semantic_and_structural_parity_when_blueprint_exists")
169
+ && canonicalTreeCompletion?.profileRef === SPEC_TREE_MODEL_REF
170
+ && canonicalTreeCompletion?.generationInputsRef === SPEC_GENERATION_INPUTS_REF
171
+ && canonicalTreeCompletion?.auditContractRef === SPEC_GENERATION_AUDIT_CONTRACT_REF
172
+ && canonicalTreeCompletion?.auditRef === SPEC_GENERATION_AUDIT_REF
173
+ && canonicalTreeCompletion?.requiredTreeState === "canonical_tree_ready"
174
+ && canonicalTreeCompletion?.requiredFilesValid === true,
175
+ canonicalTreeCompletion,
176
+ summaryRequiredFields,
177
+ summaryStatusEnum,
178
+ completionRequirements,
179
+ };
180
+ }
181
+
182
+ export function parseSpecGenerationAuditContract(text) {
183
+ const parsed = parseYamlText(text);
184
+ const requiredTopLevelFields = toStringArray(parsed?.required_top_level_fields);
185
+ const requiredFileEntryFields = toStringArray(parsed?.required_file_entry_fields);
186
+ const sourceBasisEnum = toStringArray(parsed?.source_basis_enum);
187
+ const coverageStatusEnum = toStringArray(parsed?.coverage_status_enum);
188
+ const hardConstraints = toStringArray(parsed?.hard_constraints);
189
+
190
+ return {
191
+ ok: parsed?.version === 1
192
+ && String(parsed?.audit_contract?.id ?? "") === "canonical_spec_generation_audit"
193
+ && String(parsed?.audit_contract?.target_ref ?? "") === SPEC_GENERATION_AUDIT_REF
194
+ && arraysEqual(requiredTopLevelFields, SPEC_GENERATION_AUDIT_REQUIRED_TOP_LEVEL_FIELDS)
195
+ && arraysEqual(requiredFileEntryFields, SPEC_GENERATION_AUDIT_FILE_REQUIRED_FIELDS)
196
+ && arraysEqual(sourceBasisEnum, SPEC_GENERATION_AUDIT_SOURCE_BASIS_ENUM)
197
+ && arraysEqual(coverageStatusEnum, SPEC_GENERATION_AUDIT_COVERAGE_STATUS_ENUM)
198
+ && hardConstraints.includes("every_generated_canonical_file_requires_a_matching_audit_entry")
199
+ && hardConstraints.includes("required_canonical_files_must_not_be_placeholder_not_allowed")
200
+ && hardConstraints.includes("unresolved_or_inferred_content_must_be_explicit")
201
+ && hardConstraints.includes("source_refs_must_stay_within_declared_inputs_or_optional_benchmark_root")
202
+ && hardConstraints.includes("no_empty_success_looking_audit_entries"),
203
+ requiredTopLevelFields,
204
+ requiredFileEntryFields,
205
+ sourceBasisEnum,
206
+ coverageStatusEnum,
207
+ hardConstraints,
208
+ };
209
+ }
210
+
211
+ export function parseSpecGenerationInputsContract(text) {
212
+ const parsed = parseYamlText(text);
213
+ const contract = parsed?.input_contract;
214
+ const requiredFields = toStringArray(parsed?.required_fields);
215
+ const generationOrderEnum = toStringArray(parsed?.generation_order_enum);
216
+ const hardConstraints = toStringArray(parsed?.hard_constraints);
217
+
218
+ return {
219
+ ok: parsed?.version === 1
220
+ && String(contract?.id ?? "") === "canonical_spec_generation_inputs"
221
+ && String(contract?.target_root ?? "") === ".nimi/spec"
222
+ && arraysEqual(toStringArray(contract?.mode_enum), SPEC_GENERATION_MODE_ENUM)
223
+ && arraysEqual(toStringArray(contract?.acceptance_mode_enum), SPEC_GENERATION_ACCEPTANCE_MODE_ENUM)
224
+ && arraysEqual(generationOrderEnum, SPEC_GENERATION_ORDER_ENUM)
225
+ && requiredFields.includes("mode")
226
+ && requiredFields.includes("canonical_target_root")
227
+ && requiredFields.includes("benchmark_blueprint_root")
228
+ && hardConstraints.includes("canonical_target_root_must_be_.nimi/spec")
229
+ && hardConstraints.includes("local_report_markdown_paths_must_use_topic_lifecycle_shape")
230
+ && hardConstraints.includes("human_authored_topic_reports_must_use_.nimi/topics_as_canonical_root"),
231
+ requiredFields,
232
+ generationOrderEnum,
233
+ hardConstraints,
234
+ };
235
+ }
236
+
237
+ export function parseSpecGenerationInputsConfig(text) {
238
+ const parsed = parseYamlText(text);
239
+ const config = parsed?.spec_generation_inputs;
240
+ const mode = toStringOrNull(config?.mode);
241
+ const canonicalTargetRoot = toStringOrNull(config?.canonical_target_root);
242
+ const codeRoots = toStringArray(config?.code_roots);
243
+ const docsRoots = toStringArray(config?.docs_roots);
244
+ const structureRoots = toStringArray(config?.structure_roots);
245
+ const humanNotePaths = toStringArray(config?.human_note_paths);
246
+ const benchmarkBlueprintRoot = typeof config?.benchmark_blueprint_root === "string"
247
+ ? config.benchmark_blueprint_root
248
+ : null;
249
+ const benchmarkMode = toStringOrNull(config?.benchmark_mode);
250
+ const acceptanceMode = toStringOrNull(config?.acceptance_mode);
251
+ const generationOrder = toStringArray(config?.generation_order);
252
+ const inferenceRules = toStringArray(config?.inference_rules);
253
+
254
+ return {
255
+ ok: parsed?.version === 1
256
+ && String(parsed?.contract_ref ?? "") === SPEC_GENERATION_INPUTS_CONTRACT_REF
257
+ && SPEC_GENERATION_MODE_ENUM.includes(mode)
258
+ && canonicalTargetRoot === ".nimi/spec"
259
+ && Array.isArray(codeRoots)
260
+ && Array.isArray(docsRoots)
261
+ && Array.isArray(structureRoots)
262
+ && Array.isArray(humanNotePaths)
263
+ && topicLifecycleMarkdownPathsAreSortable(humanNotePaths)
264
+ && BLUEPRINT_MODE_ENUM.includes(benchmarkMode)
265
+ && SPEC_GENERATION_ACCEPTANCE_MODE_ENUM.includes(acceptanceMode)
266
+ && generationOrder.length > 0
267
+ && generationOrder.every((entry) => SPEC_GENERATION_ORDER_ENUM.includes(entry))
268
+ && inferenceRules.length > 0
269
+ && (
270
+ benchmarkMode === "none"
271
+ ? benchmarkBlueprintRoot === null
272
+ : typeof benchmarkBlueprintRoot === "string" && benchmarkBlueprintRoot.length > 0
273
+ ),
274
+ mode,
275
+ canonicalTargetRoot,
276
+ codeRoots,
277
+ docsRoots,
278
+ structureRoots,
279
+ humanNotePaths,
280
+ benchmarkBlueprintRoot,
281
+ benchmarkMode,
282
+ acceptanceMode,
283
+ generationOrder,
284
+ inferenceRules,
285
+ };
286
+ }
287
+
288
+ export function parseSpecTreeModel(text) {
289
+ const parsed = parseYamlText(text);
290
+ const model = parsed?.spec_tree_model;
291
+ const profile = toStringOrNull(model?.profile);
292
+ const canonicalRoot = toStringOrNull(model?.canonical_root);
293
+ const authorityMode = normalizeAuthorityModeValue(toStringOrNull(model?.authority_mode));
294
+ const domains = Array.isArray(model?.domains)
295
+ ? model.domains.map(normalizeSpecDomain).filter(Boolean)
296
+ : [];
297
+ const normativeClasses = Array.isArray(model?.normative_classes)
298
+ ? model.normative_classes.map(normalizePathClass).filter(Boolean)
299
+ : [];
300
+ const derivedClasses = Array.isArray(model?.derived_classes)
301
+ ? model.derived_classes.map(normalizePathClass).filter(Boolean)
302
+ : [];
303
+ const guidanceClasses = Array.isArray(model?.guidance_classes)
304
+ ? model.guidance_classes.map(normalizePathClass).filter(Boolean)
305
+ : [];
306
+ const requiredFilesByProfile = SPEC_TREE_PROFILE_ENUM.reduce((acc, currentProfile) => {
307
+ acc[currentProfile] = toStringArray(model?.required_files?.[currentProfile]);
308
+ return acc;
309
+ }, {});
310
+ const generatedPipelines = Array.isArray(model?.generated_pipelines)
311
+ ? model.generated_pipelines.map(normalizeGeneratedPipeline).filter(Boolean)
312
+ : [];
313
+ const failClosedRules = toStringArray(model?.fail_closed_rules);
314
+ const blueprintSource = isPlainObject(model?.blueprint_source)
315
+ ? {
316
+ mode: toStringOrNull(model.blueprint_source.mode),
317
+ root: toStringOrNull(model.blueprint_source.root),
318
+ equivalenceContractRef: toStringOrNull(model.blueprint_source.equivalence_contract_ref),
319
+ }
320
+ : null;
321
+
322
+ const canonicalRootValid = canonicalRoot === ".nimi/spec";
323
+ const profileValid = SPEC_TREE_PROFILE_ENUM.includes(profile);
324
+ const authorityModeValid = AUTHORITY_MODE_ENUM.includes(authorityMode);
325
+ const domainRootsValid = domains.length > 0 && domains.every((domain) => (
326
+ typeof domain.id === "string"
327
+ && typeof domain.root === "string"
328
+ && typeof domain.normativeRoot === "string"
329
+ && typeof domain.tablesRoot === "string"
330
+ && pathStartsWithRoot(domain.root, canonicalRoot)
331
+ && pathStartsWithRoot(domain.normativeRoot, domain.root)
332
+ && pathStartsWithRoot(domain.tablesRoot, domain.normativeRoot)
333
+ && (!domain.generatedRoot || pathStartsWithRoot(domain.generatedRoot, domain.normativeRoot))
334
+ && domain.guidePaths.every((guidePath) => pathStartsWithRoot(guidePath, domain.root))
335
+ ));
336
+ const requiredFilesValid = SPEC_TREE_PROFILE_ENUM.every((currentProfile) => (
337
+ requiredFilesByProfile[currentProfile].length > 0
338
+ && requiredFilesByProfile[currentProfile].every((requiredPath) => pathStartsWithRoot(requiredPath, canonicalRoot))
339
+ ));
340
+ const fileClassIdsPresent = normativeClasses.every((entry) => entry.id && entry.pathPatterns.length > 0)
341
+ && derivedClasses.every((entry) => entry.id && entry.pathPatterns.length > 0)
342
+ && guidanceClasses.every((entry) => entry.id && entry.pathPatterns.length > 0);
343
+ const generatedPipelinesValid = generatedPipelines.every((entry) => (
344
+ entry.id
345
+ && entry.ownerSurface
346
+ && entry.generateCommand
347
+ && entry.inputRoots.length > 0
348
+ && entry.outputRoots.length > 0
349
+ ));
350
+ const blueprintSourceValid = !blueprintSource || (
351
+ typeof blueprintSource.mode === "string"
352
+ && ["repo_spec_blueprint", "custom_blueprint"].includes(blueprintSource.mode)
353
+ && typeof blueprintSource.root === "string"
354
+ && typeof blueprintSource.equivalenceContractRef === "string"
355
+ && isDatePrefixedTopicLifecycleMarkdownPath(blueprintSource.equivalenceContractRef)
356
+ );
357
+
358
+ return {
359
+ ok: parsed?.version === 1
360
+ && profileValid
361
+ && canonicalRootValid
362
+ && authorityModeValid
363
+ && domainRootsValid
364
+ && requiredFilesValid
365
+ && fileClassIdsPresent
366
+ && generatedPipelinesValid
367
+ && failClosedRules.length > 0
368
+ && blueprintSourceValid,
369
+ version: parsed?.version ?? null,
370
+ profile,
371
+ canonicalRoot,
372
+ authorityMode,
373
+ domains,
374
+ normativeClasses,
375
+ derivedClasses,
376
+ guidanceClasses,
377
+ requiredFilesByProfile,
378
+ generatedPipelines,
379
+ failClosedRules,
380
+ blueprintSource,
381
+ };
382
+ }
383
+
384
+ export function parseCommandGatingMatrix(text) {
385
+ const parsed = parseYamlText(text);
386
+ const entries = Array.isArray(parsed?.command_gating_matrix)
387
+ ? parsed.command_gating_matrix
388
+ .filter((entry) => isPlainObject(entry) && typeof entry.command === "string")
389
+ .map((entry) => ({
390
+ command: entry.command,
391
+ skill: toStringOrNull(entry.skill),
392
+ allowedTreeStates: toStringArray(entry.allowed_tree_states),
393
+ allowedAuthorityModes: toStringArray(entry.allowed_authority_modes).map(normalizeAuthorityModeValue),
394
+ completedRequires: isPlainObject(entry.completed_requires) ? entry.completed_requires : null,
395
+ requires: isPlainObject(entry.requires) ? entry.requires : null,
396
+ notes: toStringArray(entry.notes),
397
+ reports: toStringArray(entry.reports),
398
+ }))
399
+ : [];
400
+
401
+ return {
402
+ ok: parsed?.version === 1 && entries.length > 0,
403
+ entries,
404
+ };
405
+ }
406
+
407
+ export function parseBlueprintReference(text) {
408
+ if (!text) {
409
+ return {
410
+ ok: true,
411
+ present: false,
412
+ mode: null,
413
+ root: null,
414
+ canonicalTargetRoot: null,
415
+ equivalenceContractRef: null,
416
+ };
417
+ }
418
+
419
+ const parsed = parseYamlText(text);
420
+ const reference = parsed?.blueprint_reference;
421
+ const mode = toStringOrNull(reference?.mode);
422
+ const root = toStringOrNull(reference?.root);
423
+ const canonicalTargetRoot = toStringOrNull(reference?.canonical_target_root);
424
+ const equivalenceContractRef = toStringOrNull(reference?.equivalence_contract_ref);
425
+
426
+ return {
427
+ ok: parsed?.version === 1
428
+ && ["repo_spec_blueprint", "custom_blueprint"].includes(mode)
429
+ && typeof root === "string"
430
+ && typeof canonicalTargetRoot === "string"
431
+ && typeof equivalenceContractRef === "string"
432
+ && isDatePrefixedTopicLifecycleMarkdownPath(equivalenceContractRef),
433
+ present: true,
434
+ mode,
435
+ root,
436
+ canonicalTargetRoot,
437
+ equivalenceContractRef,
438
+ };
439
+ }
440
+
441
+ export function matchCommandGatingRule(commandGatingMatrix, command, skillId = null) {
442
+ if (!commandGatingMatrix?.entries) {
443
+ return null;
444
+ }
445
+
446
+ return commandGatingMatrix.entries.find((entry) => {
447
+ if (entry.command !== command) {
448
+ return false;
449
+ }
450
+
451
+ if (skillId === null) {
452
+ return !entry.skill;
453
+ }
454
+
455
+ return entry.skill === skillId;
456
+ }) ?? null;
457
+ }