@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,442 @@
1
+ import { realpath, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+
4
+ import YAML from "yaml";
5
+
6
+ import {
7
+ HIGH_RISK_ADMISSION_CONTRACT_REF,
8
+ HIGH_RISK_ADMISSION_PAYLOAD_CONTRACT_VERSION,
9
+ } from "../constants.mjs";
10
+ import {
11
+ loadHighRiskAdmissionContract,
12
+ validateHighRiskAdmissionsSpec,
13
+ } from "./contracts.mjs";
14
+ import { inspectDoctorState } from "./doctor.mjs";
15
+ import { readTextIfFile } from "./fs-helpers.mjs";
16
+ import {
17
+ localize,
18
+ styleCommand,
19
+ styleHeading,
20
+ styleLabel,
21
+ styleStatus,
22
+ } from "./ui.mjs";
23
+ import { validateExecutionPacket } from "./validators.mjs";
24
+ import { isIsoUtcTimestamp, isPlainObject } from "./value-helpers.mjs";
25
+ import { parseYamlText } from "./yaml-helpers.mjs";
26
+
27
+ const ADMISSIONS_SPEC_REF = ".nimi/spec/high-risk-admissions.yaml";
28
+
29
+ function translateAdmissionReason(reason) {
30
+ const translations = new Map([
31
+ ["imported decision payload must declare contractVersion nimicoding.high-risk-decision.v1", "导入的 decision payload 必须声明 contractVersion nimicoding.high-risk-decision.v1"],
32
+ ["imported decision payload must declare skill.id high_risk_execution", "导入的 decision payload 必须声明 skill.id 为 high_risk_execution"],
33
+ ["imported decision payload must remain localOnly true", "导入的 decision payload 必须保持 localOnly 为 true"],
34
+ ["admit-high-risk-decision requires a decision payload with ok true", "admit-high-risk-decision 需要一个 ok 为 true 的 decision payload"],
35
+ ["admit-high-risk-decision requires decisionStatus manager_decision_recorded", "admit-high-risk-decision 需要 decisionStatus 为 manager_decision_recorded"],
36
+ ["admit-high-risk-decision requires mechanically valid acceptance state", "admit-high-risk-decision 需要机械校验通过的 acceptance 状态"],
37
+ ["imported decision payload must include attachmentRefs.packet_ref", "导入的 decision payload 必须包含 attachmentRefs.packet_ref"],
38
+ ["attached packet_ref failed mechanical validation", "附加的 packet_ref 未通过机械校验"],
39
+ ["attached packet_ref is missing packet_id or topic_id", "附加的 packet_ref 缺少 packet_id 或 topic_id"],
40
+ ["Bootstrap or handoff validation is failing; repair doctor errors before semantic admission", "bootstrap 或 handoff 校验失败;请先修复 doctor 报错,再进行语义准入"],
41
+ ["Canonical admission requires the canonical tree under `.nimi/spec`", "canonical admission 需要 `.nimi/spec` 下的 canonical tree"],
42
+ ["Manager-owned local decision is ready for explicit canonical admission", "manager 拥有的本地 decision 已准备好进行显式 canonical admission"],
43
+ ]);
44
+ return translations.get(reason) ?? reason;
45
+ }
46
+
47
+ async function loadImportedDecisionPayload(projectRoot, fromPath) {
48
+ const absolutePath = path.resolve(projectRoot, fromPath);
49
+ const rawText = await readTextIfFile(absolutePath);
50
+
51
+ if (rawText === null) {
52
+ return {
53
+ ok: false,
54
+ error: `${localize(
55
+ `nimicoding admit-high-risk-decision refused: cannot read imported decision JSON at ${absolutePath}.`,
56
+ `nimicoding admit-high-risk-decision 已拒绝:无法读取 ${absolutePath} 处的导入 decision JSON。`,
57
+ )}\n`,
58
+ };
59
+ }
60
+
61
+ let parsed;
62
+ try {
63
+ parsed = JSON.parse(rawText);
64
+ } catch {
65
+ return {
66
+ ok: false,
67
+ error: `${localize(
68
+ `nimicoding admit-high-risk-decision refused: imported decision JSON at ${absolutePath} is invalid JSON.`,
69
+ `nimicoding admit-high-risk-decision 已拒绝:${absolutePath} 处的导入 decision JSON 不是合法 JSON。`,
70
+ )}\n`,
71
+ };
72
+ }
73
+
74
+ if (!isPlainObject(parsed)) {
75
+ return {
76
+ ok: false,
77
+ error: `${localize(
78
+ "nimicoding admit-high-risk-decision refused: imported decision JSON must be an object.",
79
+ "nimicoding admit-high-risk-decision 已拒绝:导入的 decision JSON 必须是对象。",
80
+ )}\n`,
81
+ };
82
+ }
83
+
84
+ if (parsed.projectRoot) {
85
+ let importedProjectRoot;
86
+ let currentProjectRoot;
87
+ try {
88
+ importedProjectRoot = await realpath(parsed.projectRoot);
89
+ currentProjectRoot = await realpath(projectRoot);
90
+ } catch {
91
+ return {
92
+ ok: false,
93
+ error: `${localize(
94
+ "nimicoding admit-high-risk-decision refused: imported decision projectRoot could not be resolved.",
95
+ "nimicoding admit-high-risk-decision 已拒绝:无法解析导入 decision 的 projectRoot。",
96
+ )}\n`,
97
+ };
98
+ }
99
+
100
+ if (importedProjectRoot !== currentProjectRoot) {
101
+ return {
102
+ ok: false,
103
+ error: `${localize(
104
+ "nimicoding admit-high-risk-decision refused: imported decision projectRoot does not match the current project.",
105
+ "nimicoding admit-high-risk-decision 已拒绝:导入 decision 的 projectRoot 与当前项目不匹配。",
106
+ )}\n`,
107
+ };
108
+ }
109
+ }
110
+
111
+ return {
112
+ ok: true,
113
+ path: absolutePath,
114
+ payload: parsed,
115
+ };
116
+ }
117
+
118
+ function validateImportedDecisionPayload(payload) {
119
+ if (payload.contractVersion !== "nimicoding.high-risk-decision.v1") {
120
+ return {
121
+ ok: false,
122
+ reason: "imported decision payload must declare contractVersion nimicoding.high-risk-decision.v1",
123
+ };
124
+ }
125
+
126
+ if (payload.skill?.id !== "high_risk_execution") {
127
+ return {
128
+ ok: false,
129
+ reason: "imported decision payload must declare skill.id high_risk_execution",
130
+ };
131
+ }
132
+
133
+ if (payload.localOnly !== true) {
134
+ return {
135
+ ok: false,
136
+ reason: "imported decision payload must remain localOnly true",
137
+ };
138
+ }
139
+
140
+ if (payload.ok !== true) {
141
+ return {
142
+ ok: false,
143
+ reason: "admit-high-risk-decision requires a decision payload with ok true",
144
+ };
145
+ }
146
+
147
+ if (payload.decisionStatus !== "manager_decision_recorded") {
148
+ return {
149
+ ok: false,
150
+ reason: "admit-high-risk-decision requires decisionStatus manager_decision_recorded",
151
+ };
152
+ }
153
+
154
+ if (payload.acceptanceValidation?.ok !== true) {
155
+ return {
156
+ ok: false,
157
+ reason: "admit-high-risk-decision requires mechanically valid acceptance state",
158
+ };
159
+ }
160
+
161
+ if (!isPlainObject(payload.attachmentRefs) || typeof payload.attachmentRefs.packet_ref !== "string") {
162
+ return {
163
+ ok: false,
164
+ reason: "imported decision payload must include attachmentRefs.packet_ref",
165
+ };
166
+ }
167
+
168
+ return { ok: true };
169
+ }
170
+
171
+ async function loadAdmissionsSpec(projectRoot, contract) {
172
+ const absolutePath = path.join(projectRoot, ADMISSIONS_SPEC_REF);
173
+ const text = await readTextIfFile(absolutePath);
174
+
175
+ if (text === null) {
176
+ return {
177
+ ok: false,
178
+ path: absolutePath,
179
+ reason: `cannot read ${ADMISSIONS_SPEC_REF}`,
180
+ };
181
+ }
182
+
183
+ const parsed = parseYamlText(text);
184
+ const validation = validateHighRiskAdmissionsSpec(parsed, contract);
185
+
186
+ return {
187
+ ok: validation.ok,
188
+ path: absolutePath,
189
+ parsed,
190
+ reason: validation.ok ? null : validation.reason,
191
+ };
192
+ }
193
+
194
+ async function loadPacketIdentity(projectRoot, packetRef) {
195
+ const absolutePath = path.resolve(projectRoot, packetRef);
196
+ const packetValidation = await validateExecutionPacket(absolutePath);
197
+ if (!packetValidation.ok) {
198
+ return {
199
+ ok: false,
200
+ reason: "attached packet_ref failed mechanical validation",
201
+ validation: packetValidation,
202
+ };
203
+ }
204
+
205
+ const text = await readTextIfFile(absolutePath);
206
+ const parsed = parseYamlText(text);
207
+ const packetId = String(parsed?.packet_id ?? "");
208
+ const topicId = String(parsed?.topic_id ?? "");
209
+
210
+ if (!packetId || !topicId) {
211
+ return {
212
+ ok: false,
213
+ reason: "attached packet_ref is missing packet_id or topic_id",
214
+ validation: packetValidation,
215
+ };
216
+ }
217
+
218
+ return {
219
+ ok: true,
220
+ packetId,
221
+ topicId,
222
+ validation: packetValidation,
223
+ };
224
+ }
225
+
226
+ function upsertAdmissionRecord(existingAdmissions, record) {
227
+ const nextAdmissions = existingAdmissions.filter((entry) => String(entry?.topic_id ?? "") !== record.topic_id);
228
+ const replaced = nextAdmissions.length !== existingAdmissions.length;
229
+ nextAdmissions.push(record);
230
+ nextAdmissions.sort((left, right) => String(left.topic_id).localeCompare(String(right.topic_id)));
231
+ return {
232
+ admissions: nextAdmissions,
233
+ action: replaced ? "updated" : "created",
234
+ };
235
+ }
236
+
237
+ function evaluateAdmissionReadiness(doctorResult, admissionsSpec, packetIdentity) {
238
+ if (!doctorResult.ok || !doctorResult.handoffReadiness.ok) {
239
+ return {
240
+ ok: false,
241
+ reason: "Bootstrap or handoff validation is failing; repair doctor errors before semantic admission",
242
+ };
243
+ }
244
+
245
+ if (doctorResult.lifecycleState?.treeState !== "canonical_tree_ready" || doctorResult.canonicalTree?.requiredFilesValid !== true) {
246
+ return {
247
+ ok: false,
248
+ reason: "Canonical admission requires canonical_tree_ready with declared canonical files present",
249
+ };
250
+ }
251
+
252
+ if (!admissionsSpec.ok) {
253
+ return {
254
+ ok: false,
255
+ reason: admissionsSpec.reason,
256
+ };
257
+ }
258
+
259
+ if (!packetIdentity.ok) {
260
+ return {
261
+ ok: false,
262
+ reason: packetIdentity.reason,
263
+ };
264
+ }
265
+
266
+ return {
267
+ ok: true,
268
+ reason: "Manager-owned local decision is ready for explicit canonical admission",
269
+ };
270
+ }
271
+
272
+ export async function buildHighRiskAdmissionPayload(projectRoot, options) {
273
+ const imported = await loadImportedDecisionPayload(projectRoot, options.fromPath);
274
+ if (!imported.ok) {
275
+ return {
276
+ ok: false,
277
+ exitCode: 2,
278
+ inputError: true,
279
+ error: imported.error,
280
+ };
281
+ }
282
+
283
+ const decisionValidation = validateImportedDecisionPayload(imported.payload);
284
+ if (!decisionValidation.ok) {
285
+ return {
286
+ ok: false,
287
+ exitCode: 2,
288
+ inputError: true,
289
+ error: `${localize(
290
+ `nimicoding admit-high-risk-decision refused: ${decisionValidation.reason}.`,
291
+ `nimicoding admit-high-risk-decision 已拒绝:${translateAdmissionReason(decisionValidation.reason)}。`,
292
+ )}\n`,
293
+ };
294
+ }
295
+
296
+ if (!isIsoUtcTimestamp(options.admittedAt)) {
297
+ return {
298
+ ok: false,
299
+ exitCode: 2,
300
+ inputError: true,
301
+ error: `${localize(
302
+ "nimicoding admit-high-risk-decision refused: --admitted-at must be an ISO-8601 UTC timestamp.",
303
+ "nimicoding admit-high-risk-decision 已拒绝:`--admitted-at` 必须是 ISO-8601 UTC 时间戳。",
304
+ )}\n`,
305
+ };
306
+ }
307
+
308
+ const admissionsContract = await loadHighRiskAdmissionContract(projectRoot);
309
+ if (!admissionsContract.ok) {
310
+ return {
311
+ contractVersion: HIGH_RISK_ADMISSION_PAYLOAD_CONTRACT_VERSION,
312
+ ok: false,
313
+ exitCode: 1,
314
+ projectRoot,
315
+ sourceDecisionRef: imported.path,
316
+ semanticTargetRef: ADMISSIONS_SPEC_REF,
317
+ artifactPath: path.join(projectRoot, ADMISSIONS_SPEC_REF),
318
+ skill: {
319
+ id: "high_risk_execution",
320
+ },
321
+ localOnly: false,
322
+ admittedAt: options.admittedAt,
323
+ admissionAction: "blocked",
324
+ admissionRecord: null,
325
+ readiness: {
326
+ ok: false,
327
+ reason: `${HIGH_RISK_ADMISSION_CONTRACT_REF} is missing or malformed`,
328
+ },
329
+ doctor: {
330
+ ok: false,
331
+ handoffReadiness: { ok: false },
332
+ delegatedContracts: {},
333
+ },
334
+ nextSpecYaml: null,
335
+ nextAction: `${HIGH_RISK_ADMISSION_CONTRACT_REF} must satisfy the package-owned canonical admission contract.`,
336
+ };
337
+ }
338
+
339
+ const doctorResult = await inspectDoctorState(projectRoot);
340
+ const admissionsSpec = await loadAdmissionsSpec(projectRoot, admissionsContract);
341
+ const packetIdentity = await loadPacketIdentity(projectRoot, imported.payload.attachmentRefs.packet_ref);
342
+ const readiness = evaluateAdmissionReadiness(doctorResult, admissionsSpec, packetIdentity);
343
+
344
+ const admissionRecord = packetIdentity.ok ? {
345
+ topic_id: packetIdentity.topicId,
346
+ packet_id: packetIdentity.packetId,
347
+ disposition: imported.payload.acceptanceDisposition,
348
+ admitted_at: options.admittedAt,
349
+ manager_review_owner: imported.payload.managerReviewOwner ?? doctorResult.delegatedContracts.semanticReviewOwner,
350
+ summary: typeof imported.payload.summary?.summary === "string" && imported.payload.summary.summary.trim().length > 0
351
+ ? imported.payload.summary.summary
352
+ : `Admitted manager-owned disposition for ${packetIdentity.topicId}.`,
353
+ source_decision_contract: imported.payload.contractVersion,
354
+ } : null;
355
+
356
+ const nextAdmissions = readiness.ok
357
+ ? upsertAdmissionRecord(admissionsSpec.parsed.admissions, admissionRecord)
358
+ : { admissions: admissionsSpec.parsed?.admissions ?? [], action: "blocked" };
359
+
360
+ const nextSpecObject = admissionsSpec.ok ? {
361
+ admissions: nextAdmissions.admissions,
362
+ admission_rules: admissionsSpec.parsed.admission_rules,
363
+ semantic_constraints: admissionsSpec.parsed.semantic_constraints,
364
+ } : null;
365
+ const nextSpecValidation = nextSpecObject ? validateHighRiskAdmissionsSpec(nextSpecObject, admissionsContract) : { ok: false };
366
+
367
+ return {
368
+ contractVersion: HIGH_RISK_ADMISSION_PAYLOAD_CONTRACT_VERSION,
369
+ ok: readiness.ok && nextSpecValidation.ok,
370
+ exitCode: readiness.ok && nextSpecValidation.ok ? 0 : 1,
371
+ projectRoot,
372
+ sourceDecisionRef: imported.path,
373
+ semanticTargetRef: ADMISSIONS_SPEC_REF,
374
+ artifactPath: path.join(projectRoot, ADMISSIONS_SPEC_REF),
375
+ skill: {
376
+ id: "high_risk_execution",
377
+ },
378
+ localOnly: false,
379
+ admittedAt: options.admittedAt,
380
+ admissionAction: nextAdmissions.action,
381
+ admissionRecord,
382
+ readiness: readiness.ok && !nextSpecValidation.ok
383
+ ? {
384
+ ok: false,
385
+ reason: nextSpecValidation.reason,
386
+ }
387
+ : readiness,
388
+ doctor: {
389
+ ok: doctorResult.ok,
390
+ handoffReadiness: doctorResult.handoffReadiness,
391
+ delegatedContracts: doctorResult.delegatedContracts,
392
+ },
393
+ nextSpecYaml: nextSpecObject ? YAML.stringify(nextSpecObject) : null,
394
+ nextAction: readiness.ok && nextSpecValidation.ok
395
+ ? options.writeSpec
396
+ ? `Write canonical admission to ${ADMISSIONS_SPEC_REF}.`
397
+ : `Review the canonical admission preview or write it with --write-spec.`
398
+ : readiness.ok && !nextSpecValidation.ok
399
+ ? nextSpecValidation.reason
400
+ : readiness.reason,
401
+ };
402
+ }
403
+
404
+ export async function writeHighRiskAdmission(projectRoot, payload) {
405
+ if (!payload.ok || typeof payload.nextSpecYaml !== "string") {
406
+ return;
407
+ }
408
+
409
+ await writeFile(path.join(projectRoot, ADMISSIONS_SPEC_REF), payload.nextSpecYaml, "utf8");
410
+ }
411
+
412
+ export function formatHighRiskAdmissionPayload(payload) {
413
+ const nextAction = payload.ok
414
+ ? payload.nextAction.startsWith("Write canonical admission to ")
415
+ ? localize(payload.nextAction, `将 canonical admission 写入 ${payload.semanticTargetRef}。`)
416
+ : localize(
417
+ payload.nextAction,
418
+ `检查 canonical admission 预览,或使用 ${styleCommand("--write-spec")} 将其写入。`,
419
+ )
420
+ : localize(payload.nextAction, translateAdmissionReason(payload.nextAction)
421
+ .replace(`${HIGH_RISK_ADMISSION_CONTRACT_REF} is missing or malformed`, `${HIGH_RISK_ADMISSION_CONTRACT_REF} 缺失或格式错误`)
422
+ .replace(`${HIGH_RISK_ADMISSION_CONTRACT_REF} must satisfy the package-owned canonical admission contract.`, `${HIGH_RISK_ADMISSION_CONTRACT_REF} 必须满足包内 canonical admission 契约。`)
423
+ .replace("cannot read .nimi/spec/high-risk-admissions.yaml", "无法读取 .nimi/spec/high-risk-admissions.yaml"));
424
+ const lines = [
425
+ styleHeading(`nimicoding admit-high-risk-decision: ${payload.projectRoot}`),
426
+ "",
427
+ styleLabel(localize("Admission:", "准入:")),
428
+ ` - target_ref: ${payload.semanticTargetRef}`,
429
+ ` - action: ${payload.admissionAction}`,
430
+ ` - ready: ${styleStatus(payload.readiness.ok ? "ready" : "needs_attention")}`,
431
+ ` - local_only: ${payload.localOnly ? "true" : "false"}`,
432
+ ];
433
+
434
+ if (payload.admissionRecord) {
435
+ lines.push(` - topic_id: ${payload.admissionRecord.topic_id}`);
436
+ lines.push(` - packet_id: ${payload.admissionRecord.packet_id}`);
437
+ lines.push(` - disposition: ${payload.admissionRecord.disposition}`);
438
+ }
439
+
440
+ lines.push("", styleLabel(localize("Next:", "下一步:")), ` - ${nextAction}`);
441
+ return `${lines.join("\n")}\n`;
442
+ }