@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,286 @@
|
|
|
1
|
+
import {
|
|
2
|
+
localize,
|
|
3
|
+
styleHeading,
|
|
4
|
+
styleLabel,
|
|
5
|
+
styleMuted,
|
|
6
|
+
styleStatus,
|
|
7
|
+
} from "../ui.mjs";
|
|
8
|
+
import { emptyHostCompatibility } from "./doctor-state.mjs";
|
|
9
|
+
|
|
10
|
+
const DOCTOR_DETAIL_TRANSLATIONS = new Map([
|
|
11
|
+
[".nimi directory is missing", ".nimi 目录缺失"],
|
|
12
|
+
[".nimi exists but is not a directory", ".nimi 已存在但不是目录"],
|
|
13
|
+
[".nimi directory exists", ".nimi 目录存在"],
|
|
14
|
+
["Local state directories are present", "本地状态目录已存在"],
|
|
15
|
+
["Local nimicoding state is ignored by .gitignore", ".gitignore 已忽略本地 nimicoding 状态"],
|
|
16
|
+
["bootstrap.yaml declares the package bootstrap identity", "bootstrap.yaml 已声明包的 bootstrap 身份"],
|
|
17
|
+
["bootstrap contract nimicoding.bootstrap version 1 is supported", "bootstrap contract nimicoding.bootstrap version 1 受支持"],
|
|
18
|
+
["bootstrap-state.yaml matches the bootstrap_only tree-state contract", "bootstrap-state.yaml 符合 bootstrap_only tree-state contract"],
|
|
19
|
+
["bootstrap-state.yaml matches the canonical_tree_ready tree-state contract", "bootstrap-state.yaml 符合 canonical_tree_ready tree-state contract"],
|
|
20
|
+
["Product scope declares standalone completion profile boundary_complete", "product scope 声明了 standalone completion profile boundary_complete"],
|
|
21
|
+
[".nimi/spec/_meta/spec-tree-model.yaml declares canonical root .nimi/spec with profile minimal", ".nimi/spec/_meta/spec-tree-model.yaml 声明了 canonical root .nimi/spec,profile 为 minimal"],
|
|
22
|
+
[".nimi/spec/_meta/command-gating-matrix.yaml declares 12 command gating rules", ".nimi/spec/_meta/command-gating-matrix.yaml 声明了 12 条命令 gating 规则"],
|
|
23
|
+
[".nimi/contracts/spec-generation-inputs.schema.yaml is present and structurally valid", ".nimi/contracts/spec-generation-inputs.schema.yaml 已存在且结构有效"],
|
|
24
|
+
[".nimi/contracts/spec-generation-audit.schema.yaml is present and structurally valid", ".nimi/contracts/spec-generation-audit.schema.yaml 已存在且结构有效"],
|
|
25
|
+
[".nimi/config/spec-generation-inputs.yaml declares mixed canonical spec generation inputs", ".nimi/config/spec-generation-inputs.yaml 已声明 mixed canonical spec generation inputs"],
|
|
26
|
+
[".nimi/spec/_meta/blueprint-reference.yaml matches blueprint mode repo_spec_blueprint", ".nimi/spec/_meta/blueprint-reference.yaml 与 blueprint mode repo_spec_blueprint 保持一致"],
|
|
27
|
+
["Benchmark audit can compare the declared blueprint root against the candidate canonical tree", "benchmark audit 可以比较声明的 blueprint root 与候选 canonical tree"],
|
|
28
|
+
["Declared canonical tree required files are present", "声明的 canonical tree 必需文件已存在"],
|
|
29
|
+
["Spec generation audit is present and structurally valid", "spec generation audit 已存在且结构有效"],
|
|
30
|
+
["No spec generation audit detected yet; it will be required before completed reconstruction closeout", "尚未检测到 spec generation audit;在 completed reconstruction closeout 之前将要求该产物"],
|
|
31
|
+
["Canonical tree is ready but spec generation audit is still missing or invalid", "canonical tree 已就绪,但 spec generation audit 仍然缺失或无效"],
|
|
32
|
+
["Command gating matrix includes high_risk_execution closeout readiness", "命令 gating matrix 已包含 high_risk_execution 的 closeout 准入规则"],
|
|
33
|
+
["bootstrap.yaml was created by nimicoding but is missing bootstrap contract metadata", "bootstrap.yaml 由 nimicoding 创建,但缺少 bootstrap contract 元数据"],
|
|
34
|
+
["bootstrap.yaml is missing and bootstrap contract compatibility could not be checked", "bootstrap.yaml 缺失,无法检查 bootstrap contract 兼容性"],
|
|
35
|
+
["bootstrap.yaml declares an unsupported bootstrap contract id or version", "bootstrap.yaml 声明了不受支持的 bootstrap contract id 或 version"],
|
|
36
|
+
["skills.yaml keeps runtime delegated and handoff-driven", "skills.yaml 保持 runtime 为委托式且以 handoff 驱动"],
|
|
37
|
+
["Delegated runtime ownership and non-self-hosted posture are consistent across contracts", "各契约中的 delegated runtime ownership 与 non-self-hosted 姿态保持一致"],
|
|
38
|
+
["Manifest, runtime, installer, host-profile, host-adapter, and handoff references are aligned", "manifest、runtime、installer、host-profile、host-adapter 与 handoff 的引用保持一致"],
|
|
39
|
+
["Skill manifest result contract refs align with the declared machine contracts", "skill manifest 的 result contract 引用与声明的机器契约保持一致"],
|
|
40
|
+
["Handoff context order contains the declared host context and all listed paths exist", "handoff context 顺序包含声明的 host context,且所有列出的路径均存在"],
|
|
41
|
+
["Manifest skills align with the expected skill surfaces declared in skills.yaml", "manifest skills 与 skills.yaml 中声明的 expected skill surfaces 保持一致"],
|
|
42
|
+
["spec-reconstruction result contract is present and structurally valid", "spec-reconstruction 结果契约存在且结构有效"],
|
|
43
|
+
["doc-spec-audit result contract is present and structurally valid", "doc-spec-audit 结果契约存在且结构有效"],
|
|
44
|
+
["audit-sweep result contract is present and structurally valid", "audit-sweep 结果契约存在且结构有效"],
|
|
45
|
+
["Packaged external host compatibility contract is present and aligned", "包内 external host 兼容契约存在且一致"],
|
|
46
|
+
["audit execution artifact landing-path contract is present and structurally valid", "audit execution artifact landing-path 契约存在且结构有效"],
|
|
47
|
+
["high-risk-execution result contract is present and structurally valid", "high-risk-execution 结果契约存在且结构有效"],
|
|
48
|
+
["Packaged high-risk admission schema contract is present and aligned", "包内 high-risk admission schema 契约存在且一致"],
|
|
49
|
+
["external execution artifact landing-path contract is present and structurally valid", "external execution artifact landing-path 契约存在且结构有效"],
|
|
50
|
+
["High-risk execution schema seeds are present and structurally valid", "high-risk execution schema seed 已存在且结构有效"],
|
|
51
|
+
["Canonical high-risk admissions truth satisfies the packaged admission schema contract", "canonical high-risk admissions truth 满足包内 admission schema 契约"],
|
|
52
|
+
["No host adapter selected; vendor-neutral delegated host posture remains active", "未选择 host adapter;vendor-neutral 的 delegated host 姿态仍然生效"],
|
|
53
|
+
["Package-owned adapter profile overlays are present and valid: codex, oh_my_codex, claude", "包内 adapter profile overlay 已存在且有效:codex, oh_my_codex, claude"],
|
|
54
|
+
["Host adapter boundary keeps semantic review in nimicoding and limits handoff to prompt/output/evidence", "host adapter 边界保持 semantic review 在 nimicoding 内,并将 handoff 限制为 prompt/output/evidence"],
|
|
55
|
+
["bootstrap-state lifecycle bootstrap_only is aligned with the current canonical tree readiness", "bootstrap-state 的 bootstrap_only lifecycle 与当前 canonical tree readiness 保持一致"],
|
|
56
|
+
["bootstrap-state lifecycle canonical_tree_ready is aligned with the current canonical tree readiness", "bootstrap-state 的 canonical_tree_ready lifecycle 与当前 canonical tree readiness 保持一致"],
|
|
57
|
+
["No local doc_spec_audit closeout artifact detected", "未检测到本地 doc_spec_audit closeout 产物"],
|
|
58
|
+
["Local doc_spec_audit artifact is consistent with the current reconstruction state", "本地 doc_spec_audit 产物与当前重建状态一致"],
|
|
59
|
+
["Managed AI entrypoint blocks detected in: AGENTS.md, CLAUDE.md", "在 AGENTS.md、CLAUDE.md 中检测到托管 AI 入口块"],
|
|
60
|
+
["No managed AI entrypoint blocks detected; this is optional", "未检测到托管 AI 入口块;这是可选的"],
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
const DOCTOR_NEXT_STEP_TRANSLATIONS = new Map([
|
|
64
|
+
["Repair the failing bootstrap checks, then rerun `nimicoding doctor`.", "修复失败的 bootstrap 检查项,然后重新运行 `nimicoding doctor`。"],
|
|
65
|
+
["Use an external AI host to reconstruct the declared canonical tree under `.nimi/spec`.", "使用外部 AI host 重建声明的 `.nimi/spec` canonical tree。"],
|
|
66
|
+
["Run `nimicoding blueprint-audit --write-local` after canonical tree generation when a benchmark blueprint is declared.", "当声明了 benchmark blueprint 且 canonical tree 生成完成后,运行 `nimicoding blueprint-audit --write-local`。"],
|
|
67
|
+
["Run `nimicoding validate-spec-audit` after generating `.nimi/spec/_meta/spec-generation-audit.yaml` for the canonical tree.", "在为 canonical tree 生成 `.nimi/spec/_meta/spec-generation-audit.yaml` 后,运行 `nimicoding validate-spec-audit`。"],
|
|
68
|
+
["Run `nimicoding handoff --skill doc_spec_audit` and close out the result locally when the audit is complete.", "运行 `nimicoding handoff --skill doc_spec_audit`,并在审计完成后于本地 closeout 结果。"],
|
|
69
|
+
["Keep runtime ownership delegated; do not assume local skill installation or self-hosting.", "保持 runtime ownership 为 delegated;不要假设本地 skill 安装或 self-hosting。"],
|
|
70
|
+
["If you want a constrained external execution host, select one in `.nimi/config/host-adapter.yaml`.", "如果你希望使用受约束的外部执行 host,请在 `.nimi/config/host-adapter.yaml` 中选择一个。"],
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
function translateDoctorDetail(detail) {
|
|
74
|
+
if (/^All required bootstrap seed files are present \(\d+\/\d+\)$/.test(detail)) {
|
|
75
|
+
return detail.replace("All required bootstrap seed files are present", "所有必需的 bootstrap seed 文件均已存在");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return DOCTOR_DETAIL_TRANSLATIONS.get(detail) ?? detail;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function translateDoctorNextStep(step) {
|
|
82
|
+
return DOCTOR_NEXT_STEP_TRANSLATIONS.get(step) ?? step;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function summarizeDoctorState(result) {
|
|
86
|
+
const blockingChecks = result.checks.filter((check) => check.severity === "error");
|
|
87
|
+
const warningChecks = result.checks.filter((check) => check.severity === "warn");
|
|
88
|
+
const importantInfoChecks = result.checks.filter((check) => check.severity === "info").slice(0, 2);
|
|
89
|
+
|
|
90
|
+
const bootstrapState = !result.bootstrapPresent
|
|
91
|
+
? localize("missing", "缺失")
|
|
92
|
+
: result.bootstrapContract.status === "supported"
|
|
93
|
+
? localize("ready", "就绪")
|
|
94
|
+
: localize("needs attention", "需要关注");
|
|
95
|
+
|
|
96
|
+
const canonicalTreeState = !result.specTreeModel?.ok
|
|
97
|
+
? localize("invalid", "无效")
|
|
98
|
+
: !result.canonicalTree.requiredFilesValid
|
|
99
|
+
? localize("incomplete", "未完成")
|
|
100
|
+
: localize("ready", "就绪");
|
|
101
|
+
|
|
102
|
+
const auditState = !result.specGenerationAudit?.present
|
|
103
|
+
? localize("not started", "未开始")
|
|
104
|
+
: result.specGenerationAudit?.ok
|
|
105
|
+
? localize("ready", "就绪")
|
|
106
|
+
: localize("needs attention", "需要关注");
|
|
107
|
+
const benchmarkAuditState = !result.benchmarkAuditReadiness?.available
|
|
108
|
+
? localize("not declared", "未声明")
|
|
109
|
+
: result.benchmarkAuditReadiness.ready
|
|
110
|
+
? localize("ready", "就绪")
|
|
111
|
+
: localize("needs attention", "需要关注");
|
|
112
|
+
|
|
113
|
+
const entrypointIntegrated = result.checks.some((check) => check.id === "entrypoint_integration" && check.detail.includes("Managed AI entrypoint blocks detected"));
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
blockingChecks,
|
|
117
|
+
warningChecks,
|
|
118
|
+
importantInfoChecks,
|
|
119
|
+
bootstrapState,
|
|
120
|
+
canonicalTreeState,
|
|
121
|
+
auditState,
|
|
122
|
+
benchmarkAuditState,
|
|
123
|
+
entrypointIntegrated,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function formatDoctorResultVerbose(result) {
|
|
128
|
+
const hostCompatibility = result.hostCompatibility ?? emptyHostCompatibility();
|
|
129
|
+
const lines = [
|
|
130
|
+
styleHeading(`nimicoding doctor: ${result.projectRoot}`),
|
|
131
|
+
"",
|
|
132
|
+
styleLabel(localize("Overall:", "总体:")),
|
|
133
|
+
` - ${localize("status", "状态")}: ${styleStatus(result.ok ? "ok" : "needs_attention")}`,
|
|
134
|
+
` - bootstrap_present: ${result.bootstrapPresent ? "true" : "false"}`,
|
|
135
|
+
` - reconstruction_required: ${result.reconstructionRequired ? "true" : "false"}`,
|
|
136
|
+
` - runtime_installed: ${result.runtimeInstalled ? "true" : "false"}`,
|
|
137
|
+
` - handoff_ready: ${result.handoffReadiness.ok ? "true" : "false"}`,
|
|
138
|
+
` - tree_state: ${result.lifecycleState.treeState ?? "unknown"}`,
|
|
139
|
+
` - authority_mode: ${result.lifecycleState.authorityMode ?? "unknown"}`,
|
|
140
|
+
` - blueprint_mode: ${result.lifecycleState.blueprintMode ?? "unknown"}`,
|
|
141
|
+
"",
|
|
142
|
+
styleLabel(localize("Bootstrap:", "Bootstrap:")),
|
|
143
|
+
` - contract_status: ${result.bootstrapContract.status}`,
|
|
144
|
+
` - contract_id: ${result.bootstrapContract.id ?? "unknown"}`,
|
|
145
|
+
` - contract_version: ${result.bootstrapContract.version ?? "unknown"}`,
|
|
146
|
+
"",
|
|
147
|
+
styleLabel(localize("Completion Posture:", "完成姿态:")),
|
|
148
|
+
` - profile: ${result.completionProfile ?? "unknown"}`,
|
|
149
|
+
` - ${localize("status", "状态")}: ${styleStatus(result.completionStatus ?? "unknown")}`,
|
|
150
|
+
` - completed_surfaces: ${result.completedSurfaces.length}`,
|
|
151
|
+
"",
|
|
152
|
+
styleLabel(localize("Supported Host Posture:", "支持的 Host 姿态:")),
|
|
153
|
+
` - contract_ref: ${hostCompatibility.contractRef ?? "unknown"}`,
|
|
154
|
+
` - supported_host_posture: ${hostCompatibility.supportedHostPosture.join(", ") || "none"}`,
|
|
155
|
+
` - supported_host_examples: ${hostCompatibility.supportedHostExamples.join(", ") || "none"}`,
|
|
156
|
+
` - required_behavior: ${hostCompatibility.requiredBehavior.length}`,
|
|
157
|
+
` - forbidden_behavior: ${hostCompatibility.forbiddenBehavior.length}`,
|
|
158
|
+
` - generic_external_host_compatible: ${hostCompatibility.genericExternalHostCompatible ? "true" : "false"}`,
|
|
159
|
+
` - named_overlay_mode: ${hostCompatibility.namedOverlaySupport.mode}`,
|
|
160
|
+
` - admitted_named_overlays: ${hostCompatibility.namedOverlaySupport.admittedOverlayIds.join(", ") || "none"}`,
|
|
161
|
+
` - selected_named_overlay: ${hostCompatibility.namedOverlaySupport.selectedOverlayId ?? "none"}`,
|
|
162
|
+
"",
|
|
163
|
+
styleLabel(localize("Delegated Contracts:", "委托契约:")),
|
|
164
|
+
` - runtime_owner: ${result.delegatedContracts.runtimeOwner ?? "unknown"}`,
|
|
165
|
+
` - runtime_mode: ${result.delegatedContracts.executionMode ?? "unknown"}`,
|
|
166
|
+
` - installer_mode: ${result.delegatedContracts.installerMode ?? "unknown"}`,
|
|
167
|
+
` - self_hosted_runtime: ${result.delegatedContracts.selfHostedRuntime ? "true" : "false"}`,
|
|
168
|
+
` - trigger_mode: ${result.delegatedContracts.triggerMode ?? "unknown"}`,
|
|
169
|
+
` - selected_adapter_id: ${result.delegatedContracts.selectedAdapterId ?? "unknown"}`,
|
|
170
|
+
` - admitted_adapter_ids: ${result.delegatedContracts.admittedAdapterIds.length}`,
|
|
171
|
+
` - adapter_handoff_mode: ${result.delegatedContracts.adapterHandoffMode ?? "unknown"}`,
|
|
172
|
+
` - semantic_review_owner: ${result.delegatedContracts.semanticReviewOwner ?? "unknown"}`,
|
|
173
|
+
"",
|
|
174
|
+
styleLabel(localize("Adapter Profiles:", "Adapter 配置:")),
|
|
175
|
+
` - admitted: ${result.adapterProfiles.admitted.length}`,
|
|
176
|
+
` - invalid: ${result.adapterProfiles.invalid.length}`,
|
|
177
|
+
` - selected_profile_ref: ${result.adapterProfiles.selected?.profileRef ?? "none"}`,
|
|
178
|
+
` - selected_host_class: ${result.adapterProfiles.selected?.hostClass ?? "none"}`,
|
|
179
|
+
"",
|
|
180
|
+
styleLabel(localize("Checks:", "检查项:")),
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
for (const check of result.checks) {
|
|
184
|
+
const marker = check.severity === "error"
|
|
185
|
+
? "fail"
|
|
186
|
+
: check.severity === "warn"
|
|
187
|
+
? "warn"
|
|
188
|
+
: check.severity === "info"
|
|
189
|
+
? "info"
|
|
190
|
+
: "ok";
|
|
191
|
+
lines.push(` - [${marker}] ${localize(check.detail, translateDoctorDetail(check.detail))}`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
lines.push("", styleLabel(localize("Canonical Tree:", "Canonical Tree:")));
|
|
195
|
+
lines.push(` - profile: ${result.canonicalTree.profile ?? "unknown"}`);
|
|
196
|
+
lines.push(` - required_files: ${result.canonicalTree.requiredFiles.length}`);
|
|
197
|
+
lines.push(` - present: ${result.canonicalTree.present.length}`);
|
|
198
|
+
lines.push(` - missing: ${result.canonicalTree.missing.length}`);
|
|
199
|
+
lines.push(` - ready: ${result.canonicalTree.ready ? "true" : "false"}`);
|
|
200
|
+
|
|
201
|
+
lines.push("", styleLabel(localize("Generation Inputs:", "生成输入:")));
|
|
202
|
+
lines.push(` - mode: ${result.specGenerationInputs.mode ?? "unknown"}`);
|
|
203
|
+
lines.push(` - code_roots: ${result.specGenerationInputs.codeRoots.length}`);
|
|
204
|
+
lines.push(` - docs_roots: ${result.specGenerationInputs.docsRoots.length}`);
|
|
205
|
+
lines.push(` - structure_roots: ${result.specGenerationInputs.structureRoots.length}`);
|
|
206
|
+
lines.push(` - human_note_paths: ${result.specGenerationInputs.humanNotePaths.length}`);
|
|
207
|
+
lines.push(` - benchmark_mode: ${result.specGenerationInputs.benchmarkMode ?? "unknown"}`);
|
|
208
|
+
lines.push(` - benchmark_root: ${result.benchmarkAuditReadiness.benchmarkRoot ?? "none"}`);
|
|
209
|
+
lines.push(` - acceptance_mode: ${result.specGenerationInputs.acceptanceMode ?? "unknown"}`);
|
|
210
|
+
|
|
211
|
+
lines.push("", styleLabel(localize("Audit:", "审计:")));
|
|
212
|
+
lines.push(` - spec_generation_audit_present: ${result.specGenerationAudit.present ? "true" : "false"}`);
|
|
213
|
+
lines.push(` - spec_generation_audit_ok: ${result.specGenerationAudit.ok ? "true" : "false"}`);
|
|
214
|
+
lines.push(` - required_audited_files: ${result.specGenerationAudit.summary?.requiredAuditedFiles ?? 0}`);
|
|
215
|
+
lines.push(` - unresolved_files: ${result.specGenerationAudit.summary?.unresolvedFiles ?? 0}`);
|
|
216
|
+
lines.push(` - inferred_files: ${result.specGenerationAudit.summary?.inferredFiles ?? 0}`);
|
|
217
|
+
lines.push(` - doc_spec_audit_artifact_present: ${result.auditArtifact.present ? "true" : "false"}`);
|
|
218
|
+
lines.push(` - doc_spec_audit_artifact_ok: ${result.auditArtifact.ok ? "true" : "false"}`);
|
|
219
|
+
|
|
220
|
+
lines.push("", styleLabel(localize("Execution Contracts:", "执行契约:")));
|
|
221
|
+
lines.push(` - total: ${result.executionContracts.total}`);
|
|
222
|
+
lines.push(` - valid: ${result.executionContracts.valid}`);
|
|
223
|
+
lines.push(` - invalid: ${result.executionContracts.invalid.length}`);
|
|
224
|
+
|
|
225
|
+
lines.push("", styleLabel(localize("Handoff:", "Handoff:")));
|
|
226
|
+
lines.push(` - required_context_order: ${result.handoffReadiness.requiredContextOrder.length}`);
|
|
227
|
+
lines.push(` - missing_context_entries: ${result.handoffReadiness.missingContextEntries.length}`);
|
|
228
|
+
lines.push(` - missing_paths: ${result.handoffReadiness.missingPaths.length}`);
|
|
229
|
+
|
|
230
|
+
if (result.nextSteps.length > 0) {
|
|
231
|
+
lines.push("", styleLabel(localize("Next:", "下一步:")));
|
|
232
|
+
for (const step of result.nextSteps) {
|
|
233
|
+
lines.push(` - ${localize(step, translateDoctorNextStep(step))}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return `${lines.join("\n")}\n`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function formatDoctorResult(result, options = {}) {
|
|
241
|
+
if (options.verbose) {
|
|
242
|
+
return formatDoctorResultVerbose(result);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const summary = summarizeDoctorState(result);
|
|
246
|
+
const lines = [
|
|
247
|
+
styleHeading(`nimicoding doctor: ${result.projectRoot}`),
|
|
248
|
+
"",
|
|
249
|
+
styleLabel(localize("Summary:", "摘要:")),
|
|
250
|
+
` - ${localize("status", "状态")}: ${styleStatus(result.ok ? "ok" : "needs_attention")}`,
|
|
251
|
+
` - ${localize("bootstrap", "bootstrap")}: ${summary.bootstrapState}`,
|
|
252
|
+
` - ${localize("project rules", "项目规则")}: ${summary.canonicalTreeState} (${localize("present", "已存在")} ${result.canonicalTree.present.length}, ${localize("missing", "缺失")} ${result.canonicalTree.missing.length})`,
|
|
253
|
+
` - ${localize("lifecycle", "生命周期")}: ${result.lifecycleState.treeState ?? "unknown"} / ${result.lifecycleState.authorityMode ?? "unknown"}`,
|
|
254
|
+
` - ${localize("benchmark audit", "benchmark 审计")}: ${summary.benchmarkAuditState}`,
|
|
255
|
+
` - ${localize("generation audit", "生成审计")}: ${summary.auditState}`,
|
|
256
|
+
` - ${localize("AI entry files", "AI 入口文件")}: ${summary.entrypointIntegrated ? localize("connected", "已接入") : localize("not connected", "未接入")}`,
|
|
257
|
+
` - ${localize("handoff", "handoff")}: ${result.handoffReadiness.ok ? localize("ready", "就绪") : localize("needs attention", "需要关注")}`,
|
|
258
|
+
];
|
|
259
|
+
|
|
260
|
+
lines.push("", styleLabel(localize("Checks:", "检查项:")));
|
|
261
|
+
if (summary.blockingChecks.length === 0 && summary.warningChecks.length === 0) {
|
|
262
|
+
lines.push(` - ${localize("No blocking issues found.", "没有发现阻塞问题。")}`);
|
|
263
|
+
} else {
|
|
264
|
+
for (const check of [...summary.blockingChecks, ...summary.warningChecks]) {
|
|
265
|
+
const marker = check.severity === "error" ? "fail" : "warn";
|
|
266
|
+
lines.push(` - [${marker}] ${localize(check.detail, translateDoctorDetail(check.detail))}`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (summary.importantInfoChecks.length > 0) {
|
|
271
|
+
lines.push("", styleLabel(localize("Notes:", "说明:")));
|
|
272
|
+
for (const check of summary.importantInfoChecks) {
|
|
273
|
+
lines.push(` - ${localize(check.detail, translateDoctorDetail(check.detail))}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (result.nextSteps.length > 0) {
|
|
278
|
+
lines.push("", styleLabel(localize("Next:", "下一步:")));
|
|
279
|
+
for (const step of result.nextSteps) {
|
|
280
|
+
lines.push(` - ${localize(step, translateDoctorNextStep(step))}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
lines.push("", styleMuted(localize("Need internal contract detail? Run `nimicoding doctor --verbose`.", "如果你需要内部契约细节,请运行 `nimicoding doctor --verbose`。")));
|
|
285
|
+
return `${lines.join("\n")}\n`;
|
|
286
|
+
}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
EXTERNAL_HOST_COMPATIBILITY_CONTRACT_REF,
|
|
5
|
+
STANDALONE_COMPLETED_SURFACES,
|
|
6
|
+
STANDALONE_COMPLETION_PROFILE,
|
|
7
|
+
STANDALONE_DEFERRED_EXECUTION_SURFACES,
|
|
8
|
+
STANDALONE_PROMOTED_PARITY_GAP_SUMMARY,
|
|
9
|
+
} from "../../constants.mjs";
|
|
10
|
+
import { readTextIfFile, pathExists } from "../fs-helpers.mjs";
|
|
11
|
+
import { arraysEqual } from "../value-helpers.mjs";
|
|
12
|
+
import {
|
|
13
|
+
parseYamlText,
|
|
14
|
+
} from "../yaml-helpers.mjs";
|
|
15
|
+
import { validateDocSpecAuditSummary } from "../contracts.mjs";
|
|
16
|
+
import {
|
|
17
|
+
emptyAuditArtifact,
|
|
18
|
+
emptyCanonicalTree,
|
|
19
|
+
} from "./doctor-state.mjs";
|
|
20
|
+
|
|
21
|
+
export function inspectStandaloneCompletionTruth(productScopeText) {
|
|
22
|
+
const parsed = parseYamlText(productScopeText);
|
|
23
|
+
const completion = parsed?.standalone_completion;
|
|
24
|
+
|
|
25
|
+
const completionProfile = typeof completion?.profile === "string" ? completion.profile : null;
|
|
26
|
+
const completedSurfaces = Array.isArray(completion?.completed_surfaces)
|
|
27
|
+
? completion.completed_surfaces.map((entry) => String(entry))
|
|
28
|
+
: [];
|
|
29
|
+
const deferredExecutionSurfaces = Array.isArray(completion?.deferred_execution_surfaces)
|
|
30
|
+
? completion.deferred_execution_surfaces.map((entry) => String(entry))
|
|
31
|
+
: [];
|
|
32
|
+
const promotedParityGapSummary = Array.isArray(completion?.promoted_parity_gap_summary)
|
|
33
|
+
? completion.promoted_parity_gap_summary.map((entry) => String(entry))
|
|
34
|
+
: [];
|
|
35
|
+
|
|
36
|
+
const ok = completionProfile === STANDALONE_COMPLETION_PROFILE
|
|
37
|
+
&& arraysEqual(completedSurfaces, STANDALONE_COMPLETED_SURFACES)
|
|
38
|
+
&& arraysEqual(deferredExecutionSurfaces, STANDALONE_DEFERRED_EXECUTION_SURFACES)
|
|
39
|
+
&& arraysEqual(promotedParityGapSummary, STANDALONE_PROMOTED_PARITY_GAP_SUMMARY);
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
ok,
|
|
43
|
+
completionProfile,
|
|
44
|
+
completedSurfaces,
|
|
45
|
+
deferredExecutionSurfaces,
|
|
46
|
+
promotedParityGapSummary,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function inspectPackageBoundaryTruth(boundariesText, changePolicyText) {
|
|
51
|
+
const boundaries = parseYamlText(boundariesText);
|
|
52
|
+
const changePolicy = parseYamlText(changePolicyText);
|
|
53
|
+
|
|
54
|
+
const boundaryOk = Array.isArray(boundaries?.boundaries)
|
|
55
|
+
&& boundaries.boundaries.some((entry) => entry?.boundary === "standalone_boundary_completion_vs_promoted_runtime_parity")
|
|
56
|
+
&& Array.isArray(boundaries?.invariants)
|
|
57
|
+
&& boundaries.invariants.includes("standalone completion posture remains boundary_complete rather than promoted_runtime_parity")
|
|
58
|
+
&& Array.isArray(boundaries?.fail_closed_rules)
|
|
59
|
+
&& boundaries.fail_closed_rules.includes("fail if package-owned standalone completion truth drifts into claiming run kernel, provider runtime, scheduler, notification, or automation ownership");
|
|
60
|
+
|
|
61
|
+
const runtimeBoundaryWorkType = Array.isArray(changePolicy?.work_types)
|
|
62
|
+
? changePolicy.work_types.find((entry) => entry?.id === "runtime_boundary_expansion")
|
|
63
|
+
: null;
|
|
64
|
+
const runtimeBoundaryGate = Array.isArray(changePolicy?.authority_gates)
|
|
65
|
+
? changePolicy.authority_gates.find((entry) => entry?.gate === "runtime_boundary_preservation")
|
|
66
|
+
: null;
|
|
67
|
+
const changePolicyOk = Boolean(runtimeBoundaryWorkType)
|
|
68
|
+
&& typeof runtimeBoundaryWorkType.notes === "string"
|
|
69
|
+
&& runtimeBoundaryWorkType.notes.includes("topic lifecycle runtime")
|
|
70
|
+
&& runtimeBoundaryWorkType.notes.includes("packet-bound run kernel")
|
|
71
|
+
&& runtimeBoundaryWorkType.notes.includes("provider execution")
|
|
72
|
+
&& Boolean(runtimeBoundaryGate)
|
|
73
|
+
&& typeof runtimeBoundaryGate.enforcement === "string"
|
|
74
|
+
&& runtimeBoundaryGate.enforcement.includes("boundary-complete standalone");
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
ok: boundaryOk && changePolicyOk,
|
|
78
|
+
boundaryOk,
|
|
79
|
+
changePolicyOk,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function buildHostCompatibilityReport(externalHostCompatibilityContract, adapterProfiles, selectedAdapterId) {
|
|
84
|
+
const admittedOverlayIds = adapterProfiles.admitted.map((profile) => profile.id).filter(Boolean);
|
|
85
|
+
const selectedOverlayId = selectedAdapterId && selectedAdapterId !== "none" ? selectedAdapterId : null;
|
|
86
|
+
const selectedOverlayProfile = adapterProfiles.selected ?? null;
|
|
87
|
+
const futureOnlyHostSurfaces = adapterProfiles.admitted.flatMap((profile) => {
|
|
88
|
+
const commands = profile.promptHandoff?.futureSurface ?? [];
|
|
89
|
+
const status = profile.promptHandoff?.futureSurfaceStatus ?? null;
|
|
90
|
+
return commands.map((command) => ({
|
|
91
|
+
adapterId: profile.id,
|
|
92
|
+
status,
|
|
93
|
+
command,
|
|
94
|
+
}));
|
|
95
|
+
});
|
|
96
|
+
const nativeReviewSurfaces = adapterProfiles.admitted
|
|
97
|
+
.filter((profile) => profile.nativeReviewBoundary)
|
|
98
|
+
.map((profile) => ({
|
|
99
|
+
adapterId: profile.id,
|
|
100
|
+
approvalReviewScope: profile.nativeReviewBoundary.approvalReview.scope,
|
|
101
|
+
approvalReviewSemanticEffect: profile.nativeReviewBoundary.approvalReview.semanticEffect,
|
|
102
|
+
githubAutoReviewScope: profile.nativeReviewBoundary.githubAutoReview.scope,
|
|
103
|
+
githubAutoReviewSemanticEffect: profile.nativeReviewBoundary.githubAutoReview.semanticEffect,
|
|
104
|
+
forbiddenSemanticSubstitutions: profile.nativeReviewBoundary.forbiddenSemanticSubstitutions,
|
|
105
|
+
}));
|
|
106
|
+
|
|
107
|
+
let overlayMode = "generic_only";
|
|
108
|
+
if (admittedOverlayIds.length > 0) {
|
|
109
|
+
overlayMode = selectedOverlayId ? "named_admitted_overlay_selected" : "named_admitted_overlay_available";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
contractRef: EXTERNAL_HOST_COMPATIBILITY_CONTRACT_REF,
|
|
114
|
+
supportedHostPosture: externalHostCompatibilityContract.supportedHostPosture,
|
|
115
|
+
supportedHostExamples: externalHostCompatibilityContract.supportedHostExamples,
|
|
116
|
+
requiredBehavior: externalHostCompatibilityContract.requiredBehavior,
|
|
117
|
+
forbiddenBehavior: externalHostCompatibilityContract.forbiddenBehavior,
|
|
118
|
+
genericExternalHostCompatible: externalHostCompatibilityContract.ok
|
|
119
|
+
&& externalHostCompatibilityContract.supportedHostPosture.includes("host_agnostic_external_host"),
|
|
120
|
+
namedOverlaySupport: {
|
|
121
|
+
mode: overlayMode,
|
|
122
|
+
admittedOverlayIds,
|
|
123
|
+
selectedOverlayId,
|
|
124
|
+
selectedOverlayProfileRef: selectedOverlayProfile?.profileRef ?? null,
|
|
125
|
+
selectedOverlayHostClass: selectedOverlayProfile?.hostClass ?? null,
|
|
126
|
+
},
|
|
127
|
+
futureOnlyHostSurfaces,
|
|
128
|
+
nativeReviewSurfaces,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export async function inspectLocalDocSpecAuditArtifact(projectRoot, auditContract) {
|
|
133
|
+
const artifact = emptyAuditArtifact();
|
|
134
|
+
const absolutePath = path.join(projectRoot, artifact.artifactPath);
|
|
135
|
+
const text = await readTextIfFile(absolutePath);
|
|
136
|
+
if (text === null) {
|
|
137
|
+
return artifact;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
artifact.present = true;
|
|
141
|
+
|
|
142
|
+
let parsed;
|
|
143
|
+
try {
|
|
144
|
+
parsed = JSON.parse(text);
|
|
145
|
+
} catch {
|
|
146
|
+
artifact.ok = false;
|
|
147
|
+
artifact.reason = "Local doc_spec_audit closeout artifact is not valid JSON";
|
|
148
|
+
return artifact;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const skillId = typeof parsed.skill === "string"
|
|
152
|
+
? parsed.skill
|
|
153
|
+
: parsed.skill && typeof parsed.skill === "object"
|
|
154
|
+
? parsed.skill.id
|
|
155
|
+
: null;
|
|
156
|
+
|
|
157
|
+
if (skillId !== "doc_spec_audit") {
|
|
158
|
+
artifact.ok = false;
|
|
159
|
+
artifact.reason = "Local doc_spec_audit closeout artifact declares the wrong skill id";
|
|
160
|
+
return artifact;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
artifact.outcome = parsed.outcome ?? null;
|
|
164
|
+
artifact.verifiedAt = parsed.verifiedAt ?? null;
|
|
165
|
+
|
|
166
|
+
if (!parsed.summary) {
|
|
167
|
+
artifact.reason = `Local doc_spec_audit closeout artifact detected with outcome ${parsed.outcome ?? "unknown"}`;
|
|
168
|
+
return artifact;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const validation = validateDocSpecAuditSummary(parsed.summary, auditContract, parsed.verifiedAt);
|
|
172
|
+
if (!validation.ok) {
|
|
173
|
+
artifact.ok = false;
|
|
174
|
+
artifact.reason = validation.reason;
|
|
175
|
+
return artifact;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
artifact.summaryStatus = parsed.summary.status;
|
|
179
|
+
artifact.reason = `Local doc_spec_audit closeout artifact detected: outcome ${parsed.outcome}, summary status ${parsed.summary.status}`;
|
|
180
|
+
return artifact;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function inspectBootstrapStateContract(bootstrapStateText) {
|
|
184
|
+
const parsed = parseYamlText(bootstrapStateText);
|
|
185
|
+
const mode = parsed?.state?.mode ?? null;
|
|
186
|
+
const treeState = parsed?.state?.tree_state ?? null;
|
|
187
|
+
const authorityMode = parsed?.state?.authority_mode === "external_blueprint_active"
|
|
188
|
+
? "external_authority_active"
|
|
189
|
+
: parsed?.state?.authority_mode ?? null;
|
|
190
|
+
const blueprintMode = parsed?.state?.blueprint_mode ?? null;
|
|
191
|
+
const reconstructionRequired = parsed?.state?.reconstruction_required;
|
|
192
|
+
const readyForAiReconstruction = parsed?.status?.ready_for_ai_reconstruction;
|
|
193
|
+
const lifecycleContract = parsed?.lifecycle_contract ?? null;
|
|
194
|
+
const supportedMode = mode === "bootstrap_only" || mode === "reconstruction_seeded";
|
|
195
|
+
const supportedTreeState = Array.isArray(lifecycleContract?.tree_state_enum)
|
|
196
|
+
? lifecycleContract.tree_state_enum.includes(treeState)
|
|
197
|
+
: false;
|
|
198
|
+
const supportedAuthorityMode = Array.isArray(lifecycleContract?.authority_mode_enum)
|
|
199
|
+
? lifecycleContract.authority_mode_enum.includes(authorityMode)
|
|
200
|
+
: false;
|
|
201
|
+
const supportedBlueprintMode = Array.isArray(lifecycleContract?.blueprint_mode_enum)
|
|
202
|
+
? lifecycleContract.blueprint_mode_enum.includes(blueprintMode)
|
|
203
|
+
: false;
|
|
204
|
+
const legacyModeMapping = Array.isArray(lifecycleContract?.legacy_mode_mapping)
|
|
205
|
+
? lifecycleContract.legacy_mode_mapping
|
|
206
|
+
.map((entry) => ({
|
|
207
|
+
...entry,
|
|
208
|
+
authority_mode: entry?.authority_mode === "external_blueprint_active"
|
|
209
|
+
? "external_authority_active"
|
|
210
|
+
: entry?.authority_mode,
|
|
211
|
+
}))
|
|
212
|
+
.find((entry) => entry?.legacy_mode === mode) ?? null
|
|
213
|
+
: null;
|
|
214
|
+
const modeSpecificContractOk = (
|
|
215
|
+
mode === "bootstrap_only"
|
|
216
|
+
&& reconstructionRequired === true
|
|
217
|
+
&& readyForAiReconstruction === true
|
|
218
|
+
) || (
|
|
219
|
+
mode === "reconstruction_seeded"
|
|
220
|
+
&& reconstructionRequired === false
|
|
221
|
+
&& readyForAiReconstruction === false
|
|
222
|
+
);
|
|
223
|
+
const multiAxisAligned = (
|
|
224
|
+
legacyModeMapping
|
|
225
|
+
&& legacyModeMapping.tree_state === treeState
|
|
226
|
+
&& legacyModeMapping.authority_mode === authorityMode
|
|
227
|
+
&& legacyModeMapping.reconstruction_required === reconstructionRequired
|
|
228
|
+
&& legacyModeMapping.ready_for_ai_reconstruction === readyForAiReconstruction
|
|
229
|
+
) || (
|
|
230
|
+
mode === "reconstruction_seeded"
|
|
231
|
+
&& treeState === "canonical_tree_ready"
|
|
232
|
+
&& authorityMode === "external_authority_active"
|
|
233
|
+
&& reconstructionRequired === false
|
|
234
|
+
&& readyForAiReconstruction === false
|
|
235
|
+
) || (
|
|
236
|
+
mode === "reconstruction_seeded"
|
|
237
|
+
&& treeState === "canonical_tree_ready"
|
|
238
|
+
&& authorityMode === "canonical_active"
|
|
239
|
+
&& reconstructionRequired === false
|
|
240
|
+
&& readyForAiReconstruction === false
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
mode,
|
|
245
|
+
treeState,
|
|
246
|
+
authorityMode,
|
|
247
|
+
blueprintMode,
|
|
248
|
+
supportedMode,
|
|
249
|
+
supportedTreeState,
|
|
250
|
+
supportedAuthorityMode,
|
|
251
|
+
supportedBlueprintMode,
|
|
252
|
+
reconstructionRequired,
|
|
253
|
+
readyForAiReconstruction,
|
|
254
|
+
cutoverReadiness: parsed?.cutover_readiness ?? {},
|
|
255
|
+
activeAuthorityRoot: parsed?.status?.active_authority_root ?? null,
|
|
256
|
+
ok: Boolean(bootstrapStateText)
|
|
257
|
+
&& supportedMode
|
|
258
|
+
&& supportedTreeState
|
|
259
|
+
&& supportedAuthorityMode
|
|
260
|
+
&& supportedBlueprintMode
|
|
261
|
+
&& modeSpecificContractOk
|
|
262
|
+
&& multiAxisAligned,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export async function inspectCanonicalTree(projectRoot, specTreeModel) {
|
|
267
|
+
if (!specTreeModel.ok) {
|
|
268
|
+
return emptyCanonicalTree();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const requiredFiles = specTreeModel.requiredFilesByProfile[specTreeModel.profile] ?? [];
|
|
272
|
+
const present = [];
|
|
273
|
+
const missing = [];
|
|
274
|
+
|
|
275
|
+
for (const relativePath of requiredFiles) {
|
|
276
|
+
const info = await pathExists(path.join(projectRoot, relativePath));
|
|
277
|
+
if (info && info.isFile()) {
|
|
278
|
+
present.push(relativePath);
|
|
279
|
+
} else {
|
|
280
|
+
missing.push(relativePath);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
profile: specTreeModel.profile,
|
|
286
|
+
canonicalRoot: specTreeModel.canonicalRoot,
|
|
287
|
+
requiredFiles,
|
|
288
|
+
present,
|
|
289
|
+
missing,
|
|
290
|
+
invalid: [],
|
|
291
|
+
requiredFilesValid: missing.length === 0,
|
|
292
|
+
ready: missing.length === 0,
|
|
293
|
+
};
|
|
294
|
+
}
|