@superbee/core 0.2.0-pre.2 → 0.2.0-pre.21

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 (517) hide show
  1. package/README.md +1000 -0
  2. package/dist/artifact-contract.d.ts +90 -0
  3. package/dist/artifact-contract.d.ts.map +1 -0
  4. package/dist/artifact-contract.js +205 -0
  5. package/dist/artifact-contract.js.map +1 -0
  6. package/dist/backend.d.ts +19 -1
  7. package/dist/backend.d.ts.map +1 -1
  8. package/dist/backend.js +83 -36
  9. package/dist/backend.js.map +1 -1
  10. package/dist/blob-contract.d.ts +80 -0
  11. package/dist/blob-contract.d.ts.map +1 -0
  12. package/dist/blob-contract.js +131 -0
  13. package/dist/blob-contract.js.map +1 -0
  14. package/dist/blob-journal.d.ts +302 -0
  15. package/dist/blob-journal.d.ts.map +1 -0
  16. package/dist/blob-journal.js +540 -0
  17. package/dist/blob-journal.js.map +1 -0
  18. package/dist/body-replace-guards.d.ts +50 -0
  19. package/dist/body-replace-guards.d.ts.map +1 -0
  20. package/dist/body-replace-guards.js +158 -0
  21. package/dist/body-replace-guards.js.map +1 -0
  22. package/dist/builtin-recipes/content.d.ts +93 -0
  23. package/dist/builtin-recipes/content.d.ts.map +1 -0
  24. package/dist/builtin-recipes/content.js +288 -0
  25. package/dist/builtin-recipes/content.js.map +1 -0
  26. package/dist/builtin-recipes.d.ts +10 -0
  27. package/dist/builtin-recipes.d.ts.map +1 -0
  28. package/dist/builtin-recipes.js +97 -0
  29. package/dist/builtin-recipes.js.map +1 -0
  30. package/dist/bundle-health/index.d.ts +14 -0
  31. package/dist/bundle-health/index.d.ts.map +1 -0
  32. package/dist/bundle-health/index.js +12 -0
  33. package/dist/bundle-health/index.js.map +1 -0
  34. package/dist/bundle-health/kind-findings.d.ts +44 -0
  35. package/dist/bundle-health/kind-findings.d.ts.map +1 -0
  36. package/dist/bundle-health/kind-findings.js +68 -0
  37. package/dist/bundle-health/kind-findings.js.map +1 -0
  38. package/dist/bundle-health/legacy-page.d.ts +39 -0
  39. package/dist/bundle-health/legacy-page.d.ts.map +1 -0
  40. package/dist/bundle-health/legacy-page.js +50 -0
  41. package/dist/bundle-health/legacy-page.js.map +1 -0
  42. package/dist/bundle-health/report.d.ts +202 -0
  43. package/dist/bundle-health/report.d.ts.map +1 -0
  44. package/dist/bundle-health/report.js +438 -0
  45. package/dist/bundle-health/report.js.map +1 -0
  46. package/dist/bundle-ops.d.ts +1 -1
  47. package/dist/bundle-ops.d.ts.map +1 -1
  48. package/dist/bundle-ops.js +2 -2
  49. package/dist/bundle-ops.js.map +1 -1
  50. package/dist/bundle.d.ts +2 -1
  51. package/dist/bundle.d.ts.map +1 -1
  52. package/dist/bundle.js +3 -3
  53. package/dist/bundle.js.map +1 -1
  54. package/dist/code-point-order.d.ts +8 -0
  55. package/dist/code-point-order.d.ts.map +1 -0
  56. package/dist/code-point-order.js +20 -0
  57. package/dist/code-point-order.js.map +1 -0
  58. package/dist/connectors.d.ts +230 -0
  59. package/dist/connectors.d.ts.map +1 -0
  60. package/dist/connectors.js +1150 -0
  61. package/dist/connectors.js.map +1 -0
  62. package/dist/cross-bundle.d.ts +135 -0
  63. package/dist/cross-bundle.d.ts.map +1 -0
  64. package/dist/cross-bundle.js +186 -0
  65. package/dist/cross-bundle.js.map +1 -0
  66. package/dist/dependencies.d.ts +94 -0
  67. package/dist/dependencies.d.ts.map +1 -0
  68. package/dist/dependencies.js +180 -0
  69. package/dist/dependencies.js.map +1 -0
  70. package/dist/document-codec.d.ts +6 -1
  71. package/dist/document-codec.d.ts.map +1 -1
  72. package/dist/document-codec.js +8 -4
  73. package/dist/document-codec.js.map +1 -1
  74. package/dist/document-field-actions.d.ts +31 -1
  75. package/dist/document-field-actions.d.ts.map +1 -1
  76. package/dist/document-field-actions.js +137 -34
  77. package/dist/document-field-actions.js.map +1 -1
  78. package/dist/document-mutation.d.ts +22 -10
  79. package/dist/document-mutation.d.ts.map +1 -1
  80. package/dist/document-mutation.js +58 -27
  81. package/dist/document-mutation.js.map +1 -1
  82. package/dist/document-write-policy.d.ts.map +1 -1
  83. package/dist/document-write-policy.js +4 -2
  84. package/dist/document-write-policy.js.map +1 -1
  85. package/dist/engine.d.ts +9 -2
  86. package/dist/engine.d.ts.map +1 -1
  87. package/dist/engine.js +11 -4
  88. package/dist/engine.js.map +1 -1
  89. package/dist/file-journaled-backend-internal.d.ts +21 -0
  90. package/dist/file-journaled-backend-internal.d.ts.map +1 -0
  91. package/dist/file-journaled-backend-internal.js +12 -0
  92. package/dist/file-journaled-backend-internal.js.map +1 -0
  93. package/dist/file-journaled-backend.d.ts +200 -0
  94. package/dist/file-journaled-backend.d.ts.map +1 -0
  95. package/dist/file-journaled-backend.js +1334 -0
  96. package/dist/file-journaled-backend.js.map +1 -0
  97. package/dist/filesystem-host.d.ts +15 -0
  98. package/dist/filesystem-host.d.ts.map +1 -0
  99. package/dist/filesystem-host.js +20 -0
  100. package/dist/filesystem-host.js.map +1 -0
  101. package/dist/filesystem-identity.d.ts +86 -4
  102. package/dist/filesystem-identity.d.ts.map +1 -1
  103. package/dist/filesystem-identity.js +338 -102
  104. package/dist/filesystem-identity.js.map +1 -1
  105. package/dist/filesystem-lock.d.ts +16 -3
  106. package/dist/filesystem-lock.d.ts.map +1 -1
  107. package/dist/filesystem-lock.js +323 -77
  108. package/dist/filesystem-lock.js.map +1 -1
  109. package/dist/filesystem-push-role.d.ts +77 -0
  110. package/dist/filesystem-push-role.d.ts.map +1 -0
  111. package/dist/filesystem-push-role.js +165 -0
  112. package/dist/filesystem-push-role.js.map +1 -0
  113. package/dist/filesystem.d.ts +17 -0
  114. package/dist/filesystem.d.ts.map +1 -0
  115. package/dist/filesystem.js +18 -0
  116. package/dist/filesystem.js.map +1 -0
  117. package/dist/frontmatter.d.ts +8 -0
  118. package/dist/frontmatter.d.ts.map +1 -1
  119. package/dist/frontmatter.js +14 -4
  120. package/dist/frontmatter.js.map +1 -1
  121. package/dist/governed-body-write.d.ts +106 -0
  122. package/dist/governed-body-write.d.ts.map +1 -0
  123. package/dist/governed-body-write.js +251 -0
  124. package/dist/governed-body-write.js.map +1 -0
  125. package/dist/host-policy-snapshot.d.ts +9 -0
  126. package/dist/host-policy-snapshot.d.ts.map +1 -0
  127. package/dist/host-policy-snapshot.js +33 -0
  128. package/dist/host-policy-snapshot.js.map +1 -0
  129. package/dist/host-text.d.ts +31 -0
  130. package/dist/host-text.d.ts.map +1 -0
  131. package/dist/host-text.js +77 -0
  132. package/dist/host-text.js.map +1 -0
  133. package/dist/hosted-client/binding.d.ts +7 -0
  134. package/dist/hosted-client/binding.d.ts.map +1 -0
  135. package/dist/hosted-client/binding.js +10 -0
  136. package/dist/hosted-client/binding.js.map +1 -0
  137. package/dist/hosted-client/blob-delivery.d.ts +67 -0
  138. package/dist/hosted-client/blob-delivery.d.ts.map +1 -0
  139. package/dist/hosted-client/blob-delivery.js +169 -0
  140. package/dist/hosted-client/blob-delivery.js.map +1 -0
  141. package/dist/hosted-client/blob-write.d.ts +386 -0
  142. package/dist/hosted-client/blob-write.d.ts.map +1 -0
  143. package/dist/hosted-client/blob-write.js +770 -0
  144. package/dist/hosted-client/blob-write.js.map +1 -0
  145. package/dist/hosted-client/bundle-listing.d.ts +46 -0
  146. package/dist/hosted-client/bundle-listing.d.ts.map +1 -0
  147. package/dist/hosted-client/bundle-listing.js +36 -0
  148. package/dist/hosted-client/bundle-listing.js.map +1 -0
  149. package/dist/hosted-client/client.d.ts +219 -0
  150. package/dist/hosted-client/client.d.ts.map +1 -0
  151. package/dist/hosted-client/client.js +309 -0
  152. package/dist/hosted-client/client.js.map +1 -0
  153. package/dist/hosted-client/directory.d.ts +46 -0
  154. package/dist/hosted-client/directory.d.ts.map +1 -0
  155. package/dist/hosted-client/directory.js +74 -0
  156. package/dist/hosted-client/directory.js.map +1 -0
  157. package/dist/hosted-client/errors.d.ts +26 -0
  158. package/dist/hosted-client/errors.d.ts.map +1 -0
  159. package/dist/hosted-client/errors.js +35 -0
  160. package/dist/hosted-client/errors.js.map +1 -0
  161. package/dist/hosted-client/inbox.d.ts +320 -0
  162. package/dist/hosted-client/inbox.d.ts.map +1 -0
  163. package/dist/hosted-client/inbox.js +379 -0
  164. package/dist/hosted-client/inbox.js.map +1 -0
  165. package/dist/hosted-client/index.d.ts +30 -0
  166. package/dist/hosted-client/index.d.ts.map +1 -0
  167. package/dist/hosted-client/index.js +32 -0
  168. package/dist/hosted-client/index.js.map +1 -0
  169. package/dist/hosted-client/journal.d.ts +103 -0
  170. package/dist/hosted-client/journal.d.ts.map +1 -0
  171. package/dist/hosted-client/journal.js +464 -0
  172. package/dist/hosted-client/journal.js.map +1 -0
  173. package/dist/hosted-client/node.d.ts +74 -0
  174. package/dist/hosted-client/node.d.ts.map +1 -0
  175. package/dist/hosted-client/node.js +219 -0
  176. package/dist/hosted-client/node.js.map +1 -0
  177. package/dist/hosted-client/reference.d.ts +12 -0
  178. package/dist/hosted-client/reference.d.ts.map +1 -0
  179. package/dist/hosted-client/reference.js +29 -0
  180. package/dist/hosted-client/reference.js.map +1 -0
  181. package/dist/hosted-client/write.d.ts +217 -0
  182. package/dist/hosted-client/write.d.ts.map +1 -0
  183. package/dist/hosted-client/write.js +418 -0
  184. package/dist/hosted-client/write.js.map +1 -0
  185. package/dist/hosted-transport/answer-rows.d.ts +243 -0
  186. package/dist/hosted-transport/answer-rows.d.ts.map +1 -0
  187. package/dist/hosted-transport/answer-rows.js +454 -0
  188. package/dist/hosted-transport/answer-rows.js.map +1 -0
  189. package/dist/hosted-transport/blob-reads.d.ts +35 -0
  190. package/dist/hosted-transport/blob-reads.d.ts.map +1 -0
  191. package/dist/hosted-transport/blob-reads.js +179 -0
  192. package/dist/hosted-transport/blob-reads.js.map +1 -0
  193. package/dist/hosted-transport/blob-write.d.ts +34 -0
  194. package/dist/hosted-transport/blob-write.d.ts.map +1 -0
  195. package/dist/hosted-transport/blob-write.js +115 -0
  196. package/dist/hosted-transport/blob-write.js.map +1 -0
  197. package/dist/hosted-transport/carrier.d.ts +151 -0
  198. package/dist/hosted-transport/carrier.d.ts.map +1 -0
  199. package/dist/hosted-transport/carrier.js +260 -0
  200. package/dist/hosted-transport/carrier.js.map +1 -0
  201. package/dist/hosted-transport/history-pages.d.ts +55 -0
  202. package/dist/hosted-transport/history-pages.d.ts.map +1 -0
  203. package/dist/hosted-transport/history-pages.js +69 -0
  204. package/dist/hosted-transport/history-pages.js.map +1 -0
  205. package/dist/hosted-transport/index.d.ts +18 -0
  206. package/dist/hosted-transport/index.d.ts.map +1 -0
  207. package/dist/hosted-transport/index.js +18 -0
  208. package/dist/hosted-transport/index.js.map +1 -0
  209. package/dist/hosted-transport/operations.d.ts +85 -0
  210. package/dist/hosted-transport/operations.d.ts.map +1 -0
  211. package/dist/hosted-transport/operations.js +128 -0
  212. package/dist/hosted-transport/operations.js.map +1 -0
  213. package/dist/hosted-transport/paged-reads.d.ts +87 -0
  214. package/dist/hosted-transport/paged-reads.d.ts.map +1 -0
  215. package/dist/hosted-transport/paged-reads.js +238 -0
  216. package/dist/hosted-transport/paged-reads.js.map +1 -0
  217. package/dist/hosted-transport/read-adapter.d.ts +256 -0
  218. package/dist/hosted-transport/read-adapter.d.ts.map +1 -0
  219. package/dist/hosted-transport/read-adapter.js +568 -0
  220. package/dist/hosted-transport/read-adapter.js.map +1 -0
  221. package/dist/hosted-transport/root-write.d.ts +114 -0
  222. package/dist/hosted-transport/root-write.d.ts.map +1 -0
  223. package/dist/hosted-transport/root-write.js +171 -0
  224. package/dist/hosted-transport/root-write.js.map +1 -0
  225. package/dist/hosted-transport/whole-document-transport.d.ts +204 -0
  226. package/dist/hosted-transport/whole-document-transport.d.ts.map +1 -0
  227. package/dist/hosted-transport/whole-document-transport.js +447 -0
  228. package/dist/hosted-transport/whole-document-transport.js.map +1 -0
  229. package/dist/index.d.ts +34 -10
  230. package/dist/index.d.ts.map +1 -1
  231. package/dist/index.js +26 -8
  232. package/dist/index.js.map +1 -1
  233. package/dist/indexeddb-backend.d.ts +28 -6
  234. package/dist/indexeddb-backend.d.ts.map +1 -1
  235. package/dist/indexeddb-backend.js +471 -92
  236. package/dist/indexeddb-backend.js.map +1 -1
  237. package/dist/journaled-backend.d.ts +247 -10
  238. package/dist/journaled-backend.d.ts.map +1 -1
  239. package/dist/journaled-backend.js +302 -1
  240. package/dist/journaled-backend.js.map +1 -1
  241. package/dist/kind-authoring.d.ts +170 -0
  242. package/dist/kind-authoring.d.ts.map +1 -0
  243. package/dist/kind-authoring.js +451 -0
  244. package/dist/kind-authoring.js.map +1 -0
  245. package/dist/kind-conformance-error.d.ts +15 -0
  246. package/dist/kind-conformance-error.d.ts.map +1 -0
  247. package/dist/kind-conformance-error.js +20 -0
  248. package/dist/kind-conformance-error.js.map +1 -0
  249. package/dist/kind-coverage.d.ts +99 -0
  250. package/dist/kind-coverage.d.ts.map +1 -0
  251. package/dist/kind-coverage.js +186 -0
  252. package/dist/kind-coverage.js.map +1 -0
  253. package/dist/kind-field-mutation.d.ts +4 -0
  254. package/dist/kind-field-mutation.d.ts.map +1 -1
  255. package/dist/kind-field-mutation.js +28 -3
  256. package/dist/kind-field-mutation.js.map +1 -1
  257. package/dist/kinds-load.js +4 -4
  258. package/dist/kinds-load.js.map +1 -1
  259. package/dist/kinds.d.ts +65 -2
  260. package/dist/kinds.d.ts.map +1 -1
  261. package/dist/kinds.js +341 -21
  262. package/dist/kinds.js.map +1 -1
  263. package/dist/link-types.d.ts +26 -0
  264. package/dist/link-types.d.ts.map +1 -0
  265. package/dist/link-types.js +57 -0
  266. package/dist/link-types.js.map +1 -0
  267. package/dist/links.d.ts +21 -3
  268. package/dist/links.d.ts.map +1 -1
  269. package/dist/links.js +213 -14
  270. package/dist/links.js.map +1 -1
  271. package/dist/managed-document-metadata.d.ts +2 -0
  272. package/dist/managed-document-metadata.d.ts.map +1 -0
  273. package/dist/managed-document-metadata.js +9 -0
  274. package/dist/managed-document-metadata.js.map +1 -0
  275. package/dist/memory-backend.d.ts +2 -2
  276. package/dist/memory-backend.d.ts.map +1 -1
  277. package/dist/memory-backend.js +26 -11
  278. package/dist/memory-backend.js.map +1 -1
  279. package/dist/paths.d.ts +16 -0
  280. package/dist/paths.d.ts.map +1 -1
  281. package/dist/paths.js +25 -0
  282. package/dist/paths.js.map +1 -1
  283. package/dist/platform.d.ts +13 -2
  284. package/dist/platform.d.ts.map +1 -1
  285. package/dist/platform.js +16 -0
  286. package/dist/platform.js.map +1 -1
  287. package/dist/portable-export.d.ts +119 -0
  288. package/dist/portable-export.d.ts.map +1 -0
  289. package/dist/portable-export.js +501 -0
  290. package/dist/portable-export.js.map +1 -0
  291. package/dist/publication-filesystem.d.ts +1 -1
  292. package/dist/publication-filesystem.d.ts.map +1 -1
  293. package/dist/publication-filesystem.js +1 -1
  294. package/dist/publication-filesystem.js.map +1 -1
  295. package/dist/qualified-links.d.ts +67 -0
  296. package/dist/qualified-links.d.ts.map +1 -0
  297. package/dist/qualified-links.js +117 -0
  298. package/dist/qualified-links.js.map +1 -0
  299. package/dist/query-filter.d.ts +16 -2
  300. package/dist/query-filter.d.ts.map +1 -1
  301. package/dist/query-filter.js +40 -1
  302. package/dist/query-filter.js.map +1 -1
  303. package/dist/query-order.d.ts +22 -0
  304. package/dist/query-order.d.ts.map +1 -0
  305. package/dist/query-order.js +42 -0
  306. package/dist/query-order.js.map +1 -0
  307. package/dist/recipe-lifecycle/errors.d.ts +96 -0
  308. package/dist/recipe-lifecycle/errors.d.ts.map +1 -0
  309. package/dist/recipe-lifecycle/errors.js +122 -0
  310. package/dist/recipe-lifecycle/errors.js.map +1 -0
  311. package/dist/recipe-lifecycle/evolution.d.ts +108 -0
  312. package/dist/recipe-lifecycle/evolution.d.ts.map +1 -0
  313. package/dist/recipe-lifecycle/evolution.js +884 -0
  314. package/dist/recipe-lifecycle/evolution.js.map +1 -0
  315. package/dist/recipe-lifecycle/index.d.ts +26 -0
  316. package/dist/recipe-lifecycle/index.d.ts.map +1 -0
  317. package/dist/recipe-lifecycle/index.js +22 -0
  318. package/dist/recipe-lifecycle/index.js.map +1 -0
  319. package/dist/recipe-lifecycle/install.d.ts +127 -0
  320. package/dist/recipe-lifecycle/install.d.ts.map +1 -0
  321. package/dist/recipe-lifecycle/install.js +526 -0
  322. package/dist/recipe-lifecycle/install.js.map +1 -0
  323. package/dist/recipe-lifecycle/legacy-aliases.d.ts +16 -0
  324. package/dist/recipe-lifecycle/legacy-aliases.d.ts.map +1 -0
  325. package/dist/recipe-lifecycle/legacy-aliases.js +51 -0
  326. package/dist/recipe-lifecycle/legacy-aliases.js.map +1 -0
  327. package/dist/recipe-lifecycle/stamp.d.ts +136 -0
  328. package/dist/recipe-lifecycle/stamp.d.ts.map +1 -0
  329. package/dist/recipe-lifecycle/stamp.js +326 -0
  330. package/dist/recipe-lifecycle/stamp.js.map +1 -0
  331. package/dist/recipes.d.ts +29 -1
  332. package/dist/recipes.d.ts.map +1 -1
  333. package/dist/recipes.js +137 -1
  334. package/dist/recipes.js.map +1 -1
  335. package/dist/record-overlay/apply.d.ts +43 -0
  336. package/dist/record-overlay/apply.d.ts.map +1 -0
  337. package/dist/record-overlay/apply.js +527 -0
  338. package/dist/record-overlay/apply.js.map +1 -0
  339. package/dist/record-overlay/canonical.d.ts +22 -0
  340. package/dist/record-overlay/canonical.d.ts.map +1 -0
  341. package/dist/record-overlay/canonical.js +47 -0
  342. package/dist/record-overlay/canonical.js.map +1 -0
  343. package/dist/record-overlay/digest.d.ts +4 -0
  344. package/dist/record-overlay/digest.d.ts.map +1 -0
  345. package/dist/record-overlay/digest.js +11 -0
  346. package/dist/record-overlay/digest.js.map +1 -0
  347. package/dist/record-overlay/errors.d.ts +44 -0
  348. package/dist/record-overlay/errors.d.ts.map +1 -0
  349. package/dist/record-overlay/errors.js +55 -0
  350. package/dist/record-overlay/errors.js.map +1 -0
  351. package/dist/record-overlay/findings.d.ts +5 -0
  352. package/dist/record-overlay/findings.d.ts.map +1 -0
  353. package/dist/record-overlay/findings.js +83 -0
  354. package/dist/record-overlay/findings.js.map +1 -0
  355. package/dist/record-overlay/index.d.ts +23 -0
  356. package/dist/record-overlay/index.d.ts.map +1 -0
  357. package/dist/record-overlay/index.js +21 -0
  358. package/dist/record-overlay/index.js.map +1 -0
  359. package/dist/record-overlay/plan.d.ts +12 -0
  360. package/dist/record-overlay/plan.d.ts.map +1 -0
  361. package/dist/record-overlay/plan.js +285 -0
  362. package/dist/record-overlay/plan.js.map +1 -0
  363. package/dist/record-overlay/policy.d.ts +69 -0
  364. package/dist/record-overlay/policy.d.ts.map +1 -0
  365. package/dist/record-overlay/policy.js +228 -0
  366. package/dist/record-overlay/policy.js.map +1 -0
  367. package/dist/record-overlay/redact.d.ts +3 -0
  368. package/dist/record-overlay/redact.d.ts.map +1 -0
  369. package/dist/record-overlay/redact.js +27 -0
  370. package/dist/record-overlay/redact.js.map +1 -0
  371. package/dist/record-overlay/signal-kind.d.ts +30 -0
  372. package/dist/record-overlay/signal-kind.d.ts.map +1 -0
  373. package/dist/record-overlay/signal-kind.js +38 -0
  374. package/dist/record-overlay/signal-kind.js.map +1 -0
  375. package/dist/record-overlay/signal-text.d.ts +45 -0
  376. package/dist/record-overlay/signal-text.d.ts.map +1 -0
  377. package/dist/record-overlay/signal-text.js +116 -0
  378. package/dist/record-overlay/signal-text.js.map +1 -0
  379. package/dist/record-overlay/source-index.d.ts +19 -0
  380. package/dist/record-overlay/source-index.d.ts.map +1 -0
  381. package/dist/record-overlay/source-index.js +102 -0
  382. package/dist/record-overlay/source-index.js.map +1 -0
  383. package/dist/record-overlay/types.d.ts +291 -0
  384. package/dist/record-overlay/types.d.ts.map +1 -0
  385. package/dist/record-overlay/types.js +2 -0
  386. package/dist/record-overlay/types.js.map +1 -0
  387. package/dist/remote-backend.d.ts +67 -67
  388. package/dist/remote-backend.d.ts.map +1 -1
  389. package/dist/remote-backend.js +169 -201
  390. package/dist/remote-backend.js.map +1 -1
  391. package/dist/remote-document-codec.d.ts +15 -0
  392. package/dist/remote-document-codec.d.ts.map +1 -0
  393. package/dist/remote-document-codec.js +103 -0
  394. package/dist/remote-document-codec.js.map +1 -0
  395. package/dist/remote-error.d.ts +38 -0
  396. package/dist/remote-error.d.ts.map +1 -0
  397. package/dist/remote-error.js +59 -0
  398. package/dist/remote-error.js.map +1 -0
  399. package/dist/remote-operations.d.ts +6 -3
  400. package/dist/remote-operations.d.ts.map +1 -1
  401. package/dist/remote-operations.js +23 -5
  402. package/dist/remote-operations.js.map +1 -1
  403. package/dist/remote-parsers.d.ts +86 -0
  404. package/dist/remote-parsers.d.ts.map +1 -0
  405. package/dist/remote-parsers.js +181 -0
  406. package/dist/remote-parsers.js.map +1 -0
  407. package/dist/remote.d.ts +5 -2
  408. package/dist/remote.d.ts.map +1 -1
  409. package/dist/remote.js +5 -1
  410. package/dist/remote.js.map +1 -1
  411. package/dist/search/backend-source.d.ts +6 -0
  412. package/dist/search/backend-source.d.ts.map +1 -0
  413. package/dist/search/backend-source.js +33 -0
  414. package/dist/search/backend-source.js.map +1 -0
  415. package/dist/search/contract.d.ts +151 -0
  416. package/dist/search/contract.d.ts.map +1 -0
  417. package/dist/search/contract.js +78 -0
  418. package/dist/search/contract.js.map +1 -0
  419. package/dist/search/errors.d.ts +30 -0
  420. package/dist/search/errors.d.ts.map +1 -0
  421. package/dist/search/errors.js +50 -0
  422. package/dist/search/errors.js.map +1 -0
  423. package/dist/search/identity.d.ts +12 -0
  424. package/dist/search/identity.d.ts.map +1 -0
  425. package/dist/search/identity.js +90 -0
  426. package/dist/search/identity.js.map +1 -0
  427. package/dist/search/index.d.ts +16 -0
  428. package/dist/search/index.d.ts.map +1 -0
  429. package/dist/search/index.js +15 -0
  430. package/dist/search/index.js.map +1 -0
  431. package/dist/search/memory-source.d.ts +36 -0
  432. package/dist/search/memory-source.d.ts.map +1 -0
  433. package/dist/search/memory-source.js +235 -0
  434. package/dist/search/memory-source.js.map +1 -0
  435. package/dist/search/rank.d.ts +34 -0
  436. package/dist/search/rank.d.ts.map +1 -0
  437. package/dist/search/rank.js +129 -0
  438. package/dist/search/rank.js.map +1 -0
  439. package/dist/search/row-text.d.ts +38 -0
  440. package/dist/search/row-text.d.ts.map +1 -0
  441. package/dist/search/row-text.js +88 -0
  442. package/dist/search/row-text.js.map +1 -0
  443. package/dist/search/search.d.ts +28 -0
  444. package/dist/search/search.d.ts.map +1 -0
  445. package/dist/search/search.js +257 -0
  446. package/dist/search/search.js.map +1 -0
  447. package/dist/search/source.d.ts +67 -0
  448. package/dist/search/source.d.ts.map +1 -0
  449. package/dist/search/source.js +2 -0
  450. package/dist/search/source.js.map +1 -0
  451. package/dist/search/text.d.ts +47 -0
  452. package/dist/search/text.d.ts.map +1 -0
  453. package/dist/search/text.js +148 -0
  454. package/dist/search/text.js.map +1 -0
  455. package/dist/site-capabilities.d.ts +339 -0
  456. package/dist/site-capabilities.d.ts.map +1 -0
  457. package/dist/site-capabilities.js +717 -0
  458. package/dist/site-capabilities.js.map +1 -0
  459. package/dist/source-engine/guard.d.ts +82 -0
  460. package/dist/source-engine/guard.d.ts.map +1 -0
  461. package/dist/source-engine/guard.js +396 -0
  462. package/dist/source-engine/guard.js.map +1 -0
  463. package/dist/source-engine/index.d.ts +24 -0
  464. package/dist/source-engine/index.d.ts.map +1 -0
  465. package/dist/source-engine/index.js +20 -0
  466. package/dist/source-engine/index.js.map +1 -0
  467. package/dist/source-engine/machine-state.d.ts +74 -0
  468. package/dist/source-engine/machine-state.d.ts.map +1 -0
  469. package/dist/source-engine/machine-state.js +48 -0
  470. package/dist/source-engine/machine-state.js.map +1 -0
  471. package/dist/source-engine/observe.d.ts +18 -0
  472. package/dist/source-engine/observe.d.ts.map +1 -0
  473. package/dist/source-engine/observe.js +598 -0
  474. package/dist/source-engine/observe.js.map +1 -0
  475. package/dist/source-engine/robots.d.ts +53 -0
  476. package/dist/source-engine/robots.d.ts.map +1 -0
  477. package/dist/source-engine/robots.js +158 -0
  478. package/dist/source-engine/robots.js.map +1 -0
  479. package/dist/source-engine/types.d.ts +285 -0
  480. package/dist/source-engine/types.d.ts.map +1 -0
  481. package/dist/source-engine/types.js +2 -0
  482. package/dist/source-engine/types.js.map +1 -0
  483. package/dist/source-harness/conformance.d.ts +16 -0
  484. package/dist/source-harness/conformance.d.ts.map +1 -0
  485. package/dist/source-harness/conformance.js +265 -0
  486. package/dist/source-harness/conformance.js.map +1 -0
  487. package/dist/source-harness/fixture.d.ts +95 -0
  488. package/dist/source-harness/fixture.d.ts.map +1 -0
  489. package/dist/source-harness/fixture.js +261 -0
  490. package/dist/source-harness/fixture.js.map +1 -0
  491. package/dist/source-harness/index.d.ts +22 -0
  492. package/dist/source-harness/index.d.ts.map +1 -0
  493. package/dist/source-harness/index.js +19 -0
  494. package/dist/source-harness/index.js.map +1 -0
  495. package/dist/source-harness/peer.d.ts +74 -0
  496. package/dist/source-harness/peer.d.ts.map +1 -0
  497. package/dist/source-harness/peer.js +106 -0
  498. package/dist/source-harness/peer.js.map +1 -0
  499. package/dist/storage.d.ts +6 -2
  500. package/dist/storage.d.ts.map +1 -1
  501. package/dist/storage.js +4 -1
  502. package/dist/storage.js.map +1 -1
  503. package/dist/types.d.ts +33 -5
  504. package/dist/types.d.ts.map +1 -1
  505. package/dist/uncertain-write.d.ts +156 -19
  506. package/dist/uncertain-write.d.ts.map +1 -1
  507. package/dist/uncertain-write.js +90 -31
  508. package/dist/uncertain-write.js.map +1 -1
  509. package/dist/versioning.d.ts +9 -4
  510. package/dist/versioning.d.ts.map +1 -1
  511. package/dist/versioning.js +9 -4
  512. package/dist/versioning.js.map +1 -1
  513. package/dist/write-identity.d.ts +5 -0
  514. package/dist/write-identity.d.ts.map +1 -0
  515. package/dist/write-identity.js +9 -0
  516. package/dist/write-identity.js.map +1 -0
  517. package/package.json +94 -2
package/README.md ADDED
@@ -0,0 +1,1000 @@
1
+ # @superbee/core
2
+
3
+ The Superbee knowledge engine reads and writes Open Knowledge Format bundles, with versioned
4
+ writes, cross-links and storage backends. It is a library; installing it does not install the
5
+ `superbee` executable.
6
+
7
+ Core and `@superbee/server` are released together, independently of the `superbee` CLI. A CLI
8
+ release bundles the engine source selected at build time; it does not load `@superbee/core@latest`.
9
+
10
+ ## Document presentation values
11
+
12
+ The additive `@superbee/core/artifact-contract` subpath provides pure v1 document target,
13
+ observation, request, receipt and capability decoders. See the [contract reference](https://github.com/Holaxis-ai/superbee/blob/main/packages/core/ARTIFACT-CONTRACT.md)
14
+ for identity, provenance, lifecycle and host responsibilities.
15
+
16
+ ## Site capabilities
17
+
18
+ The additive `@superbee/core/site-capabilities` subpath is the contract for read-only tools a
19
+ site or a recipe pack provides (`SiteCapability`), and the one owner of their composition rules:
20
+ `composeSiteCapabilities` refuses duplicate names, `superbee_` names outside the generic tier and
21
+ names outside a pack's or site's namespace, orders generic, pack and site tools deterministically,
22
+ applies a surface's exclusions after those checks, enforces size limits and, with
23
+ `requireReadOnly`, refuses any capability not declared read-only. It returns frozen snapshots and
24
+ an identity digest for the surface's list. `toProviderTool`, `toPortalAdditionalTool`,
25
+ `toToolDescriptor` and `siteCapabilityFromPortalTool` are the explicit mappings to the existing
26
+ tool shapes. `checkSiteCapability` and `siteCapabilityConformanceCases` test a capability against
27
+ recorded fixtures (read-only declaration, dated results, closed error codes that never echo input,
28
+ bounded and deterministic output, same-origin URLs, citations of records the result contains, and
29
+ refusal of unknown arguments). `siteRecordSource` mints a `site-record` citation only for a path
30
+ the host admits, bound to the publication's digests.
31
+
32
+ Annotations such as `readOnlyHint` and `untrustedContentHint` are self-declared and enforce
33
+ nothing at runtime; the checks above test recorded fixtures, not the code. Treat them as a control
34
+ only for in-process code from the same reviewed deploy. The module has no Node built-in import, so
35
+ it runs unchanged in a Worker or a browser.
36
+
37
+ ## Mutation attribution and local history
38
+
39
+ `mutateDocument` and `prepareDocumentMutationCandidate` persist a supplied `actor` by default:
40
+ `actor` in OKF v0.1, `superbee_updated_by` in v0.2 (plus `actor` when the Kind requires it).
41
+ Use `persistActor: false` to keep candidate attribution unchanged, or `persistActor: true` to apply
42
+ the attribution policy even without a supplied actor. Supplying the actor option alone leaves an
43
+ otherwise unchanged candidate a no-op, preserving its original attribution and bytes. In v0.1,
44
+ deliberate edits to the candidate's legacy `actor` field still count as authored changes in `patch`
45
+ and `replace-document` modes; overwrite retains its automatic attribution comparison. The pure preview
46
+ prepares patches. This default changes library writes that previously
47
+ required `persistActor: true`; callers needing the old opt-in behavior can explicitly pass false.
48
+
49
+ `producer` controls `generated.by` independently of the version mutator. Local filesystem history
50
+ returns only the current revision, including for Git-tracked bundles. It reads mutation attribution
51
+ from `superbee_updated_by`, legacy `updated_by`, or `actor`; when absent it returns the stable string
52
+ `unattributed`. It does not infer an identity from the reader's environment or the content producer.
53
+ History-keeping backends retain their existing per-write attribution.
54
+
55
+ ## Filesystem platform migration
56
+
57
+ The Windows extraction changes the default Node.js filesystem backend. Its built-in host policy
58
+ supports macOS and Linux. On native Windows or another unsupported host, filesystem construction
59
+ requires an explicit `FilesystemHostPolicy`; there is no automatic Windows fallback.
60
+
61
+ | Entry point | Default on macOS/Linux | On other hosts |
62
+ | --- | --- | --- |
63
+ | `new FilesystemBackend(root)` | Uses the built-in policy | Pass `{ hostPolicy }` as the second argument |
64
+ | `initBundle(root, options)` | Uses the built-in policy | Pass `{ hostPolicy }` as the third argument |
65
+ | `createFilesystemRuntime()` | Captures the built-in policy | Pass a host policy as its argument |
66
+ | `withFilesystemMutationLock(target, body, options)` | Uses the built-in policy | Include `hostPolicy` in `options` |
67
+ | `filesystemMutationLockPath(target, portableRoot)` | Uses the built-in policy | Pass a host policy as the third argument |
68
+
69
+ Without that policy, these filesystem entry points throw `InvalidInputError` on unsupported hosts;
70
+ backend construction and bundle initialization refuse before creating or modifying the target.
71
+ The package has no npm `os` restriction: storage contracts and non-filesystem backends remain
72
+ available independently of the default filesystem policy. Choose a backend and imports appropriate
73
+ to your runtime; installing the library alone does not establish native filesystem support.
74
+
75
+ There is no Windows adapter on npm to install. The experimental first-party adapters are built
76
+ from source in a separate repository, https://github.com/Holaxis-ai/superbee-windows-cli.
77
+ Supplying a policy is an integration contract for an adapter owner, not a promise that arbitrary
78
+ policies make Windows supported.
79
+
80
+ ## Keep the backend returned by initBundle
81
+
82
+ `initBundle` now returns `{ root, backend }`. Pass that returned bundle to engine operations so they
83
+ retain the backend and host policy selected during initialization:
84
+
85
+ ```ts
86
+ import { initBundle, readDoc, writeDoc } from '@superbee/core';
87
+
88
+ const bundle = await initBundle('./knowledge'); // Default filesystem: macOS/Linux.
89
+ await writeDoc(bundle, {
90
+ id: 'notes/example',
91
+ frontmatter: { type: 'Note', title: 'Example' },
92
+ body: 'Shared knowledge.\n',
93
+ });
94
+ const document = await readDoc(bundle, 'notes/example');
95
+ ```
96
+
97
+ Reading `bundle.root` still works. Code that assumes the return value has exactly one property,
98
+ serializes the whole return value, or uses backend presence as a synonym for a remote bundle must
99
+ be updated. A filesystem bundle now has a backend too. Serialize only the fields your application
100
+ intends to persist; do not reconstruct `{ root: bundle.root }` when continuing engine operations
101
+ that need the selected backend.
102
+
103
+ ## Integrating an explicit filesystem policy
104
+
105
+ `@superbee/core/filesystem` exports `createFilesystemRuntime`, `FilesystemBackend`, and the
106
+ `FilesystemHostPolicy` type. An application that owns its host adapter can bind it once:
107
+
108
+ ```ts
109
+ import { createFilesystemRuntime, type FilesystemHostPolicy } from '@superbee/core/filesystem';
110
+
111
+ export async function openFilesystemBundle(root: string, hostPolicy: FilesystemHostPolicy) {
112
+ const filesystem = createFilesystemRuntime(hostPolicy);
113
+ return filesystem.initBundle(root);
114
+ }
115
+ ```
116
+
117
+ The resulting runtime supplies `backend`, `initBundle`, `withMutationLock`, and `mutationLockPath`
118
+ with the same captured policy. Direct backend construction uses
119
+ `new FilesystemBackend(root, { hostPolicy })`. Lock helpers are also exported from `@superbee/core`.
120
+
121
+ A policy supplies the lock-parent directory, owner-namespace key, private-mode enforcement choice,
122
+ and host error classifications. Core still owns lock ownership, version checks, retry bounds and
123
+ cleanup. Policies are structural snapshots: public data and methods are captured, while callbacks
124
+ may continue to observe live operating-system facts. Do not use opaque private fields or methods
125
+ pre-bound to a mutable receiver.
126
+
127
+ ## Recipe lifecycle
128
+
129
+ `@superbee/core/recipe-lifecycle` installs and evolves a parsed recipe on any bundle. The caller
130
+ acquires the recipe (`parseRecipeFiles` from `@superbee/core/recipes` turns files into a
131
+ `LoadedRecipe`) and presents the results; the `superbee` CLI is one such caller. It uses no Node
132
+ globals or builtins, so it runs in Node, a browser or a Worker over any storage backend.
133
+
134
+ ```ts
135
+ import { MemoryBackend } from "@superbee/core/memory-backend";
136
+ import { parseRecipeFiles } from "@superbee/core/recipes";
137
+ import { applyRecipeEvolution, installRecipe, planRecipeEvolution } from "@superbee/core/recipe-lifecycle";
138
+
139
+ const bundle = { root: "memory://glossary", backend: new MemoryBackend() };
140
+ const v1 = parseRecipeFiles(glossaryV1Files, "memory:glossary@1");
141
+ if (!v1.ok) throw new Error(v1.error.message);
142
+ await installRecipe(bundle, v1.recipe);
143
+
144
+ const v2 = parseRecipeFiles(glossaryV2Files, "memory:glossary@2");
145
+ if (!v2.ok) throw new Error(v2.error.message);
146
+ const plan = await planRecipeEvolution(bundle, v2.recipe);
147
+ if (plan.ready && plan.writes > 0) {
148
+ await applyRecipeEvolution(bundle, v2.recipe, plan.plan_token, { actor: "process:glossary-sync" });
149
+ }
150
+ ```
151
+
152
+ `installRecipe` is create-only: each convention, Connector, View pair and Reference is written with
153
+ an expect-absent compare-and-swap, an existing convention or Connector is left untouched (its row
154
+ reports `source_differs` when it differs from the recipe), and a differing View or Reference target
155
+ is refused before anything is written. Before any write it checks each Connector against the
156
+ authoring rules of the bundle's edition (`RecipeInputError` when OKF v0.2 would refuse it) and its
157
+ sources against the Kinds the bundle will hold (its conventions plus the recipe's own), throwing
158
+ `ConnectorTargetUnsatisfied` when one does not fit. `planRecipeEvolution` is read-only: it merges each recipe
159
+ convention into the installed one additively, rechecks existing instances, and returns blockers
160
+ and a `plan_token` bound to everything it read. `applyRecipeEvolution` recomputes the plan,
161
+ refuses a different token, and writes each changed convention at exactly the version the plan
162
+ saw. The bundle has no cross-document transaction, so a failure part way reports the definitions
163
+ written and those still pending. A Connector has no additive merge: the plan replaces it whole,
164
+ after the conventions, and only when this recipe's stamp holds and its definition digest shows
165
+ nobody changed it; an edited, unstamped-and-different or foreign Connector is a blocker and is
166
+ never written, stamp included.
167
+
168
+ Every convention and Connector either path writes carries an `x_recipe` stamp in the same write:
169
+ the recipe's id, version, declared convention count (`of`), `recipeSourceDigest` of the recipe,
170
+ the `definitionDigest` of the document as written, and the `digest_rules` version. A recipe that
171
+ carries connectors adds `connectors`, its declared Connector count, which readers that predate it
172
+ ignore; a recipe without connectors writes and digests exactly as before. A plan's `changed` is
173
+ about content only; its `writes` also counts definitions whose stamp is stale or missing, so a
174
+ ready plan with `writes > 0` has work to apply. `installedRecipes(docs, known?)` reads each
175
+ recipe's state (`installed`, `partial`, `diverged`, `unknown`) from the stamps alone, without the
176
+ recipe source; give it the conventions and the Connectors (`installedConventionDocs` and
177
+ `installedConnectorDocs`).
178
+
179
+ Failures are typed subclasses of `RecipeLifecycleError`: `RecipeInputError` (the recipe cannot
180
+ target this bundle's edition), `ConnectorTargetUnsatisfied` (`recipe`, `connector`, `source`,
181
+ `kind`, `code`, `fields`), `RecipeAssetConflict` (`recipe`, `key`), `RecipeEditionChanged`,
182
+ `RecipeEvolutionPlanChanged` (`expected`, `current`), `RecipeEvolutionBlocked` (`blockers`),
183
+ `RecipeEvolutionInterrupted` (`reason`, `completed`, `pending`, `cause`) and
184
+ `RecipeEvolutionPostcondition` (`reason`, `completed`).
185
+
186
+ ## Record overlay
187
+
188
+ `@superbee/core/record-overlay` is the write half of binding documents to external records. A
189
+ Source Feed policy names the fields a feed owns; the caller fetches and diffs its records and hands
190
+ the changes to the planner. It uses no Node globals or builtins, so it runs in Node, a browser or a
191
+ Worker over any storage backend.
192
+
193
+ ```ts
194
+ import { MemoryBackend } from "@superbee/core/memory-backend";
195
+ import { buildKindRegistry } from "@superbee/core/kinds";
196
+ import { applyRecordOverlay, buildSourceIndex, planRecordOverlay, readOverlayPolicy } from "@superbee/core/record-overlay";
197
+
198
+ const bundle = { root: "memory://guide", backend: new MemoryBackend() };
199
+ const registry = buildKindRegistry(conventionDocs);
200
+ const guide = { bundle, registry, okfVersion: "0.2" as const };
201
+ const policy = readOverlayPolicy(sourceFeedDoc, { targetKind: registry.kinds.get("Event") });
202
+ const overlays = await buildSourceIndex(bundle, { type: policy.target_kind });
203
+ const plan = await planRecordOverlay({ content: guide, ops: guide, policy, changes, observedAt, overlays });
204
+ const receipt = await applyRecordOverlay(plan, { content: guide, ops: guide, policy, actor: "process:calendar-sync", overlays });
205
+ ```
206
+
207
+ For each owned field the plan compares the last accepted feed value, the new feed value and the
208
+ document's value: a document that already says the new value is left alone, one that still says
209
+ the old value is written, and anything else is an `OVERLAY_DOC_CONFLICT` with a Signal, so an
210
+ editor's override is never overwritten. Each step carries the version it was planned against.
211
+ `applyRecordOverlay` writes every step through `mutateDocument` with a guard core builds itself and
212
+ runs on the fresh read: an owned-field write reaches documents of the policy's target Kind only,
213
+ may change only owned and derived fields, never the body, and needs the record's `sources`
214
+ identity; a Signal may be created only as a new, unclaimed document of the attention Kind under its
215
+ path, and updated only in a fixed set of fields plus one appended observation line. Signal writes
216
+ are bound to the provider of the policy the caller passes, never to a field of the plan, and an id
217
+ held by another provider's Signal is refused. A Signal's text must be the planner's rendering of
218
+ its own fields and the run (its observation line, links to the plan's feed and to documents, the
219
+ adoption hint): every plan the planner makes passes, and other text is a held `INVALID_INPUT` row,
220
+ so a mismatch is visible and planned again rather than dropped. Nothing of the
221
+ target Kind is created and nothing is deleted. A feed never owns its target's work state: the
222
+ progress storage fields and the target Kind's claim fields are refused like `status`. The
223
+ attention Kind (default `Signal`) must declare a claim owner and state field and the fields
224
+ `change_kind`, `observed_at`, `provider`, `external_id` and `diff_summary`.
225
+
226
+ Site rules are hooks with safe defaults: `textGate` (identity), `interval` (none, so a disappeared
227
+ record is always raised), `correlate`, `sourceProperties` (an upsert of the record's `sources`
228
+ entry in the same write, OKF v0.2 only), `assertCandidate` (site candidate checks after the core
229
+ guards) and `deriveFields` (fields such as `updated_at` written in the same version). Refusals are
230
+ receipt rows with a `REFUSAL_CODES` code; `RETRYABLE` and `HOLD_STORE` say which leave a record to be
231
+ planned again. A value core's write policy refuses (an OKF v0.2 field type, for example) is a held
232
+ `INVALID_INPUT` row, so one bad record never stops the rest of a run. A malformed plan (an unknown
233
+ step, a non-canonical target, written values that are not JSON data with finite numbers, prototype
234
+ keys, non-scalar or identity `sources` properties), actor or bundle edition throws `OverlayRefusal`
235
+ before any write, and so does a plan with owned-field steps whose policy names no declared target
236
+ Kind or whose owned or derived list names a field a feed can never own.
237
+
238
+ `SourceIndex` holds one head read for a whole run. It is bounded to that run: an edited or deleted
239
+ overlay is still caught at write time, but a second document given an identity during the run is
240
+ only refused as ambiguous on the next run.
241
+
242
+ ## Connectors
243
+
244
+ `@superbee/core/connectors` parses Connector declarations: `type: Connector` documents under
245
+ `connectors/` that say what an integration is, independent of any one target Kind. A declaration
246
+ holds no authority and nothing in the subpath fetches, executes or receives. It uses no Node globals
247
+ or builtins.
248
+
249
+ ```ts
250
+ import { bindSourceFeed, changeBetween, connectorPinDigest, findingsFor, mapRecord, parseConnectorDoc } from "@superbee/core/connectors";
251
+
252
+ const parsed = parseConnectorDoc(connectorDoc, { kinds: registry });
253
+ if (!parsed.ok) throw new Error(parsed.errors.map((e) => `${e.code} ${e.path}`).join("; "));
254
+ const pin = connectorPinDigest(parsed.connector);
255
+ const bound = bindSourceFeed(parsed.connector, sourceFeedDoc, registry, { okfVersion: "0.2" });
256
+ const mapped = mapRecord(bound.source, normalizedRecord, bound.kind);
257
+ if (mapped.kind === "mapped") {
258
+ const change = changeBetween(lastAcceptedAfter, mapped, { external_id: mapped.external_id, change_kind: "changed" });
259
+ }
260
+ const findings = findingsFor(mapped.diagnostics, { source: bound.source.name });
261
+ ```
262
+
263
+ `auth.kind` is `none`, `token`, `github-app`, `oauth2` or `signed-webhook`. A credential's hosts
264
+ come only from core: `token` and `oauth2` name a row of `AUTH_PROVIDERS`, `github-app` is fixed,
265
+ and `credentialHosts` is the one reader. `domains` lists hosts for keyless fetches and may not
266
+ repeat a credential host. The sources live under `record_sources` (OKF v0.2 reserves `sources` for
267
+ provenance, and `CONNECTOR_DOCUMENT_KEYS` lists every top-level key a Connector adds, none of them
268
+ an OKF field); the parsed `Connector.sources` holds them. Each source names a catalog `profile`, a
269
+ `target_kind`, mode `overlay`, an identity template (`identity.resource`, an https URL whose
270
+ placeholders are feed parameters, and `identity.id` over record paths), `completeness`, and
271
+ `fields`: target field to a dotted record
272
+ path, or to `{ from, values, unmapped }`. Mapping keys are the most a Source Feed may own; never-owned
273
+ fields, managed metadata, work state, the body and list-typed fields are refused. Actions and events
274
+ are declared shapes only. Every nested block is closed by `CONNECTOR_KEYS`; `parseConnectorDoc`
275
+ returns every refusal with a typed `code` and a JSON pointer `path`. A token shape or URL userinfo
276
+ anywhere in a frontmatter string, key or the body, or a credential key name outside the data-name
277
+ maps, refuses the document alone, naming a path and never the value; so does a YAML alias, which
278
+ keeps every reader bounded by the document's size. The secret rule is a tripwire; the pin, the
279
+ credential store and the fetcher are the boundaries.
280
+
281
+ `connectorPinDigest` covers every key `CONNECTOR_KEYS` accepts, with defaults filled and order-free
282
+ lists sorted, plus the credential hosts core resolves the auth kind to, so a host joining an
283
+ `AUTH_PROVIDERS` row changes the pin. It is blind to the title, description, version, body and
284
+ unknown top-level keys. `CONNECTOR_PIN_RULES` changes whenever a default or sort rule does.
285
+
286
+ `bindSourceFeed` binds a Source Feed (`connector`, `source`, `url`, `owned_fields`) to one source:
287
+ the `url` must equal the identity template rendered from parameters of `[A-Za-z0-9._~-]+` byte for
288
+ byte, `owned_fields` must be mapping keys, and the source's `target_kind`, `completeness` and
289
+ `profile` win. `okfVersion` is the content bundle's edition (absent means v0.1); a feed owning a
290
+ Kind-declared list field binds only on v0.2. Each refusal is an `OverlayRefusal` with
291
+ `POLICY_INVALID`, the feed and the key.
292
+ `mapRecord` never lets a mapping gap erase a stored value: an absent path is unobserved, an
293
+ unmapped native is kept (`unmapped: keep`, the default) or refuses the record while it still counts
294
+ as present (`refuse`), and only an empty value (`null` or blank text) clears an optional field; on a
295
+ required field or `title` it is kept with a finding. An id template separates its placeholders with
296
+ literal text, and a record whose value holds that text, is blank or makes an id past 2048
297
+ characters is refused as `IDENTITY_MISSING` or `IDENTITY_INVALID`, so two records never share one id. `changeBetween` takes the last accepted `after`, carries
298
+ unobserved fields and leaves them out of `changed_fields`. `findingsFor` groups diagnostics per
299
+ source, field, code and value (a list, mapping or empty value by its shape) for `planFindings`, so
300
+ one problem across many records raises one Signal.
301
+
302
+ A recipe carries Connectors through its manifest's `connectors:` list (`parseRecipeFiles` reads
303
+ each with `parseConnectorDoc`, checking the sources that target the recipe's own Kinds, and refuses
304
+ a malformed one as `RECIPE_CONNECTOR_INVALID`); `@superbee/core/recipe-lifecycle` installs, stamps
305
+ and evolves them as definitions.
306
+
307
+ `examples/recipes/source-feeds` is an example recipe with the `Source Feed` and `Signal` Kinds a
308
+ connector-backed feed uses.
309
+
310
+ ## Source engine
311
+
312
+ `@superbee/core/source-engine` is the portable half of the integration source engine: the one
313
+ loop that observes a bound Source Feed for an author's test, a local run or a managed runner. It
314
+ uses no Node globals or builtins and never calls `fetch`; the caller supplies the network as a
315
+ `Transport`.
316
+
317
+ ```ts
318
+ import { MemoryMachineState, fetchIdentity, observeSource } from "@superbee/core/source-engine";
319
+
320
+ const receipt = await observeSource({
321
+ connector, bound, profile, spec, transport, state: new MemoryMachineState(),
322
+ identity: fetchIdentity("Product/1.0 (+https://example.org/about)")!,
323
+ mode: "incremental", signal: AbortSignal.timeout(60_000),
324
+ commit: async (page) => {
325
+ const plan = await planRecordOverlay({ content, ops, policy: bound.policy, changes: page.changes, observedAt: page.observed_at, records: page.records });
326
+ await applyRecordOverlay(plan, { content, ops, policy: bound.policy, actor: "process:calendar-sync" });
327
+ },
328
+ });
329
+ ```
330
+
331
+ A `SourceProfile` (`id`, `version`, `kind`, `validateSpec`, `hostsFor`, `observe`) turns a bound
332
+ feed, a cursor and a transport into pages of normalized records: `{ records, nextCursor, more,
333
+ complete }`, all JSON data. It never opens a socket, never sees a credential and never writes. A
334
+ `Transport` answers `{ url, method, headers, body, maxBytes, credentialHost, signal }`; it never
335
+ follows a redirect and returns `{ ok: false, refused: "too_large" }` rather than a truncated body.
336
+ A credential-injecting transport adds its credential only when the engine's `credentialHost` is
337
+ true and its own exact match agrees: https, the default port, a host exactly one of the auth
338
+ kind's credential hosts.
339
+
340
+ The profile sees the engine's guarded transport, which checks every URL and every redirect hop:
341
+ https only, the lexical address rule (`refusedAddress`: no IP literal, local name, port or
342
+ userinfo), and a host within the profile's `hostsFor` and the connector's credential hosts plus
343
+ keyless `domains`. A `hostsFor` naming any other host refuses the run before a request. A redirect
344
+ never changes host when either side is a credential host. Keyless hosts are read through robots.txt
345
+ (RFC 9309, the declared identity's product token, the first 512 KiB) first, and a bot-mitigation
346
+ page from one is refused and never retried. Only `GET` is sent unless `rules.post` admits a POST
347
+ toward a credential host; a keyless host is GET only. A GET with a body, a POST redirect, and the
348
+ headers `authorization`, `cookie`, `proxy-authorization`, `host`, `user-agent` and the method
349
+ overrides are always refused; beyond `accept` a header needs `rules.header`, and the validators
350
+ `if-none-match` and `if-modified-since` go out in incremental mode only. The budget counts every
351
+ request, robots.txt and hops included. Each request ends after `requestTimeoutMs` (default 20
352
+ seconds). The deadline is the caller's `signal`; without one the run ends after `deadlineMs`
353
+ (default 5 minutes). The deadline bounds the profile's page too: a page still pending is discarded
354
+ and the run ends `deferred`. A commit or state write in progress is never abandoned, so a hung one
355
+ holds the run. Statuses map to outcomes: 401 and 403 `credential_refused` (no retry), 404 and
356
+ 410 `not_found`, 429 `deferred` with `retry_after`, others `provider_unavailable` (503 included,
357
+ with a retry); `statuses` moves a status between the sets. Any refusal or error status stops the
358
+ run, even when the profile catches the error, and the guarded transport is closed when the run
359
+ ends.
360
+
361
+ A profile runs in the engine's realm. The guard bounds what a declaration and a provider's data can
362
+ reach; profile code itself is bounded by the repository's static egress check
363
+ (`scripts/source-egress-check.mjs`) and by review, until profiles run in an isolate. The check
364
+ refuses globals outside a short list, imports outside an allowlist or the scanned roots, writes to
365
+ anything reached from a global, the prototype, property-definition and descriptor names however
366
+ they are read, `this` outside a class body and CommonJS; a key computed at run time is beyond any
367
+ static check.
368
+
369
+ Each page is mapped with `mapRecord`, diffed against the stored records into `RecordChange` rows
370
+ and handed to `commit`; the cursor and the records are stored only after `commit` resolves. A page
371
+ that promises more with no cursor, or with a cursor the run already asked for, fails the run as
372
+ `cursor_stuck`. `commit` may decline to write changes: a `retry` change (a lost compare-and-swap, a
373
+ claimed Signal) stays pending in the machine state and is planned again at the start of every run,
374
+ before anything is fetched, until it is written, refused for good, or superseded by a newer
375
+ observation of the record (its listing, or its absence from a complete pass); a replay keeps the
376
+ record's missing count, and a record that disappeared is not replayed. A replay whose commit throws
377
+ stays pending with a finding while the run reads on, and is dropped with a finding after
378
+ `MAX_REPLAY_FAILURES` (5). A page that supersedes pending changes stores their removal before its
379
+ commit, so a crash after the commit never replays an older change. A `hold` change is not pending,
380
+ and its record is planned again when it is next observed. Either way the cursor moves on, so one
381
+ record never stops a feed. An incremental
382
+ run continues the stored cursor (`resumed` when the last run left its pass open); a complete run
383
+ lists from the start, sends no validator, and stops on a not-modified answer. A pass is a complete
384
+ observation only when it ran in complete mode to its last page within the run, the profile claimed
385
+ it complete and the source is declared `complete`; only then is a listed record's absence counted,
386
+ and a record is `disappeared` after the feed's `missing_threshold` (default 2) of them. A cursor
387
+ from another profile id or version is never handed back. The receipt names the outcome, every
388
+ request, the pass, the stored cursor, the held and pending counts, the mapping diagnostics and the
389
+ findings for `planFindings`. `MachineState` is the port for the stored state, written with a
390
+ compare-and-swap; `MemoryMachineState` keeps it in memory.
391
+
392
+ ## Source harness
393
+
394
+ `@superbee/core/source-harness` is how a profile and a transport prove the engine's rules offline.
395
+ It returns rows and problem lists rather than asserting, so any test runner can use it, and it uses
396
+ no Node globals or builtins.
397
+
398
+ ```ts
399
+ import { peerTransport, runSourceFixture, runTransportConformance } from "@superbee/core/source-harness";
400
+
401
+ const { problems, missing } = await runSourceFixture(fixture, scenarios);
402
+ const rows = await runTransportConformance({ name: "mine", create: (peer, credential) => myTransport(peer, credential) });
403
+ ```
404
+
405
+ `runSourceScenario` replays a scenario's scripted provider answers (`exchanges`, each used once,
406
+ matched on method and URL; a `hang` answer aborts the run's deadline) through `observeSource`,
407
+ writing each committed page through record-overlay over a MemoryBackend bundle built from the
408
+ fixture's documents, with editor edits between runs. `checkScenario` holds the result to the rule
409
+ of its required scenario (`REQUIRED_SCENARIOS`: provider errors, an incomplete page, cursor resume,
410
+ a hostile cursor refused by the guard, disappearance only from complete observations, owned-field conflicts, unmapped
411
+ and empty values, a missing identity, a cross-host redirect and an undeclared host), and fails any
412
+ request to an undeclared host or one the fixture did not script; a name may add a variant
413
+ (`cursor.resume.page-number`). `missingScenarios` lists what a fixture lacks.
414
+
415
+ `runTransportConformance` is the suite every transport passes against a `ScriptedPeer`, the
416
+ network that records exactly what arrived: the credential reaches its host under its own header
417
+ name (a custom `accessToken` included) and no other host, lookalikes included (a longer or
418
+ shorter name, a trailing dot, userinfo, another port), nor its own host when the engine's
419
+ `credentialHost` is false; a redirect is returned unfollowed, a body
420
+ over the bound is refused while one at the bound is returned whole, and behind the engine a
421
+ redirect to another host or a private address reaches nothing. `peerTransport` is a memory
422
+ transport that passes it.
423
+
424
+ ## Bundle health
425
+
426
+ `@superbee/core/bundle-health` computes the health report behind `superbee status` from a core
427
+ `Bundle` on any backend: malformed documents, Kind lint and conformance debt, registry warnings,
428
+ unresolved links, orphans, freshness and invalid timestamps, trust tiers, the two graph lints, the
429
+ View surface, cross-bundle links, the OKF v0.1 upgrade finding and legacy naming. It uses no Node
430
+ globals or builtins, so it runs in Node, a browser or a Worker. The caller presents the report;
431
+ the CLI adds row caps, help text and its home and sync blocks.
432
+
433
+ ```ts
434
+ import { MemoryBackend } from "@superbee/core/memory-backend";
435
+ import { bundleHealth } from "@superbee/core/bundle-health";
436
+
437
+ const bundle = { root: "memory://guide", backend: new MemoryBackend() };
438
+ const report = await bundleHealth(bundle, { now: new Date() });
439
+ const gate = { malformed: report.malformed.length, kind_warnings: report.kindLint.length, unresolved_links: report.unresolvedLinks.length };
440
+ ```
441
+
442
+ The report reads only: one Kind registry load, one lenient query, two prefix-scoped blob listings
443
+ and one root-index read, with every finding derived in memory. A document whose frontmatter does
444
+ not parse is a `malformed` row (the root index first), never a thrown error. Top-level keys are
445
+ camelCase; rows keep snake_case keys (`age_ms`, `bundle_id`, `incompatible_values`) and arrive
446
+ whole, in report order, for the caller to cap. Sections whose presence depends on the bundle are
447
+ null when absent: `conformanceDebt` without any declared Kind, `trust` outside a non-empty v0.2
448
+ bundle, `crossBundle` without `docs/dependencies` or any `okf:` link, `okfUpgrade` outside a v0.1
449
+ bundle with a workflow-status collision. `crossLinkTypeViolations` is always an array, because a
450
+ local edge under a qualified declaration needs no dependencies. The stored values in
451
+ `invalidStaleAfter`, `invalidTimestamps` and `invalidLegacyTimestamps` are made JSON-safe;
452
+ `missingExpectedLinks[].status` is the raw stored value.
453
+
454
+ Nothing outside the bundle is read unless the caller binds it. `bindings` is a map of bound copies
455
+ by alias, or a hook core calls exactly once, after every read and with its own parse of
456
+ `docs/dependencies` (a self alias already dropped as `DEP_SELF`, using `selfBundleId` when
457
+ `docs/bundle` declares no id). Whatever the hook throws propagates unchanged. Each binding's
458
+ verdict comes from core's resolver, and `dependencies[].bound` is the binding's `label`.
459
+
460
+ `bundleKindFindings(documents, okfVersion, { actor, producer })` answers a different question:
461
+ which Kind problems a strict host write would refuse, preparing each document as its first edit,
462
+ so a timestamp or actor the write supplies is no finding. The CLI's publish preview runs it; the
463
+ report's `kindLint` stays advisory. Both use core's one validator, `validateAgainstKind`.
464
+
465
+ ## Search
466
+
467
+ `@superbee/core/search` is the one `documents.search.v1` engine: the request and result contract,
468
+ query normalization, ranking (`portal-webmcp-search/v1`), the any-term fallback, facets, cursors
469
+ and page budgets. A backend supplies what it searches through `SearchSource`: one `evidence()`
470
+ call per request and `text()` for the page's excerpts. `memorySearchSource` indexes a complete
471
+ corpus in memory. `searchSourceFromBackend(backend, options?)` reads a complete local
472
+ backend in bounded batches, applies the same memory-source bounds, and derives the corpus key
473
+ from the versions actually read. Validate the request before calling it and pass an opaque
474
+ bundle identity as the search scope. Failed reads refuse the whole corpus. The subpath uses
475
+ no Node globals or builtins.
476
+
477
+ ```ts
478
+ import { memorySearchSource, searchDocuments } from "@superbee/core/search";
479
+
480
+ const source = memorySearchSource(documents, { key: headsDigest });
481
+ const result = await searchDocuments(source, { query: "lock keeper", limit: 10 }, { scope: bundleId });
482
+ ```
483
+
484
+ `key` identifies the corpus; a cursor is refused for another key, scope, query, match or filters
485
+ (`type`, `tag`, `prefix`). A request outside the contract throws `SearchInputError`; a corpus over a
486
+ bound throws `SearchLimitError`; a request whose terms hold more than `maxEvidenceInstances` capped
487
+ instances (a `searchDocuments` option, at most `SEARCH_LIMITS.maxEvidenceInstances`) throws
488
+ `SearchBroadQueryError`, a `SearchLimitError` whose message asks for fewer or more specific terms.
489
+ A source must hold one document per id.
490
+ A document over 65,536 body terms is left out and counted: the result's `corpus` reports
491
+ `documents`, `indexed`, `skipped` and `reindexPending`. `searchRowText` is what one document
492
+ contributes to any index, and `textVersion()` names the algorithm, `SEARCH_TEXT_REVISION` and the
493
+ runtime's normalizer fingerprint, so a stored index can tell when its rows need re-deriving.
494
+
495
+ ## Hosted client
496
+
497
+ `@superbee/core/hosted-client` is the headless client of a hosted Superbee's sync routes
498
+ (`/sync/v1`). It is a supported public API under core's normal versioning; the `superbee` CLI uses
499
+ it. It runs wherever `fetch` does (Node, a browser or a Worker) and reads identity (`whoami`),
500
+ the bundle list (`bundles`, with `readBundleListing`), documents (`reader`), document history
501
+ (`history`) and operations run by id (`listOperations`, `runOperation`). `within(slug)` returns
502
+ the same client naming every bundle-scoped request's bundle as `<slug>/<bundle-id>`.
503
+
504
+ ```ts
505
+ import { bearerCredentials, createHostedClient } from "@superbee/core/hosted-client";
506
+
507
+ const client = createHostedClient({
508
+ target: { origin: "https://mcp.getsuperbee.com", syncPrefix: "/sync/v1" },
509
+ credentials: bearerCredentials(() => getToken()),
510
+ });
511
+ const identity = await client.whoami();
512
+ ```
513
+
514
+ The target is the origin and the sync prefix: `/sync/v1`, or `/agents/<id>/sync/v1` for an agent
515
+ connection. `syncRoutePrefix({ origin, audience })` derives the prefix from a protected-resource
516
+ audience and returns null when the audience has none. `credentials` is the transport carrier's
517
+ own credential provider: an async function returning the headers for one request. It is called
518
+ before every request, so it may renew a short-lived token. `bearerCredentials` builds one from a
519
+ token getter. The client never logs credentials, and its errors carry the route and code, never a
520
+ token.
521
+
522
+ Failures are typed and carry no wording for any particular surface:
523
+
524
+ - `HostedCarrierError`: transport. `denied` means no credential (nothing was sent) or the request
525
+ was refused before sending; `unavailable` means the answer is unknown. `isAnswerTooLarge`
526
+ distinguishes an answer larger than its bound.
527
+ - `MalformedAnswer` (`code` `MALFORMED_ANSWER`, `route`): the host answered something the
528
+ contract does not admit. Retrying gets the same answer.
529
+ - `RemoteError` (`code`, `status`): the host refused the request, with its own code.
530
+ - `HostedRouteNotServed` (`code` `ROUTE_NOT_SERVED`, status 404, `route`): a `RemoteError` for a
531
+ host from before the route.
532
+
533
+ Operation and history refusals the host states in a `200` (and `bundle_not_found`,
534
+ `unknown_operation` and `invalid_input`) come back as `{ ok: false, refusal }`, not as errors.
535
+
536
+ ### Writing a document
537
+
538
+ `write` sends one whole serialized document (frontmatter and body, as a file holds it) to a hosted
539
+ bundle: a create of an absent id, or a replace at exactly the version the caller read.
540
+
541
+ ```ts
542
+ const created = await client.write("notes", { id: "notes/today", document: text, expectAbsent: true });
543
+ const replaced = await client.write("notes", { id: "notes/today", document: edited, expectedVersion: version });
544
+ ```
545
+
546
+ It returns one of:
547
+
548
+ - `{ status: "committed", version }`: the host stored it; `version` is the new head.
549
+ - `{ status: "conflict", reason: "version_conflict", currentVersion }`: the document is not at the
550
+ expected version (for a create: it exists). `currentVersion` is the version the host serves, or
551
+ null when it serves none. Nothing was written.
552
+ - `{ status: "conflict", reason: "concurrent_change" }`: another write to the bundle overlapped
553
+ this one past the host's own retries. Nothing was written.
554
+ - `{ status: "refused", code, message }`: the host refused this write (`validation_failed`,
555
+ `invalid_input`, `AUTH_REQUIRED`, `PERMISSION_DENIED`, a sync quota's `REQUEST_CAPACITY_*`, or
556
+ `read_only` when the bundle serves no writes). Nothing was written.
557
+ - `{ status: "unknown", requestId }`: the host may or may not hold the write.
558
+
559
+ Every result names its `requestId`. A conflict is final for that identity: the client never
560
+ resends it, under that identity or a new one. Read the document and decide; another attempt is a
561
+ new `write`.
562
+
563
+ A write needs the bundle's capabilities (whether it serves writes, its OKF edition, the write
564
+ bound and the retention window). The client reads them once per bundle and workspace and holds the
565
+ answer for `capabilitiesTtlMs` (default 60,000 ms), shared by the client and its `within` clients
566
+ and never by `reader()`. So a write costs one round trip while its bundle's capabilities are fresh,
567
+ and two when they are read first; concurrent writes to one bundle share one read. A write refused
568
+ `read_only`, `invalid_input`, `validation_failed` or `PERMISSION_DENIED` (which is how a
569
+ `bundle_not_found` write answer arrives), or whose result is `unknown`, drops the held answer, and
570
+ a capabilities read that fails is never held, so the next write reads them again. Set
571
+ `capabilitiesTtlMs: 0` to read them before every write. The retention window that decides a
572
+ resend after an `absent` lookup (below) is never taken from a held answer: it is read again then.
573
+
574
+ Each write has one request identity and carries the client's binding, a digest of a random id the
575
+ client mints once (its `within` clients share it). When an answer is lost (a transport failure or
576
+ a deadline after sending), the client looks the identity up on the host and reports what the host
577
+ recorded. The host's outcome route tells a request it never received ("absent") from one still in
578
+ progress ("pending"), and the shared transport keeps the two apart. On either answer `write`
579
+ sends the same identity once more, which the host applies at most once; after "pending", an
580
+ earlier delivery is known to be held. Absence counts as
581
+ evidence only within the retention window the host serves in its capabilities
582
+ (`operationsRetentionMs`, less an hour's margin); past it, the result stays `unknown` and nothing
583
+ is resent. The client measures an intent's age by its own clock against the host's window, so that
584
+ margin holds only while the client's clock is within an hour of the host's. A resend refused before
585
+ it reaches the host's execution (no credential, access withdrawn, a spent quota) leaves the result
586
+ `unknown` too, since the first delivery may still commit; after a "pending" lookup, so does any
587
+ refusal before execution, a gateway's `400` included. The result's `refusal` (`{ code, message }`)
588
+ names that refusal, so an app can say why the write is waiting (for example, access withdrawn)
589
+ while it stays held; only the call that met the refusal reports it. Absence alone never settles
590
+ such a write: a delayed first delivery is authorized when it arrives, so it can still commit once
591
+ access returns. When the lookups themselves fail, or the caller's `signal` aborts after sending, the
592
+ result is `unknown`.
593
+
594
+ An `unknown` write can be resolved later in two ways:
595
+
596
+ - `lookupWrite(requestId)` makes one outcome lookup and never sends the write. When the host holds
597
+ no outcome yet (absent or pending), the answer stays `unknown`.
598
+ - `settleWrite(requestId)` looks the identity up and, when the host never recorded it within its
599
+ retention window or holds it pending, sends the same identity once more, as `write` would have.
600
+
601
+ Both answer `{ status: "unrecorded", requestId }` when the intent store holds no write under that
602
+ identity: it was never recorded there, or it already settled and left the store. Both return
603
+ `unknown` when the bundle's capabilities cannot be read, because the write may already have left.
604
+ `settleWrite` only looks up, sending nothing, when the host serves the bundle no writes. A
605
+ `signal` already aborted when `write`, `lookupWrite` or `settleWrite` is called is thrown as its
606
+ reason before any request; an abort during delivery leaves the write `unknown` and held.
607
+ `write` instead throws that failure, because it has sent nothing yet.
608
+
609
+ Until it settles, a write is held in an intent store: its operation, bundle (and workspace),
610
+ document id, document and digest, expected version, request identity and binding, which is
611
+ everything needed to rebuild the lookup exactly. The default store is in memory and lives as long
612
+ as the client. Pass `intentStore` (`put`, `get`, `delete`, and optionally `list`) to keep intents
613
+ durable across restarts (`openHostedClientJournal`, below, is one for Node); lookups use the
614
+ binding recorded with each intent. A service that
615
+ restarts with a durable store calls `heldWrites()` to find the writes that never settled
616
+ (including any recorded but never sent), then settles each with `settleWrite`. `heldWrites()`
617
+ throws `HostedIntentsNotListable` when the store has no `list`. A settled write leaves the store.
618
+
619
+ `write(..., { requestId })` with the identity of a held write resumes that write and applies
620
+ nothing new. A different write under a held identity, including the same write named in another
621
+ workspace, is refused before sending. The host refuses an identity it recorded for another
622
+ document (`invalid_input`). Calls on one client that name the same identity run one after another.
623
+
624
+ `write` throws only before anything is sent: a malformed input (`TypeError`), the typed error of
625
+ its capabilities read, or a failure of the intent store to record the write. Document deletion is
626
+ not part of `write`.
627
+
628
+ ### Writing a blob
629
+
630
+ `writeBlob` and `deleteBlob` write one ordinary file (a blob: an image, a diagram, any bytes) to a
631
+ hosted bundle. A create names an absent key; a replace and a delete name the head they expect,
632
+ its version **and** content type (a `BlobPin`), so a type-only change is a replace too.
633
+
634
+ ```ts
635
+ const svg = new TextEncoder().encode("<svg xmlns='http://www.w3.org/2000/svg'/>");
636
+ const created = await client.writeBlob("notes", { key: "diagrams/flow.svg", bytes: svg, contentType: "image/svg+xml", expectAbsent: true });
637
+ if (created.status !== "committed") throw new Error(created.status);
638
+ let pin = created.head!; // { key, version, contentType }
639
+
640
+ const replaced = await client.writeBlob("notes", { key: pin.key, bytes: edited, contentType: "image/svg+xml", expected: pin });
641
+ if (replaced.status === "committed") pin = replaced.head!;
642
+
643
+ const read = await client.readBlob("notes", pin.key, { pin }); // bytes verified, or null if no longer at the pin
644
+ const deleted = await client.deleteBlob("notes", { key: pin.key, expected: pin });
645
+ ```
646
+
647
+ Each call returns one of:
648
+
649
+ - `{ status: "committed", head, changed? }`: applied. `head` is the new head (null after a
650
+ delete); `changed` is false for a replace the head already matched.
651
+ - `{ status: "conflict", reason: "version_conflict", actual }`: the head is not at the expected pin
652
+ (for a create: the key exists). `actual` is the head now, or null when there is none (a delete of
653
+ an absent key). Final for that identity.
654
+ - `{ status: "conflict", reason: "concurrent_change", message }`: another write overlapped this one
655
+ past the host's own retries. Nothing was applied.
656
+ - `{ status: "refused", code, message }`: the host's own refusal, or one the client names. The
657
+ host's codes are those it records under the identity (`backend_unavailable`, ...) and those it
658
+ answers before dispatch and never records (`insufficient_scope` for a grant without blob writes,
659
+ `bundle_not_found`, and `request_capacity` in a `200` or a `429`). A refusal of the second kind
660
+ proves only that this attempt did not run, so it is final only when every earlier attempt of the
661
+ write is proven not applied, which is always so for a write the client sends; a write whose
662
+ earlier attempt was unclear is never sent again, only looked up, and stays `unknown`.
663
+ `insufficient_scope`, `bundle_not_found` and `invalid_input` also drop the held capabilities
664
+ answer, so the next write reads them again. The client's own codes are `invalid_input` (an
665
+ unsafe key, a key under one of Superbee's own record folders or a reserved file, an invalid content type, a body over the host's `blobWriteBytes` or 16 MiB, or a held
666
+ identity reused for another write), `scope_mismatch` (a held write, or a reused `requestId`, recorded
667
+ for another origin or sync prefix) or `unsettled_write` (an earlier write of the same key in the bundle is still
668
+ unsettled: settle or forget it first). Nothing was sent for a client-named refusal.
669
+ - `{ status: "held", deletions, baseline }`: the host holds this mass delete (`428
670
+ deletions_held`). Nothing was applied. A person confirms the count, and
671
+ `settleBlobWrite(requestId, { acceptDeletes })` with at least `deletions` sends it again; without
672
+ `acceptDeletes` it stays held and nothing is sent, and with fewer it throws
673
+ `HostedBlobDeletionsNotAccepted`, sending nothing. `deleteBlob(..., { acceptDeletes })` sends the
674
+ confirmation with the first attempt.
675
+ - `{ status: "unknown" }`: the host may or may not hold the write.
676
+
677
+ Every result names its `requestId`, `key` and `operation`.
678
+
679
+ **The capability gate.** A blob write needs the bundle's `blobWrites: "allowed"` and its
680
+ `blobWriteBytes` bound, from the same held capabilities answer `write` uses. When the host serves
681
+ the bundle no blob writes (an older host's answer reads as refused), `writeBlob` and `deleteBlob`
682
+ throw `HostedBlobWritesNotServed`, a `HostedRouteNotServed` (`isRouteNotServed` is true), before
683
+ anything is journaled or sent. This differs from `write()`, which answers a bundle without writes
684
+ with the result `refused: read_only`: the blob routes are a newer capability a caller probes for, as
685
+ it probes for any route a host may lack. The checks a send would otherwise fail (the capability, the
686
+ byte bound, the key and the content type) all run against that one answer before the write is
687
+ journaled, and the delivery is handed the same answer, so the two cannot disagree.
688
+
689
+ **Exactly once, never resent after it may have left.** Each blob write is journaled under one
690
+ request identity (with the client's binding, the bundle and workspace and the bytes), durably
691
+ claimed, then sent once. After an unclear answer the client looks the identity up on the host
692
+ (`/outcome`, under the binding recorded with the write) and reports what the host recorded. Unlike a
693
+ document write, an `absent` lookup is never permission to send again: once an attempt may have left,
694
+ a blob write is only ever looked up. A send proven never to have left (the credential provider
695
+ failed or was denied, the signal was already aborted, the identity was malformed, a local check
696
+ failed) is rolled back instead: `writeBlob` throws `HostedBlobWriteNotSent` with its `requestId` and
697
+ `reason`, and the write stays held and resendable (`held: true`), so `settleBlobWrite` or the same
698
+ `writeBlob` call (same `requestId`, same input) sends it. A transport failure after the request was
699
+ handed to the carrier is `unknown`.
700
+
701
+ **Recovery.** `heldBlobWrites()` lists every unsettled blob write (`requestId`, `bundleId`,
702
+ `workspace`, `key`, `operation`, `state`, `attempts`, `resendable`, `hold`, `createdAt`) and sweeps any
703
+ settled one a crash left behind.
704
+
705
+ - `lookupBlobWrite(requestId)` makes one outcome lookup and never sends. A write never sent (or
706
+ proven not applied) answers `{ status: "pending" }` without a request, and a held delete `held`.
707
+ - `settleBlobWrite(requestId, { acceptDeletes? })` submits the write when its state is pending or
708
+ unknown and every attempt is proven not applied (`attempts === notAppliedThrough`); otherwise it
709
+ only looks it up, and an absent lookup is `unknown`.
710
+ - `forgetBlobWrite(requestId)` drops an `unknown` write once one fresh lookup answers `absent`,
711
+ returning `{ status: "forgotten" }`. **The earlier delivery may still commit**; the host's
712
+ compare-and-swap is what keeps a later write from losing that update. Any other record is
713
+ `{ status: "kept", reason: "not_unknown" }` (settle an `in_flight` one first), a lookup that fails,
714
+ is refused or is malformed is `kept` with `lookup_unavailable`, and a lookup that finds the outcome
715
+ settles the write instead. A forgotten identity is no longer held, so `writeBlob` with that same
716
+ `requestId` journals and uploads it again under that identity. That is safe: the host answers a
717
+ delivery it recorded under the identity with the recorded result, and one it did not record is
718
+ checked against the expected pin like any write.
719
+
720
+ All three answer `{ status: "unrecorded", requestId }` when the journal holds no blob write under the
721
+ identity. A reused `requestId` with the same input resumes (settles) the held write; with different
722
+ input it is `refused: invalid_input` and nothing is admitted. Calls are serialized per request
723
+ identity and per key (the journal's guard covers a key across bundles), so concurrent calls of one
724
+ client never trip each other.
725
+
726
+ ```ts
727
+ // A service restarting after a crash: settle what it held, forgetting only proven-absent unknowns.
728
+ for (const held of await client.heldBlobWrites()) {
729
+ const settled = await client.settleBlobWrite(held.requestId);
730
+ if (settled.status === "unknown") console.warn(`blob write ${held.requestId} is still unknown`); // forgetBlobWrite once you choose to move on
731
+ }
732
+ ```
733
+
734
+ **Reads.** `readBlob(bundleId, key, { pin })` requires the pin: it reads exactly that version and
735
+ content type, verifies length, digest and type, and answers null when the key is no longer at the pin
736
+ (the host's `409`). `blobHead(bundleId, key)` is the explicit lookup of one head (or null), and
737
+ `blobHeads(bundleId)` the whole listing; both read the full verified listing (up to 10,000 heads in
738
+ 4 MiB pages), since the host has no single-key head route yet.
739
+
740
+ **Keys.** `isOrdinaryBlobKey` is the one key policy every blob writer uses, this client and the
741
+ CLI's checkout sync alike: a safe key that is not a reserved file (`index.md`, `log.md`) and does not
742
+ sit under Superbee's own record folders (`views/`, `pages/`, their registries, `conventions/`,
743
+ `recipes/`, `references/`, compared case-insensitively). Any other key is refused `invalid_input`
744
+ before any request.
745
+
746
+ **Aborts.** A `signal` aborted before the write is admitted throws its reason and journals nothing;
747
+ that includes an abort while the capabilities are still being read, so a hung credential provider
748
+ never holds the call (or its key) past the caller's abort. Once admitted, a write is held until it
749
+ settles, and an abort before its send leaves it resendable (`HostedBlobWriteNotSent`).
750
+
751
+ **Journal.** The default blob journal is in memory (`createMemoryBlobJournal`), owned by the client.
752
+ Give a blob journal to one client (its `within` clients share it). Two clients over one journal
753
+ coordinate only through its per-key guard: concurrent writes of different identities still settle,
754
+ but two calls on the same request identity from both clients can fail with a raw journal conflict.
755
+ Pass `blobJournal` to keep blob writes across restarts: in Node, `openHostedClientJournal(...).blobs`
756
+ (below). `driveJournaledBlob(journal, context, requestId, mode)` is the owner of the journaled
757
+ sequence (admit, claim, deliver, persist, retire) for a caller with its own journal and premises; its
758
+ `premises.snapshot(key, step)` returns each transaction's guard plus the caller's own metadata
759
+ changes, checked atomically with it.
760
+
761
+ **Limitations.**
762
+
763
+ - Lookups currently need `blobWrites`: a grant downgraded to read-only cannot settle its earlier
764
+ blob writes until the host authorizes the outcome route on its own (a hosted follow-up).
765
+ - A lookup across principals is unverified: a new token of the same principal finds its outcomes
766
+ (the binding travels in the journal), but what another principal's lookup gets is not yet proven.
767
+ - Every journal record of a write carries its bytes, and each transaction validates the record.
768
+ Validation is cached per payload, so a write costs about 0.17 s per MiB in Node (3 MiB about
769
+ 0.5 s, 16 MiB about 2.6 s, measured with the in-memory journal); without the cache it was about
770
+ 1.7 s per MiB (16 MiB about 28 s). The first validation of a payload still runs on the caller's
771
+ thread in one slice, about 1.2 s for 16 MiB.
772
+
773
+ ### Inbox
774
+
775
+ `createHostedInbox(client, { tenantId })` is a person's hosted inbox in one workspace, over the
776
+ same client (its credential, deadline and abort; a `within` client serves too). It reaches the
777
+ attention routes, `POST <prefix>/attention/<operationId>`, and names the workspace in the
778
+ `Superbee-Workspace` header on every request, since a sync sign-in spans all of the person's
779
+ workspaces. Notifications are a hosted capability: a host without these routes answers
780
+ `HostedRouteNotServed`, and nothing here emulates a local inbox.
781
+
782
+ ```ts
783
+ import { createHostedInbox } from "@superbee/core/hosted-client";
784
+
785
+ const identity = await client.whoami();
786
+ const inbox = createHostedInbox(client, { tenantId: identity.tenantIds[0]!, via: "my-agent" });
787
+ const receipt = await inbox.request({ recipient: { kind: "principal", principalId: identity.principalId }, kind: "needs_input", note: "Ready for you" });
788
+ const page = await inbox.list({ threadId: receipt.threadId, afterSeq: 0 });
789
+ ```
790
+
791
+ The methods are `list`, `threads`, `summary`, `get` (does not mark read), `open` (marks read),
792
+ `ack` (at the revision read), `request` (send) and `cancel` (withdraw your own send), with the
793
+ open contract's shapes (`InboxEntry`, `InboxThread`, `AttentionReceipt` and the inputs). A
794
+ recipient is a principal id; to notify yourself, use `whoami().principalId`. A document target
795
+ names `whoami().targetAuthorityKey` as its `authorityKey` and the document's current version; a
796
+ host that reports no key (the field is absent, as on every `/sync/v1` host today) cannot take one yet.
797
+
798
+ `request` mints one UUIDv7 ticket per send (or takes the one you pass). When an answer is lost or
799
+ the host marks its refusal retryable, it sends the same body under the same ticket again
800
+ (`sendAttempts`, default 3), which the host answers as a replay of the first send, never as a second
801
+ item. A send still unclear after that throws `HostedAttentionOutcomeUnknown` with its `ticket`:
802
+ `request({ ...input, ticket })` within 24 hours settles it. Refusals are
803
+ `HostedAttentionRefusal` (a `RemoteError`) with the catalog's `code` (`attention_disabled`,
804
+ `not_found`, `target_unavailable`, `target_unsupported`, `quota_exceeded`, `request_conflict`,
805
+ `version_conflict`, `ticket_expired` with `serverTime`, `workspace_not_available` and the rest),
806
+ `retryable`, and a message in this client's words: the host's message and hint are never repeated.
807
+
808
+ ### Durable intent store (Node only)
809
+
810
+ `openHostedClientJournal` from `@superbee/core/hosted-client/node` is a durable `intentStore`, and its
811
+ `blobs` a durable `blobJournal`, so a short-lived process that exits with a write `unknown` can recover
812
+ it after a restart.
813
+
814
+ ```ts
815
+ import { createHostedClient } from "@superbee/core/hosted-client";
816
+ import { openHostedClientJournal } from "@superbee/core/hosted-client/node";
817
+
818
+ const journal = await openHostedClientJournal({ directory: path.join(stateDir, "superbee-writes") });
819
+ const client = createHostedClient({ target, credentials, intentStore: journal, blobJournal: journal.blobs });
820
+ for (const held of await client.heldWrites()) await client.settleWrite(held.requestId);
821
+ for (const held of await client.heldBlobWrites()) await client.settleBlobWrite(held.requestId);
822
+ // ... writes ...
823
+ await journal.close();
824
+ ```
825
+
826
+ The journal is a `FileJournaledBackend` directory: an fsynced, checksummed log with snapshot
827
+ compaction. Its on-disk format is a supported compatibility promise: a format bump ships a reader
828
+ for every earlier layout, and an older reader refuses a newer store with `FileJournalFormatError`
829
+ rather than misreading it. The current format is 3 (see `JOURNALED-BACKENDS.md`). Each unsettled write is one metadata row, `hosted-client.write.<requestId>`,
830
+ holding the whole document and the binding it was sent under, so a new process with a new token
831
+ looks it up exactly as the first would have. A settled write is removed. Each unsettled blob write is
832
+ one row of the backend's own blob intent family, with its bytes, under the same transition rules as
833
+ the in-memory journal; a settled one is retired, and since every record of a blob write carries its
834
+ bytes, retiring or forgetting one compacts the store once its log or snapshot is past 1 MiB. Metadata
835
+ a blob write carries may not name the journal's own rows (`hosted-client.*`) or a checkout's
836
+ blob-sync state.
837
+
838
+ - **Ownership.** Open takes an exclusive same-user lock on the directory until `close()`. Another
839
+ process waits `lock.waitMs` (default 5,000 ms) and then gets `FilesystemMutationLockError`. A
840
+ lock left by a process that died on this host is reclaimed.
841
+ - **Local file system only.** The lock lives in a per-user runtime directory on this host, so it
842
+ cannot exclude a process on another host. Keep the journal on a local disk, never on NFS, SMB
843
+ or another shared mount. Open does not detect a network mount; this is the operator's rule.
844
+ - **Privacy.** The journal holds whole documents. A directory open creates is mode 0700, and its
845
+ files 0600. On POSIX, an existing directory that is a symbolic link, is owned by another user,
846
+ or grants its group or others any permission, is refused as `HostedClientJournalError`
847
+ `JOURNAL_DIRECTORY_NOT_PRIVATE`. The check runs again once the lock is held, and a directory
848
+ replaced in between is refused too. The directory is the journal's alone: an existing one may
849
+ hold only `store.log`, `store.snapshot` and `store.snapshot.tmp`, and any other entry (an
850
+ operating system's `.DS_Store` included) is refused as `JOURNAL_NOT_HOSTED_CLIENT`, as is a
851
+ `store.log` that is not a journal's log, and a store file that is a symbolic link, a
852
+ directory, has more than one link, or (on POSIX) belongs to another user. The listing and the
853
+ files are checked again under the lock. Another process of the same user changing the
854
+ directory while it opens is out of scope: in that window the open can truncate a torn or
855
+ foreign log tail.
856
+ - **Marker.** A new journal records `hosted-client.journal = { schemaVersion: 1 }` in its first
857
+ transaction. Open refuses, as `JOURNAL_NOT_HOSTED_CLIENT`, a store with rows but no marker or one
858
+ holding documents, blob files or a checkout's blob-sync state, so it is never pointed at a CLI
859
+ checkout store. A marked journal holding blob write intents is its own and reopens. A marker of another schema is `JOURNAL_SCHEMA_UNSUPPORTED`. Open decides on a read-only
860
+ pass first, so a refused directory's files are left byte for byte. A crash before the marker
861
+ lands leaves an empty store, which the next open marks.
862
+ - **Size.** Once the store is adopted, the log is compacted into the snapshot whenever it grows past the greater of 1 MiB
863
+ (`HOSTED_CLIENT_JOURNAL_COMPACT_FLOOR_BYTES`) and twice the last snapshot, so steady writing keeps
864
+ it near 1 MiB. The snapshot, which holds every write still unsettled, and any one transaction are
865
+ bounded at 256 MiB. A write that would pass that bound is refused before anything is recorded,
866
+ and its `write` throws. Unsettled writes count toward the bound until `settleWrite` resolves them.
867
+ - **Ordering.** Unlike the in-memory store, each call takes effect when its asynchronous steps
868
+ complete, so unawaited overlapping calls on one request identity can land out of call order.
869
+ Await each call before the next, as the client does.
870
+ - **Damage.** `FileJournalCorruptError` (a damaged record before the log's end or a damaged
871
+ snapshot) and `FileJournalFormatError` (a newer format) refuse the open and change no file. A torn
872
+ final record, which was never acknowledged, is truncated. Nothing is repaired automatically:
873
+ moving the directory aside starts an empty journal and loses the writes it held, which can then
874
+ be settled only by reading the documents on the host.
875
+
876
+ ### Export download (Node only)
877
+
878
+ `@superbee/core/hosted-client/node` downloads and verifies a hosted bundle's portable export. It
879
+ is a supported public API under the same versioning. It is Node-only because verification
880
+ (`@superbee/core/portable-export`) needs Node; `@superbee/core/hosted-client` never imports it,
881
+ so the client itself stays browser-safe.
882
+
883
+ ```ts
884
+ import { bearerCredentials, createHostedClient } from "@superbee/core/hosted-client";
885
+ import { downloadExport, EXPORT_WINDOW_MS } from "@superbee/core/hosted-client/node";
886
+ import { materializeExport } from "@superbee/core/portable-export";
887
+
888
+ const client = createHostedClient({ target, credentials: bearerCredentials(getToken), deadlineMs: EXPORT_WINDOW_MS });
889
+ const verified = await downloadExport(client, "team.notes", { signal });
890
+ const folder = await materializeExport(verified, parentDirectory);
891
+ ```
892
+
893
+ `downloadExport(client, bundleId, { signal })` returns a `VerifiedExport`: every document,
894
+ reserved file and blob at the bundle's latest confirmed revision, read whole and checked against
895
+ the host's digest manifests. It writes nothing. A stopped, tampered or oversized export never
896
+ becomes a result; placing the files is the caller's (`materializeExport`, or its own staging). It
897
+ reads a paged export one page at a time and joins the pages. On a host from before paging it reads
898
+ the whole archive. A bundle that changes between pages is read again from its first page, up to
899
+ three attempts, and a busy or stopped page is asked for once more. Create the client with
900
+ `deadlineMs: EXPORT_WINDOW_MS` (60 s), the host's export window; the default 15 s can cut a large
901
+ bundle. A `within(slug)` client names the bundle in that workspace.
902
+
903
+ Failures are typed:
904
+
905
+ - `HostedCarrierError`, `HostedRouteNotServed` (a host without the export route) and
906
+ `RemoteError` (the host's refusal: `bundle_not_found`, `result_too_large`, `validation_failed`,
907
+ an unauthenticated bearer and so on), as for the rest of the client.
908
+ - `ExportArchiveError` (`problem`): the verifier refused the archive. `incomplete` means the host
909
+ stopped it, and the same request may succeed.
910
+ - `HostedExportError` (`problem`): `stopped` (the answer's body failed part way), `too_large`
911
+ (past `MAX_EXPORT_BYTES`) or `source_changed` (the bundle kept changing between pages).
912
+ - The signal's reason, once `signal` or the client's own signal aborts.
913
+
914
+ ## Release compatibility
915
+
916
+ This migration describes the source after the Windows extraction. Previously published versions
917
+ are unchanged. Consult the release notes for the first affected version before upgrading a pinned
918
+ consumer. CLI and engine version numbers are independent; do not infer matching embedded code from
919
+ package names or version labels.
920
+
921
+ [Source and contribution guide](https://github.com/Holaxis-ai/superbee)
922
+
923
+ ### Node portable export boundary
924
+
925
+ `@superbee/core/portable-export` (also bundled as `superbee/portable-export`) exposes the same
926
+ store-only archive verifier used by the CLI: `verifyExport`, `ExportPageChain` and
927
+ `joinExportPages`. The page chain charges the sum of complete ZIP sizes, including manifests
928
+ and headers, against `MAX_EXPORT_BYTES` before retaining a page; returned `bytes` counts only
929
+ stored bundle content. It performs no acquisition, sign-in or network access. A complete accepted
930
+ handle can be passed to `materializeExport(handle, existingPrivateParent)` to create a fresh
931
+ private directory; callers own its cleanup and isolation while publishing. No target is replaced.
932
+ `exportProvenance(handle)` returns exact ordered raw-manifest SHA-256/size evidence plus a combined
933
+ inventory SHA-256. Keep that evidence outside the directory and private when it identifies a
934
+ private source. Handles cannot be reconstructed from JSON or moved between independently bundled
935
+ module instances; modified handles are refused. This surface requires Node, not a browser or Worker.
936
+
937
+ ## Built-in recipes and Kind authoring
938
+
939
+ `@superbee/core/builtin-recipes` is an opt-in catalog: `builtinNames()` lists names,
940
+ `builtinRecipeFiles(name)` returns detached source files, and `loadBuiltinRecipe(name)` returns
941
+ the existing parser's result or `null` for an unknown name. The generic parser, package root,
942
+ and `initBundle` do not import the catalog or install recipes.
943
+
944
+ `@superbee/core/kind-authoring` provides inference, forecasts, dismissal classification, templates,
945
+ and guarded draft/apply/dismiss operations on a caller-selected `Bundle` backend. An outside app
946
+ can use the existing backend and mutation seams without a CLI subprocess:
947
+
948
+ ```ts
949
+ import { MemoryBackend } from "@superbee/core/memory-backend";
950
+ import { query } from "@superbee/core/engine";
951
+ import { mutateDocument } from "@superbee/core/document-mutation";
952
+ import { buildKindRegistry } from "@superbee/core/kinds";
953
+ import { loadBuiltinRecipe } from "@superbee/core/builtin-recipes";
954
+ import { installRecipe } from "@superbee/core/recipe-lifecycle";
955
+ import { kindInstanceCandidate, prepareKindDraft, applyKindDraft } from "@superbee/core/kind-authoring";
956
+
957
+ const backend = new MemoryBackend(), bundle = { root: "memory://project", backend };
958
+ await backend.writeReserved("", "index.md", "---\nokf_version: '0.2'\n---\n# Project\n");
959
+ const loaded = loadBuiltinRecipe("work-tracking");
960
+ if (!loaded?.ok) throw new Error("Recipe unavailable");
961
+ await installRecipe(bundle, loaded.recipe);
962
+ const registry = buildKindRegistry(await query(backend, { prefix: "conventions/" }));
963
+ const task = registry.kinds.get("Task")!;
964
+ const candidate = kindInstanceCandidate(task, { title: "First task", superbee_progress_status: "todo" });
965
+ await mutateDocument({ bundle, id: "tasks/first", mode: "create-only", registry, strict: true,
966
+ buildCandidate: () => candidate });
967
+
968
+ // Once an ungoverned type has existing instances, inspect its draft before applying.
969
+ const text = { inferredBody: () => "The accepted shape of existing research records.\n" };
970
+ const plan = await prepareKindDraft(bundle, "Research", text); // requires existing Research docs
971
+ // Present candidateDoc, warnings and promotions; after acceptance:
972
+ await applyKindDraft(bundle, "Research", plan.token, text);
973
+ ```
974
+
975
+ `kindInstanceCandidate` accepts already-typed field values. Existing validators own their meaning;
976
+ there is no CLI flag coercion or path-alias resolution. Supply a canonical id and use strict
977
+ create-only mutation. The template keeps `type` equal to `governs`, preserves own prototype-shaped
978
+ fields, scaffolds sections only when body is omitted, and retains an explicit empty body.
979
+
980
+ Public draft/apply/dismiss operations trim type names and refuse blank or non-string names before
981
+ any backend access, matching convention parsing. `prepareKindDraft` selects a catalog definition
982
+ or infers from instances. Its token binds candidate
983
+ bytes (including supplied `inferredBody`/`inferredDescription`), edition, presence statistics and
984
+ warning forecast. Pass the same prose options to `applyKindDraft`, which recomputes the plan and
985
+ checks edition and declaration-free redraft eligibility against every fresh CAS head. New
986
+ conventions are expect-absent creates; accepted redrafts replace dismissal prose in place and
987
+ report dropped links. `dismissKind` accepts presentation-owned description/body, records no schema
988
+ obligations, refuses a real schema, and is idempotent. Definition writes retain recipe
989
+ installation's generation-clock opt-out. `KindAuthoringError.reason` identifies domain refusals;
990
+ normal mutation/storage errors retain their types.
991
+
992
+ Presentations with an established mutation adapter may pass the options from
993
+ `prepareKindDraftMutation` or `prepareKindDismissal` through that adapter. These are the same
994
+ specifications used by public apply/dismiss; keep preparation adjacent to mutation. Outside apps
995
+ normally use `applyKindDraft`/`dismissKind`, which own the read-and-apply sequence. No cross-document
996
+ transaction or authorization boundary is added.
997
+
998
+ The packed proof runs the public workflow over `MemoryBackend` in Node and a browser bundle without
999
+ Node globals. This proves source/package availability. Hosted provisioning routes, MCP modeling
1000
+ tools, and publication of a core version remain separate adoption and release decisions.