kibi-cli 2.3.0 → 2.5.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 (380) 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 +39 -11
  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 +13 -5
  7. package/dist/cli-operation-metadata.d.ts.map +1 -1
  8. package/dist/cli-operation-metadata.js +15 -5
  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-maintenance.d.ts.map +1 -1
  13. package/dist/cli-register-maintenance.js +7 -0
  14. package/dist/cli-register-proof.d.ts.map +1 -1
  15. package/dist/cli-register-proof.js +13 -0
  16. package/dist/commands/check.d.ts.map +1 -1
  17. package/dist/commands/check.js +11 -2
  18. package/dist/commands/discovery-shared.d.ts.map +1 -1
  19. package/dist/commands/discovery-shared.js +8 -2
  20. package/dist/commands/engine.d.ts.map +1 -1
  21. package/dist/commands/engine.js +4 -2
  22. package/dist/commands/gc.d.ts.map +1 -1
  23. package/dist/commands/gc.js +7 -0
  24. package/dist/commands/init-helpers.d.ts.map +1 -1
  25. package/dist/commands/init-helpers.js +0 -1
  26. package/dist/commands/merge-driver.d.ts +31 -0
  27. package/dist/commands/merge-driver.d.ts.map +1 -0
  28. package/dist/commands/merge-driver.js +268 -0
  29. package/dist/commands/migrate.d.ts.map +1 -1
  30. package/dist/commands/migrate.js +281 -14
  31. package/dist/commands/proof-compact.d.ts +9 -0
  32. package/dist/commands/proof-compact.d.ts.map +1 -0
  33. package/dist/commands/proof-compact.js +58 -0
  34. package/dist/commands/prove.d.ts +38 -0
  35. package/dist/commands/prove.d.ts.map +1 -1
  36. package/dist/commands/prove.js +222 -47
  37. package/dist/commands/sync/persistence.d.ts.map +1 -1
  38. package/dist/commands/sync/persistence.js +61 -16
  39. package/dist/commands/sync.d.ts +8 -0
  40. package/dist/commands/sync.d.ts.map +1 -1
  41. package/dist/commands/sync.js +42 -16
  42. package/dist/diagnostics.js +1 -1
  43. package/dist/engine-limits.d.ts +74 -0
  44. package/dist/engine-limits.d.ts.map +1 -0
  45. package/dist/engine-limits.js +201 -0
  46. package/dist/engine.d.ts +10 -0
  47. package/dist/engine.d.ts.map +1 -1
  48. package/dist/engine.js +57 -9
  49. package/dist/extractors/manifest.d.ts +8 -0
  50. package/dist/extractors/manifest.d.ts.map +1 -1
  51. package/dist/extractors/manifest.js +34 -5
  52. package/dist/extractors/markdown.d.ts +8 -0
  53. package/dist/extractors/markdown.d.ts.map +1 -1
  54. package/dist/extractors/markdown.js +57 -0
  55. package/dist/extractors/relationships.d.ts.map +1 -1
  56. package/dist/extractors/relationships.js +2 -0
  57. package/dist/intent-search.d.ts +4 -1
  58. package/dist/intent-search.d.ts.map +1 -1
  59. package/dist/intent-search.js +186 -33
  60. package/dist/operations/bootstrap/candidate-helpers.d.ts.map +1 -1
  61. package/dist/operations/bootstrap/candidate-helpers.js +8 -1
  62. package/dist/operations/bootstrap/candidates.d.ts.map +1 -1
  63. package/dist/operations/bootstrap/candidates.js +55 -39
  64. package/dist/operations/bootstrap/discovery.js +1 -1
  65. package/dist/operations/bootstrap/generate.d.ts +2 -0
  66. package/dist/operations/bootstrap/generate.d.ts.map +1 -1
  67. package/dist/operations/bootstrap/generate.js +107 -37
  68. package/dist/operations/bootstrap/guidance-actions.d.ts.map +1 -1
  69. package/dist/operations/bootstrap/guidance-actions.js +3 -0
  70. package/dist/operations/bootstrap/guidance.d.ts.map +1 -1
  71. package/dist/operations/bootstrap/guidance.js +7 -1
  72. package/dist/operations/bootstrap/intent-claims.d.ts +17 -0
  73. package/dist/operations/bootstrap/intent-claims.d.ts.map +1 -0
  74. package/dist/operations/bootstrap/intent-claims.js +103 -0
  75. package/dist/operations/bootstrap/markdown-candidates.d.ts +2 -0
  76. package/dist/operations/bootstrap/markdown-candidates.d.ts.map +1 -1
  77. package/dist/operations/bootstrap/markdown-candidates.js +32 -10
  78. package/dist/operations/bootstrap/presentation.d.ts +2 -0
  79. package/dist/operations/bootstrap/presentation.d.ts.map +1 -1
  80. package/dist/operations/bootstrap/presentation.js +66 -3
  81. package/dist/operations/bootstrap/requirement-claims.d.ts +1 -0
  82. package/dist/operations/bootstrap/requirement-claims.d.ts.map +1 -1
  83. package/dist/operations/bootstrap/requirement-claims.js +13 -1
  84. package/dist/operations/bootstrap/types.d.ts +28 -0
  85. package/dist/operations/bootstrap/types.d.ts.map +1 -1
  86. package/dist/operations/bootstrap/types.js +16 -0
  87. package/dist/operations/bootstrap/validation.d.ts +4 -0
  88. package/dist/operations/bootstrap/validation.d.ts.map +1 -0
  89. package/dist/operations/bootstrap/validation.js +20 -0
  90. package/dist/operations/migration/inventory-rederive.d.ts +81 -0
  91. package/dist/operations/migration/inventory-rederive.d.ts.map +1 -0
  92. package/dist/operations/migration/inventory-rederive.js +484 -0
  93. package/dist/operations/migration/kb-sources.d.ts +53 -0
  94. package/dist/operations/migration/kb-sources.d.ts.map +1 -0
  95. package/dist/operations/migration/kb-sources.js +233 -0
  96. package/dist/operations/migration/origin-backfill.d.ts +39 -0
  97. package/dist/operations/migration/origin-backfill.d.ts.map +1 -0
  98. package/dist/operations/migration/origin-backfill.js +164 -0
  99. package/dist/operations/migration/polarity-values.d.ts +10 -0
  100. package/dist/operations/migration/polarity-values.d.ts.map +1 -0
  101. package/dist/operations/migration/polarity-values.js +60 -0
  102. package/dist/operations/migration/schema6.d.ts +59 -0
  103. package/dist/operations/migration/schema6.d.ts.map +1 -0
  104. package/dist/operations/migration/schema6.js +379 -0
  105. package/dist/operations/migration/source-paths.d.ts +131 -0
  106. package/dist/operations/migration/source-paths.d.ts.map +1 -0
  107. package/dist/operations/migration/source-paths.js +308 -0
  108. package/dist/operations/migration/superseded-closure.d.ts +54 -0
  109. package/dist/operations/migration/superseded-closure.d.ts.map +1 -0
  110. package/dist/operations/migration/superseded-closure.js +187 -0
  111. package/dist/operations/modeling/logic-modeling.d.ts.map +1 -1
  112. package/dist/operations/modeling/logic-modeling.js +3 -5
  113. package/dist/operations/modeling/model-requirement.d.ts.map +1 -1
  114. package/dist/operations/modeling/model-requirement.js +225 -67
  115. package/dist/operations/mutation/contradictions.d.ts +16 -0
  116. package/dist/operations/mutation/contradictions.d.ts.map +1 -1
  117. package/dist/operations/mutation/contradictions.js +32 -0
  118. package/dist/operations/mutation/delete.d.ts.map +1 -1
  119. package/dist/operations/mutation/delete.js +15 -7
  120. package/dist/operations/mutation/origin.d.ts +21 -0
  121. package/dist/operations/mutation/origin.d.ts.map +1 -0
  122. package/dist/operations/mutation/origin.js +78 -0
  123. package/dist/operations/mutation/plan-apply-journal.d.ts +182 -0
  124. package/dist/operations/mutation/plan-apply-journal.d.ts.map +1 -0
  125. package/dist/operations/mutation/plan-apply-journal.js +613 -0
  126. package/dist/operations/mutation/predicate-vocabulary-guard.d.ts +3 -1
  127. package/dist/operations/mutation/predicate-vocabulary-guard.d.ts.map +1 -1
  128. package/dist/operations/mutation/predicate-vocabulary-guard.js +13 -2
  129. package/dist/operations/mutation/relationships.d.ts +5 -5
  130. package/dist/operations/mutation/relationships.d.ts.map +1 -1
  131. package/dist/operations/mutation/relationships.js +31 -11
  132. package/dist/operations/mutation/serialization.d.ts.map +1 -1
  133. package/dist/operations/mutation/serialization.js +3 -1
  134. package/dist/operations/mutation/source-authoring.d.ts +6 -0
  135. package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
  136. package/dist/operations/mutation/source-authoring.js +7 -1
  137. package/dist/operations/mutation/symbol-refresh.d.ts.map +1 -1
  138. package/dist/operations/mutation/symbol-refresh.js +4 -1
  139. package/dist/operations/mutation/types.d.ts +14 -0
  140. package/dist/operations/mutation/types.d.ts.map +1 -1
  141. package/dist/operations/mutation/upsert.d.ts +30 -3
  142. package/dist/operations/mutation/upsert.d.ts.map +1 -1
  143. package/dist/operations/mutation/upsert.js +105 -35
  144. package/dist/operations/mutation/validate-upsert.d.ts +6 -0
  145. package/dist/operations/mutation/validate-upsert.d.ts.map +1 -1
  146. package/dist/operations/mutation/validate-upsert.js +34 -30
  147. package/dist/operations/planning/apply-plan.d.ts.map +1 -1
  148. package/dist/operations/planning/apply-plan.js +751 -144
  149. package/dist/operations/planning/compile-intent.d.ts +34 -1
  150. package/dist/operations/planning/compile-intent.d.ts.map +1 -1
  151. package/dist/operations/planning/compile-intent.js +314 -98
  152. package/dist/operations/proof/code-scope.d.ts +28 -0
  153. package/dist/operations/proof/code-scope.d.ts.map +1 -1
  154. package/dist/operations/proof/code-scope.js +72 -0
  155. package/dist/operations/proof/compact-receipts.d.ts +49 -0
  156. package/dist/operations/proof/compact-receipts.d.ts.map +1 -0
  157. package/dist/operations/proof/compact-receipts.js +160 -0
  158. package/dist/operations/proof/ingest-proof.d.ts +2 -0
  159. package/dist/operations/proof/ingest-proof.d.ts.map +1 -1
  160. package/dist/operations/proof/ingest-proof.js +44 -23
  161. package/dist/operations/proof/prune-receipts.d.ts.map +1 -1
  162. package/dist/operations/proof/prune-receipts.js +5 -9
  163. package/dist/operations/proof/receipt-compaction.d.ts +48 -0
  164. package/dist/operations/proof/receipt-compaction.d.ts.map +1 -0
  165. package/dist/operations/proof/receipt-compaction.js +121 -0
  166. package/dist/operations/semantic-advisor/analysis-receipt.d.ts.map +1 -1
  167. package/dist/operations/semantic-advisor/analysis-receipt.js +12 -1
  168. package/dist/operations/semantic-advisor/analyze-prose.d.ts +2 -1
  169. package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
  170. package/dist/operations/semantic-advisor/analyze-prose.js +51 -5
  171. package/dist/operations/semantic-advisor/clauses.d.ts +1 -0
  172. package/dist/operations/semantic-advisor/clauses.d.ts.map +1 -1
  173. package/dist/operations/semantic-advisor/clauses.js +11 -1
  174. package/dist/operations/semantic-advisor/conditional-rules.d.ts +33 -0
  175. package/dist/operations/semantic-advisor/conditional-rules.d.ts.map +1 -0
  176. package/dist/operations/semantic-advisor/conditional-rules.js +262 -0
  177. package/dist/operations/semantic-advisor/ingestion-boundary.d.ts +3 -1
  178. package/dist/operations/semantic-advisor/ingestion-boundary.d.ts.map +1 -1
  179. package/dist/operations/semantic-advisor/ingestion-boundary.js +14 -6
  180. package/dist/operations/semantic-advisor/plugin-orchestration.js +1 -1
  181. package/dist/prolog/codec.d.ts.map +1 -1
  182. package/dist/prolog/codec.js +2 -1
  183. package/dist/prolog.d.ts +9 -0
  184. package/dist/prolog.d.ts.map +1 -1
  185. package/dist/prolog.js +56 -10
  186. package/dist/proof/command-test-report.d.ts +74 -0
  187. package/dist/proof/command-test-report.d.ts.map +1 -0
  188. package/dist/proof/command-test-report.js +206 -0
  189. package/dist/public/branch-resolver.d.ts +1 -0
  190. package/dist/public/branch-resolver.d.ts.map +1 -1
  191. package/dist/public/branch-resolver.js +1 -0
  192. package/dist/public/diagnostic-usage.d.ts +14 -0
  193. package/dist/public/diagnostic-usage.d.ts.map +1 -1
  194. package/dist/public/diagnostic-usage.js +55 -1
  195. package/dist/public/entity-origin.d.ts +69 -0
  196. package/dist/public/entity-origin.d.ts.map +1 -0
  197. package/dist/public/entity-origin.js +142 -0
  198. package/dist/public/impact/full-kb-quality.d.ts.map +1 -1
  199. package/dist/public/impact/full-kb-quality.js +1 -0
  200. package/dist/public/impact/requirement-quality.js +1 -1
  201. package/dist/public/operations/catalog.d.ts +203 -20
  202. package/dist/public/operations/catalog.d.ts.map +1 -1
  203. package/dist/public/operations/catalog.js +3 -0
  204. package/dist/public/operations/check-executor.d.ts.map +1 -1
  205. package/dist/public/operations/check-executor.js +8 -0
  206. package/dist/public/operations/contracts.d.ts +0 -5
  207. package/dist/public/operations/contracts.d.ts.map +1 -1
  208. package/dist/public/operations/contracts.js +63 -14
  209. package/dist/public/operations/discovery-entities.d.ts +15 -0
  210. package/dist/public/operations/discovery-entities.d.ts.map +1 -1
  211. package/dist/public/operations/discovery-entities.js +59 -0
  212. package/dist/public/operations/discovery-executors.d.ts +6 -1
  213. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  214. package/dist/public/operations/discovery-executors.js +65 -6
  215. package/dist/public/operations/index.d.ts +1 -0
  216. package/dist/public/operations/index.d.ts.map +1 -1
  217. package/dist/public/operations/index.js +1 -0
  218. package/dist/public/operations/migration-plan.d.ts +5 -0
  219. package/dist/public/operations/migration-plan.d.ts.map +1 -1
  220. package/dist/public/operations/migration-plan.js +26 -1
  221. package/dist/public/operations/node-ports.d.ts.map +1 -1
  222. package/dist/public/operations/node-ports.js +24 -3
  223. package/dist/public/operations/origin-review.d.ts +51 -0
  224. package/dist/public/operations/origin-review.d.ts.map +1 -0
  225. package/dist/public/operations/origin-review.js +307 -0
  226. package/dist/public/operations/predicate-schema-conformance.js +1 -1
  227. package/dist/public/operations/repair-plan.d.ts.map +1 -1
  228. package/dist/public/operations/repair-plan.js +4 -0
  229. package/dist/public/operations/result-envelope.d.ts.map +1 -1
  230. package/dist/public/operations/result-envelope.js +17 -5
  231. package/dist/public/operations/runtime-types.d.ts +9 -0
  232. package/dist/public/operations/runtime-types.d.ts.map +1 -1
  233. package/dist/public/operations/schema6-check-actions.d.ts +127 -0
  234. package/dist/public/operations/schema6-check-actions.d.ts.map +1 -0
  235. package/dist/public/operations/schema6-check-actions.js +575 -0
  236. package/dist/public/operations/source-path-dangling.d.ts +17 -0
  237. package/dist/public/operations/source-path-dangling.d.ts.map +1 -0
  238. package/dist/public/operations/source-path-dangling.js +66 -0
  239. package/dist/public/operations/specs/bootstrap.d.ts +73 -1
  240. package/dist/public/operations/specs/bootstrap.d.ts.map +1 -1
  241. package/dist/public/operations/specs/bootstrap.js +66 -1
  242. package/dist/public/operations/specs/check-rules.generated.d.ts +1 -1
  243. package/dist/public/operations/specs/check-rules.generated.d.ts.map +1 -1
  244. package/dist/public/operations/specs/check-rules.generated.js +15 -1
  245. package/dist/public/operations/specs/check.d.ts +2 -2
  246. package/dist/public/operations/specs/check.js +1 -1
  247. package/dist/public/operations/specs/composite.d.ts +351 -0
  248. package/dist/public/operations/specs/composite.d.ts.map +1 -0
  249. package/dist/public/operations/specs/composite.js +96 -0
  250. package/dist/public/operations/specs/discovery.d.ts +8 -3
  251. package/dist/public/operations/specs/discovery.d.ts.map +1 -1
  252. package/dist/public/operations/specs/discovery.js +8 -3
  253. package/dist/public/operations/specs/modeling.d.ts +2 -2
  254. package/dist/public/operations/specs/modeling.js +2 -2
  255. package/dist/public/operations/specs/mutation-schemas.d.ts +73 -8
  256. package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
  257. package/dist/public/operations/specs/mutation-schemas.js +43 -7
  258. package/dist/public/operations/specs/mutation.d.ts +1 -1
  259. package/dist/public/operations/specs/mutation.js +1 -1
  260. package/dist/public/operations/specs/planning.d.ts +2 -2
  261. package/dist/public/operations/specs/planning.js +4 -4
  262. package/dist/public/operations/specs/reporting.d.ts.map +1 -1
  263. package/dist/public/operations/specs/reporting.js +13 -26
  264. package/dist/public/operations/specs/upsert.d.ts +79 -9
  265. package/dist/public/operations/specs/upsert.d.ts.map +1 -1
  266. package/dist/public/operations/specs/upsert.js +6 -1
  267. package/dist/public/operations/symbol-repair-plan.d.ts.map +1 -1
  268. package/dist/public/operations/symbol-repair-plan.js +3 -1
  269. package/dist/public/operations/types.d.ts +1 -1
  270. package/dist/public/operations/types.d.ts.map +1 -1
  271. package/dist/public/proof-receipt.d.ts +1 -0
  272. package/dist/public/proof-receipt.d.ts.map +1 -1
  273. package/dist/public/schemas/entity.d.ts.map +1 -1
  274. package/dist/public/schemas/entity.js +52 -0
  275. package/dist/public/schemas/relationship.d.ts.map +1 -1
  276. package/dist/public/schemas/relationship.js +2 -0
  277. package/dist/public/skills/kibi-bootstrap/SKILL.md +52 -11
  278. package/dist/public/skills/kibi-bootstrap/resources/bootstrap.md +12 -1
  279. package/dist/public/skills/kibi-usage/SKILL.md +82 -18
  280. package/dist/public/skills/kibi-usage/resources/fact-lanes.md +5 -5
  281. package/dist/public/skills/kibi-usage/resources/kb-improvement.md +1 -1
  282. package/dist/public/skills/kibi-usage/resources/logic-ir.md +2 -2
  283. package/dist/public/skills/kibi-usage/resources/operation-access.md +29 -1
  284. package/dist/public/skills/kibi-usage/resources/ui-requirements.md +4 -4
  285. package/dist/public/skills/kibi-usage/resources/workflows.md +42 -25
  286. package/dist/public/telemetry-acceptance.d.ts +64 -0
  287. package/dist/public/telemetry-acceptance.d.ts.map +1 -1
  288. package/dist/public/telemetry-acceptance.js +143 -7
  289. package/dist/public/telemetry-remediation.d.ts.map +1 -1
  290. package/dist/public/telemetry-remediation.js +26 -10
  291. package/dist/relationships/shards.d.ts +13 -0
  292. package/dist/relationships/shards.d.ts.map +1 -1
  293. package/dist/relationships/shards.js +54 -28
  294. package/dist/runtime/cli-runtime.d.ts.map +1 -1
  295. package/dist/runtime/cli-runtime.js +44 -3
  296. package/dist/runtime/detached-snapshot.d.ts +38 -0
  297. package/dist/runtime/detached-snapshot.d.ts.map +1 -0
  298. package/dist/runtime/detached-snapshot.js +92 -0
  299. package/dist/schemas/entity.schema.json +122 -0
  300. package/dist/search-answer.d.ts +157 -0
  301. package/dist/search-answer.d.ts.map +1 -0
  302. package/dist/search-answer.js +846 -0
  303. package/dist/traceability/impact-knowledge.d.ts.map +1 -1
  304. package/dist/traceability/impact-knowledge.js +7 -0
  305. package/dist/traceability/symbol-extract.js +3 -3
  306. package/dist/traceability/temp-kb.d.ts.map +1 -1
  307. package/dist/traceability/temp-kb.js +12 -0
  308. package/dist/types/entities.d.ts +13 -0
  309. package/dist/types/entities.d.ts.map +1 -1
  310. package/dist/types/relationships.d.ts +1 -1
  311. package/dist/types/relationships.d.ts.map +1 -1
  312. package/dist/utils/branch-resolver.d.ts +37 -1
  313. package/dist/utils/branch-resolver.d.ts.map +1 -1
  314. package/dist/utils/branch-resolver.js +130 -3
  315. package/dist/utils/repo-relative-path.d.ts +18 -0
  316. package/dist/utils/repo-relative-path.d.ts.map +1 -0
  317. package/dist/utils/repo-relative-path.js +58 -0
  318. package/dist/utils/rule-registry.generated.d.ts +75 -3
  319. package/dist/utils/rule-registry.generated.d.ts.map +1 -1
  320. package/dist/utils/rule-registry.generated.js +89 -3
  321. package/dist/utils/schema-version.d.ts +1 -1
  322. package/dist/utils/schema-version.d.ts.map +1 -1
  323. package/dist/utils/schema-version.js +4 -3
  324. package/dist/utils/strict-modeling.d.ts +1 -1
  325. package/dist/utils/strict-modeling.d.ts.map +1 -1
  326. package/dist/utils/strict-modeling.js +23 -3
  327. package/package.json +3 -3
  328. package/schema/entities.pl +22 -0
  329. package/schema/relationships.pl +14 -0
  330. package/schema/validation.pl +45 -1
  331. package/src/public/branch-resolver.ts +1 -0
  332. package/src/public/diagnostic-usage.ts +59 -1
  333. package/src/public/entity-origin.ts +175 -0
  334. package/src/public/impact/full-kb-quality.ts +1 -0
  335. package/src/public/impact/requirement-quality.ts +1 -1
  336. package/src/public/operations/catalog.ts +3 -0
  337. package/src/public/operations/check-executor.ts +15 -0
  338. package/src/public/operations/contracts.ts +75 -15
  339. package/src/public/operations/discovery-entities.ts +74 -0
  340. package/src/public/operations/discovery-executors.ts +88 -10
  341. package/src/public/operations/index.ts +1 -0
  342. package/src/public/operations/migration-plan.ts +30 -1
  343. package/src/public/operations/node-ports.ts +24 -3
  344. package/src/public/operations/origin-review.ts +375 -0
  345. package/src/public/operations/predicate-schema-conformance.ts +1 -1
  346. package/src/public/operations/repair-plan.ts +4 -0
  347. package/src/public/operations/result-envelope.ts +18 -6
  348. package/src/public/operations/runtime-types.ts +9 -0
  349. package/src/public/operations/schema6-check-actions.ts +699 -0
  350. package/src/public/operations/source-path-dangling.ts +81 -0
  351. package/src/public/operations/specs/bootstrap.ts +78 -4
  352. package/src/public/operations/specs/check-rules.generated.ts +15 -1
  353. package/src/public/operations/specs/check.ts +1 -1
  354. package/src/public/operations/specs/composite.ts +141 -0
  355. package/src/public/operations/specs/discovery.ts +9 -3
  356. package/src/public/operations/specs/modeling.ts +2 -2
  357. package/src/public/operations/specs/mutation-schemas.ts +49 -7
  358. package/src/public/operations/specs/mutation.ts +1 -1
  359. package/src/public/operations/specs/planning.ts +4 -4
  360. package/src/public/operations/specs/reporting.ts +13 -38
  361. package/src/public/operations/specs/upsert.ts +7 -1
  362. package/src/public/operations/symbol-repair-plan.ts +3 -1
  363. package/src/public/operations/types.ts +2 -0
  364. package/src/public/proof-receipt.ts +1 -0
  365. package/src/public/schemas/entity.ts +57 -0
  366. package/src/public/schemas/relationship.ts +2 -0
  367. package/src/public/skills/kibi-bootstrap/SKILL.md +52 -11
  368. package/src/public/skills/kibi-bootstrap/resources/bootstrap.md +12 -1
  369. package/src/public/skills/kibi-usage/SKILL.md +82 -18
  370. package/src/public/skills/kibi-usage/resources/fact-lanes.md +5 -5
  371. package/src/public/skills/kibi-usage/resources/kb-improvement.md +1 -1
  372. package/src/public/skills/kibi-usage/resources/logic-ir.md +2 -2
  373. package/src/public/skills/kibi-usage/resources/operation-access.md +29 -1
  374. package/src/public/skills/kibi-usage/resources/ui-requirements.md +4 -4
  375. package/src/public/skills/kibi-usage/resources/workflows.md +42 -25
  376. package/src/public/telemetry-acceptance-types.d.ts +4 -1
  377. package/src/public/telemetry-acceptance.ts +210 -7
  378. package/src/public/telemetry-remediation.ts +39 -10
  379. package/src/schemas/entity.schema.json +122 -0
  380. package/src/schemas/relationship.schema.json +2 -0
@@ -0,0 +1,613 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, writeSync, } from "node:fs";
3
+ import path from "node:path";
4
+ import { InputError, OperationError } from "../../cli-errors.js";
5
+ import { escapeAtom } from "../../prolog/codec.js";
6
+ import { resolveBranchAttachment } from "../../utils/branch-resolver.js";
7
+ import { branchStoreKey, branchStoreManifestPath, } from "../../utils/branch-store-locator.js";
8
+ import { RELATIONSHIP_TYPES } from "./relationships.js";
9
+ import { retirePendingSourceReceipt, writePendingSourceReceipt, } from "./source-authoring.js";
10
+ /**
11
+ * Write-ahead journal for atomic kb_apply_plan application.
12
+ *
13
+ * A compile plan changes two resources: workspace files (the plan's
14
+ * sourceWrites and the relationship shards its steps append) and the compiled
15
+ * branch store. Before the first write, the journal records every intended
16
+ * file change with exact before/after bytes and hashes, every store upsert,
17
+ * and a fingerprint of the store entities the plan touches. Files are then
18
+ * published with temp-file + rename (fsync where the port supports it), and
19
+ * the store mutations commit in one Prolog transaction. The store commit is
20
+ * the only commit point: any failure before it restores every file from the
21
+ * journal, and an interrupted application is completed or rolled back from
22
+ * the journal by the next mutating call.
23
+ */
24
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2, REQ-core-journaled-engine-persistence
25
+ export const PLAN_APPLY_JOURNAL_VERSION = "kibi.plan-apply-journal.v1";
26
+ const JOURNAL_DIRECTORY = "plan-apply";
27
+ const JOURNAL_ID_PATTERN = /^plan-apply-[a-f0-9]{16}$/;
28
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2
29
+ export function planApplyJournalId(planHash) {
30
+ return `plan-apply-${planHash.slice(0, 16).toLowerCase()}`;
31
+ }
32
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2
33
+ export function isPlanApplyJournalId(value) {
34
+ return JOURNAL_ID_PATTERN.test(value);
35
+ }
36
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2
37
+ export function contentHash(value) {
38
+ return createHash("sha256").update(value).digest("hex");
39
+ }
40
+ function errorText(error) {
41
+ return error instanceof Error ? error.message : String(error);
42
+ }
43
+ /**
44
+ * Journals live in the branch store directory, next to the compiled store
45
+ * they guard. Before the store has been created (no identity manifest yet)
46
+ * they live in the branch-keyed `.kb/recovery/plan-apply/<key>` directory, so
47
+ * the store's adoption check never finds unexpected entries.
48
+ */
49
+ function journalLocation(context) {
50
+ const attachment = context.branchAttachment ?? resolveBranchAttachment(context.workspaceRoot);
51
+ if ("error" in attachment)
52
+ return null;
53
+ const storeDirectory = path.join(attachment.storePath, JOURNAL_DIRECTORY);
54
+ const fallback = path.join(path.resolve(context.workspaceRoot), ".kb", "recovery", JOURNAL_DIRECTORY, branchStoreKey(attachment.kbBranch));
55
+ let storeReady = false;
56
+ try {
57
+ storeReady =
58
+ statSync(attachment.storePath).isDirectory() &&
59
+ existsSync(branchStoreManifestPath(attachment.storePath));
60
+ }
61
+ catch {
62
+ storeReady = false;
63
+ }
64
+ return {
65
+ branch: attachment.kbBranch,
66
+ primary: storeReady ? storeDirectory : fallback,
67
+ all: [...new Set([storeDirectory, fallback])],
68
+ };
69
+ }
70
+ function fsyncDirectory(directory) {
71
+ if (process.platform === "win32")
72
+ return;
73
+ try {
74
+ const descriptor = openSync(directory, "r");
75
+ try {
76
+ fsyncSync(descriptor);
77
+ }
78
+ finally {
79
+ closeSync(descriptor);
80
+ }
81
+ }
82
+ catch {
83
+ // Directory fsync is best effort; the journal file itself is synced.
84
+ }
85
+ }
86
+ /** Publish the journal with temp-file + fsync + rename + directory fsync. */
87
+ function writeJournal(journalPath, journal) {
88
+ const directory = path.dirname(journalPath);
89
+ mkdirSync(directory, { recursive: true, mode: 0o700 });
90
+ const temporary = `${journalPath}.tmp-${process.pid}-${randomUUID()}`;
91
+ try {
92
+ const descriptor = openSync(temporary, "w", 0o600);
93
+ try {
94
+ writeSync(descriptor, `${JSON.stringify(journal, null, 2)}\n`);
95
+ fsyncSync(descriptor);
96
+ }
97
+ finally {
98
+ closeSync(descriptor);
99
+ }
100
+ renameSync(temporary, journalPath);
101
+ }
102
+ catch (error) {
103
+ try {
104
+ unlinkSync(temporary);
105
+ }
106
+ catch {
107
+ // The temporary file may never have been created.
108
+ }
109
+ throw error;
110
+ }
111
+ fsyncDirectory(directory);
112
+ }
113
+ function isHash(value) {
114
+ return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
115
+ }
116
+ function isFileWrite(value) {
117
+ if (value === null || typeof value !== "object")
118
+ return false;
119
+ const entry = value;
120
+ const before = entry.before;
121
+ const after = entry.after;
122
+ return (typeof entry.path === "string" &&
123
+ entry.path !== "" &&
124
+ !path.isAbsolute(entry.path) &&
125
+ !entry.path.split(/[\\/]/).includes("..") &&
126
+ (entry.origin === "plan" ||
127
+ entry.origin === "entity-document" ||
128
+ entry.origin === "relationship-shard") &&
129
+ (entry.mode === "write" || entry.mode === "delete") &&
130
+ (before === null
131
+ ? entry.beforeHash === null
132
+ : typeof before === "string" &&
133
+ entry.beforeHash === contentHash(before)) &&
134
+ (after === null
135
+ ? entry.afterHash === null
136
+ : typeof after === "string" && entry.afterHash === contentHash(after)) &&
137
+ (entry.mode === "delete") === (after === null));
138
+ }
139
+ /**
140
+ * Parse a journal and check its internal consistency: every recorded file
141
+ * carries bytes whose hashes match the recorded before/after hashes.
142
+ */
143
+ function parseJournal(journalId, text) {
144
+ const parsed = JSON.parse(text);
145
+ const terminal = parsed.state === "committed" || parsed.state === "rolled_back";
146
+ if (parsed.version !== PLAN_APPLY_JOURNAL_VERSION ||
147
+ parsed.journalId !== journalId ||
148
+ !isHash(parsed.planHash) ||
149
+ planApplyJournalId(parsed.planHash) !== journalId ||
150
+ typeof parsed.branch !== "string" ||
151
+ ![
152
+ "prepared",
153
+ "store_committing",
154
+ "store_committed",
155
+ "committed",
156
+ "rolled_back",
157
+ ].includes(String(parsed.state)) ||
158
+ !Array.isArray(parsed.files) ||
159
+ !parsed.files.every(isFileWrite) ||
160
+ parsed.store === undefined ||
161
+ !Array.isArray(parsed.store.entityIds) ||
162
+ !parsed.store.entityIds.every((id) => typeof id === "string") ||
163
+ typeof parsed.store.preCommitFingerprint !== "string" ||
164
+ !Array.isArray(parsed.store.entries) ||
165
+ (!terminal && parsed.store.entries.length === 0) ||
166
+ parsed.summary === undefined) {
167
+ throw new Error("journal fields are missing or inconsistent");
168
+ }
169
+ return parsed;
170
+ }
171
+ function unrecoverableJournal(journalPath, detail) {
172
+ throw new OperationError("PARTIAL_COMMIT_REPAIR_REQUIRED", `Plan application journal ${journalPath} cannot be recovered automatically: ${detail}. Kibi will not apply or recover plans on this branch until it is resolved: restore each listed file to its journaled before or after bytes and retry, or reconcile the workspace and branch store by hand (kibi sync) and remove the journal`, false);
173
+ }
174
+ function readJournalFile(journalPath) {
175
+ const journalId = path.basename(journalPath, ".json");
176
+ let text;
177
+ try {
178
+ text = readFileSync(journalPath, "utf8");
179
+ }
180
+ catch (error) {
181
+ unrecoverableJournal(journalPath, `unreadable (${errorText(error)})`);
182
+ }
183
+ try {
184
+ return parseJournal(journalId, text);
185
+ }
186
+ catch (error) {
187
+ unrecoverableJournal(journalPath, `invalid (${errorText(error)})`);
188
+ }
189
+ }
190
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2, REQ-core-journaled-engine-persistence
191
+ /**
192
+ * The terminal-or-absent journal slot for a plan. Returns the existing
193
+ * journal (if any) so the caller can refuse a plan that already committed.
194
+ */
195
+ export function openPlanApplyJournal(context, planHash) {
196
+ const location = journalLocation(context);
197
+ if (location === null) {
198
+ throw new Error("Apply plan failed before any write: the KB branch could not be resolved for the plan journal");
199
+ }
200
+ const journalId = planApplyJournalId(planHash);
201
+ let existing = null;
202
+ let journalPath = path.join(location.primary, `${journalId}.json`);
203
+ for (const directory of location.all) {
204
+ const candidate = path.join(directory, `${journalId}.json`);
205
+ if (existsSync(candidate)) {
206
+ existing = readJournalFile(candidate);
207
+ journalPath = candidate;
208
+ break;
209
+ }
210
+ }
211
+ return { journalId, journalPath, branch: location.branch, existing };
212
+ }
213
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2, REQ-core-journaled-engine-persistence
214
+ /** Durably record a journal state; the handle tracks the latest record. */
215
+ export function recordPlanApplyJournal(handle, update, now = new Date()) {
216
+ const next = {
217
+ ...handle.journal,
218
+ ...update,
219
+ updatedAt: now.toISOString(),
220
+ };
221
+ const terminal = next.state === "committed" || next.state === "rolled_back";
222
+ // Terminal journals keep only their summary: the bytes and store payload
223
+ // are no longer needed and the record stays small.
224
+ const compacted = terminal
225
+ ? { ...next, files: [], store: { ...next.store, entries: [] } }
226
+ : next;
227
+ writeJournal(handle.journalPath, compacted);
228
+ handle.journal = compacted;
229
+ }
230
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2, REQ-core-journaled-engine-persistence
231
+ export function createPlanApplyJournal(journalPath, journal) {
232
+ writeJournal(journalPath, journal);
233
+ return { journalPath, journal };
234
+ }
235
+ /** Bind pending-source receipts to the bytes a committed plan published. */
236
+ // implements REQ-core-journaled-engine-persistence
237
+ export function reconcilePendingSourceReceipts(workspaceRoot, writes) {
238
+ for (const write of writes) {
239
+ if ((write.mode ?? "write") === "delete") {
240
+ retirePendingSourceReceipt(workspaceRoot, write.path);
241
+ }
242
+ else if (write.afterHash !== null) {
243
+ writePendingSourceReceipt(workspaceRoot, write.path, write.afterHash);
244
+ }
245
+ }
246
+ }
247
+ async function currentHash(fsPort, absolute) {
248
+ try {
249
+ return contentHash(await fsPort.readFile(absolute));
250
+ }
251
+ catch {
252
+ return null;
253
+ }
254
+ }
255
+ // implements REQ-core-journaled-engine-persistence
256
+ /**
257
+ * Replace one workspace file atomically: write a unique staged file in the
258
+ * target directory, fsync it, rename it over the target, then fsync the
259
+ * directory. Ports without rename (test and constrained hosts) write the
260
+ * target directly.
261
+ */
262
+ export async function publishWorkspaceFile(fsPort, absolute, content) {
263
+ await fsPort.mkdir(path.dirname(absolute));
264
+ if (fsPort.rename) {
265
+ const staged = `${absolute}.kibi-stage-${process.pid}-${randomUUID()}`;
266
+ try {
267
+ await fsPort.writeFile(staged, content);
268
+ if (fsPort.fsync)
269
+ await fsPort.fsync(staged);
270
+ await fsPort.rename(staged, absolute);
271
+ }
272
+ catch (error) {
273
+ if (fsPort.unlink)
274
+ await fsPort.unlink(staged).catch(() => undefined);
275
+ throw error;
276
+ }
277
+ }
278
+ else {
279
+ await fsPort.writeFile(absolute, content);
280
+ if (fsPort.fsync)
281
+ await fsPort.fsync(absolute);
282
+ }
283
+ if (fsPort.fsync)
284
+ await fsPort.fsync(path.dirname(absolute)).catch(() => undefined);
285
+ }
286
+ async function removeWorkspaceFile(fsPort, absolute, relative) {
287
+ if (!fsPort.unlink) {
288
+ throw new Error(`Apply plan failed: delete requires filesystem unlink support: ${relative}`);
289
+ }
290
+ try {
291
+ await fsPort.unlink(absolute);
292
+ }
293
+ catch (error) {
294
+ if (error.code !== "ENOENT")
295
+ throw error;
296
+ }
297
+ if (fsPort.fsync)
298
+ await fsPort.fsync(path.dirname(absolute)).catch(() => undefined);
299
+ }
300
+ async function setFile(fsPort, workspaceRoot, relative, content) {
301
+ const absolute = path.resolve(workspaceRoot, relative);
302
+ if (content === null)
303
+ await removeWorkspaceFile(fsPort, absolute, relative);
304
+ else
305
+ await publishWorkspaceFile(fsPort, absolute, content);
306
+ }
307
+ // implements REQ-core-journaled-engine-persistence
308
+ /**
309
+ * Publish every journaled file's after-bytes in journal order. Throws on the
310
+ * first failure; the caller restores the before-bytes from the journal.
311
+ */
312
+ export async function publishJournaledFiles(fsPort, workspaceRoot, files) {
313
+ for (const file of files) {
314
+ await setFile(fsPort, workspaceRoot, file.path, file.after);
315
+ }
316
+ }
317
+ /**
318
+ * Move every journaled file to one side of the plan. With `verify`, every
319
+ * file must currently hold its journaled before or after bytes, checked for
320
+ * all files before any of them changes; a file another writer changed makes
321
+ * the move refuse without touching anything. Without `verify` (the in-process
322
+ * rollback of a write this call just attempted) files are restored as-is.
323
+ */
324
+ async function moveJournaledFiles(fsPort, workspaceRoot, journalPath, files, side, verify) {
325
+ const pending = [];
326
+ const conflicts = [];
327
+ for (const file of files) {
328
+ const actual = await currentHash(fsPort, path.resolve(workspaceRoot, file.path));
329
+ const wanted = side === "before" ? file.beforeHash : file.afterHash;
330
+ if (actual === wanted)
331
+ continue;
332
+ const other = side === "before" ? file.afterHash : file.beforeHash;
333
+ if (verify && actual !== other) {
334
+ conflicts.push(file.path);
335
+ continue;
336
+ }
337
+ pending.push(file);
338
+ }
339
+ if (conflicts.length > 0) {
340
+ unrecoverableJournal(journalPath, `${conflicts.join(", ")} changed outside the journal (neither the journaled before nor after bytes)`);
341
+ }
342
+ const ordered = side === "before" ? [...pending].reverse() : pending;
343
+ for (const file of ordered) {
344
+ await setFile(fsPort, workspaceRoot, file.path, side === "before" ? file.before : file.after);
345
+ }
346
+ return ordered.map((file) => file.path);
347
+ }
348
+ /**
349
+ * An interrupted publish can leave its unique staged temp file next to the
350
+ * target. Remove those (best effort) once recovery has settled the target.
351
+ */
352
+ function removeOrphanedStagedFiles(workspaceRoot, files) {
353
+ for (const file of files) {
354
+ const absolute = path.resolve(workspaceRoot, file.path);
355
+ const prefix = `${path.basename(absolute)}.kibi-stage-`;
356
+ let names;
357
+ try {
358
+ names = readdirSync(path.dirname(absolute));
359
+ }
360
+ catch {
361
+ continue;
362
+ }
363
+ for (const name of names) {
364
+ if (!name.startsWith(prefix))
365
+ continue;
366
+ try {
367
+ unlinkSync(path.join(path.dirname(absolute), name));
368
+ }
369
+ catch {
370
+ // A leftover staged file is untracked noise, never plan state.
371
+ }
372
+ }
373
+ }
374
+ }
375
+ // implements REQ-core-journaled-engine-persistence
376
+ /** Restore every journaled file's before-bytes (in-process rollback). */
377
+ export async function restoreJournaledFiles(fsPort, workspaceRoot, journalPath, files) {
378
+ return moveJournaledFiles(fsPort, workspaceRoot, journalPath, files, "before", false);
379
+ }
380
+ // implements REQ-core-journaled-engine-persistence
381
+ /**
382
+ * Fingerprint the store state of the entities a plan touches: their sorted
383
+ * properties and outgoing relationships. Comparing the fingerprint taken
384
+ * before the commit with a later one decides whether an interrupted batch
385
+ * commit took effect; the batch is one transaction, so it either changed the
386
+ * plan's entities or changed nothing.
387
+ */
388
+ export async function storeFingerprint(prolog, entityIds) {
389
+ const ids = entityIds.map((id) => `'${escapeAtom(id)}'`).join(", ");
390
+ const state = `findall(Id-Entities-Relationships, (member(Id, [${ids}]), findall(Type-Sorted, (kb_entity(Id, Type, Props), msort(Props, Sorted)), Entities), findall(Rel-To, (member(Rel, [${RELATIONSHIP_TYPES.join(", ")}]), kb_relationship(Rel, Id, To)), Unsorted), msort(Unsorted, Relationships)), State)`;
391
+ // Hash inside Prolog so large entity properties never cross the
392
+ // transport; the outer findall leaves every intermediate variable unbound,
393
+ // so only the SHA-256 hex digest is reported back.
394
+ const goal = `findall(Hex, (${state}, format(string(StateText), '~q', [State]), sha:sha_hash(StateText, Digest, [algorithm(sha256)]), sha:hash_atom(Digest, Hex)), [FingerprintHash])`;
395
+ // Never answer a fingerprint from a cached read of an earlier state.
396
+ prolog.invalidateCache?.();
397
+ const result = await prolog.query(goal);
398
+ if (!result.success) {
399
+ throw new Error(`store fingerprint query failed: ${result.error ?? "unknown error"}`);
400
+ }
401
+ return contentHash((result.bindings.FingerprintHash ?? "").replace(/^['"]|['"]$/g, ""));
402
+ }
403
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2, REQ-core-journaled-engine-persistence
404
+ /**
405
+ * Deterministically finish one journal:
406
+ * - prepared: the store was never asked to commit, so every file is restored
407
+ * to its before-bytes and the journal ends rolled_back;
408
+ * - store_committing: the store fingerprint decides. Unchanged since the
409
+ * journal was written means the batch never took effect (roll back);
410
+ * changed means it committed (complete: publish every after-byte);
411
+ * - store_committed: complete;
412
+ * - committed / rolled_back: nothing to do (replay is idempotent).
413
+ * Recovery refuses, changing nothing, when a journaled file holds neither
414
+ * its before nor its after bytes.
415
+ */
416
+ export async function recoverPlanApplyJournal(context, handle, options) {
417
+ const { journal } = handle;
418
+ const now = options.now ?? (() => new Date());
419
+ if (journal.state === "committed" || journal.state === "rolled_back") {
420
+ return {
421
+ journalId: journal.journalId,
422
+ planHash: journal.planHash,
423
+ action: "none",
424
+ state: journal.state,
425
+ restoredPaths: [],
426
+ detail: `Journal ${journal.journalId} is already ${journal.state === "committed" ? "committed" : "rolled back"}; nothing to recover.`,
427
+ };
428
+ }
429
+ const fsPort = context.fs;
430
+ if (!fsPort) {
431
+ throw new Error(`Plan journal ${journal.journalId} recovery requires a filesystem-capable runtime`);
432
+ }
433
+ let committed;
434
+ let reason;
435
+ if (journal.state === "prepared") {
436
+ committed = false;
437
+ reason = "the store commit was never submitted";
438
+ }
439
+ else if (journal.state === "store_committed") {
440
+ committed = true;
441
+ reason = "the store had accepted the batch";
442
+ }
443
+ else {
444
+ const prolog = await options.prolog();
445
+ if (!prolog) {
446
+ throw new OperationError("PLAN_APPLY_RECOVERY_REQUIRED", `Plan journal ${journal.journalId} was interrupted during its store commit; recovering it needs the branch engine, which is unavailable. Retry kb_apply_plan with recoveryJournalId=${journal.journalId} once the engine is available`, false);
447
+ }
448
+ let fingerprint;
449
+ try {
450
+ fingerprint = await storeFingerprint(prolog, journal.store.entityIds);
451
+ }
452
+ catch (error) {
453
+ throw new OperationError("PLAN_APPLY_RECOVERY_REQUIRED", `Plan journal ${journal.journalId} was interrupted during its store commit and the store could not be inspected (${errorText(error)}). Retry kb_apply_plan with recoveryJournalId=${journal.journalId}`, false);
454
+ }
455
+ committed = fingerprint !== journal.store.preCommitFingerprint;
456
+ reason = committed
457
+ ? "the store shows the batch took effect"
458
+ : "the store shows the batch never took effect";
459
+ }
460
+ // Record the decision before touching files, so a second interruption
461
+ // resumes the same direction without inspecting the store again.
462
+ if (committed && journal.state !== "store_committed") {
463
+ recordPlanApplyJournal(handle, { state: "store_committed" }, now());
464
+ }
465
+ const restoredPaths = await moveJournaledFiles(fsPort, context.workspaceRoot, handle.journalPath, journal.files, committed ? "after" : "before", true);
466
+ removeOrphanedStagedFiles(context.workspaceRoot, journal.files);
467
+ if (!committed) {
468
+ const detail = `Rolled back interrupted plan ${journal.planHash.slice(0, 12)} (journal ${journal.journalId}): ${reason}; restored ${restoredPaths.length} of ${journal.files.length} journaled file(s) to their before-bytes.`;
469
+ recordPlanApplyJournal(handle, {
470
+ state: "rolled_back",
471
+ resolution: { action: "rolled_back", by: "recovery", detail },
472
+ }, now());
473
+ return {
474
+ journalId: journal.journalId,
475
+ planHash: journal.planHash,
476
+ action: "rolled_back",
477
+ state: "rolled_back",
478
+ restoredPaths,
479
+ detail,
480
+ };
481
+ }
482
+ const detail = `Completed interrupted plan ${journal.planHash.slice(0, 12)} (journal ${journal.journalId}): ${reason}; published ${restoredPaths.length} of ${journal.files.length} journaled file(s) at their after-bytes.`;
483
+ try {
484
+ reconcilePendingSourceReceipts(context.workspaceRoot, journal.files);
485
+ }
486
+ catch (error) {
487
+ // The store and files are complete; receipts stay pending in the
488
+ // store_committed journal for the next recovery.
489
+ return {
490
+ journalId: journal.journalId,
491
+ planHash: journal.planHash,
492
+ action: "completed",
493
+ state: "committed",
494
+ restoredPaths,
495
+ detail,
496
+ receiptFailure: errorText(error),
497
+ };
498
+ }
499
+ recordPlanApplyJournal(handle, {
500
+ state: "committed",
501
+ resolution: { action: "completed", by: "recovery", detail },
502
+ }, now());
503
+ return {
504
+ journalId: journal.journalId,
505
+ planHash: journal.planHash,
506
+ action: "completed",
507
+ state: "committed",
508
+ restoredPaths,
509
+ detail,
510
+ };
511
+ }
512
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2, REQ-core-journaled-engine-persistence
513
+ /** Open an explicit journal by id for kb_apply_plan recoveryJournalId. */
514
+ export function openPlanApplyJournalById(context, journalId) {
515
+ if (!isPlanApplyJournalId(journalId)) {
516
+ throw new Error("Plan journal ID is invalid");
517
+ }
518
+ const location = journalLocation(context);
519
+ if (location === null) {
520
+ throw new Error("Plan journal recovery failed: the KB branch could not be resolved");
521
+ }
522
+ for (const directory of location.all) {
523
+ const candidate = path.join(directory, `${journalId}.json`);
524
+ if (existsSync(candidate)) {
525
+ return { journalPath: candidate, journal: readJournalFile(candidate) };
526
+ }
527
+ }
528
+ throw new Error(`Plan journal ${journalId} does not exist for branch ${location.branch}`);
529
+ }
530
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2, REQ-core-journaled-engine-persistence
531
+ /**
532
+ * Detect and finish every incomplete plan journal of the active branch. Runs
533
+ * at the start of each mutating call that holds the workspace mutation lock,
534
+ * before that call reads or writes anything. Journals are finished in path
535
+ * order so the outcome does not depend on directory enumeration order.
536
+ */
537
+ export async function recoverPendingPlanApplyJournals(context, options) {
538
+ if (!context.fs)
539
+ return [];
540
+ const location = journalLocation(context);
541
+ if (location === null)
542
+ return [];
543
+ const candidates = [];
544
+ for (const directory of location.all) {
545
+ let names;
546
+ try {
547
+ names = readdirSync(directory);
548
+ }
549
+ catch {
550
+ continue;
551
+ }
552
+ for (const name of names) {
553
+ if (name.endsWith(".json") && isPlanApplyJournalId(name.slice(0, -5))) {
554
+ candidates.push(path.join(directory, name));
555
+ }
556
+ }
557
+ }
558
+ const recoveries = [];
559
+ for (const journalPath of candidates.sort()) {
560
+ const journal = readJournalFile(journalPath);
561
+ if (journal.state === "committed" || journal.state === "rolled_back")
562
+ continue;
563
+ recoveries.push(await recoverPlanApplyJournal(context, { journalPath, journal }, options));
564
+ }
565
+ return recoveries;
566
+ }
567
+ /** One line per journal a recovery actually changed, for result warnings. */
568
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2
569
+ export function planRecoveryNotes(recoveries) {
570
+ return recoveries
571
+ .filter((recovery) => recovery.action !== "none")
572
+ .map((recovery) => recovery.detail);
573
+ }
574
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2, REQ-core-journaled-engine-persistence
575
+ /**
576
+ * A mutating call that settled an interrupted plan and then failed on its own
577
+ * work still reports the settlement in its failure text. Typed errors keep
578
+ * their class, code and retryability.
579
+ */
580
+ export function withPlanRecoveryNotes(error, notes) {
581
+ if (notes.length === 0)
582
+ return error;
583
+ const suffix = ` [settled before this failure: ${notes.join(" ")}]`;
584
+ if (error instanceof OperationError) {
585
+ return new OperationError(error.code, `${error.detail}${suffix}`, error.retryable);
586
+ }
587
+ if (error instanceof InputError) {
588
+ return new InputError(error.code, `${error.detail}${suffix}`);
589
+ }
590
+ if (error instanceof Error) {
591
+ error.message = `${error.message}${suffix}`;
592
+ return error;
593
+ }
594
+ return new Error(`${String(error)}${suffix}`);
595
+ }
596
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2, REQ-core-journaled-engine-persistence
597
+ /**
598
+ * Recover every pending plan journal before another mutation, then refuse
599
+ * that mutation while a recovered plan still has unfinished postcommit work
600
+ * (pending-source receipts): a later write could change the files the
601
+ * journal guards and leave it unrecoverable. `explicitJournalId` is the
602
+ * journal the caller is recovering on purpose, which reports its own
603
+ * unfinished work instead.
604
+ */
605
+ export async function settlePendingPlanApplyJournals(context, options, explicitJournalId) {
606
+ const recoveries = await recoverPendingPlanApplyJournals(context, options);
607
+ const unfinished = recoveries.find((recovery) => recovery.receiptFailure !== undefined &&
608
+ recovery.journalId !== explicitJournalId);
609
+ if (unfinished !== undefined) {
610
+ throw new OperationError("PLAN_APPLY_RECOVERY_REQUIRED", `${unfinished.detail} Binding its new source files to pending-source receipts failed (${unfinished.receiptFailure}); run kb_apply_plan with recoveryJournalId=${unfinished.journalId} before further writes`, false);
611
+ }
612
+ return recoveries;
613
+ }
@@ -4,5 +4,7 @@ import type { PrologPort } from "../../public/operations/runtime-types.js";
4
4
  * project-local schema declares. An alias is rejected too, with the declared
5
5
  * constant to use instead, so new facts never need migrating.
6
6
  */
7
- export declare function assertPredicateArgumentVocabulary(prolog: Pick<PrologPort, "query">, entity: Readonly<Record<string, unknown>>): Promise<void>;
7
+ export declare function assertPredicateArgumentVocabulary(prolog: Pick<PrologPort, "query">, entity: Readonly<Record<string, unknown>>, staged?: Readonly<{
8
+ entities: ReadonlyMap<string, Readonly<Record<string, unknown>>>;
9
+ }>): Promise<void>;
8
10
  //# sourceMappingURL=predicate-vocabulary-guard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"predicate-vocabulary-guard.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/predicate-vocabulary-guard.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAY3E;;;;GAIG;AAEH,wBAAsB,iCAAiC,CACrD,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EACjC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxC,OAAO,CAAC,IAAI,CAAC,CA+Cf"}
1
+ {"version":3,"file":"predicate-vocabulary-guard.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/predicate-vocabulary-guard.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAY3E;;;;GAIG;AAEH,wBAAsB,iCAAiC,CACrD,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EACjC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,MAAM,CAAC,EAAE,QAAQ,CAAC;IAChB,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CAClE,CAAC,GACD,OAAO,CAAC,IAAI,CAAC,CA+Df"}
@@ -28,7 +28,7 @@ function text(value) {
28
28
  * constant to use instead, so new facts never need migrating.
29
29
  */
30
30
  // implements REQ-kibi-predicate-argument-constants
31
- export async function assertPredicateArgumentVocabulary(prolog, entity) {
31
+ export async function assertPredicateArgumentVocabulary(prolog, entity, staged) {
32
32
  if (entity.type !== "fact" || entity.fact_kind !== "predicate")
33
33
  return;
34
34
  const name = text(entity.predicate_name);
@@ -42,9 +42,20 @@ export async function assertPredicateArgumentVocabulary(prolog, entity) {
42
42
  if (!result.success) {
43
43
  throw new Error(`Predicate schema lookup failed: ${result.error ?? "unknown error"}`);
44
44
  }
45
- const schemas = result.bindings.Results
45
+ const stored = result.bindings.Results
46
46
  ? parseListOfLists(result.bindings.Results).map(parseEntityFromList)
47
47
  : [];
48
+ // Schemas an earlier plan step writes replace the stored version of the
49
+ // same id and count as declared.
50
+ const plannedSchemas = [...(staged?.entities.values() ?? [])].filter((candidate) => candidate.type === "fact" &&
51
+ candidate.fact_kind === "predicate_schema" &&
52
+ text(candidate.predicate_name) === name &&
53
+ (text(candidate.predicate_namespace) ?? "default") === namespace &&
54
+ Number(candidate.predicate_arity) === args.length);
55
+ const schemas = [
56
+ ...stored.filter((schema) => !staged?.entities.has(String(schema.id ?? ""))),
57
+ ...plannedSchemas,
58
+ ];
48
59
  const problems = [];
49
60
  for (const schema of schemas) {
50
61
  const argumentNames = Array.isArray(schema.argument_names)