@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,359 @@
1
+ import path from "node:path";
2
+
3
+ import {
4
+ BLUEPRINT_REFERENCE_REF,
5
+ COMMAND_GATING_MATRIX_REF,
6
+ LOCAL_GITIGNORE_ENTRIES,
7
+ PACKAGE_NAME,
8
+ REQUIRED_BOOTSTRAP_FILES,
9
+ REQUIRED_LOCAL_DIRS,
10
+ SPEC_GENERATION_AUDIT_CONTRACT_REF,
11
+ SPEC_GENERATION_AUDIT_REF,
12
+ SPEC_GENERATION_INPUTS_CONTRACT_REF,
13
+ SPEC_GENERATION_INPUTS_REF,
14
+ SPEC_TREE_MODEL_REF,
15
+ } from "../../constants.mjs";
16
+ import { inspectBootstrapCompatibility } from "../bootstrap.mjs";
17
+ import {
18
+ findCommandGatingRule,
19
+ loadBlueprintReference,
20
+ loadCommandGatingMatrix,
21
+ loadSpecGenerationAuditContract,
22
+ loadSpecGenerationInputsConfig,
23
+ loadSpecGenerationInputsContract,
24
+ loadSpecTreeModelContract,
25
+ } from "../contracts.mjs";
26
+ import { pathExists, readTextIfFile } from "../fs-helpers.mjs";
27
+ import { validateSpecAudit } from "../validators.mjs";
28
+ import {
29
+ readYamlScalar,
30
+ } from "../yaml-helpers.mjs";
31
+ import {
32
+ buildCheck,
33
+ createDoctorMissingRootResult,
34
+ emptySpecGenerationAudit,
35
+ } from "./doctor-state.mjs";
36
+ import {
37
+ inspectBootstrapStateContract,
38
+ inspectCanonicalTree,
39
+ inspectStandaloneCompletionTruth,
40
+ } from "./doctor-inspectors.mjs";
41
+
42
+ export async function inspectDoctorBootstrapSurface(projectRoot) {
43
+ const nimiRoot = path.join(projectRoot, ".nimi");
44
+ const checks = [];
45
+
46
+ const nimiInfo = await pathExists(nimiRoot);
47
+ if (!nimiInfo) {
48
+ return {
49
+ done: true,
50
+ result: createDoctorMissingRootResult(
51
+ projectRoot,
52
+ ".nimi directory is missing",
53
+ "Run `nimicoding start` to seed the project-local .nimi bootstrap truth.",
54
+ ),
55
+ };
56
+ }
57
+
58
+ if (!nimiInfo.isDirectory()) {
59
+ return {
60
+ done: true,
61
+ result: createDoctorMissingRootResult(
62
+ projectRoot,
63
+ ".nimi exists but is not a directory",
64
+ "Replace the non-directory `.nimi` path, then rerun `nimicoding start`.",
65
+ ),
66
+ };
67
+ }
68
+
69
+ checks.push(buildCheck("nimi_root", true, ".nimi directory exists"));
70
+
71
+ const missingBootstrapFiles = [];
72
+ for (const relativePath of REQUIRED_BOOTSTRAP_FILES) {
73
+ const info = await pathExists(path.join(projectRoot, relativePath));
74
+ if (!info || !info.isFile()) {
75
+ missingBootstrapFiles.push(relativePath);
76
+ }
77
+ }
78
+ checks.push(
79
+ buildCheck(
80
+ "bootstrap_seed_files",
81
+ missingBootstrapFiles.length === 0,
82
+ missingBootstrapFiles.length === 0
83
+ ? `All required bootstrap seed files are present (${REQUIRED_BOOTSTRAP_FILES.length}/${REQUIRED_BOOTSTRAP_FILES.length})`
84
+ : `Missing required bootstrap seed files: ${missingBootstrapFiles.join(", ")}`,
85
+ ),
86
+ );
87
+
88
+ const missingLocalDirs = [];
89
+ for (const relativePath of REQUIRED_LOCAL_DIRS) {
90
+ const info = await pathExists(path.join(projectRoot, relativePath));
91
+ if (!info || !info.isDirectory()) {
92
+ missingLocalDirs.push(relativePath);
93
+ }
94
+ }
95
+ checks.push({
96
+ id: "local_state_dirs",
97
+ ok: true,
98
+ severity: missingLocalDirs.length === 0 ? "ok" : "warn",
99
+ detail: missingLocalDirs.length === 0
100
+ ? "Local state directories are present"
101
+ : `Local state directories are absent and can be recreated on demand: ${missingLocalDirs.join(", ")}`,
102
+ });
103
+
104
+ const gitignoreText = await readTextIfFile(path.join(projectRoot, ".gitignore"));
105
+ const missingGitignoreEntries = gitignoreText === null
106
+ ? LOCAL_GITIGNORE_ENTRIES.slice()
107
+ : LOCAL_GITIGNORE_ENTRIES.filter((entry) => !gitignoreText.includes(entry));
108
+ checks.push(
109
+ buildCheck(
110
+ "gitignore_local_state",
111
+ missingGitignoreEntries.length === 0,
112
+ missingGitignoreEntries.length === 0
113
+ ? "Local nimicoding state is ignored by .gitignore"
114
+ : `.gitignore is missing local-state entries: ${missingGitignoreEntries.join(", ")}`,
115
+ ),
116
+ );
117
+
118
+ const bootstrapConfigText = await readTextIfFile(path.join(projectRoot, ".nimi", "config", "bootstrap.yaml"));
119
+ const bootstrapIdentityOk = Boolean(bootstrapConfigText)
120
+ && readYamlScalar(bootstrapConfigText, "initialized_by") === PACKAGE_NAME
121
+ && Boolean(readYamlScalar(bootstrapConfigText, "cli_version"));
122
+ checks.push(
123
+ buildCheck(
124
+ "bootstrap_config_contract",
125
+ bootstrapIdentityOk,
126
+ bootstrapIdentityOk
127
+ ? "bootstrap.yaml declares the package bootstrap identity"
128
+ : "bootstrap.yaml is missing or does not match the package bootstrap identity",
129
+ ),
130
+ );
131
+
132
+ const bootstrapCompatibility = await inspectBootstrapCompatibility(projectRoot);
133
+ checks.push({
134
+ id: "bootstrap_contract_version",
135
+ ok: bootstrapCompatibility.status !== "unsupported",
136
+ severity: bootstrapCompatibility.status === "supported"
137
+ ? "ok"
138
+ : bootstrapCompatibility.status === "legacy"
139
+ ? "warn"
140
+ : bootstrapCompatibility.status === "missing"
141
+ ? "warn"
142
+ : "error",
143
+ detail: bootstrapCompatibility.status === "supported"
144
+ ? `bootstrap contract ${bootstrapCompatibility.contractId} version ${bootstrapCompatibility.contractVersion} is supported`
145
+ : bootstrapCompatibility.status === "legacy"
146
+ ? "bootstrap.yaml was created by nimicoding but is missing bootstrap contract metadata"
147
+ : bootstrapCompatibility.status === "missing"
148
+ ? "bootstrap.yaml is missing and bootstrap contract compatibility could not be checked"
149
+ : "bootstrap.yaml declares an unsupported bootstrap contract id or version",
150
+ });
151
+
152
+ const bootstrapStateText = await readTextIfFile(path.join(projectRoot, ".nimi", "spec", "bootstrap-state.yaml"));
153
+ const bootstrapStateContract = inspectBootstrapStateContract(bootstrapStateText);
154
+ checks.push(
155
+ buildCheck(
156
+ "bootstrap_state_contract",
157
+ bootstrapStateContract.ok,
158
+ bootstrapStateContract.ok
159
+ ? `bootstrap-state.yaml matches the ${bootstrapStateContract.treeState} tree-state contract`
160
+ : "bootstrap-state.yaml is missing required lifecycle fields or declares an unsupported lifecycle state",
161
+ ),
162
+ );
163
+
164
+ const productScopeText = await readTextIfFile(path.join(projectRoot, ".nimi", "spec", "product-scope.yaml"));
165
+ const completionTruth = inspectStandaloneCompletionTruth(productScopeText);
166
+ checks.push(
167
+ buildCheck(
168
+ "standalone_completion_truth",
169
+ completionTruth.ok,
170
+ completionTruth.ok
171
+ ? `Product scope declares standalone completion profile ${completionTruth.completionProfile}`
172
+ : "product-scope.yaml is missing or drifted from the package-owned standalone completion truth",
173
+ ),
174
+ );
175
+
176
+ const specTreeModel = await loadSpecTreeModelContract(projectRoot);
177
+ checks.push(
178
+ buildCheck(
179
+ "spec_tree_model_contract",
180
+ specTreeModel.ok,
181
+ specTreeModel.ok
182
+ ? `${SPEC_TREE_MODEL_REF} declares canonical root ${specTreeModel.canonicalRoot} with profile ${specTreeModel.profile}`
183
+ : `${SPEC_TREE_MODEL_REF} is missing or malformed`,
184
+ ),
185
+ );
186
+
187
+ const specGenerationInputsContract = await loadSpecGenerationInputsContract(projectRoot);
188
+ checks.push(
189
+ buildCheck(
190
+ "spec_generation_inputs_contract",
191
+ specGenerationInputsContract.ok,
192
+ specGenerationInputsContract.ok
193
+ ? `${SPEC_GENERATION_INPUTS_CONTRACT_REF} is present and structurally valid`
194
+ : `${SPEC_GENERATION_INPUTS_CONTRACT_REF} is missing or malformed`,
195
+ ),
196
+ );
197
+
198
+ const specGenerationAuditContract = await loadSpecGenerationAuditContract(projectRoot);
199
+ checks.push(
200
+ buildCheck(
201
+ "spec_generation_audit_contract",
202
+ specGenerationAuditContract.ok,
203
+ specGenerationAuditContract.ok
204
+ ? `${SPEC_GENERATION_AUDIT_CONTRACT_REF} is present and structurally valid`
205
+ : `${SPEC_GENERATION_AUDIT_CONTRACT_REF} is missing or malformed`,
206
+ ),
207
+ );
208
+
209
+ const specGenerationInputs = await loadSpecGenerationInputsConfig(projectRoot);
210
+ checks.push(
211
+ buildCheck(
212
+ "spec_generation_inputs_config",
213
+ specGenerationInputs.ok,
214
+ specGenerationInputs.ok
215
+ ? `${SPEC_GENERATION_INPUTS_REF} declares mixed canonical spec generation inputs`
216
+ : `${SPEC_GENERATION_INPUTS_REF} is missing or malformed`,
217
+ ),
218
+ );
219
+
220
+ const commandGatingMatrix = await loadCommandGatingMatrix(projectRoot);
221
+ checks.push(
222
+ buildCheck(
223
+ "command_gating_matrix_contract",
224
+ commandGatingMatrix.ok,
225
+ commandGatingMatrix.ok
226
+ ? `${COMMAND_GATING_MATRIX_REF} declares ${commandGatingMatrix.entries.length} command gating rules`
227
+ : `${COMMAND_GATING_MATRIX_REF} is missing or malformed`,
228
+ ),
229
+ );
230
+
231
+ const blueprintReference = await loadBlueprintReference(projectRoot);
232
+ const blueprintReferenceExpected = bootstrapStateContract.blueprintMode !== "none";
233
+ const blueprintReferenceAligned = !blueprintReferenceExpected
234
+ ? !blueprintReference.present
235
+ : blueprintReference.present
236
+ && blueprintReference.ok
237
+ && blueprintReference.mode === bootstrapStateContract.blueprintMode
238
+ && blueprintReference.canonicalTargetRoot === specTreeModel.canonicalRoot;
239
+ checks.push(
240
+ buildCheck(
241
+ "blueprint_reference_contract",
242
+ blueprintReferenceAligned,
243
+ blueprintReferenceExpected
244
+ ? blueprintReferenceAligned
245
+ ? `${BLUEPRINT_REFERENCE_REF} matches blueprint mode ${bootstrapStateContract.blueprintMode}`
246
+ : `${BLUEPRINT_REFERENCE_REF} is required and must match the declared blueprint mode`
247
+ : "No explicit project-local blueprint reference is declared",
248
+ blueprintReferenceExpected
249
+ ? (blueprintReferenceAligned ? "ok" : "error")
250
+ : "info",
251
+ ),
252
+ );
253
+
254
+ const benchmarkRoot = specGenerationInputs.benchmarkBlueprintRoot ?? blueprintReference.root ?? null;
255
+ const benchmarkAvailable = typeof benchmarkRoot === "string" && benchmarkRoot.length > 0;
256
+ const benchmarkAuditReadiness = {
257
+ available: benchmarkAvailable,
258
+ ready: benchmarkAvailable && Boolean(
259
+ specGenerationInputs.ok
260
+ && specTreeModel.ok
261
+ && (
262
+ specGenerationInputs.benchmarkMode === "none"
263
+ ? !blueprintReference.present
264
+ : blueprintReference.present
265
+ && blueprintReference.ok
266
+ && blueprintReference.root === benchmarkRoot
267
+ ),
268
+ ),
269
+ benchmarkRoot,
270
+ acceptanceMode: specGenerationInputs.acceptanceMode ?? null,
271
+ reason: typeof benchmarkRoot === "string" && benchmarkRoot.length > 0
272
+ ? "Benchmark audit can compare the declared blueprint root against the candidate canonical tree"
273
+ : "No benchmark blueprint is declared for this project.",
274
+ };
275
+ checks.push({
276
+ id: "benchmark_audit_readiness",
277
+ ok: !benchmarkAuditReadiness.available || benchmarkAuditReadiness.ready,
278
+ severity: benchmarkAuditReadiness.available
279
+ ? benchmarkAuditReadiness.ready ? "ok" : "warn"
280
+ : "info",
281
+ detail: benchmarkAuditReadiness.reason,
282
+ });
283
+
284
+ const canonicalTree = await inspectCanonicalTree(projectRoot, specTreeModel);
285
+ checks.push({
286
+ id: "canonical_tree_progress",
287
+ ok: true,
288
+ severity: canonicalTree.requiredFilesValid
289
+ ? "ok"
290
+ : bootstrapStateContract.treeState === "bootstrap_only"
291
+ ? "info"
292
+ : "warn",
293
+ detail: canonicalTree.requiredFilesValid
294
+ ? "Declared canonical tree required files are present"
295
+ : `Canonical tree required files are still missing: ${canonicalTree.missing.join(", ")}`,
296
+ });
297
+
298
+ const specGenerationAuditReport = await validateSpecAudit(
299
+ path.join(projectRoot, SPEC_GENERATION_AUDIT_REF),
300
+ { projectRoot },
301
+ );
302
+ const specGenerationAudit = specGenerationAuditReport.refusal?.code === "SPEC_AUDIT_MISSING"
303
+ ? emptySpecGenerationAudit()
304
+ : {
305
+ present: true,
306
+ ok: specGenerationAuditReport.ok,
307
+ auditPath: SPEC_GENERATION_AUDIT_REF,
308
+ validator: "validate-spec-audit",
309
+ summary: specGenerationAuditReport.summary ?? null,
310
+ reason: specGenerationAuditReport.ok
311
+ ? "Spec generation audit is present and structurally valid"
312
+ : specGenerationAuditReport.errors.join("; "),
313
+ };
314
+ const specGenerationAuditCheckSeverity = !specGenerationAudit.present
315
+ ? canonicalTree.requiredFilesValid ? "error" : "info"
316
+ : specGenerationAudit.ok ? "ok" : canonicalTree.requiredFilesValid ? "error" : "warn";
317
+ checks.push({
318
+ id: "spec_generation_audit",
319
+ ok: !specGenerationAudit.present ? !canonicalTree.requiredFilesValid : specGenerationAudit.ok,
320
+ severity: specGenerationAuditCheckSeverity,
321
+ detail: !specGenerationAudit.present
322
+ ? canonicalTree.requiredFilesValid
323
+ ? "Canonical tree is ready but spec generation audit is still missing or invalid"
324
+ : specGenerationAudit.reason
325
+ : specGenerationAudit.ok
326
+ ? specGenerationAudit.reason
327
+ : `Canonical tree is ready but spec generation audit is still missing or invalid: ${specGenerationAudit.reason}`,
328
+ });
329
+
330
+ const highRiskCloseoutGate = findCommandGatingRule(commandGatingMatrix, "closeout", "high_risk_execution");
331
+ checks.push(
332
+ buildCheck(
333
+ "high_risk_closeout_gate",
334
+ Boolean(highRiskCloseoutGate?.completedRequires?.tree_state === "canonical_tree_ready"),
335
+ highRiskCloseoutGate
336
+ ? "Command gating matrix includes high_risk_execution closeout readiness"
337
+ : "command gating matrix is missing closeout gating for high_risk_execution",
338
+ ),
339
+ );
340
+
341
+ return {
342
+ done: false,
343
+ projectRoot,
344
+ checks,
345
+ bootstrapCompatibility,
346
+ bootstrapStateContract,
347
+ completionTruth,
348
+ specTreeModel,
349
+ specGenerationInputsContract,
350
+ specGenerationAuditContract,
351
+ specGenerationInputs,
352
+ commandGatingMatrix,
353
+ blueprintReference,
354
+ blueprintReferenceAligned,
355
+ canonicalTree,
356
+ benchmarkAuditReadiness,
357
+ specGenerationAudit,
358
+ };
359
+ }
@@ -0,0 +1,256 @@
1
+ import path from "node:path";
2
+
3
+ import {
4
+ AUDIT_SWEEP_RESULT_CONTRACT_REF,
5
+ DOC_SPEC_AUDIT_RESULT_CONTRACT_REF,
6
+ EXTERNAL_EXECUTION_ARTIFACTS_CONFIG_REF,
7
+ EXTERNAL_HOST_COMPATIBILITY_CONTRACT_REF,
8
+ HIGH_RISK_EXECUTION_RESULT_CONTRACT_REF,
9
+ HOST_ADAPTER_CONFIG_REF,
10
+ SPEC_RECONSTRUCTION_RESULT_CONTRACT_REF,
11
+ } from "../../constants.mjs";
12
+ import { readTextIfFile, pathExists } from "../fs-helpers.mjs";
13
+ import { arraysEqual } from "../value-helpers.mjs";
14
+ import {
15
+ parseSkillSection,
16
+ readYamlList,
17
+ readYamlScalar,
18
+ } from "../yaml-helpers.mjs";
19
+ import { buildCheck } from "./doctor-state.mjs";
20
+
21
+ export async function inspectDoctorDelegatedSurface(projectRoot) {
22
+ const checks = [];
23
+
24
+ const skillsConfigText = await readTextIfFile(path.join(projectRoot, ".nimi", "config", "skills.yaml"));
25
+ const skillsConfigOk = Boolean(skillsConfigText)
26
+ && skillsConfigText.includes("runtime_installed: false")
27
+ && skillsConfigText.includes("runtime_owner: external_ai_host")
28
+ && skillsConfigText.includes("handoff_contract: .nimi/methodology/skill-handoff.yaml")
29
+ && skillsConfigText.includes(`canonical_host_adapter: ${HOST_ADAPTER_CONFIG_REF}`);
30
+ checks.push(
31
+ buildCheck(
32
+ "skills_contract",
33
+ skillsConfigOk,
34
+ skillsConfigOk
35
+ ? "skills.yaml keeps runtime delegated and handoff-driven"
36
+ : "skills.yaml is missing delegated runtime contract fields",
37
+ ),
38
+ );
39
+
40
+ const handoffText = await readTextIfFile(path.join(projectRoot, ".nimi", "methodology", "skill-handoff.yaml"));
41
+ const manifestText = await readTextIfFile(path.join(projectRoot, ".nimi", "config", "skill-manifest.yaml"));
42
+ const hostProfileText = await readTextIfFile(path.join(projectRoot, ".nimi", "config", "host-profile.yaml"));
43
+ const hostAdapterText = await readTextIfFile(path.join(projectRoot, HOST_ADAPTER_CONFIG_REF));
44
+ const runtimeContractText = await readTextIfFile(path.join(projectRoot, ".nimi", "methodology", "skill-runtime.yaml"));
45
+ const installerText = await readTextIfFile(path.join(projectRoot, ".nimi", "config", "skill-installer.yaml"));
46
+ const installerResultText = await readTextIfFile(path.join(projectRoot, ".nimi", "methodology", "skill-installer-result.yaml"));
47
+
48
+ const contractRuntimeOwnerValues = [
49
+ readYamlScalar(skillsConfigText, "runtime_owner"),
50
+ readYamlScalar(manifestText, "runtime_owner"),
51
+ readYamlScalar(handoffText, "runtime_owner"),
52
+ readYamlScalar(runtimeContractText, "runtime_owner"),
53
+ readYamlScalar(hostProfileText, "id"),
54
+ readYamlScalar(installerText, "installer_owner"),
55
+ ];
56
+ const contractRuntimeOwnersAligned = contractRuntimeOwnerValues.every((value) => value === "external_ai_host");
57
+
58
+ const delegatedModeAligned = [
59
+ readYamlScalar(manifestText, "execution_mode"),
60
+ readYamlScalar(hostProfileText, "execution_mode"),
61
+ readYamlScalar(runtimeContractText, "runtime_mode"),
62
+ readYamlScalar(installerText, "installer_mode"),
63
+ ].every((value) => value === "delegated");
64
+
65
+ const selfHostedAligned = [
66
+ readYamlScalar(handoffText, "self_hosted_runtime"),
67
+ readYamlScalar(hostProfileText, "self_hosted"),
68
+ readYamlScalar(runtimeContractText, "self_hosted"),
69
+ readYamlScalar(installerText, "self_hosted"),
70
+ ].every((value) => value === "false");
71
+
72
+ checks.push(
73
+ buildCheck(
74
+ "delegated_contract_posture",
75
+ contractRuntimeOwnersAligned && delegatedModeAligned && selfHostedAligned,
76
+ contractRuntimeOwnersAligned && delegatedModeAligned && selfHostedAligned
77
+ ? "Delegated runtime ownership and non-self-hosted posture are consistent across contracts"
78
+ : "Delegated runtime ownership, execution mode, or self-hosted posture drifted across contracts",
79
+ ),
80
+ );
81
+
82
+ const referenceChecks = [
83
+ readYamlScalar(manifestText, "runtime_contract_ref") === ".nimi/methodology/skill-runtime.yaml",
84
+ readYamlScalar(manifestText, "host_profile_ref") === ".nimi/config/host-profile.yaml",
85
+ readYamlScalar(manifestText, "installer_ref") === ".nimi/config/skill-installer.yaml",
86
+ readYamlScalar(manifestText, "installer_result_contract_ref") === ".nimi/methodology/skill-installer-result.yaml",
87
+ readYamlScalar(hostProfileText, "runtime_contract_ref") === ".nimi/methodology/skill-runtime.yaml",
88
+ readYamlScalar(hostProfileText, "compatibility_contract_ref") === EXTERNAL_HOST_COMPATIBILITY_CONTRACT_REF,
89
+ readYamlScalar(hostProfileText, "installer_ref") === ".nimi/config/skill-installer.yaml",
90
+ readYamlScalar(hostProfileText, "installer_result_contract_ref") === ".nimi/methodology/skill-installer-result.yaml",
91
+ readYamlScalar(hostAdapterText, "runtime_owner") === "external_ai_host",
92
+ readYamlScalar(hostAdapterText, "host_profile_ref") === ".nimi/config/host-profile.yaml",
93
+ readYamlScalar(hostAdapterText, "manifest_ref") === ".nimi/config/skill-manifest.yaml",
94
+ readYamlScalar(hostAdapterText, "artifact_contract_ref") === EXTERNAL_EXECUTION_ARTIFACTS_CONFIG_REF,
95
+ readYamlScalar(hostAdapterText, "handoff_ref") === ".nimi/methodology/skill-handoff.yaml",
96
+ readYamlScalar(runtimeContractText, "manifest_ref") === ".nimi/config/skill-manifest.yaml",
97
+ readYamlScalar(runtimeContractText, "host_profile_ref") === ".nimi/config/host-profile.yaml",
98
+ readYamlScalar(runtimeContractText, "installer_ref") === ".nimi/config/skill-installer.yaml",
99
+ readYamlScalar(runtimeContractText, "installer_result_contract_ref") === ".nimi/methodology/skill-installer-result.yaml",
100
+ readYamlScalar(runtimeContractText, "handoff_ref") === ".nimi/methodology/skill-handoff.yaml",
101
+ readYamlScalar(installerText, "manifest_ref") === ".nimi/config/skill-manifest.yaml",
102
+ readYamlScalar(installerText, "runtime_contract_ref") === ".nimi/methodology/skill-runtime.yaml",
103
+ readYamlScalar(installerText, "host_profile_ref") === ".nimi/config/host-profile.yaml",
104
+ readYamlScalar(installerText, "result_contract_ref") === ".nimi/methodology/skill-installer-result.yaml",
105
+ readYamlScalar(installerResultText, "installer_ref") === ".nimi/config/skill-installer.yaml",
106
+ readYamlScalar(handoffText, "runtime_contract_ref") === ".nimi/methodology/skill-runtime.yaml",
107
+ readYamlScalar(handoffText, "host_profile_ref") === ".nimi/config/host-profile.yaml",
108
+ readYamlScalar(handoffText, "host_compatibility_contract_ref") === EXTERNAL_HOST_COMPATIBILITY_CONTRACT_REF,
109
+ readYamlScalar(handoffText, "installer_ref") === ".nimi/config/skill-installer.yaml",
110
+ readYamlScalar(handoffText, "installer_result_contract_ref") === ".nimi/methodology/skill-installer-result.yaml",
111
+ readYamlScalar(handoffText, "exchange_projection_contract_ref") === ".nimi/methodology/skill-exchange-projection.yaml",
112
+ ];
113
+ checks.push(
114
+ buildCheck(
115
+ "contract_reference_alignment",
116
+ referenceChecks.every(Boolean),
117
+ referenceChecks.every(Boolean)
118
+ ? "Manifest, runtime, installer, host-profile, host-adapter, and handoff references are aligned"
119
+ : "Cross-contract reference drift detected between manifest/runtime/installer/host-profile/host-adapter/handoff truth",
120
+ ),
121
+ );
122
+
123
+ const manifestSkills = parseSkillSection(manifestText, "skills");
124
+ const expectedSkills = parseSkillSection(skillsConfigText, "expected_skill_surfaces");
125
+
126
+ const reconstructionSkill = manifestSkills.find((skill) => skill.id === "spec_reconstruction") ?? null;
127
+ const docAuditSkill = manifestSkills.find((skill) => skill.id === "doc_spec_audit") ?? null;
128
+ const auditSweepSkill = manifestSkills.find((skill) => skill.id === "audit_sweep") ?? null;
129
+ const highRiskSkill = manifestSkills.find((skill) => skill.id === "high_risk_execution") ?? null;
130
+ const resultContractAlignment = reconstructionSkill?.result_contract_ref === SPEC_RECONSTRUCTION_RESULT_CONTRACT_REF
131
+ && docAuditSkill?.result_contract_ref === DOC_SPEC_AUDIT_RESULT_CONTRACT_REF
132
+ && auditSweepSkill?.result_contract_ref === AUDIT_SWEEP_RESULT_CONTRACT_REF
133
+ && highRiskSkill?.result_contract_ref === HIGH_RISK_EXECUTION_RESULT_CONTRACT_REF;
134
+ checks.push(
135
+ buildCheck(
136
+ "skill_result_contract_alignment",
137
+ resultContractAlignment,
138
+ resultContractAlignment
139
+ ? "Skill manifest result contract refs align with the declared machine contracts"
140
+ : "Skill manifest result contract refs drifted away from the declared machine contracts",
141
+ ),
142
+ );
143
+
144
+ const hostRequiredContext = readYamlList(hostProfileText, "required_context");
145
+ const handoffRequiredContext = readYamlList(handoffText, "required_context_order");
146
+ const missingHandoffContextEntries = hostRequiredContext.filter((entry) => !handoffRequiredContext.includes(entry));
147
+ const missingHandoffPaths = [];
148
+ for (const relativePath of handoffRequiredContext) {
149
+ const info = await pathExists(path.join(projectRoot, relativePath));
150
+ if (!info) {
151
+ missingHandoffPaths.push(relativePath);
152
+ }
153
+ }
154
+ const handoffContextOk = missingHandoffContextEntries.length === 0 && missingHandoffPaths.length === 0;
155
+ checks.push(
156
+ buildCheck(
157
+ "handoff_context_contract",
158
+ handoffContextOk,
159
+ handoffContextOk
160
+ ? "Handoff context order contains the declared host context and all listed paths exist"
161
+ : [
162
+ missingHandoffContextEntries.length > 0
163
+ ? `handoff context is missing host-required entries: ${missingHandoffContextEntries.join(", ")}`
164
+ : null,
165
+ missingHandoffPaths.length > 0
166
+ ? `handoff context paths are missing on disk: ${missingHandoffPaths.join(", ")}`
167
+ : null,
168
+ ].filter(Boolean).join("; "),
169
+ ),
170
+ );
171
+
172
+ const manifestSkillIds = manifestSkills.map((skill) => skill.id);
173
+ const expectedSkillIds = expectedSkills.map((skill) => skill.id);
174
+ const skillSurfaceAligned = arraysEqual(manifestSkillIds, expectedSkillIds);
175
+ checks.push(
176
+ buildCheck(
177
+ "skill_surface_alignment",
178
+ skillSurfaceAligned,
179
+ skillSurfaceAligned
180
+ ? "Manifest skills align with the expected skill surfaces declared in skills.yaml"
181
+ : "Manifest skills and expected skill surfaces drifted out of alignment",
182
+ ),
183
+ );
184
+
185
+ const admittedAdapterIds = readYamlList(hostAdapterText, "admitted_adapter_ids");
186
+ const selectedAdapterId = readYamlScalar(hostAdapterText, "selected_adapter_id");
187
+ const adapterSelected = selectedAdapterId !== null && selectedAdapterId !== "none";
188
+ const adapterSelectionValid = selectedAdapterId !== null
189
+ && (!adapterSelected || admittedAdapterIds.includes(selectedAdapterId));
190
+ checks.push(
191
+ buildCheck(
192
+ "host_adapter_contract",
193
+ adapterSelectionValid,
194
+ adapterSelectionValid
195
+ ? adapterSelected
196
+ ? `Host adapter ${selectedAdapterId} is selected and admitted`
197
+ : "No host adapter selected; vendor-neutral delegated host posture remains active"
198
+ : "host-adapter selected_adapter_id must be none or one of admitted_adapter_ids",
199
+ ),
200
+ );
201
+
202
+ const adapterBoundaryAligned = readYamlScalar(hostAdapterText, "semantic_review_owner") === "nimicoding_manager"
203
+ && readYamlScalar(hostAdapterText, "handoff_mode") === "prompt_output_evidence_handoff"
204
+ && readYamlScalar(hostAdapterText, "evidence_mode") === "candidate_only";
205
+ checks.push(
206
+ buildCheck(
207
+ "host_adapter_boundary",
208
+ adapterBoundaryAligned,
209
+ adapterBoundaryAligned
210
+ ? "Host adapter boundary keeps semantic review in nimicoding and limits handoff to prompt/output/evidence"
211
+ : "host-adapter boundary drifted away from prompt/output/evidence-only handoff with nimicoding semantic review ownership",
212
+ ),
213
+ );
214
+
215
+ const delegatedContracts = {
216
+ runtimeOwner: readYamlScalar(handoffText, "runtime_owner"),
217
+ executionMode: readYamlScalar(runtimeContractText, "runtime_mode"),
218
+ installerMode: readYamlScalar(installerText, "installer_mode"),
219
+ selfHostedRuntime: readYamlScalar(handoffText, "self_hosted_runtime") === "true",
220
+ triggerMode: readYamlScalar(handoffText, "trigger_mode"),
221
+ expectedSkillIds: manifestSkillIds,
222
+ selectedAdapterId,
223
+ admittedAdapterIds,
224
+ adapterHandoffMode: readYamlScalar(hostAdapterText, "handoff_mode"),
225
+ semanticReviewOwner: readYamlScalar(hostAdapterText, "semantic_review_owner"),
226
+ };
227
+
228
+ return {
229
+ checks,
230
+ skillsConfigText,
231
+ runtimeInstalled: Boolean(skillsConfigText) && skillsConfigText.includes("runtime_installed: true"),
232
+ handoffText,
233
+ manifestText,
234
+ hostProfileText,
235
+ hostAdapterText,
236
+ runtimeContractText,
237
+ installerText,
238
+ installerResultText,
239
+ contractRuntimeOwnersAligned,
240
+ delegatedModeAligned,
241
+ selfHostedAligned,
242
+ referenceChecks,
243
+ resultContractAlignment,
244
+ handoffContextOk,
245
+ handoffRequiredContext,
246
+ missingHandoffContextEntries,
247
+ missingHandoffPaths,
248
+ manifestSkillIds,
249
+ selectedAdapterId,
250
+ admittedAdapterIds,
251
+ adapterSelected,
252
+ adapterSelectionValid,
253
+ adapterBoundaryAligned,
254
+ delegatedContracts,
255
+ };
256
+ }