kibi-cli 2.4.0 → 2.5.1

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 (354) hide show
  1. package/dist/cli-json-command.d.ts +6 -0
  2. package/dist/cli-json-command.d.ts.map +1 -1
  3. package/dist/cli-json-command.js +37 -9
  4. package/dist/cli-operation-loader.d.ts.map +1 -1
  5. package/dist/cli-operation-loader.js +7 -0
  6. package/dist/cli-operation-metadata.d.ts +12 -4
  7. package/dist/cli-operation-metadata.d.ts.map +1 -1
  8. package/dist/cli-operation-metadata.js +14 -4
  9. package/dist/cli-protocol.d.ts +14 -0
  10. package/dist/cli-protocol.d.ts.map +1 -1
  11. package/dist/cli-protocol.js +31 -2
  12. package/dist/cli-register-proof.d.ts.map +1 -1
  13. package/dist/cli-register-proof.js +13 -0
  14. package/dist/commands/check.d.ts.map +1 -1
  15. package/dist/commands/check.js +11 -2
  16. package/dist/commands/discovery-shared.d.ts.map +1 -1
  17. package/dist/commands/discovery-shared.js +8 -2
  18. package/dist/commands/engine.d.ts.map +1 -1
  19. package/dist/commands/engine.js +4 -2
  20. package/dist/commands/gc.d.ts.map +1 -1
  21. package/dist/commands/gc.js +7 -0
  22. package/dist/commands/init-helpers.d.ts.map +1 -1
  23. package/dist/commands/init-helpers.js +38 -12
  24. package/dist/commands/init.d.ts.map +1 -1
  25. package/dist/commands/init.js +10 -15
  26. package/dist/commands/migrate.d.ts.map +1 -1
  27. package/dist/commands/migrate.js +281 -14
  28. package/dist/commands/proof-compact.d.ts +9 -0
  29. package/dist/commands/proof-compact.d.ts.map +1 -0
  30. package/dist/commands/proof-compact.js +58 -0
  31. package/dist/commands/prove.d.ts +38 -0
  32. package/dist/commands/prove.d.ts.map +1 -1
  33. package/dist/commands/prove.js +222 -47
  34. package/dist/commands/sync/persistence.d.ts.map +1 -1
  35. package/dist/commands/sync/persistence.js +61 -16
  36. package/dist/commands/sync.d.ts +8 -0
  37. package/dist/commands/sync.d.ts.map +1 -1
  38. package/dist/commands/sync.js +42 -16
  39. package/dist/diagnostics.js +1 -1
  40. package/dist/engine-limits.d.ts +74 -0
  41. package/dist/engine-limits.d.ts.map +1 -0
  42. package/dist/engine-limits.js +201 -0
  43. package/dist/engine.d.ts +10 -0
  44. package/dist/engine.d.ts.map +1 -1
  45. package/dist/engine.js +57 -9
  46. package/dist/extractors/manifest.d.ts +8 -0
  47. package/dist/extractors/manifest.d.ts.map +1 -1
  48. package/dist/extractors/manifest.js +34 -5
  49. package/dist/extractors/markdown.d.ts +8 -0
  50. package/dist/extractors/markdown.d.ts.map +1 -1
  51. package/dist/extractors/markdown.js +57 -0
  52. package/dist/extractors/relationships.d.ts.map +1 -1
  53. package/dist/extractors/relationships.js +2 -0
  54. package/dist/intent-search.d.ts +4 -1
  55. package/dist/intent-search.d.ts.map +1 -1
  56. package/dist/intent-search.js +186 -33
  57. package/dist/operations/bootstrap/candidate-helpers.d.ts.map +1 -1
  58. package/dist/operations/bootstrap/candidate-helpers.js +8 -1
  59. package/dist/operations/bootstrap/candidates.d.ts.map +1 -1
  60. package/dist/operations/bootstrap/candidates.js +54 -38
  61. package/dist/operations/bootstrap/generate.d.ts +2 -0
  62. package/dist/operations/bootstrap/generate.d.ts.map +1 -1
  63. package/dist/operations/bootstrap/generate.js +87 -33
  64. package/dist/operations/bootstrap/intent-claims.d.ts.map +1 -1
  65. package/dist/operations/bootstrap/intent-claims.js +12 -4
  66. package/dist/operations/bootstrap/markdown-candidates.d.ts +2 -0
  67. package/dist/operations/bootstrap/markdown-candidates.d.ts.map +1 -1
  68. package/dist/operations/bootstrap/markdown-candidates.js +32 -10
  69. package/dist/operations/bootstrap/presentation.d.ts.map +1 -1
  70. package/dist/operations/bootstrap/presentation.js +6 -2
  71. package/dist/operations/bootstrap/requirement-claims.d.ts +1 -0
  72. package/dist/operations/bootstrap/requirement-claims.d.ts.map +1 -1
  73. package/dist/operations/bootstrap/requirement-claims.js +13 -1
  74. package/dist/operations/bootstrap/validation.d.ts +4 -0
  75. package/dist/operations/bootstrap/validation.d.ts.map +1 -0
  76. package/dist/operations/bootstrap/validation.js +20 -0
  77. package/dist/operations/migration/inventory-rederive.d.ts +81 -0
  78. package/dist/operations/migration/inventory-rederive.d.ts.map +1 -0
  79. package/dist/operations/migration/inventory-rederive.js +484 -0
  80. package/dist/operations/migration/kb-sources.d.ts +53 -0
  81. package/dist/operations/migration/kb-sources.d.ts.map +1 -0
  82. package/dist/operations/migration/kb-sources.js +233 -0
  83. package/dist/operations/migration/origin-backfill.d.ts +39 -0
  84. package/dist/operations/migration/origin-backfill.d.ts.map +1 -0
  85. package/dist/operations/migration/origin-backfill.js +164 -0
  86. package/dist/operations/migration/polarity-values.d.ts +10 -0
  87. package/dist/operations/migration/polarity-values.d.ts.map +1 -0
  88. package/dist/operations/migration/polarity-values.js +60 -0
  89. package/dist/operations/migration/schema6.d.ts +59 -0
  90. package/dist/operations/migration/schema6.d.ts.map +1 -0
  91. package/dist/operations/migration/schema6.js +379 -0
  92. package/dist/operations/migration/source-paths.d.ts +131 -0
  93. package/dist/operations/migration/source-paths.d.ts.map +1 -0
  94. package/dist/operations/migration/source-paths.js +308 -0
  95. package/dist/operations/migration/superseded-closure.d.ts +54 -0
  96. package/dist/operations/migration/superseded-closure.d.ts.map +1 -0
  97. package/dist/operations/migration/superseded-closure.js +187 -0
  98. package/dist/operations/modeling/logic-modeling.d.ts.map +1 -1
  99. package/dist/operations/modeling/logic-modeling.js +3 -5
  100. package/dist/operations/modeling/model-requirement.d.ts.map +1 -1
  101. package/dist/operations/modeling/model-requirement.js +225 -67
  102. package/dist/operations/mutation/contradictions.d.ts +16 -0
  103. package/dist/operations/mutation/contradictions.d.ts.map +1 -1
  104. package/dist/operations/mutation/contradictions.js +32 -0
  105. package/dist/operations/mutation/delete.d.ts.map +1 -1
  106. package/dist/operations/mutation/delete.js +15 -7
  107. package/dist/operations/mutation/origin.d.ts +21 -0
  108. package/dist/operations/mutation/origin.d.ts.map +1 -0
  109. package/dist/operations/mutation/origin.js +78 -0
  110. package/dist/operations/mutation/plan-apply-journal.d.ts +182 -0
  111. package/dist/operations/mutation/plan-apply-journal.d.ts.map +1 -0
  112. package/dist/operations/mutation/plan-apply-journal.js +613 -0
  113. package/dist/operations/mutation/predicate-vocabulary-guard.d.ts +3 -1
  114. package/dist/operations/mutation/predicate-vocabulary-guard.d.ts.map +1 -1
  115. package/dist/operations/mutation/predicate-vocabulary-guard.js +13 -2
  116. package/dist/operations/mutation/relationships.d.ts +5 -5
  117. package/dist/operations/mutation/relationships.d.ts.map +1 -1
  118. package/dist/operations/mutation/relationships.js +31 -11
  119. package/dist/operations/mutation/serialization.d.ts.map +1 -1
  120. package/dist/operations/mutation/serialization.js +3 -1
  121. package/dist/operations/mutation/source-authoring.d.ts +6 -0
  122. package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
  123. package/dist/operations/mutation/source-authoring.js +7 -1
  124. package/dist/operations/mutation/types.d.ts +14 -0
  125. package/dist/operations/mutation/types.d.ts.map +1 -1
  126. package/dist/operations/mutation/upsert.d.ts +30 -3
  127. package/dist/operations/mutation/upsert.d.ts.map +1 -1
  128. package/dist/operations/mutation/upsert.js +105 -35
  129. package/dist/operations/mutation/validate-upsert.d.ts +6 -0
  130. package/dist/operations/mutation/validate-upsert.d.ts.map +1 -1
  131. package/dist/operations/mutation/validate-upsert.js +34 -30
  132. package/dist/operations/planning/apply-plan.d.ts.map +1 -1
  133. package/dist/operations/planning/apply-plan.js +753 -145
  134. package/dist/operations/planning/compile-intent.d.ts +34 -1
  135. package/dist/operations/planning/compile-intent.d.ts.map +1 -1
  136. package/dist/operations/planning/compile-intent.js +314 -98
  137. package/dist/operations/proof/code-scope.d.ts +28 -0
  138. package/dist/operations/proof/code-scope.d.ts.map +1 -1
  139. package/dist/operations/proof/code-scope.js +72 -0
  140. package/dist/operations/proof/compact-receipts.d.ts +49 -0
  141. package/dist/operations/proof/compact-receipts.d.ts.map +1 -0
  142. package/dist/operations/proof/compact-receipts.js +160 -0
  143. package/dist/operations/proof/ingest-proof.d.ts +2 -0
  144. package/dist/operations/proof/ingest-proof.d.ts.map +1 -1
  145. package/dist/operations/proof/ingest-proof.js +44 -23
  146. package/dist/operations/proof/prune-receipts.d.ts.map +1 -1
  147. package/dist/operations/proof/prune-receipts.js +5 -9
  148. package/dist/operations/proof/receipt-compaction.d.ts +48 -0
  149. package/dist/operations/proof/receipt-compaction.d.ts.map +1 -0
  150. package/dist/operations/proof/receipt-compaction.js +121 -0
  151. package/dist/operations/semantic-advisor/analysis-receipt.d.ts.map +1 -1
  152. package/dist/operations/semantic-advisor/analysis-receipt.js +12 -1
  153. package/dist/operations/semantic-advisor/analyze-prose.d.ts +2 -1
  154. package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
  155. package/dist/operations/semantic-advisor/analyze-prose.js +51 -5
  156. package/dist/operations/semantic-advisor/clauses.d.ts +1 -0
  157. package/dist/operations/semantic-advisor/clauses.d.ts.map +1 -1
  158. package/dist/operations/semantic-advisor/clauses.js +11 -1
  159. package/dist/operations/semantic-advisor/conditional-rules.d.ts +33 -0
  160. package/dist/operations/semantic-advisor/conditional-rules.d.ts.map +1 -0
  161. package/dist/operations/semantic-advisor/conditional-rules.js +262 -0
  162. package/dist/operations/semantic-advisor/ingestion-boundary.d.ts +3 -1
  163. package/dist/operations/semantic-advisor/ingestion-boundary.d.ts.map +1 -1
  164. package/dist/operations/semantic-advisor/ingestion-boundary.js +14 -6
  165. package/dist/operations/semantic-advisor/plugin-orchestration.js +1 -1
  166. package/dist/prolog/codec.d.ts.map +1 -1
  167. package/dist/prolog/codec.js +2 -1
  168. package/dist/prolog.d.ts +9 -0
  169. package/dist/prolog.d.ts.map +1 -1
  170. package/dist/prolog.js +56 -10
  171. package/dist/proof/command-test-report.d.ts +74 -0
  172. package/dist/proof/command-test-report.d.ts.map +1 -0
  173. package/dist/proof/command-test-report.js +206 -0
  174. package/dist/public/branch-resolver.d.ts +1 -0
  175. package/dist/public/branch-resolver.d.ts.map +1 -1
  176. package/dist/public/branch-resolver.js +1 -0
  177. package/dist/public/entity-origin.d.ts +69 -0
  178. package/dist/public/entity-origin.d.ts.map +1 -0
  179. package/dist/public/entity-origin.js +142 -0
  180. package/dist/public/impact/full-kb-quality.d.ts.map +1 -1
  181. package/dist/public/impact/full-kb-quality.js +1 -0
  182. package/dist/public/impact/requirement-quality.js +1 -1
  183. package/dist/public/operations/catalog.d.ts +130 -19
  184. package/dist/public/operations/catalog.d.ts.map +1 -1
  185. package/dist/public/operations/catalog.js +3 -0
  186. package/dist/public/operations/check-executor.d.ts.map +1 -1
  187. package/dist/public/operations/check-executor.js +8 -0
  188. package/dist/public/operations/contracts.d.ts +0 -5
  189. package/dist/public/operations/contracts.d.ts.map +1 -1
  190. package/dist/public/operations/contracts.js +63 -14
  191. package/dist/public/operations/discovery-entities.d.ts +15 -0
  192. package/dist/public/operations/discovery-entities.d.ts.map +1 -1
  193. package/dist/public/operations/discovery-entities.js +59 -0
  194. package/dist/public/operations/discovery-executors.d.ts +6 -1
  195. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  196. package/dist/public/operations/discovery-executors.js +65 -6
  197. package/dist/public/operations/index.d.ts +1 -0
  198. package/dist/public/operations/index.d.ts.map +1 -1
  199. package/dist/public/operations/index.js +1 -0
  200. package/dist/public/operations/migration-plan.d.ts +5 -0
  201. package/dist/public/operations/migration-plan.d.ts.map +1 -1
  202. package/dist/public/operations/migration-plan.js +26 -1
  203. package/dist/public/operations/node-ports.d.ts.map +1 -1
  204. package/dist/public/operations/node-ports.js +24 -3
  205. package/dist/public/operations/origin-review.d.ts +51 -0
  206. package/dist/public/operations/origin-review.d.ts.map +1 -0
  207. package/dist/public/operations/origin-review.js +307 -0
  208. package/dist/public/operations/predicate-schema-conformance.js +1 -1
  209. package/dist/public/operations/repair-plan.d.ts.map +1 -1
  210. package/dist/public/operations/repair-plan.js +4 -0
  211. package/dist/public/operations/result-envelope.d.ts.map +1 -1
  212. package/dist/public/operations/result-envelope.js +17 -5
  213. package/dist/public/operations/runtime-types.d.ts +9 -0
  214. package/dist/public/operations/runtime-types.d.ts.map +1 -1
  215. package/dist/public/operations/schema6-check-actions.d.ts +127 -0
  216. package/dist/public/operations/schema6-check-actions.d.ts.map +1 -0
  217. package/dist/public/operations/schema6-check-actions.js +575 -0
  218. package/dist/public/operations/source-path-dangling.d.ts +17 -0
  219. package/dist/public/operations/source-path-dangling.d.ts.map +1 -0
  220. package/dist/public/operations/source-path-dangling.js +66 -0
  221. package/dist/public/operations/specs/check-rules.generated.d.ts +1 -1
  222. package/dist/public/operations/specs/check-rules.generated.d.ts.map +1 -1
  223. package/dist/public/operations/specs/check-rules.generated.js +15 -1
  224. package/dist/public/operations/specs/check.d.ts +2 -2
  225. package/dist/public/operations/specs/check.js +1 -1
  226. package/dist/public/operations/specs/composite.d.ts +351 -0
  227. package/dist/public/operations/specs/composite.d.ts.map +1 -0
  228. package/dist/public/operations/specs/composite.js +96 -0
  229. package/dist/public/operations/specs/discovery.d.ts +8 -3
  230. package/dist/public/operations/specs/discovery.d.ts.map +1 -1
  231. package/dist/public/operations/specs/discovery.js +8 -3
  232. package/dist/public/operations/specs/modeling.d.ts +2 -2
  233. package/dist/public/operations/specs/modeling.js +2 -2
  234. package/dist/public/operations/specs/mutation-schemas.d.ts +73 -8
  235. package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
  236. package/dist/public/operations/specs/mutation-schemas.js +43 -7
  237. package/dist/public/operations/specs/mutation.d.ts +1 -1
  238. package/dist/public/operations/specs/mutation.js +1 -1
  239. package/dist/public/operations/specs/planning.d.ts +2 -2
  240. package/dist/public/operations/specs/planning.js +4 -4
  241. package/dist/public/operations/specs/reporting.d.ts.map +1 -1
  242. package/dist/public/operations/specs/reporting.js +13 -26
  243. package/dist/public/operations/specs/upsert.d.ts +79 -9
  244. package/dist/public/operations/specs/upsert.d.ts.map +1 -1
  245. package/dist/public/operations/specs/upsert.js +6 -1
  246. package/dist/public/operations/types.d.ts +1 -1
  247. package/dist/public/operations/types.d.ts.map +1 -1
  248. package/dist/public/proof-receipt.d.ts +1 -0
  249. package/dist/public/proof-receipt.d.ts.map +1 -1
  250. package/dist/public/schemas/entity.d.ts.map +1 -1
  251. package/dist/public/schemas/entity.js +52 -0
  252. package/dist/public/schemas/relationship.d.ts.map +1 -1
  253. package/dist/public/schemas/relationship.js +2 -0
  254. package/dist/public/skills/kibi-bootstrap/SKILL.md +35 -67
  255. package/dist/public/skills/kibi-bootstrap/resources/bootstrap.md +6 -0
  256. package/dist/public/skills/kibi-usage/SKILL.md +68 -18
  257. package/dist/public/skills/kibi-usage/resources/fact-lanes.md +5 -5
  258. package/dist/public/skills/kibi-usage/resources/kb-improvement.md +1 -1
  259. package/dist/public/skills/kibi-usage/resources/logic-ir.md +2 -2
  260. package/dist/public/skills/kibi-usage/resources/operation-access.md +25 -1
  261. package/dist/public/skills/kibi-usage/resources/ui-requirements.md +4 -4
  262. package/dist/public/skills/kibi-usage/resources/workflows.md +42 -25
  263. package/dist/public/telemetry-acceptance.d.ts +58 -2
  264. package/dist/public/telemetry-acceptance.d.ts.map +1 -1
  265. package/dist/public/telemetry-acceptance.js +136 -10
  266. package/dist/public/telemetry-remediation.d.ts.map +1 -1
  267. package/dist/public/telemetry-remediation.js +26 -10
  268. package/dist/relationships/shards.d.ts +13 -0
  269. package/dist/relationships/shards.d.ts.map +1 -1
  270. package/dist/relationships/shards.js +54 -28
  271. package/dist/runtime/cli-runtime.d.ts.map +1 -1
  272. package/dist/runtime/cli-runtime.js +44 -3
  273. package/dist/runtime/detached-snapshot.d.ts +38 -0
  274. package/dist/runtime/detached-snapshot.d.ts.map +1 -0
  275. package/dist/runtime/detached-snapshot.js +92 -0
  276. package/dist/schemas/entity.schema.json +122 -0
  277. package/dist/search-answer.d.ts +157 -0
  278. package/dist/search-answer.d.ts.map +1 -0
  279. package/dist/search-answer.js +846 -0
  280. package/dist/traceability/impact-knowledge.d.ts.map +1 -1
  281. package/dist/traceability/impact-knowledge.js +7 -0
  282. package/dist/traceability/symbol-extract.js +3 -3
  283. package/dist/traceability/temp-kb.d.ts.map +1 -1
  284. package/dist/traceability/temp-kb.js +12 -0
  285. package/dist/types/entities.d.ts +13 -0
  286. package/dist/types/entities.d.ts.map +1 -1
  287. package/dist/types/relationships.d.ts +1 -1
  288. package/dist/types/relationships.d.ts.map +1 -1
  289. package/dist/utils/branch-resolver.d.ts +37 -1
  290. package/dist/utils/branch-resolver.d.ts.map +1 -1
  291. package/dist/utils/branch-resolver.js +130 -3
  292. package/dist/utils/repo-relative-path.d.ts +18 -0
  293. package/dist/utils/repo-relative-path.d.ts.map +1 -0
  294. package/dist/utils/repo-relative-path.js +58 -0
  295. package/dist/utils/rule-registry.generated.d.ts +75 -3
  296. package/dist/utils/rule-registry.generated.d.ts.map +1 -1
  297. package/dist/utils/rule-registry.generated.js +89 -3
  298. package/dist/utils/schema-version.d.ts +1 -1
  299. package/dist/utils/schema-version.d.ts.map +1 -1
  300. package/dist/utils/schema-version.js +4 -3
  301. package/dist/utils/strict-modeling.d.ts +1 -1
  302. package/dist/utils/strict-modeling.d.ts.map +1 -1
  303. package/dist/utils/strict-modeling.js +23 -3
  304. package/package.json +3 -3
  305. package/schema/entities.pl +22 -0
  306. package/schema/relationships.pl +14 -0
  307. package/schema/validation.pl +45 -1
  308. package/src/public/branch-resolver.ts +1 -0
  309. package/src/public/entity-origin.ts +175 -0
  310. package/src/public/impact/full-kb-quality.ts +1 -0
  311. package/src/public/impact/requirement-quality.ts +1 -1
  312. package/src/public/operations/catalog.ts +3 -0
  313. package/src/public/operations/check-executor.ts +15 -0
  314. package/src/public/operations/contracts.ts +75 -15
  315. package/src/public/operations/discovery-entities.ts +74 -0
  316. package/src/public/operations/discovery-executors.ts +88 -10
  317. package/src/public/operations/index.ts +1 -0
  318. package/src/public/operations/migration-plan.ts +30 -1
  319. package/src/public/operations/node-ports.ts +24 -3
  320. package/src/public/operations/origin-review.ts +375 -0
  321. package/src/public/operations/predicate-schema-conformance.ts +1 -1
  322. package/src/public/operations/repair-plan.ts +4 -0
  323. package/src/public/operations/result-envelope.ts +18 -6
  324. package/src/public/operations/runtime-types.ts +9 -0
  325. package/src/public/operations/schema6-check-actions.ts +699 -0
  326. package/src/public/operations/source-path-dangling.ts +81 -0
  327. package/src/public/operations/specs/check-rules.generated.ts +15 -1
  328. package/src/public/operations/specs/check.ts +1 -1
  329. package/src/public/operations/specs/composite.ts +141 -0
  330. package/src/public/operations/specs/discovery.ts +9 -3
  331. package/src/public/operations/specs/modeling.ts +2 -2
  332. package/src/public/operations/specs/mutation-schemas.ts +49 -7
  333. package/src/public/operations/specs/mutation.ts +1 -1
  334. package/src/public/operations/specs/planning.ts +4 -4
  335. package/src/public/operations/specs/reporting.ts +13 -38
  336. package/src/public/operations/specs/upsert.ts +7 -1
  337. package/src/public/operations/types.ts +2 -0
  338. package/src/public/proof-receipt.ts +1 -0
  339. package/src/public/schemas/entity.ts +57 -0
  340. package/src/public/schemas/relationship.ts +2 -0
  341. package/src/public/skills/kibi-bootstrap/SKILL.md +35 -67
  342. package/src/public/skills/kibi-bootstrap/resources/bootstrap.md +6 -0
  343. package/src/public/skills/kibi-usage/SKILL.md +68 -18
  344. package/src/public/skills/kibi-usage/resources/fact-lanes.md +5 -5
  345. package/src/public/skills/kibi-usage/resources/kb-improvement.md +1 -1
  346. package/src/public/skills/kibi-usage/resources/logic-ir.md +2 -2
  347. package/src/public/skills/kibi-usage/resources/operation-access.md +25 -1
  348. package/src/public/skills/kibi-usage/resources/ui-requirements.md +4 -4
  349. package/src/public/skills/kibi-usage/resources/workflows.md +42 -25
  350. package/src/public/telemetry-acceptance-types.d.ts +4 -1
  351. package/src/public/telemetry-acceptance.ts +204 -10
  352. package/src/public/telemetry-remediation.ts +39 -10
  353. package/src/schemas/entity.schema.json +122 -0
  354. package/src/schemas/relationship.schema.json +2 -0
@@ -9,15 +9,20 @@ import { executeStatus } from "../../public/operations/discovery-executors.js";
9
9
  import { PREDICATE_SCHEMA_ALIGNMENT_CODE, migrationPlanHash, } from "../../public/operations/migration-plan.js";
10
10
  import { readMigrationConfigStatus } from "../../public/operations/migration-plan.js";
11
11
  import { readWorkspaceSnapshot } from "../../public/operations/workspace-snapshot.js";
12
+ import { computeShardPath, renderShardWithRelationship, } from "../../relationships/shards.js";
12
13
  import { canonicalFilesystemPath } from "../../utils/canonical-path.js";
13
14
  import { isDerivedKbPath } from "../../utils/kb-paths.js";
14
15
  import { bootstrapEmptyKbSnapshotId, bootstrapPlanHash, } from "../bootstrap/types.js";
16
+ import { validateBootstrapPayload } from "../bootstrap/validation.js";
17
+ import { SCHEMA6_AUTOMATIC_CODES, applySchema6MigrationAction, } from "../migration/schema6.js";
18
+ import { buildUpsertBatchCommitGoal, formatUpsertError, } from "../mutation/contradictions.js";
15
19
  import { executeDelete } from "../mutation/delete.js";
16
- import { retirePendingSourceReceipt, writePendingSourceReceipt, } from "../mutation/source-authoring.js";
17
- import { executeUpsert } from "../mutation/upsert.js";
20
+ import { PLAN_APPLY_JOURNAL_VERSION, contentHash, createPlanApplyJournal, isPlanApplyJournalId, openPlanApplyJournal, openPlanApplyJournalById, planRecoveryNotes, publishJournaledFiles, reconcilePendingSourceReceipts, recordPlanApplyJournal, recoverPlanApplyJournal, restoreJournaledFiles, settlePendingPlanApplyJournals, storeFingerprint, withPlanRecoveryNotes, } from "../mutation/plan-apply-journal.js";
21
+ import { canonicalSourcePath, renderSourceDocument, } from "../mutation/source-authoring.js";
22
+ import { assertRelationshipShardContained, executeUpsert, validateUpsertForCommit, } from "../mutation/upsert.js";
18
23
  import { acquireWorkspaceMutationLock, releaseWorkspaceMutationLock, } from "../mutation/workspace-mutation-lock.js";
19
- import { compilePlanHash, } from "./compile-intent.js";
20
- // implements REQ-kibi-change-to-proof-plan-compiler
24
+ import { compilePlanHash, isBlockingWitness, parseWhatIfAnalysis, planWhatIfGoal, } from "./compile-intent.js";
25
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2
21
26
  export const PLAN_APPLY_RESULT_VERSION = "kibi.plan-apply-result.v1";
22
27
  const ENTITY_TYPES = new Set([
23
28
  "req",
@@ -38,21 +43,6 @@ function isRecord(value) {
38
43
  function digest(value) {
39
44
  return createHash("sha256").update(value).digest("hex");
40
45
  }
41
- /**
42
- * Reconcile pending-source receipts with committed source writes: a written
43
- * file is bound to its exact bytes until Git tracks it, and a deleted file
44
- * must not leave a receipt that later syncs would report as missing.
45
- */
46
- function reconcilePendingSourceReceipts(workspaceRoot, writes) {
47
- for (const write of writes) {
48
- if ((write.mode ?? "write") === "delete") {
49
- retirePendingSourceReceipt(workspaceRoot, write.path);
50
- }
51
- else if (write.afterHash !== null) {
52
- writePendingSourceReceipt(workspaceRoot, write.path, write.afterHash);
53
- }
54
- }
55
- }
56
46
  function relationships(step) {
57
47
  if (!Array.isArray(step.relationships))
58
48
  return [];
@@ -191,7 +181,8 @@ function validateBootstrapPlanShape(args) {
191
181
  throw new Error("Bootstrap apply failed: ready plans require exact branch, KB snapshot, and workspace snapshot bindings");
192
182
  }
193
183
  if (!/^[a-f0-9]{64}$/i.test(plan.expected.kbSnapshotId) &&
194
- !/^empty-source-state-[a-f0-9]{64}$/i.test(plan.expected.kbSnapshotId)) {
184
+ !/^empty-source-state-[a-f0-9]{64}$/i.test(plan.expected.kbSnapshotId) &&
185
+ !/^generation-[^:\s]+:\d+$/.test(plan.expected.kbSnapshotId)) {
195
186
  throw new Error("Bootstrap apply failed: ready plans require an exact KB snapshot binding");
196
187
  }
197
188
  for (const [sourcePath, sourceHash] of Object.entries(plan.expected.sourceHashes)) {
@@ -204,6 +195,33 @@ function validateBootstrapPlanShape(args) {
204
195
  throw new Error("Bootstrap apply failed: ready plans must contain actions");
205
196
  return orderBootstrapActions(plan.actions);
206
197
  }
198
+ // implements REQ-KIBI-BOOTSTRAP-PLAN
199
+ function preflightBootstrapActions(actions, context) {
200
+ for (const action of actions) {
201
+ try {
202
+ const input = validateBootstrapPayload(action.payload, context.clock());
203
+ if (input.document?.path !== undefined)
204
+ canonicalSourcePath(context, input, {
205
+ id: input.id,
206
+ ...input.properties,
207
+ });
208
+ }
209
+ catch (error) {
210
+ throw new OperationError("BOOTSTRAP_PLAN_INVALID", `Bootstrap action ${action.id} is invalid: ${error instanceof Error ? error.message : String(error)}. Re-plan before applying; no bootstrap writes or journal were created.`, false);
211
+ }
212
+ }
213
+ }
214
+ // implements REQ-KIBI-BOOTSTRAP-PLAN
215
+ function isDeterministicBootstrapFailure(error) {
216
+ const detail = error instanceof Error ? error.message : String(error);
217
+ return (/(?:Entity|Relationship) validation failed|Proposition-complete ingestion failed|Contradiction detected for requirement|(?:'type' and 'id' are required|unsupported step entity type|every step needs an entity id)/i.test(detail) ||
218
+ (error instanceof OperationError &&
219
+ [
220
+ "BOOTSTRAP_PLAN_INVALID",
221
+ "SOURCE_PATH_INVALID",
222
+ "DOCUMENT_PATH_REQUIRED",
223
+ ].includes(error.code)));
224
+ }
207
225
  function isEntityDeletionApplyArgs(args) {
208
226
  return "plan" in args && args.plan.version === "kibi.entity-deletion-plan.v1";
209
227
  }
@@ -307,6 +325,48 @@ async function validateSources(context, sourceHashes) {
307
325
  }
308
326
  return checked;
309
327
  }
328
+ /**
329
+ * Validate one approved source write against the live workspace: the target
330
+ * must be workspace-relative, stay inside the workspace (also through
331
+ * symlinks), avoid Kibi's derived runtime trees, still hold the approved
332
+ * before-bytes, and carry an after-hash that matches its staged body.
333
+ */
334
+ async function resolveSourceWriteTarget(workspaceRoot, fsPort, write) {
335
+ if (!write.path ||
336
+ path.isAbsolute(write.path) ||
337
+ write.path.split(/[\\/]/).includes("..")) {
338
+ throw new Error(`Apply plan failed: sourceWrites.path must be workspace-relative: ${write.path}`);
339
+ }
340
+ const absolute = path.resolve(workspaceRoot, write.path);
341
+ const root = path.resolve(workspaceRoot);
342
+ assertSourceWriteStaysInWorkspace(root, absolute, write.path);
343
+ const relative = path.relative(root, absolute).split(path.sep).join("/");
344
+ if (relative === ".kb" || isDerivedKbPath(relative)) {
345
+ throw new Error("Apply plan failed: sourceWrites.path cannot target Kibi's derived .kb runtime trees");
346
+ }
347
+ const realRoot = canonicalFilesystemPath(root);
348
+ const realExisting = canonicalFilesystemPath(absolute);
349
+ if (realExisting !== realRoot &&
350
+ !realExisting.startsWith(`${realRoot}${path.sep}`)) {
351
+ throw new Error(`Apply plan failed: sourceWrites.path follows a symlink outside the workspace: ${write.path}`);
352
+ }
353
+ const existing = await fsPort.readFile(absolute).catch(() => undefined);
354
+ const beforeHash = existing === undefined ? null : digest(existing);
355
+ if (beforeHash !== write.beforeHash) {
356
+ throw new Error(`Apply plan failed: source hash changed for ${write.path}`);
357
+ }
358
+ const mode = write.mode ?? "write";
359
+ if (mode === "write" &&
360
+ (write.body === undefined ||
361
+ write.afterHash === null ||
362
+ digest(write.body) !== write.afterHash)) {
363
+ throw new Error(`Apply plan failed: afterHash does not match staged body for ${write.path}`);
364
+ }
365
+ if (mode === "delete" && write.afterHash !== null) {
366
+ throw new Error(`Apply plan failed: delete source write must have a null afterHash for ${write.path}`);
367
+ }
368
+ return { absolute, relative, existing, mode };
369
+ }
310
370
  async function applySourceWrites(context, writes, planHash, allowReplay = false, onCommitted = () => undefined) {
311
371
  if (writes.length === 0) {
312
372
  return { paths: [], rollback: async () => undefined, journalId: null };
@@ -412,44 +472,7 @@ async function applySourceWrites(context, writes, planHash, allowReplay = false,
412
472
  try {
413
473
  // Validate every target and hash before touching the working tree.
414
474
  for (const write of writes) {
415
- if (!write.path ||
416
- path.isAbsolute(write.path) ||
417
- write.path.split(/[\\/]/).includes("..")) {
418
- throw new Error(`Apply plan failed: sourceWrites.path must be workspace-relative: ${write.path}`);
419
- }
420
- const absolute = path.resolve(context.workspaceRoot, write.path);
421
- const root = path.resolve(context.workspaceRoot);
422
- assertSourceWriteStaysInWorkspace(root, absolute, write.path);
423
- const workspaceRelative = path
424
- .relative(root, absolute)
425
- .split(path.sep)
426
- .join("/");
427
- if (workspaceRelative === ".kb" || isDerivedKbPath(workspaceRelative)) {
428
- throw new Error("Apply plan failed: sourceWrites.path cannot target Kibi's derived .kb runtime trees");
429
- }
430
- const realRoot = canonicalFilesystemPath(root);
431
- const realExisting = canonicalFilesystemPath(absolute);
432
- if (realExisting !== realRoot &&
433
- !realExisting.startsWith(`${realRoot}${path.sep}`)) {
434
- throw new Error(`Apply plan failed: sourceWrites.path follows a symlink outside the workspace: ${write.path}`);
435
- }
436
- const existing = await context.fs
437
- .readFile(absolute)
438
- .catch(() => undefined);
439
- const beforeHash = existing === undefined ? null : digest(existing);
440
- if (beforeHash !== write.beforeHash) {
441
- throw new Error(`Apply plan failed: source hash changed for ${write.path}`);
442
- }
443
- const mode = write.mode ?? "write";
444
- if (mode === "write" &&
445
- (write.body === undefined ||
446
- write.afterHash === null ||
447
- digest(write.body) !== write.afterHash)) {
448
- throw new Error(`Apply plan failed: afterHash does not match staged body for ${write.path}`);
449
- }
450
- if (mode === "delete" && write.afterHash !== null) {
451
- throw new Error(`Apply plan failed: delete source write must have a null afterHash for ${write.path}`);
452
- }
475
+ const { absolute, existing, mode } = await resolveSourceWriteTarget(context.workspaceRoot, fsPort, write);
453
476
  originals.push({ absolute, body: existing });
454
477
  paths.push(write.path);
455
478
  const stageBase = path.join(context.workspaceRoot, ".kb", "recovery", `${journalId}-${entries.length}`);
@@ -656,6 +679,7 @@ async function executeBootstrapPlan(args, context, recovery = false, remainingAc
656
679
  const actions = recovery
657
680
  ? [...(remainingActions ?? args.plan.actions)]
658
681
  : validateBootstrapPlanShape(args);
682
+ preflightBootstrapActions(actions, context);
659
683
  const prolog = context.prolog ?? (await context.ensureProlog?.());
660
684
  if (!prolog)
661
685
  throw new Error("Bootstrap apply requires a Prolog runtime");
@@ -828,19 +852,22 @@ async function executeBootstrapPlan(args, context, recovery = false, remainingAc
828
852
  }
829
853
  catch (error) {
830
854
  const detail = error instanceof Error ? error.message : String(error);
855
+ const deterministic = isDeterministicBootstrapFailure(error);
831
856
  results.push({ actionId: action.id, outcome: "failed", detail });
832
- failures.push({ actionId: action.id, detail });
833
- await writeJournal("repair_required", activeActionId, lastCheckpoint);
857
+ failures.push({ actionId: action.id, detail, deterministic });
858
+ await writeJournal(deterministic ? "rejected" : "repair_required", activeActionId, lastCheckpoint);
834
859
  return {
835
860
  content: [
836
861
  {
837
862
  type: "text",
838
- text: `Bootstrap plan ${args.plan.planHash.slice(0, 12)} partially applied; repair journal ${journalId}.`,
863
+ text: deterministic
864
+ ? `Bootstrap plan rejected at ${action.id}: ${detail}. ${results.filter((row) => row.outcome === "applied").length} action(s) committed; re-plan from the current state.`
865
+ : `Bootstrap plan ${args.plan.planHash.slice(0, 12)} partially applied; repair journal ${journalId}.`,
839
866
  },
840
867
  ],
841
868
  structuredContent: {
842
869
  version: PLAN_APPLY_RESULT_VERSION,
843
- outcome: "partially_applied",
870
+ outcome: deterministic ? "rejected" : "partially_applied",
844
871
  planHash: args.plan.planHash,
845
872
  actionResults: results,
846
873
  changedEntities,
@@ -859,16 +886,24 @@ async function executeBootstrapPlan(args, context, recovery = false, remainingAc
859
886
  sourceHashesChecked: Object.keys(args.plan.expected.sourceHashes)
860
887
  .length,
861
888
  notes: [
862
- "Bootstrap application stopped at a repairable action failure.",
889
+ deterministic
890
+ ? "Bootstrap stopped at a deterministic action failure; the journal is terminal. Re-plan from the current state."
891
+ : "Bootstrap application stopped at a repairable action failure.",
863
892
  ],
864
893
  },
865
- status: "committed_with_repairs",
894
+ status: deterministic ? "rejected" : "committed_with_repairs",
866
895
  effectFailures: failures,
867
896
  nextActions: [
868
897
  {
869
- operation: "kb_apply_plan",
870
- input: { recoveryJournalId: journalId },
871
- reason: "Resume the remaining bootstrap actions from the immutable recovery journal; do not retry the original plan.",
898
+ operation: deterministic
899
+ ? "kb_plan_bootstrap"
900
+ : "kb_apply_plan",
901
+ ...(deterministic
902
+ ? {}
903
+ : { input: { recoveryJournalId: journalId } }),
904
+ reason: deterministic
905
+ ? "Re-plan a corrected bootstrap from the current state; this journal cannot be replayed."
906
+ : "Resume the remaining bootstrap actions from the immutable recovery journal; do not retry the original plan.",
872
907
  required: true,
873
908
  },
874
909
  ],
@@ -963,9 +998,29 @@ async function executeBootstrapPlan(args, context, recovery = false, remainingAc
963
998
  },
964
999
  };
965
1000
  }
966
- // implements REQ-kibi-change-to-proof-plan-compiler, REQ-agent-guided-migration-orchestration
1001
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2, REQ-core-atomic-upsert-persistence
967
1002
  async function executeApplyPlanUnlocked(args, context, onCommitted = () => undefined) {
1003
+ // Finish any interrupted plan application before this call reads or writes
1004
+ // anything, and report what was done alongside this call's own result.
1005
+ const recovered = await recoverInterruptedPlans(context, "recoveryJournalId" in args ? args.recoveryJournalId : undefined);
1006
+ if (recovered.some((recovery) => recovery.action !== "none"))
1007
+ onCommitted();
1008
+ let result;
1009
+ try {
1010
+ result = await dispatchApplyPlan(args, context, onCommitted, recovered);
1011
+ }
1012
+ catch (error) {
1013
+ // A plan compiled before the recovery commonly fails its snapshot check
1014
+ // now; the failure still names the interrupted plan that was settled.
1015
+ throw withPlanRecoveryNotes(error, planRecoveryNotes(recovered));
1016
+ }
1017
+ return withRecoveryReport(result, recovered);
1018
+ }
1019
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2, REQ-agent-guided-migration-orchestration
1020
+ async function dispatchApplyPlan(args, context, onCommitted, recovered) {
968
1021
  if ("recoveryJournalId" in args) {
1022
+ if (isPlanApplyJournalId(args.recoveryJournalId))
1023
+ return executePlanJournalRecovery(args.recoveryJournalId, context, recovered);
969
1024
  if (args.recoveryJournalId.startsWith("bootstrap-")) {
970
1025
  if (!context.fs)
971
1026
  throw new Error("Bootstrap recovery requires a filesystem-capable runtime");
@@ -977,6 +1032,10 @@ async function executeApplyPlanUnlocked(args, context, onCommitted = () => undef
977
1032
  journal.kind !== "bootstrap" ||
978
1033
  !journal.plan)
979
1034
  throw new Error("Bootstrap recovery journal is invalid");
1035
+ if (journal.state === "rejected" ||
1036
+ (journal.results ?? []).some((row) => row.outcome === "failed" &&
1037
+ isDeterministicBootstrapFailure(new Error(String(row.detail)))))
1038
+ throw new OperationError("BOOTSTRAP_PLAN_REJECTED", "Bootstrap recovery refused: deterministic failure is terminal; re-plan from the current state.", false);
980
1039
  if (!/^[a-f0-9]{64}$/i.test(journal.plan.planHash) ||
981
1040
  bootstrapPlanHash(journal.plan) !== journal.plan.planHash)
982
1041
  throw new Error("Bootstrap recovery journal plan hash is invalid");
@@ -1098,7 +1157,428 @@ async function executeApplyPlanUnlocked(args, context, onCommitted = () => undef
1098
1157
  },
1099
1158
  };
1100
1159
  }
1160
+ return executeCompilePlan(args, context, onCommitted, recovered);
1161
+ }
1162
+ // implements REQ-kibi-truthful-consistency
1163
+ /**
1164
+ * Run every step through the validation chain executeUpsert runs. Each step
1165
+ * is validated against the live store plus what the earlier steps will have
1166
+ * written by then (`staged`): entities they create count as relationship
1167
+ * targets, their kinds and claim keys are read from the plan, their
1168
+ * relationships merge into a later upsert of the same entity, and their
1169
+ * predicate schemas govern later predicate facts.
1170
+ */
1171
+ async function validatePlanSteps(steps, context) {
1172
+ const stagedEntities = new Map();
1173
+ const stagedRelationships = [];
1174
+ const validated = [];
1175
+ for (const step of steps) {
1176
+ try {
1177
+ const { validated: result } = await validateUpsertForCommit(step, context, {
1178
+ staged: {
1179
+ entities: stagedEntities,
1180
+ relationships: [...stagedRelationships],
1181
+ },
1182
+ });
1183
+ stagedEntities.set(step.id, result.entity);
1184
+ stagedRelationships.push(...result.relationships);
1185
+ validated.push({
1186
+ input: step,
1187
+ entity: result.entity,
1188
+ relationships: result.relationships,
1189
+ });
1190
+ }
1191
+ catch (error) {
1192
+ throw new Error(`step ${step.id} is invalid: ${error instanceof Error ? error.message : String(error)}`);
1193
+ }
1194
+ }
1195
+ return validated;
1196
+ }
1197
+ /**
1198
+ * Every workspace file the plan changes, with exact before/after bytes: the
1199
+ * plan's approved sourceWrites, the authored document of every entity step,
1200
+ * and the relationship shards its steps append, each rendered exactly as
1201
+ * executeUpsert would write it, in step order. `sources` maps a step index to
1202
+ * its document, which becomes the committed entity's `source`.
1203
+ */
1204
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2
1205
+ async function planFileWrites(context, prolog, fsPort, sourceWrites, steps, now) {
1206
+ const workspaceRoot = context.workspaceRoot;
1207
+ const files = new Map();
1208
+ for (const write of sourceWrites) {
1209
+ const target = await resolveSourceWriteTarget(workspaceRoot, fsPort, write);
1210
+ if (files.has(target.relative)) {
1211
+ throw new Error(`Apply plan failed: sourceWrites lists ${write.path} more than once`);
1212
+ }
1213
+ if (target.mode === "delete" && !fsPort.unlink) {
1214
+ throw new Error(`Apply plan failed: delete requires filesystem unlink support: ${write.path}`);
1215
+ }
1216
+ const before = target.existing ?? null;
1217
+ const after = target.mode === "write" ? (write.body ?? "") : null;
1218
+ files.set(target.relative, {
1219
+ path: target.relative,
1220
+ origin: "plan",
1221
+ mode: target.mode,
1222
+ before,
1223
+ beforeHash: before === null ? null : contentHash(before),
1224
+ after,
1225
+ afterHash: after === null ? null : contentHash(after),
1226
+ });
1227
+ }
1228
+ // Entity documents: the bytes kb_upsert would publish for the validated
1229
+ // entity, merged into the document as the plan leaves it. A plan without
1230
+ // them leaves its entities only in the branch store, so a rebuild or a
1231
+ // fresh clone loses them. Symbols keep the kb_upsert path, which also
1232
+ // refreshes their generated coordinates.
1233
+ const sources = new Map();
1234
+ for (const [index, step] of steps.entries()) {
1235
+ if (step.input.type === "symbol")
1236
+ continue;
1237
+ const [existing] = step.input.document?.path === undefined
1238
+ ? await loadEntities(prolog, {
1239
+ id: step.input.id,
1240
+ type: step.input.type,
1241
+ })
1242
+ : [];
1243
+ const relative = canonicalSourcePath(context, step.input, step.entity, existing);
1244
+ let file = files.get(relative);
1245
+ if (file === undefined) {
1246
+ // canonicalSourcePath already refused traversal, derived .kb trees and
1247
+ // symlinks that leave the workspace.
1248
+ const existingBytes = await fsPort
1249
+ .readFile(path.join(workspaceRoot, relative))
1250
+ .catch(() => undefined);
1251
+ const before = existingBytes ?? null;
1252
+ file = {
1253
+ path: relative,
1254
+ origin: "entity-document",
1255
+ mode: "write",
1256
+ before,
1257
+ beforeHash: before === null ? null : contentHash(before),
1258
+ after: before,
1259
+ afterHash: before === null ? null : contentHash(before),
1260
+ };
1261
+ files.set(relative, file);
1262
+ }
1263
+ if (file.mode === "delete") {
1264
+ throw new Error(`Apply plan failed: step ${step.input.id} writes ${relative}, which the plan deletes`);
1265
+ }
1266
+ sources.set(index, relative);
1267
+ // A plan sourceWrite for the same file is approved, byte-exact content:
1268
+ // it is the document, not a base to render into.
1269
+ if (file.origin === "plan")
1270
+ continue;
1271
+ const after = renderSourceDocument(step.input, step.entity, file.after ?? undefined, relative);
1272
+ file.after = after;
1273
+ file.afterHash = contentHash(after);
1274
+ }
1275
+ const root = path.resolve(workspaceRoot);
1276
+ const kbRoot = path.join(workspaceRoot, ".kb");
1277
+ const createdAt = now.toISOString();
1278
+ for (const step of steps) {
1279
+ for (const relationship of step.relationships) {
1280
+ const type = typeof relationship.type === "string" ? relationship.type : "";
1281
+ const from = typeof relationship.from === "string"
1282
+ ? relationship.from
1283
+ : step.input.id;
1284
+ const to = typeof relationship.to === "string" ? relationship.to : "";
1285
+ if (!type || !from || !to)
1286
+ continue;
1287
+ const shardPath = computeShardPath(kbRoot, from);
1288
+ assertRelationshipShardContained(workspaceRoot, shardPath);
1289
+ const relative = path
1290
+ .relative(root, path.resolve(shardPath))
1291
+ .split(path.sep)
1292
+ .join("/");
1293
+ let file = files.get(relative);
1294
+ if (file === undefined) {
1295
+ const existing = await fsPort.readFile(shardPath).catch(() => null);
1296
+ const existingHash = existing === null ? null : contentHash(existing);
1297
+ file = {
1298
+ path: relative,
1299
+ origin: "relationship-shard",
1300
+ mode: "write",
1301
+ before: existing,
1302
+ beforeHash: existingHash,
1303
+ after: existing,
1304
+ afterHash: existingHash,
1305
+ };
1306
+ files.set(relative, file);
1307
+ }
1308
+ const next = renderShardWithRelationship(shardPath, file.after, {
1309
+ type,
1310
+ from,
1311
+ to,
1312
+ created_at: createdAt,
1313
+ created_by: "kibi/upsert",
1314
+ source: "mcp://kibi/upsert",
1315
+ });
1316
+ if (next !== null) {
1317
+ file.mode = "write";
1318
+ file.after = next;
1319
+ file.afterHash = contentHash(next);
1320
+ }
1321
+ }
1322
+ }
1323
+ return {
1324
+ files: [...files.values()].filter((file) => file.origin === "plan" ||
1325
+ file.origin === "entity-document" ||
1326
+ file.afterHash !== file.beforeHash),
1327
+ sources,
1328
+ };
1329
+ }
1330
+ function errorMessage(error) {
1331
+ return error instanceof Error ? error.message : String(error);
1332
+ }
1333
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2, REQ-core-atomic-upsert-persistence
1334
+ /**
1335
+ * Apply a validated compile plan all-or-nothing:
1336
+ * 1. journal every file change (exact bytes) and every store upsert, plus a
1337
+ * fingerprint of the touched store entities, before the first write;
1338
+ * 2. publish the files with temp-file + rename (fsync where available);
1339
+ * 3. commit every step in one Prolog batch transaction — the only commit
1340
+ * point.
1341
+ * Any failure before the commit restores every file from the journal and
1342
+ * leaves the store untouched; an interrupted application is completed or
1343
+ * rolled back from the journal by the next mutating call.
1344
+ */
1345
+ async function commitCompilePlan(context, prolog, plan, steps, revalidate, now, onCommitted) {
1346
+ // Store upserts, one per step. A step with an authored document commits
1347
+ // that document as its source, exactly as a source-first kb_upsert does.
1348
+ const storeEntries = (sources = new Map()) => steps.map((step, index) => {
1349
+ const source = sources.get(index);
1350
+ return {
1351
+ entity: source === undefined ? step.entity : { ...step.entity, source },
1352
+ relationships: step.relationships,
1353
+ skipContradictionCheck: step.input._skipContradictionCheck === true,
1354
+ };
1355
+ });
1356
+ let entries = storeEntries();
1357
+ const entityIds = [
1358
+ ...new Set(entries.map((entry) => String(entry.entity.id))),
1359
+ ];
1360
+ const changedRelationships = entries.reduce((sum, entry) => sum + entry.relationships.length, 0);
1361
+ const storeRejection = (result) => new Error(`the store rejected the plan's batch transaction: ${formatUpsertError(entityIds.length === 1
1362
+ ? String(entityIds[0])
1363
+ : `plan batch [${entityIds.join(", ")}]`, result.error, result.errorRecord)}`);
1364
+ const fsPort = context.fs;
1365
+ if (fsPort === undefined) {
1366
+ if (plan.sourceWrites.length > 0) {
1367
+ throw new Error("Apply plan failed: sourceWrites require a filesystem-capable runtime");
1368
+ }
1369
+ // Without a filesystem there are no workspace files to coordinate: the
1370
+ // single store transaction is the whole application.
1371
+ const written = await prolog.query(buildUpsertBatchCommitGoal(entries));
1372
+ if (!written.success) {
1373
+ throw new Error(`Apply plan failed; no change was applied: ${storeRejection(written).message}`);
1374
+ }
1375
+ onCommitted();
1376
+ prolog.invalidateCache?.();
1377
+ return {
1378
+ journalId: null,
1379
+ changedEntities: entries.length,
1380
+ changedRelationships,
1381
+ documentPaths: [],
1382
+ effectFailures: [],
1383
+ nextActions: [],
1384
+ };
1385
+ }
1386
+ const slot = openPlanApplyJournal(context, plan.planHash);
1387
+ if (slot.existing?.state === "committed") {
1388
+ throw new OperationError("MUTATION_ALREADY_COMMITTED", `MUTATION_ALREADY_COMMITTED: plan ${plan.planHash} was already applied (journal ${slot.journalId}); compile a fresh plan instead of applying it again`, false);
1389
+ }
1390
+ if (slot.existing !== null && slot.existing.state !== "rolled_back") {
1391
+ throw new OperationError("PLAN_APPLY_RECOVERY_REQUIRED", `Plan ${plan.planHash} has an unfinished journal ${slot.journalId}; run kb_apply_plan with recoveryJournalId=${slot.journalId} before applying it again`, false);
1392
+ }
1393
+ const { files, sources } = await planFileWrites(context, prolog, fsPort, plan.sourceWrites, steps, now);
1394
+ entries = storeEntries(sources);
1395
+ const goal = buildUpsertBatchCommitGoal(entries);
1396
+ const documentPaths = [
1397
+ ...new Set([
1398
+ ...plan.sourceWrites.map((write) => write.path),
1399
+ ...sources.values(),
1400
+ ]),
1401
+ ];
1402
+ let preCommitFingerprint;
1403
+ try {
1404
+ preCommitFingerprint = await storeFingerprint(prolog, entityIds);
1405
+ }
1406
+ catch (error) {
1407
+ throw new Error(`Apply plan failed before any write: the store could not be fingerprinted for the plan journal: ${errorMessage(error)}`);
1408
+ }
1409
+ const handle = createPlanApplyJournal(slot.journalPath, {
1410
+ version: PLAN_APPLY_JOURNAL_VERSION,
1411
+ journalId: slot.journalId,
1412
+ planHash: plan.planHash,
1413
+ branch: slot.branch,
1414
+ state: "prepared",
1415
+ createdAt: now.toISOString(),
1416
+ updatedAt: now.toISOString(),
1417
+ files,
1418
+ store: { entityIds, preCommitFingerprint, entries },
1419
+ summary: {
1420
+ planPaths: plan.sourceWrites.map((write) => write.path),
1421
+ filePaths: files.map((file) => file.path),
1422
+ changedEntities: entries.length,
1423
+ changedRelationships,
1424
+ },
1425
+ });
1426
+ const rollBack = async (error, phase) => {
1427
+ let restored;
1428
+ try {
1429
+ restored = await restoreJournaledFiles(fsPort, context.workspaceRoot, handle.journalPath, files);
1430
+ }
1431
+ catch (rollbackError) {
1432
+ throw new OperationError("PLAN_APPLY_RECOVERY_REQUIRED", `Plan ${plan.planHash} failed ${phase} (${errorMessage(error)}), and restoring its source writes also failed (${errorMessage(rollbackError)}). Journal ${slot.journalId} is kept: the next kb_apply_plan or kb_upsert call rolls it back, or run kb_apply_plan with recoveryJournalId=${slot.journalId}`, false);
1433
+ }
1434
+ const detail = `Plan ${plan.planHash.slice(0, 12)} failed ${phase}; no change was applied: the store is unchanged and ${restored.length} source file(s) were restored from journal ${slot.journalId}.`;
1435
+ try {
1436
+ recordPlanApplyJournal(handle, {
1437
+ state: "rolled_back",
1438
+ resolution: { action: "rolled_back", by: "apply", detail },
1439
+ });
1440
+ }
1441
+ catch {
1442
+ // The files are restored and the store never committed, so a journal
1443
+ // left in an earlier state resolves to the same rollback next time.
1444
+ }
1445
+ const message = `Apply plan failed ${phase}; no change was applied (store unchanged, ${restored.length} source file(s) restored from journal ${slot.journalId}): ${errorMessage(error)}`;
1446
+ if (error instanceof OperationError) {
1447
+ throw new OperationError(error.code, message, error.retryable);
1448
+ }
1449
+ throw new Error(message);
1450
+ };
1451
+ try {
1452
+ await publishJournaledFiles(fsPort, context.workspaceRoot, files);
1453
+ }
1454
+ catch (error) {
1455
+ return rollBack(error, "while publishing its source writes");
1456
+ }
1457
+ if (plan.sourceWrites.length > 0) {
1458
+ // Steps were validated against the workspace as it was; validate them
1459
+ // again against the published source bytes before the commit.
1460
+ try {
1461
+ await revalidate();
1462
+ }
1463
+ catch (error) {
1464
+ return rollBack(error, "validation against its published source writes");
1465
+ }
1466
+ }
1467
+ try {
1468
+ recordPlanApplyJournal(handle, { state: "store_committing" });
1469
+ }
1470
+ catch (error) {
1471
+ return rollBack(error, "while journaling its store commit");
1472
+ }
1473
+ let written;
1474
+ let commitFailure;
1475
+ try {
1476
+ written = await prolog.query(goal);
1477
+ if (!written.success)
1478
+ commitFailure = storeRejection(written);
1479
+ }
1480
+ catch (error) {
1481
+ commitFailure = error;
1482
+ }
1483
+ const effectFailures = [];
1484
+ const nextActions = [];
1485
+ if (commitFailure !== undefined) {
1486
+ // The batch is one transaction: it either changed the plan's entities or
1487
+ // changed nothing. Decide from the store, not from the transport.
1488
+ let fingerprint;
1489
+ try {
1490
+ fingerprint = await storeFingerprint(prolog, entityIds);
1491
+ }
1492
+ catch (probeError) {
1493
+ if (written === undefined) {
1494
+ throw new OperationError("PLAN_APPLY_RECOVERY_REQUIRED", `Plan ${plan.planHash} lost contact with the store during its commit (${errorMessage(commitFailure)}), and the store could not be inspected (${errorMessage(probeError)}). Journal ${slot.journalId} is kept: the next kb_apply_plan or kb_upsert call completes or rolls it back, or run kb_apply_plan with recoveryJournalId=${slot.journalId}`, false);
1495
+ }
1496
+ // The store answered with a rejection; trust it.
1497
+ }
1498
+ if (fingerprint === undefined || fingerprint === preCommitFingerprint) {
1499
+ return rollBack(commitFailure, "at its store commit");
1500
+ }
1501
+ effectFailures.push({
1502
+ kind: "store-commit",
1503
+ errorCode: "STORE_COMMIT_REPORTED_FAILURE",
1504
+ detail: `The store reported a failure (${errorMessage(commitFailure)}) but shows the plan's batch committed; the plan is kept as applied.`,
1505
+ });
1506
+ nextActions.push({
1507
+ operation: "kb_status",
1508
+ reason: "The store committed the plan's batch while reporting a failure; confirm the branch snapshot is persisted before further writes.",
1509
+ required: true,
1510
+ }, {
1511
+ operation: "kb_check",
1512
+ reason: "Run the consistency checks after the unconfirmed store commit and follow their typed repair actions.",
1513
+ required: true,
1514
+ });
1515
+ }
1516
+ // The batch transaction committed: every step is in the store.
1517
+ onCommitted();
1518
+ prolog.invalidateCache?.();
1519
+ try {
1520
+ recordPlanApplyJournal(handle, { state: "store_committed" });
1521
+ }
1522
+ catch {
1523
+ // A journal left at store_committing is completed on the next call: the
1524
+ // store fingerprint now shows the commit.
1525
+ }
1526
+ try {
1527
+ reconcilePendingSourceReceipts(context.workspaceRoot, files);
1528
+ }
1529
+ catch (error) {
1530
+ effectFailures.push({
1531
+ kind: "pending-source-receipt",
1532
+ errorCode: "PENDING_SOURCE_RECEIPT_FAILED",
1533
+ detail: errorMessage(error),
1534
+ });
1535
+ nextActions.push({
1536
+ operation: "kb_apply_plan",
1537
+ input: { recoveryJournalId: slot.journalId },
1538
+ reason: "The plan committed, but binding its new source files to pending-source receipts failed; recovering the journal finishes the receipts. Do not apply the original plan again.",
1539
+ required: true,
1540
+ });
1541
+ return {
1542
+ journalId: slot.journalId,
1543
+ changedEntities: entries.length,
1544
+ changedRelationships,
1545
+ documentPaths,
1546
+ effectFailures,
1547
+ nextActions,
1548
+ };
1549
+ }
1550
+ try {
1551
+ recordPlanApplyJournal(handle, {
1552
+ state: "committed",
1553
+ resolution: {
1554
+ action: "completed",
1555
+ by: "apply",
1556
+ detail: `Applied plan ${plan.planHash.slice(0, 12)}: ${entries.length} step(s) in one store transaction and ${files.length} journaled file(s).`,
1557
+ },
1558
+ });
1559
+ }
1560
+ catch {
1561
+ // A store_committed journal completes idempotently on the next call.
1562
+ }
1563
+ return {
1564
+ journalId: slot.journalId,
1565
+ changedEntities: entries.length,
1566
+ changedRelationships,
1567
+ documentPaths,
1568
+ effectFailures,
1569
+ nextActions,
1570
+ };
1571
+ }
1572
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2
1573
+ async function executeCompilePlan(args, context, onCommitted, recovered) {
1101
1574
  validateCompilePlanShape(args);
1575
+ // Re-applying a plan whose interrupted application this call just
1576
+ // completed returns that completion instead of failing its snapshot check.
1577
+ const completed = recovered.find((recovery) => recovery.planHash === args.plan.planHash &&
1578
+ recovery.action === "completed");
1579
+ if (completed !== undefined) {
1580
+ return planJournalRecoveryResult(context, completed, openPlanApplyJournalById(context, completed.journalId).journal);
1581
+ }
1102
1582
  const prolog = context.prolog ?? (await context.ensureProlog?.()) ?? undefined;
1103
1583
  if (!prolog)
1104
1584
  throw new Error("Apply plan requires a Prolog runtime");
@@ -1106,10 +1586,16 @@ async function executeApplyPlanUnlocked(args, context, onCommitted = () => undef
1106
1586
  // compiled entity steps against the staged source snapshot without asking
1107
1587
  // each step to independently select a document target; otherwise a plan
1108
1588
  // with multiple authored entity kinds could be rejected for an ambiguous
1109
- // per-entity path after its source batch has already been validated.
1110
- const operationContext = context.prolog
1111
- ? { ...context, sourceFirst: false }
1112
- : { ...context, prolog, sourceFirst: false };
1589
+ // per-entity path after its source batch has already been validated. One
1590
+ // clock reading stamps every step, so the journaled store payload is exactly
1591
+ // what validation produced.
1592
+ const now = context.clock();
1593
+ const operationContext = {
1594
+ ...context,
1595
+ prolog,
1596
+ sourceFirst: false,
1597
+ clock: () => now,
1598
+ };
1113
1599
  const statusResult = await executeStatus({}, operationContext);
1114
1600
  const status = statusResult.structuredContent;
1115
1601
  if (!status)
@@ -1125,77 +1611,55 @@ async function executeApplyPlanUnlocked(args, context, onCommitted = () => undef
1125
1611
  throw new Error("Apply plan failed: workspace snapshot changed since compilation");
1126
1612
  const sourceHashesChecked = await validateSources(operationContext, args.plan.expected.sourceHashes);
1127
1613
  const steps = args.plan.steps.map((step) => asUpsert(step));
1128
- const sourceWrites = await applySourceWrites(operationContext, args.plan.sourceWrites, args.plan.planHash, false, onCommitted);
1129
- const notes = [
1130
- "Plan steps and tracked source writes are validated before sequential application; source writes are journaled for replay.",
1131
- ];
1132
- let changedEntities = 0;
1133
- let changedRelationships = 0;
1134
- let appliedSteps = 0;
1135
- const effectFailures = [];
1136
- const nextActions = [];
1137
- let compiledCommit = false;
1614
+ // implements REQ-kibi-truthful-consistency
1615
+ // Run every step through the same validation chain executeUpsert runs,
1616
+ // then stage all of them together in a rolled-back transaction, before the
1617
+ // first write. A step executeUpsert would reject, or a final state that
1618
+ // introduces a contradiction or an infeasible success scenario, fails the
1619
+ // whole plan up front. Limits of the staged emulation:
1620
+ // - symbol granularity reads source code from the workspace as it is now;
1621
+ // the plan's sourceWrites are validated again once they are published,
1622
+ // before the store commit, and a failure then rolls them back;
1623
+ // - staged entities are the validated plan payloads; fields executeUpsert
1624
+ // derives while committing source-first (canonical symbol re-extraction,
1625
+ // chosen source paths) do not arise, because compile plans apply with
1626
+ // sourceFirst disabled;
1627
+ // - the store's own checks (entity schema, relationship endpoints,
1628
+ // requirement contradictions) run again inside the batch transaction, so
1629
+ // state another writer changed after this preflight aborts the whole
1630
+ // batch instead of committing part of it.
1631
+ let validated;
1138
1632
  try {
1139
- for (const step of steps) {
1140
- const result = await executeUpsert(step, operationContext);
1141
- appliedSteps += 1;
1142
- // Each step's compiled commit is an authoritative mutation boundary.
1143
- // Mark it immediately so a later step's failure can never be mistaken
1144
- // for a retryable whole-plan failure.
1145
- onCommitted();
1146
- const payload = result.structuredContent;
1147
- if (payload && typeof payload === "object") {
1148
- const row = payload;
1149
- changedEntities += Number(row.created ?? 0) + Number(row.updated ?? 0);
1150
- changedRelationships += Number(row.relationships_created ?? 0);
1151
- const details = payload;
1152
- if (Array.isArray(details.effectFailures)) {
1153
- effectFailures.push(...details.effectFailures.filter(isRecord));
1154
- }
1155
- if (Array.isArray(details.nextActions)) {
1156
- nextActions.push(...details.nextActions.filter(isRecord));
1157
- }
1158
- }
1159
- }
1160
- compiledCommit = true;
1161
- onCommitted();
1633
+ validated = await validatePlanSteps(steps, operationContext);
1162
1634
  }
1163
1635
  catch (error) {
1164
- if (sourceWrites.journalId !== null) {
1165
- effectFailures.push({
1166
- kind: "compiled-store",
1167
- errorCode: "DERIVED_COMMIT_FAILED",
1168
- detail: error instanceof Error ? error.message : String(error),
1169
- });
1170
- nextActions.push({
1171
- operation: "kb_apply_plan",
1172
- input: { recoveryJournalId: sourceWrites.journalId },
1173
- reason: "Authoritative source files are committed but compiled effects failed; replay the recovery journal instead of retrying the original mutation.",
1174
- required: true,
1175
- });
1176
- onCommitted();
1177
- await markSourceJournal(operationContext, sourceWrites.journalId, "repair_required");
1178
- }
1179
- else if (appliedSteps > 0) {
1180
- // A journalless plan (sourceWrites: []) already committed earlier steps
1181
- // when a later step failed. Expose an explicit non-retryable
1182
- // partial-commit error instead of a generic exception that invites a
1183
- // blind retry of the whole plan.
1184
- if (error instanceof OperationError)
1185
- throw error;
1186
- throw new OperationError("PARTIAL_COMMIT_REPAIR_REQUIRED", `Plan ${args.plan.planHash} committed ${appliedSteps} of ${steps.length} step(s) before failing: ${error instanceof Error ? error.message : String(error)}. This plan carries no recovery journal; do not retry the original plan; inspect the committed snapshot and apply a fresh plan for the remaining steps`, false);
1187
- }
1188
- else {
1189
- throw error;
1190
- }
1191
- }
1192
- if (compiledCommit) {
1193
- await markSourceJournal(operationContext, sourceWrites.journalId, "compiled_published");
1636
+ throw new Error(`Apply plan failed before any write: ${errorMessage(error)}`);
1194
1637
  }
1638
+ const preflight = await prolog.query(planWhatIfGoal(args.plan.steps, now));
1639
+ if (!preflight.success)
1640
+ throw new Error(`Apply plan failed before any write: the store rejected the staged plan: ${preflight.error ?? "unknown error"}`);
1641
+ const staged = parseWhatIfAnalysis(preflight.bindings.JsonString);
1642
+ if (staged === null)
1643
+ throw new Error("Apply plan failed before any write: the staged plan's contradiction analysis could not be read");
1644
+ const introducedBlocking = staged.introduced.filter(isBlockingWitness);
1645
+ if (introducedBlocking.length > 0)
1646
+ throw new Error(`Apply plan failed before any write: the staged plan introduces ${introducedBlocking.length} contradiction(s) or infeasible scenario(s): ${introducedBlocking
1647
+ .map((witness) => witness.reason || witness.requirements.join("/"))
1648
+ .join("; ")}`);
1649
+ const commit = await commitCompilePlan(operationContext, prolog, args.plan, validated, async () => {
1650
+ await validatePlanSteps(steps, operationContext);
1651
+ }, now, onCommitted);
1652
+ const effectFailures = [...commit.effectFailures];
1653
+ const nextActions = [...commit.nextActions];
1654
+ const notes = [
1655
+ commit.journalId === null
1656
+ ? "Plan steps were validated together and committed in one store transaction."
1657
+ : `Plan steps were validated together, journaled in ${commit.journalId}, and committed in one store transaction; source writes are restored from the journal if the commit fails.`,
1658
+ ];
1195
1659
  // Everything before this point is authoritative. A status/workspace
1196
1660
  // readback failure therefore cannot turn the operation into a retryable
1197
- // mutation: return a repairable partial completion with deterministic next
1198
- // actions instead.
1661
+ // mutation: return a repairable completion with deterministic next actions
1662
+ // instead.
1199
1663
  let finalStatus;
1200
1664
  let finalWorkspace;
1201
1665
  let postCommitFailure;
@@ -1211,7 +1675,7 @@ async function executeApplyPlanUnlocked(args, context, onCommitted = () => undef
1211
1675
  }
1212
1676
  }
1213
1677
  catch (error) {
1214
- postCommitFailure = error instanceof Error ? error.message : String(error);
1678
+ postCommitFailure = errorMessage(error);
1215
1679
  effectFailures.push({
1216
1680
  kind: "post-commit-readback",
1217
1681
  errorCode: "POST_COMMIT_READBACK_FAILED",
@@ -1226,7 +1690,6 @@ async function executeApplyPlanUnlocked(args, context, onCommitted = () => undef
1226
1690
  reason: "After status is readable, run the consistency checks and follow their typed repair actions.",
1227
1691
  required: true,
1228
1692
  });
1229
- await markSourceJournal(operationContext, sourceWrites.journalId, "repair_required");
1230
1693
  }
1231
1694
  const finalSnapshots = finalStatus !== undefined && finalWorkspace?.available === true
1232
1695
  ? {
@@ -1243,9 +1706,9 @@ async function executeApplyPlanUnlocked(args, context, onCommitted = () => undef
1243
1706
  version: PLAN_APPLY_RESULT_VERSION,
1244
1707
  outcome: "applied",
1245
1708
  planHash: args.plan.planHash,
1246
- changedEntities,
1247
- changedRelationships,
1248
- changedPaths: sourceWrites.paths,
1709
+ changedEntities: commit.changedEntities,
1710
+ changedRelationships: commit.changedRelationships,
1711
+ changedPaths: [...commit.documentPaths],
1249
1712
  finalSnapshots,
1250
1713
  validationSummary: {
1251
1714
  stepsValidated: steps.length,
@@ -1253,7 +1716,7 @@ async function executeApplyPlanUnlocked(args, context, onCommitted = () => undef
1253
1716
  sourceHashesChecked,
1254
1717
  notes,
1255
1718
  },
1256
- recoveryJournalId: sourceWrites.journalId,
1719
+ recoveryJournalId: commit.journalId,
1257
1720
  ...(effectFailures.length > 0 || postCommitFailure !== undefined
1258
1721
  ? {
1259
1722
  status: "committed_with_repairs",
@@ -1266,14 +1729,155 @@ async function executeApplyPlanUnlocked(args, context, onCommitted = () => undef
1266
1729
  content: [
1267
1730
  {
1268
1731
  type: "text",
1269
- text: `Applied plan ${args.plan.planHash.slice(0, 12)} with ${steps.length} sequential step(s).`,
1732
+ text: `Applied plan ${args.plan.planHash.slice(0, 12)}: ${steps.length} step(s) committed in one store transaction.`,
1270
1733
  },
1271
1734
  ],
1272
1735
  structuredContent: payload,
1273
1736
  };
1274
1737
  }
1275
- // implements REQ-agent-guided-migration-orchestration, REQ-cli-canonical-runtime, REQ-KIBI-BOOTSTRAP-PLAN, REQ-kibi-change-to-proof-plan-compiler, REQ-kibi-predicate-vocabulary-migration
1738
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2, REQ-core-atomic-upsert-persistence
1739
+ /** The kb_apply_plan result for a plan journal this call recovered or found finished. */
1740
+ async function planJournalRecoveryResult(context, recovery, journal) {
1741
+ let finalSnapshots = {
1742
+ branch: journal.branch,
1743
+ kbSnapshotId: "recovered",
1744
+ workspaceSnapshot: "recovered",
1745
+ };
1746
+ try {
1747
+ const status = (await executeStatus({}, context)).structuredContent;
1748
+ const workspace = await readWorkspaceSnapshot(context);
1749
+ if (status !== undefined && workspace.available) {
1750
+ finalSnapshots = {
1751
+ branch: status.branch,
1752
+ kbSnapshotId: status.snapshotId,
1753
+ workspaceSnapshot: workspace.snapshot.hash,
1754
+ };
1755
+ }
1756
+ }
1757
+ catch {
1758
+ // Snapshot readback is informational after a recovery.
1759
+ }
1760
+ const completedNow = recovery.action === "completed";
1761
+ return {
1762
+ content: [{ type: "text", text: recovery.detail }],
1763
+ structuredContent: {
1764
+ version: PLAN_APPLY_RESULT_VERSION,
1765
+ outcome: recovery.state === "committed" ? "replayed" : "rolled_back",
1766
+ planHash: recovery.planHash,
1767
+ changedEntities: completedNow ? journal.summary.changedEntities : 0,
1768
+ changedRelationships: completedNow
1769
+ ? journal.summary.changedRelationships
1770
+ : 0,
1771
+ changedPaths: recovery.restoredPaths,
1772
+ finalSnapshots,
1773
+ validationSummary: {
1774
+ stepsValidated: 0,
1775
+ stepsApplied: completedNow ? journal.summary.changedEntities : 0,
1776
+ sourceHashesChecked: journal.summary.filePaths.length,
1777
+ notes: [recovery.detail],
1778
+ },
1779
+ recoveryJournalId: recovery.journalId,
1780
+ ...(recovery.receiptFailure !== undefined
1781
+ ? {
1782
+ status: "committed_with_repairs",
1783
+ effectFailures: [
1784
+ {
1785
+ kind: "pending-source-receipt",
1786
+ errorCode: "PENDING_SOURCE_RECEIPT_FAILED",
1787
+ detail: recovery.receiptFailure,
1788
+ },
1789
+ ],
1790
+ nextActions: [
1791
+ {
1792
+ operation: "kb_apply_plan",
1793
+ input: { recoveryJournalId: recovery.journalId },
1794
+ reason: "The plan is committed, but binding its new source files to pending-source receipts failed; recover the journal again to finish them.",
1795
+ required: true,
1796
+ },
1797
+ ],
1798
+ }
1799
+ : {}),
1800
+ },
1801
+ };
1802
+ }
1803
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2, REQ-core-atomic-upsert-persistence
1804
+ async function executePlanJournalRecovery(journalId, context, recovered) {
1805
+ if (!context.fs)
1806
+ throw new Error("Plan journal recovery requires a filesystem-capable runtime");
1807
+ const handle = openPlanApplyJournalById(context, journalId);
1808
+ const recovery = recovered.find((candidate) => candidate.journalId === journalId) ??
1809
+ (await recoverPlanApplyJournal(context, handle, {
1810
+ prolog: async () => context.prolog ?? (await context.ensureProlog?.()),
1811
+ }));
1812
+ return planJournalRecoveryResult(context, recovery, handle.journal);
1813
+ }
1814
+ /**
1815
+ * Finish every interrupted plan application of the active branch before
1816
+ * this call reads or writes anything.
1817
+ */
1818
+ async function recoverInterruptedPlans(context, explicitJournalId) {
1819
+ if (!context.fs)
1820
+ return [];
1821
+ return settlePendingPlanApplyJournals(context, {
1822
+ prolog: async () => context.prolog ?? (await context.ensureProlog?.()),
1823
+ }, explicitJournalId);
1824
+ }
1825
+ /** Report recoveries this call performed alongside its own result. */
1826
+ function withRecoveryReport(result, recovered) {
1827
+ const structured = result.structuredContent;
1828
+ const own = typeof structured.recoveryJournalId === "string"
1829
+ ? structured.recoveryJournalId
1830
+ : undefined;
1831
+ const reported = recovered.filter((recovery) => recovery.action !== "none" && recovery.journalId !== own);
1832
+ if (reported.length === 0)
1833
+ return result;
1834
+ const notes = reported.map((recovery) => recovery.detail);
1835
+ const [first, ...rest] = result.content;
1836
+ const content = [
1837
+ {
1838
+ type: "text",
1839
+ text: [first?.text ?? "", ...notes].join(" ").trim(),
1840
+ },
1841
+ ...rest,
1842
+ ];
1843
+ const recoveredJournals = reported.map((recovery) => ({
1844
+ journalId: recovery.journalId,
1845
+ planHash: recovery.planHash,
1846
+ action: recovery.action,
1847
+ restoredPaths: recovery.restoredPaths,
1848
+ }));
1849
+ if (isRecord(structured.validationSummary)) {
1850
+ const summary = structured.validationSummary;
1851
+ return {
1852
+ content,
1853
+ structuredContent: {
1854
+ ...structured,
1855
+ validationSummary: {
1856
+ ...summary,
1857
+ notes: [
1858
+ ...(Array.isArray(summary.notes) ? summary.notes : []),
1859
+ ...notes,
1860
+ ],
1861
+ recoveredJournals,
1862
+ },
1863
+ },
1864
+ };
1865
+ }
1866
+ if (Array.isArray(structured.notes)) {
1867
+ return {
1868
+ content,
1869
+ structuredContent: {
1870
+ ...structured,
1871
+ notes: [...structured.notes, ...notes],
1872
+ },
1873
+ };
1874
+ }
1875
+ return { content, structuredContent: result.structuredContent };
1876
+ }
1877
+ // implements REQ-agent-guided-migration-orchestration, REQ-cli-canonical-runtime, REQ-KIBI-BOOTSTRAP-PLAN, REQ-kibi-change-to-proof-plan-compiler-v2, REQ-kibi-predicate-vocabulary-migration
1276
1878
  export async function executeApplyPlan(args, context) {
1879
+ if (isBootstrapApplyArgs(args))
1880
+ preflightBootstrapActions(validateBootstrapPlanShape(args), context);
1277
1881
  if (!context.fs || context.sourceMutationLockHeld === true)
1278
1882
  return executeApplyPlanUnlocked(args, context);
1279
1883
  const lock = await acquireWorkspaceMutationLock(context.workspaceRoot);
@@ -1513,6 +2117,10 @@ async function applyMigrationAction(action, context) {
1513
2117
  await applyPredicateSchemaAlignment(action, context);
1514
2118
  return;
1515
2119
  default:
2120
+ if (SCHEMA6_AUTOMATIC_CODES.has(action.code)) {
2121
+ await applySchema6MigrationAction(action, context);
2122
+ return;
2123
+ }
1516
2124
  throw new Error(`Migration action '${action.code}' has no automatic executor.`);
1517
2125
  }
1518
2126
  }