@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.
- package/LICENSE +21 -0
- package/README.md +348 -0
- package/adapters/README.md +25 -0
- package/adapters/claude/README.md +89 -0
- package/adapters/claude/profile.yaml +70 -0
- package/adapters/codex/README.md +53 -0
- package/adapters/codex/profile.yaml +78 -0
- package/adapters/oh-my-codex/README.md +185 -0
- package/adapters/oh-my-codex/profile.yaml +46 -0
- package/bin/nimicoding.mjs +6 -0
- package/cli/commands/admit-high-risk-decision.mjs +108 -0
- package/cli/commands/audit-sweep.mjs +341 -0
- package/cli/commands/blueprint-audit.mjs +91 -0
- package/cli/commands/clear.mjs +168 -0
- package/cli/commands/closeout.mjs +183 -0
- package/cli/commands/decide-high-risk-execution.mjs +124 -0
- package/cli/commands/doctor.mjs +53 -0
- package/cli/commands/generate-spec-derived-docs.mjs +131 -0
- package/cli/commands/handoff.mjs +123 -0
- package/cli/commands/ingest-high-risk-execution.mjs +95 -0
- package/cli/commands/review-high-risk-execution.mjs +95 -0
- package/cli/commands/start.mjs +717 -0
- package/cli/commands/topic-formatters.mjs +382 -0
- package/cli/commands/topic-goal.mjs +33 -0
- package/cli/commands/topic-options-shared.mjs +27 -0
- package/cli/commands/topic-options-workflow.mjs +767 -0
- package/cli/commands/topic-options.mjs +626 -0
- package/cli/commands/topic-runner.mjs +169 -0
- package/cli/commands/topic.mjs +795 -0
- package/cli/commands/validate-acceptance.mjs +5 -0
- package/cli/commands/validate-ai-governance.mjs +214 -0
- package/cli/commands/validate-execution-packet.mjs +5 -0
- package/cli/commands/validate-orchestration-state.mjs +5 -0
- package/cli/commands/validate-prompt.mjs +5 -0
- package/cli/commands/validate-spec-audit.mjs +27 -0
- package/cli/commands/validate-spec-governance.mjs +124 -0
- package/cli/commands/validate-spec-tree.mjs +27 -0
- package/cli/commands/validate-worker-output.mjs +5 -0
- package/cli/constants.mjs +489 -0
- package/cli/help.mjs +134 -0
- package/cli/index.mjs +103 -0
- package/cli/lib/adapter-profiles.mjs +403 -0
- package/cli/lib/audit-execution.mjs +52 -0
- package/cli/lib/audit-sweep-runtime/admissions.mjs +381 -0
- package/cli/lib/audit-sweep-runtime/audit-validity.mjs +333 -0
- package/cli/lib/audit-sweep-runtime/chunks.mjs +697 -0
- package/cli/lib/audit-sweep-runtime/closeout.mjs +144 -0
- package/cli/lib/audit-sweep-runtime/codex-auditor-evidence.mjs +639 -0
- package/cli/lib/audit-sweep-runtime/codex-auditor.mjs +515 -0
- package/cli/lib/audit-sweep-runtime/common.mjs +329 -0
- package/cli/lib/audit-sweep-runtime/coverage-quality.mjs +172 -0
- package/cli/lib/audit-sweep-runtime/evidence-assignment.mjs +152 -0
- package/cli/lib/audit-sweep-runtime/format.mjs +57 -0
- package/cli/lib/audit-sweep-runtime/ingest.mjs +486 -0
- package/cli/lib/audit-sweep-runtime/inventory-spec-chunks.mjs +198 -0
- package/cli/lib/audit-sweep-runtime/inventory.mjs +728 -0
- package/cli/lib/audit-sweep-runtime/ledger.mjs +315 -0
- package/cli/lib/audit-sweep-runtime/p0p1-profile.mjs +101 -0
- package/cli/lib/audit-sweep-runtime/remediation.mjs +349 -0
- package/cli/lib/audit-sweep-runtime/rerun.mjs +129 -0
- package/cli/lib/audit-sweep-runtime/risk-budget.mjs +300 -0
- package/cli/lib/audit-sweep-runtime/status.mjs +62 -0
- package/cli/lib/audit-sweep-runtime/validators-ledger.mjs +215 -0
- package/cli/lib/audit-sweep-runtime/validators.mjs +758 -0
- package/cli/lib/audit-sweep.mjs +18 -0
- package/cli/lib/authority-convergence.mjs +309 -0
- package/cli/lib/blueprint-audit.mjs +370 -0
- package/cli/lib/bootstrap.mjs +228 -0
- package/cli/lib/closeout.mjs +623 -0
- package/cli/lib/codex-sdk-runner.mjs +76 -0
- package/cli/lib/contracts.mjs +180 -0
- package/cli/lib/doctor.mjs +18 -0
- package/cli/lib/entrypoints.mjs +274 -0
- package/cli/lib/external-execution.mjs +101 -0
- package/cli/lib/fs-helpers.mjs +33 -0
- package/cli/lib/handoff.mjs +785 -0
- package/cli/lib/high-risk-admission.mjs +442 -0
- package/cli/lib/high-risk-decision.mjs +324 -0
- package/cli/lib/high-risk-ingest.mjs +317 -0
- package/cli/lib/high-risk-review.mjs +263 -0
- package/cli/lib/internal/contracts-loaders.mjs +132 -0
- package/cli/lib/internal/contracts-parse-high-risk.mjs +131 -0
- package/cli/lib/internal/contracts-parse.mjs +457 -0
- package/cli/lib/internal/contracts-validators.mjs +398 -0
- package/cli/lib/internal/doctor-bootstrap-surface.mjs +359 -0
- package/cli/lib/internal/doctor-delegated-surface.mjs +256 -0
- package/cli/lib/internal/doctor-finalize.mjs +385 -0
- package/cli/lib/internal/doctor-format.mjs +286 -0
- package/cli/lib/internal/doctor-inspectors.mjs +294 -0
- package/cli/lib/internal/doctor-state.mjs +205 -0
- package/cli/lib/internal/governance/ai/ai-context-budget-core.mjs +315 -0
- package/cli/lib/internal/governance/ai/ai-structure-budget-core.mjs +358 -0
- package/cli/lib/internal/governance/ai/check-agents-freshness.mjs +155 -0
- package/cli/lib/internal/governance/ai/check-high-risk-doc-metadata-core.mjs +173 -0
- package/cli/lib/internal/governance/config.mjs +150 -0
- package/cli/lib/internal/governance/runner.mjs +35 -0
- package/cli/lib/internal/governance/shared/read-yaml-with-fragments.mjs +49 -0
- package/cli/lib/internal/validators-artifacts.mjs +515 -0
- package/cli/lib/internal/validators-shared.mjs +28 -0
- package/cli/lib/internal/validators-spec-helpers.mjs +186 -0
- package/cli/lib/internal/validators-spec.mjs +410 -0
- package/cli/lib/shared.mjs +83 -0
- package/cli/lib/topic-draft-packets.mjs +48 -0
- package/cli/lib/topic-goal.mjs +361 -0
- package/cli/lib/topic-runner.mjs +772 -0
- package/cli/lib/topic.mjs +93 -0
- package/cli/lib/ui.mjs +178 -0
- package/cli/lib/validators.mjs +78 -0
- package/cli/lib/value-helpers.mjs +24 -0
- package/cli/lib/yaml-helpers.mjs +133 -0
- package/cli/nimicoding.mjs +1 -0
- package/cli/seeds/bootstrap.mjs +47 -0
- package/config/audit-execution-artifacts.yaml +20 -0
- package/config/bootstrap.yaml +6 -0
- package/config/external-execution-artifacts.yaml +16 -0
- package/config/host-adapter.yaml +30 -0
- package/config/host-profile.yaml +29 -0
- package/config/installer-evidence.yaml +31 -0
- package/config/skill-installer.yaml +23 -0
- package/config/skill-manifest.yaml +46 -0
- package/config/skills.yaml +30 -0
- package/config/spec-generation-inputs.yaml +25 -0
- package/contracts/acceptance.schema.yaml +16 -0
- package/contracts/admission-checklist.schema.yaml +15 -0
- package/contracts/audit-chunk.schema.yaml +110 -0
- package/contracts/audit-closeout.schema.yaml +51 -0
- package/contracts/audit-finding.schema.yaml +61 -0
- package/contracts/audit-ledger.schema.yaml +138 -0
- package/contracts/audit-plan.schema.yaml +123 -0
- package/contracts/audit-remediation-map.schema.yaml +51 -0
- package/contracts/audit-rerun.schema.yaml +31 -0
- package/contracts/audit-sweep-result.yaml +49 -0
- package/contracts/authority-convergence-audit.schema.yaml +19 -0
- package/contracts/closeout.schema.yaml +25 -0
- package/contracts/decision-review.schema.yaml +16 -0
- package/contracts/doc-spec-audit-result.yaml +19 -0
- package/contracts/execution-packet.schema.yaml +49 -0
- package/contracts/external-host-compatibility.yaml +22 -0
- package/contracts/forbidden-shortcuts.catalog.yaml +23 -0
- package/contracts/high-risk-admission.schema.yaml +23 -0
- package/contracts/high-risk-execution-result.yaml +20 -0
- package/contracts/orchestration-state.schema.yaml +41 -0
- package/contracts/overflow-continuation.schema.yaml +12 -0
- package/contracts/packet.schema.yaml +30 -0
- package/contracts/pending-note.schema.yaml +17 -0
- package/contracts/prompt.schema.yaml +12 -0
- package/contracts/remediation.schema.yaml +16 -0
- package/contracts/result.schema.yaml +24 -0
- package/contracts/spec-generation-audit.schema.yaml +31 -0
- package/contracts/spec-generation-inputs.schema.yaml +39 -0
- package/contracts/spec-reconstruction-result.yaml +37 -0
- package/contracts/topic-goal.schema.yaml +78 -0
- package/contracts/topic-run-ledger.schema.yaml +72 -0
- package/contracts/topic-step-decision.schema.yaml +45 -0
- package/contracts/topic.schema.yaml +65 -0
- package/contracts/true-close.schema.yaml +15 -0
- package/contracts/wave.schema.yaml +29 -0
- package/contracts/worker-output.schema.yaml +15 -0
- package/methodology/audit-sweep-p0p1-recall.yaml +45 -0
- package/methodology/authority-convergence-policy.yaml +42 -0
- package/methodology/core.yaml +25 -0
- package/methodology/four-closure-policy.yaml +28 -0
- package/methodology/overflow-continuation-policy.yaml +14 -0
- package/methodology/role-separation-policy.yaml +28 -0
- package/methodology/skill-exchange-projection.yaml +114 -0
- package/methodology/skill-handoff.yaml +34 -0
- package/methodology/skill-installer-result.yaml +27 -0
- package/methodology/skill-installer-summary-projection.yaml +181 -0
- package/methodology/skill-runtime.yaml +23 -0
- package/methodology/spec-reconstruction.yaml +63 -0
- package/methodology/spec-target-truth-profile.yaml +53 -0
- package/methodology/topic-lifecycle-report.yaml +144 -0
- package/methodology/topic-lifecycle.yaml +37 -0
- package/methodology/topic-naming-ontology.yaml +21 -0
- package/methodology/topic-ontology.yaml +38 -0
- package/methodology/topic-validation-policy.yaml +9 -0
- package/methodology/wave-dag-policy.yaml +14 -0
- package/package.json +50 -0
- package/spec/_meta/command-gating-matrix.yaml +110 -0
- package/spec/_meta/generate-drift-migration-checklist.yaml +155 -0
- package/spec/_meta/governance-routing-cutover-checklist.yaml +35 -0
- package/spec/_meta/phase2-impacted-surface-matrix.yaml +44 -0
- package/spec/_meta/spec-authority-cutover-readiness.yaml +104 -0
- package/spec/_meta/spec-tree-model.yaml +72 -0
- package/spec/bootstrap-state.yaml +99 -0
- 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
|
+
}
|