@abloatai/ablo 0.35.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (622) hide show
  1. package/AGENTS.md +2 -2
  2. package/CHANGELOG.md +71 -1929
  3. package/NOTICE +2 -2
  4. package/README.md +23 -532
  5. package/assets/banner.png +0 -0
  6. package/dist/auth.d.ts +2 -0
  7. package/dist/auth.d.ts.map +1 -0
  8. package/dist/auth.js +2 -0
  9. package/dist/auth.js.map +1 -0
  10. package/dist/client.d.ts +3 -0
  11. package/dist/client.d.ts.map +1 -0
  12. package/dist/client.js +3 -0
  13. package/dist/client.js.map +1 -0
  14. package/dist/coordination.d.ts +2 -0
  15. package/dist/coordination.d.ts.map +1 -0
  16. package/dist/coordination.js +2 -0
  17. package/dist/coordination.js.map +1 -0
  18. package/dist/index.d.ts +3 -112
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +3 -161
  21. package/dist/index.js.map +1 -0
  22. package/dist/react.d.ts +4 -0
  23. package/dist/react.d.ts.map +1 -0
  24. package/dist/react.js +3 -0
  25. package/dist/react.js.map +1 -0
  26. package/dist/schema.d.ts +2 -0
  27. package/dist/schema.d.ts.map +1 -0
  28. package/dist/schema.js +2 -0
  29. package/dist/schema.js.map +1 -0
  30. package/dist/server.d.ts +2 -0
  31. package/dist/server.d.ts.map +1 -0
  32. package/dist/server.js +2 -0
  33. package/dist/server.js.map +1 -0
  34. package/dist/source-conformance.d.ts +2 -0
  35. package/dist/source-conformance.d.ts.map +1 -0
  36. package/dist/source-conformance.js +2 -0
  37. package/dist/source-conformance.js.map +1 -0
  38. package/dist/source-drizzle.d.ts +2 -0
  39. package/dist/source-drizzle.d.ts.map +1 -0
  40. package/dist/source-drizzle.js +2 -0
  41. package/dist/source-drizzle.js.map +1 -0
  42. package/dist/source-kysely.d.ts +2 -0
  43. package/dist/source-kysely.d.ts.map +1 -0
  44. package/dist/source-kysely.js +2 -0
  45. package/dist/source-kysely.js.map +1 -0
  46. package/dist/source-next.d.ts +2 -0
  47. package/dist/source-next.d.ts.map +1 -0
  48. package/dist/source-next.js +2 -0
  49. package/dist/source-next.js.map +1 -0
  50. package/dist/source.d.ts +2 -0
  51. package/dist/source.d.ts.map +1 -0
  52. package/dist/source.js +2 -0
  53. package/dist/source.js.map +1 -0
  54. package/dist/wire.d.ts +2 -0
  55. package/dist/wire.d.ts.map +1 -0
  56. package/dist/wire.js +2 -0
  57. package/dist/wire.js.map +1 -0
  58. package/docs/agents.md +2 -2
  59. package/docs/api-keys.md +13 -12
  60. package/docs/api.md +15 -53
  61. package/docs/audit.md +4 -3
  62. package/docs/cli.md +11 -11
  63. package/docs/client-behavior.md +9 -9
  64. package/docs/concurrency-convention.md +28 -42
  65. package/docs/coordination.md +228 -86
  66. package/docs/data-sources.md +5 -5
  67. package/docs/debugging.md +34 -12
  68. package/docs/deployment.md +8 -8
  69. package/docs/examples/agent-human.md +4 -4
  70. package/docs/examples/ai-sdk-tool.md +1 -1
  71. package/docs/examples/existing-python-backend.md +15 -4
  72. package/docs/examples/nextjs.md +27 -6
  73. package/docs/examples/scoped-agent.md +3 -3
  74. package/docs/examples/server-agent.md +2 -2
  75. package/docs/groups.md +57 -3
  76. package/docs/guarantees.md +37 -10
  77. package/docs/how-it-works.md +32 -8
  78. package/docs/idempotency.md +6 -6
  79. package/docs/identity.md +24 -24
  80. package/docs/index.md +8 -8
  81. package/docs/integration-guide.md +38 -16
  82. package/docs/internal/README.md +18 -0
  83. package/docs/internal/agent-fleet-coordination-design.md +171 -0
  84. package/docs/internal/agent-orchestration.md +58 -0
  85. package/docs/internal/commit-identifiers.md +91 -0
  86. package/docs/internal/concurrency-open-decisions.md +37 -0
  87. package/docs/internal/data-source-reverse-channel.md +150 -0
  88. package/docs/internal/per-field-conflict-detection.md +165 -0
  89. package/docs/internal/postgres-replication.md +64 -0
  90. package/docs/internal/serializable-schema.md +119 -0
  91. package/docs/internal/structure.md +32 -0
  92. package/docs/mcp.md +9 -9
  93. package/docs/migration.md +37 -18
  94. package/docs/projects.md +1 -1
  95. package/docs/quickstart.md +2 -2
  96. package/docs/react.md +24 -13
  97. package/docs/schema-contract.md +3 -3
  98. package/docs/sessions.md +91 -37
  99. package/docs/webhooks.md +9 -9
  100. package/examples/README.md +2 -2
  101. package/examples/data-source/README.md +1 -1
  102. package/examples/data-source/ablo-driver.ts +1 -1
  103. package/examples/data-source/customer-server.ts +1 -1
  104. package/examples/data-source/run.ts +1 -1
  105. package/examples/data-source/schema.ts +1 -1
  106. package/examples/quickstart.ts +2 -2
  107. package/llms.txt +12 -12
  108. package/package.json +64 -174
  109. package/dist/BaseSyncedStore.d.ts +0 -823
  110. package/dist/BaseSyncedStore.js +0 -1955
  111. package/dist/Database.d.ts +0 -335
  112. package/dist/Database.js +0 -1500
  113. package/dist/InstanceCache.d.ts +0 -233
  114. package/dist/InstanceCache.js +0 -1164
  115. package/dist/LazyReferenceCollection.d.ts +0 -177
  116. package/dist/LazyReferenceCollection.js +0 -461
  117. package/dist/Model.d.ts +0 -444
  118. package/dist/Model.js +0 -909
  119. package/dist/ModelRegistry.d.ts +0 -221
  120. package/dist/ModelRegistry.js +0 -537
  121. package/dist/NetworkMonitor.d.ts +0 -26
  122. package/dist/NetworkMonitor.js +0 -77
  123. package/dist/RuntimeContext.d.ts +0 -52
  124. package/dist/RuntimeContext.js +0 -80
  125. package/dist/SyncClient.d.ts +0 -551
  126. package/dist/SyncClient.js +0 -2199
  127. package/dist/adapters/alwaysOnline.d.ts +0 -14
  128. package/dist/adapters/alwaysOnline.js +0 -17
  129. package/dist/adapters/inMemoryStorage.d.ts +0 -31
  130. package/dist/adapters/inMemoryStorage.js +0 -110
  131. package/dist/ai-sdk/coordinatedTool.d.ts +0 -120
  132. package/dist/ai-sdk/coordinatedTool.js +0 -134
  133. package/dist/ai-sdk/coordinationContext.d.ts +0 -46
  134. package/dist/ai-sdk/coordinationContext.js +0 -106
  135. package/dist/ai-sdk/index.d.ts +0 -121
  136. package/dist/ai-sdk/index.js +0 -121
  137. package/dist/ai-sdk/wrap.d.ts +0 -65
  138. package/dist/ai-sdk/wrap.js +0 -39
  139. package/dist/auth/index.d.ts +0 -1
  140. package/dist/auth/index.js +0 -8
  141. package/dist/batching/index.d.ts +0 -55
  142. package/dist/batching/index.js +0 -147
  143. package/dist/cli.cjs +0 -288600
  144. package/dist/client/Ablo.d.ts +0 -231
  145. package/dist/client/Ablo.js +0 -149
  146. package/dist/client/abloClient.d.ts +0 -309
  147. package/dist/client/abloClient.js +0 -13
  148. package/dist/client/clientPrelude.d.ts +0 -52
  149. package/dist/client/clientPrelude.js +0 -60
  150. package/dist/client/consoleLogger.d.ts +0 -35
  151. package/dist/client/consoleLogger.js +0 -44
  152. package/dist/client/coreClient.d.ts +0 -60
  153. package/dist/client/coreClient.js +0 -118
  154. package/dist/client/createInternalComponents.d.ts +0 -46
  155. package/dist/client/createInternalComponents.js +0 -92
  156. package/dist/client/createModelProxy.d.ts +0 -228
  157. package/dist/client/createModelProxy.js +0 -818
  158. package/dist/client/humans.d.ts +0 -48
  159. package/dist/client/humans.js +0 -52
  160. package/dist/client/modelRegistration.d.ts +0 -10
  161. package/dist/client/modelRegistration.js +0 -312
  162. package/dist/client/options.d.ts +0 -461
  163. package/dist/client/options.js +0 -7
  164. package/dist/client/reactiveEngine.d.ts +0 -48
  165. package/dist/client/reactiveEngine.js +0 -910
  166. package/dist/client/resourceTypes.d.ts +0 -12
  167. package/dist/client/resourceTypes.js +0 -10
  168. package/dist/client/schemaConfig.d.ts +0 -44
  169. package/dist/client/schemaConfig.js +0 -185
  170. package/dist/client/validateAbloOptions.d.ts +0 -42
  171. package/dist/client/validateAbloOptions.js +0 -43
  172. package/dist/client/wsMutationExecutor.d.ts +0 -27
  173. package/dist/client/wsMutationExecutor.js +0 -72
  174. package/dist/context.d.ts +0 -29
  175. package/dist/context.js +0 -58
  176. package/dist/coordination/ClaimLog.d.ts +0 -26
  177. package/dist/coordination/ClaimLog.js +0 -32
  178. package/dist/coordination/index.d.ts +0 -1
  179. package/dist/coordination/index.js +0 -8
  180. package/dist/core/DatabaseManager.d.ts +0 -105
  181. package/dist/core/DatabaseManager.js +0 -387
  182. package/dist/core/QueryProcessor.d.ts +0 -75
  183. package/dist/core/QueryProcessor.js +0 -255
  184. package/dist/core/QueryView.d.ts +0 -79
  185. package/dist/core/QueryView.js +0 -218
  186. package/dist/core/StoreManager.d.ts +0 -112
  187. package/dist/core/StoreManager.js +0 -302
  188. package/dist/core/ViewRegistry.d.ts +0 -20
  189. package/dist/core/ViewRegistry.js +0 -55
  190. package/dist/core/index.d.ts +0 -33
  191. package/dist/core/index.js +0 -48
  192. package/dist/core/openIDBWithTimeout.d.ts +0 -65
  193. package/dist/core/openIDBWithTimeout.js +0 -153
  194. package/dist/core/queryUtils.d.ts +0 -45
  195. package/dist/core/queryUtils.js +0 -69
  196. package/dist/core/storeContract.d.ts +0 -145
  197. package/dist/core/storeContract.js +0 -12
  198. package/dist/docs/catalog.d.ts +0 -72
  199. package/dist/docs/catalog.js +0 -227
  200. package/dist/docs/index.d.ts +0 -10
  201. package/dist/docs/index.js +0 -10
  202. package/dist/environment.d.ts +0 -1
  203. package/dist/environment.js +0 -8
  204. package/dist/interfaces/index.d.ts +0 -311
  205. package/dist/interfaces/index.js +0 -9
  206. package/dist/keys/index.d.ts +0 -1
  207. package/dist/keys/index.js +0 -8
  208. package/dist/mutators/RecordingMutation.d.ts +0 -36
  209. package/dist/mutators/RecordingMutation.js +0 -182
  210. package/dist/mutators/Transaction.d.ts +0 -40
  211. package/dist/mutators/Transaction.js +0 -58
  212. package/dist/mutators/UndoManager.d.ts +0 -258
  213. package/dist/mutators/UndoManager.js +0 -658
  214. package/dist/mutators/defineMutators.d.ts +0 -60
  215. package/dist/mutators/defineMutators.js +0 -18
  216. package/dist/mutators/inverseOp.d.ts +0 -126
  217. package/dist/mutators/inverseOp.js +0 -71
  218. package/dist/mutators/mutateActions.d.ts +0 -45
  219. package/dist/mutators/mutateActions.js +0 -105
  220. package/dist/mutators/readerActions.d.ts +0 -33
  221. package/dist/mutators/readerActions.js +0 -57
  222. package/dist/mutators/undoApply.d.ts +0 -51
  223. package/dist/mutators/undoApply.js +0 -117
  224. package/dist/policy/index.d.ts +0 -21
  225. package/dist/policy/index.js +0 -20
  226. package/dist/query/client.d.ts +0 -61
  227. package/dist/query/client.js +0 -137
  228. package/dist/query/types.d.ts +0 -85
  229. package/dist/query/types.js +0 -16
  230. package/dist/react/AbloProvider.d.ts +0 -230
  231. package/dist/react/AbloProvider.js +0 -455
  232. package/dist/react/ClientSideSuspense.d.ts +0 -36
  233. package/dist/react/ClientSideSuspense.js +0 -17
  234. package/dist/react/DefaultFallback.d.ts +0 -24
  235. package/dist/react/DefaultFallback.js +0 -43
  236. package/dist/react/context.d.ts +0 -55
  237. package/dist/react/context.js +0 -29
  238. package/dist/react/index.d.ts +0 -61
  239. package/dist/react/index.js +0 -66
  240. package/dist/react/internalContext.d.ts +0 -33
  241. package/dist/react/internalContext.js +0 -3
  242. package/dist/react/useAblo.d.ts +0 -75
  243. package/dist/react/useAblo.js +0 -102
  244. package/dist/react/useCurrentUserId.d.ts +0 -22
  245. package/dist/react/useCurrentUserId.js +0 -34
  246. package/dist/react/useErrorListener.d.ts +0 -20
  247. package/dist/react/useErrorListener.js +0 -38
  248. package/dist/react/useMutationFailureListener.d.ts +0 -26
  249. package/dist/react/useMutationFailureListener.js +0 -38
  250. package/dist/react/useMutators.d.ts +0 -56
  251. package/dist/react/useMutators.js +0 -84
  252. package/dist/react/useReactive.d.ts +0 -35
  253. package/dist/react/useReactive.js +0 -123
  254. package/dist/react/useSyncStatus.d.ts +0 -59
  255. package/dist/react/useSyncStatus.js +0 -76
  256. package/dist/react/useUndoScope.d.ts +0 -34
  257. package/dist/react/useUndoScope.js +0 -81
  258. package/dist/schema/coordination.d.ts +0 -112
  259. package/dist/schema/coordination.js +0 -129
  260. package/dist/schema/ddl.d.ts +0 -97
  261. package/dist/schema/ddl.js +0 -491
  262. package/dist/schema/ddlLock.d.ts +0 -35
  263. package/dist/schema/ddlLock.js +0 -46
  264. package/dist/schema/diff.d.ts +0 -225
  265. package/dist/schema/diff.js +0 -289
  266. package/dist/schema/generate.d.ts +0 -19
  267. package/dist/schema/generate.js +0 -86
  268. package/dist/schema/index.d.ts +0 -41
  269. package/dist/schema/index.js +0 -76
  270. package/dist/schema/queries.d.ts +0 -201
  271. package/dist/schema/queries.js +0 -144
  272. package/dist/schema/select.d.ts +0 -40
  273. package/dist/schema/select.js +0 -87
  274. package/dist/schema/serialize.d.ts +0 -115
  275. package/dist/schema/serialize.js +0 -262
  276. package/dist/schema/sugar.d.ts +0 -109
  277. package/dist/schema/sugar.js +0 -83
  278. package/dist/schema/syncDeltaRow.d.ts +0 -6
  279. package/dist/schema/syncDeltaRow.js +0 -6
  280. package/dist/server/adapter.d.ts +0 -173
  281. package/dist/server/adapter.js +0 -18
  282. package/dist/server/commit.d.ts +0 -107
  283. package/dist/server/commit.js +0 -1
  284. package/dist/server/index.d.ts +0 -14
  285. package/dist/server/index.js +0 -2
  286. package/dist/server/readConfig.d.ts +0 -80
  287. package/dist/server/readConfig.js +0 -8
  288. package/dist/server/storageMode.d.ts +0 -23
  289. package/dist/server/storageMode.js +0 -17
  290. package/dist/source/adapter.d.ts +0 -81
  291. package/dist/source/adapter.js +0 -22
  292. package/dist/source/adapters/drizzle.d.ts +0 -48
  293. package/dist/source/adapters/drizzle.js +0 -219
  294. package/dist/source/adapters/kysely.d.ts +0 -42
  295. package/dist/source/adapters/kysely.js +0 -205
  296. package/dist/source/adapters/kyselyMutationCore.d.ts +0 -76
  297. package/dist/source/adapters/kyselyMutationCore.js +0 -125
  298. package/dist/source/adapters/memory.d.ts +0 -13
  299. package/dist/source/adapters/memory.js +0 -130
  300. package/dist/source/adapters/prisma.d.ts +0 -63
  301. package/dist/source/adapters/prisma.js +0 -202
  302. package/dist/source/conformance.d.ts +0 -37
  303. package/dist/source/conformance.js +0 -215
  304. package/dist/source/connector.d.ts +0 -95
  305. package/dist/source/connector.js +0 -266
  306. package/dist/source/connectorProtocol.d.ts +0 -154
  307. package/dist/source/connectorProtocol.js +0 -163
  308. package/dist/source/contract.d.ts +0 -195
  309. package/dist/source/contract.js +0 -164
  310. package/dist/source/factory.d.ts +0 -92
  311. package/dist/source/factory.js +0 -286
  312. package/dist/source/footprint.d.ts +0 -111
  313. package/dist/source/footprint.js +0 -0
  314. package/dist/source/idempotency.d.ts +0 -61
  315. package/dist/source/idempotency.js +0 -144
  316. package/dist/source/index.d.ts +0 -23
  317. package/dist/source/index.js +0 -30
  318. package/dist/source/migrations.d.ts +0 -21
  319. package/dist/source/migrations.js +0 -103
  320. package/dist/source/next.d.ts +0 -32
  321. package/dist/source/next.js +0 -25
  322. package/dist/source/pushQueue.d.ts +0 -134
  323. package/dist/source/pushQueue.js +0 -256
  324. package/dist/source/signing.d.ts +0 -92
  325. package/dist/source/signing.js +0 -162
  326. package/dist/source/types.d.ts +0 -401
  327. package/dist/source/types.js +0 -59
  328. package/dist/stores/ObjectStore.d.ts +0 -115
  329. package/dist/stores/ObjectStore.js +0 -393
  330. package/dist/stores/ObjectStoreContract.d.ts +0 -38
  331. package/dist/stores/ObjectStoreContract.js +0 -1
  332. package/dist/stores/SyncActionStore.d.ts +0 -97
  333. package/dist/stores/SyncActionStore.js +0 -504
  334. package/dist/stores/syncAction.d.ts +0 -26
  335. package/dist/stores/syncAction.js +0 -16
  336. package/dist/surface.d.ts +0 -36
  337. package/dist/surface.js +0 -75
  338. package/dist/sync/BootstrapFetcher.d.ts +0 -280
  339. package/dist/sync/BootstrapFetcher.js +0 -962
  340. package/dist/sync/ConnectionManager.d.ts +0 -8
  341. package/dist/sync/ConnectionManager.js +0 -8
  342. package/dist/sync/OnDemandLoader.d.ts +0 -228
  343. package/dist/sync/OnDemandLoader.js +0 -742
  344. package/dist/sync/SubscriptionManager.d.ts +0 -159
  345. package/dist/sync/SubscriptionManager.js +0 -243
  346. package/dist/sync/SyncWebSocket.d.ts +0 -173
  347. package/dist/sync/SyncWebSocket.js +0 -438
  348. package/dist/sync/awaitClaimGrant.d.ts +0 -6
  349. package/dist/sync/awaitClaimGrant.js +0 -6
  350. package/dist/sync/bootstrapApply.d.ts +0 -70
  351. package/dist/sync/bootstrapApply.js +0 -73
  352. package/dist/sync/commitFrames.d.ts +0 -8
  353. package/dist/sync/commitFrames.js +0 -8
  354. package/dist/sync/contextPorts.d.ts +0 -18
  355. package/dist/sync/contextPorts.js +0 -31
  356. package/dist/sync/createClaimStream.d.ts +0 -7
  357. package/dist/sync/createClaimStream.js +0 -7
  358. package/dist/sync/createPresenceStream.d.ts +0 -69
  359. package/dist/sync/createPresenceStream.js +0 -200
  360. package/dist/sync/createSnapshot.d.ts +0 -29
  361. package/dist/sync/createSnapshot.js +0 -118
  362. package/dist/sync/credentialLifecycle.d.ts +0 -7
  363. package/dist/sync/credentialLifecycle.js +0 -7
  364. package/dist/sync/deltaPipeline.d.ts +0 -113
  365. package/dist/sync/deltaPipeline.js +0 -261
  366. package/dist/sync/groupChange.d.ts +0 -113
  367. package/dist/sync/groupChange.js +0 -242
  368. package/dist/sync/participants.d.ts +0 -115
  369. package/dist/sync/participants.js +0 -344
  370. package/dist/sync/persistedPrefix.d.ts +0 -12
  371. package/dist/sync/persistedPrefix.js +0 -22
  372. package/dist/sync/schemaDrift.d.ts +0 -55
  373. package/dist/sync/schemaDrift.js +0 -53
  374. package/dist/sync/schemas.d.ts +0 -70
  375. package/dist/sync/schemas.js +0 -94
  376. package/dist/sync/syncCursor.d.ts +0 -40
  377. package/dist/sync/syncCursor.js +0 -55
  378. package/dist/sync/syncPlan.d.ts +0 -54
  379. package/dist/sync/syncPlan.js +0 -50
  380. package/dist/sync/wsFrameHandlers.d.ts +0 -8
  381. package/dist/sync/wsFrameHandlers.js +0 -8
  382. package/dist/testing/fixtures/bootstrap.d.ts +0 -49
  383. package/dist/testing/fixtures/bootstrap.js +0 -59
  384. package/dist/testing/fixtures/deltas.d.ts +0 -83
  385. package/dist/testing/fixtures/deltas.js +0 -136
  386. package/dist/testing/fixtures/httpResponses.d.ts +0 -70
  387. package/dist/testing/fixtures/httpResponses.js +0 -90
  388. package/dist/testing/fixtures/models.d.ts +0 -83
  389. package/dist/testing/fixtures/models.js +0 -272
  390. package/dist/testing/helpers/reactWrapper.d.ts +0 -69
  391. package/dist/testing/helpers/reactWrapper.js +0 -67
  392. package/dist/testing/helpers/syncEngineHarness.d.ts +0 -54
  393. package/dist/testing/helpers/syncEngineHarness.js +0 -73
  394. package/dist/testing/helpers/wait.d.ts +0 -30
  395. package/dist/testing/helpers/wait.js +0 -49
  396. package/dist/testing/index.d.ts +0 -23
  397. package/dist/testing/index.js +0 -33
  398. package/dist/testing/mocks/FakeDatabase.d.ts +0 -18
  399. package/dist/testing/mocks/FakeDatabase.js +0 -10
  400. package/dist/testing/mocks/MockMutationExecutor.d.ts +0 -87
  401. package/dist/testing/mocks/MockMutationExecutor.js +0 -186
  402. package/dist/testing/mocks/MockNetworkMonitor.d.ts +0 -20
  403. package/dist/testing/mocks/MockNetworkMonitor.js +0 -46
  404. package/dist/testing/mocks/MockSyncContext.d.ts +0 -51
  405. package/dist/testing/mocks/MockSyncContext.js +0 -72
  406. package/dist/testing/mocks/MockSyncStore.d.ts +0 -88
  407. package/dist/testing/mocks/MockSyncStore.js +0 -171
  408. package/dist/testing/mocks/MockWebSocket.d.ts +0 -71
  409. package/dist/testing/mocks/MockWebSocket.js +0 -118
  410. package/dist/transaction/ablo.d.ts +0 -88
  411. package/dist/transaction/ablo.js +0 -33
  412. package/dist/transaction/auth/apiKey.d.ts +0 -152
  413. package/dist/transaction/auth/apiKey.js +0 -419
  414. package/dist/transaction/auth/bootstrapScope.d.ts +0 -15
  415. package/dist/transaction/auth/bootstrapScope.js +0 -1
  416. package/dist/transaction/auth/capability.d.ts +0 -177
  417. package/dist/transaction/auth/capability.js +0 -199
  418. package/dist/transaction/auth/credentialEndpoint.d.ts +0 -61
  419. package/dist/transaction/auth/credentialEndpoint.js +0 -86
  420. package/dist/transaction/auth/credentialPolicy.d.ts +0 -148
  421. package/dist/transaction/auth/credentialPolicy.js +0 -125
  422. package/dist/transaction/auth/credentialSource.d.ts +0 -30
  423. package/dist/transaction/auth/credentialSource.js +0 -55
  424. package/dist/transaction/auth/hostedEndpoints.d.ts +0 -21
  425. package/dist/transaction/auth/hostedEndpoints.js +0 -21
  426. package/dist/transaction/auth/identity.d.ts +0 -55
  427. package/dist/transaction/auth/identity.js +0 -210
  428. package/dist/transaction/auth/index.d.ts +0 -162
  429. package/dist/transaction/auth/index.js +0 -304
  430. package/dist/transaction/auth/schemas.d.ts +0 -59
  431. package/dist/transaction/auth/schemas.js +0 -85
  432. package/dist/transaction/auth/sessionMint.d.ts +0 -28
  433. package/dist/transaction/auth/sessionMint.js +0 -85
  434. package/dist/transaction/coordination/awaitClaimGrant.d.ts +0 -49
  435. package/dist/transaction/coordination/awaitClaimGrant.js +0 -112
  436. package/dist/transaction/coordination/claimHeartbeatLoop.d.ts +0 -50
  437. package/dist/transaction/coordination/claimHeartbeatLoop.js +0 -88
  438. package/dist/transaction/coordination/claimMeta.d.ts +0 -49
  439. package/dist/transaction/coordination/claimMeta.js +0 -52
  440. package/dist/transaction/coordination/createClaimStream.d.ts +0 -64
  441. package/dist/transaction/coordination/createClaimStream.js +0 -475
  442. package/dist/transaction/coordination/events.d.ts +0 -74
  443. package/dist/transaction/coordination/events.js +0 -7
  444. package/dist/transaction/coordination/index.d.ts +0 -19
  445. package/dist/transaction/coordination/index.js +0 -44
  446. package/dist/transaction/coordination/locator.d.ts +0 -83
  447. package/dist/transaction/coordination/locator.js +0 -82
  448. package/dist/transaction/coordination/schema.d.ts +0 -1473
  449. package/dist/transaction/coordination/schema.js +0 -1013
  450. package/dist/transaction/coordination/targetConflict.d.ts +0 -2
  451. package/dist/transaction/coordination/targetConflict.js +0 -103
  452. package/dist/transaction/coordination/trace.d.ts +0 -78
  453. package/dist/transaction/coordination/trace.js +0 -138
  454. package/dist/transaction/durableWrites.d.ts +0 -62
  455. package/dist/transaction/durableWrites.js +0 -71
  456. package/dist/transaction/environment.d.ts +0 -105
  457. package/dist/transaction/environment.js +0 -108
  458. package/dist/transaction/errorCodes.d.ts +0 -403
  459. package/dist/transaction/errorCodes.js +0 -480
  460. package/dist/transaction/errors.d.ts +0 -428
  461. package/dist/transaction/errors.js +0 -686
  462. package/dist/transaction/index.d.ts +0 -20
  463. package/dist/transaction/index.js +0 -20
  464. package/dist/transaction/keys/index.d.ts +0 -87
  465. package/dist/transaction/keys/index.js +0 -207
  466. package/dist/transaction/log/syncDeltaRow.d.ts +0 -158
  467. package/dist/transaction/log/syncDeltaRow.js +0 -95
  468. package/dist/transaction/logPosition.d.ts +0 -97
  469. package/dist/transaction/logPosition.js +0 -125
  470. package/dist/transaction/logger.d.ts +0 -16
  471. package/dist/transaction/logger.js +0 -7
  472. package/dist/transaction/observability.d.ts +0 -53
  473. package/dist/transaction/observability.js +0 -19
  474. package/dist/transaction/persistence.d.ts +0 -12
  475. package/dist/transaction/persistence.js +0 -11
  476. package/dist/transaction/plugin.d.ts +0 -192
  477. package/dist/transaction/plugin.js +0 -87
  478. package/dist/transaction/policy/types.d.ts +0 -217
  479. package/dist/transaction/policy/types.js +0 -126
  480. package/dist/transaction/resources/functionalUpdate.d.ts +0 -79
  481. package/dist/transaction/resources/functionalUpdate.js +0 -87
  482. package/dist/transaction/resources/httpResources.d.ts +0 -266
  483. package/dist/transaction/resources/httpResources.js +0 -7
  484. package/dist/transaction/resources/modelOperations.d.ts +0 -319
  485. package/dist/transaction/resources/modelOperations.js +0 -12
  486. package/dist/transaction/resources/mutationOptions.d.ts +0 -66
  487. package/dist/transaction/resources/mutationOptions.js +0 -9
  488. package/dist/transaction/resources/where.d.ts +0 -85
  489. package/dist/transaction/resources/where.js +0 -70
  490. package/dist/transaction/resources/writeOptionsSchema.d.ts +0 -47
  491. package/dist/transaction/resources/writeOptionsSchema.js +0 -73
  492. package/dist/transaction/schema/field.d.ts +0 -126
  493. package/dist/transaction/schema/field.js +0 -265
  494. package/dist/transaction/schema/loadStrategy.d.ts +0 -45
  495. package/dist/transaction/schema/loadStrategy.js +0 -46
  496. package/dist/transaction/schema/model.d.ts +0 -379
  497. package/dist/transaction/schema/model.js +0 -123
  498. package/dist/transaction/schema/openapi.d.ts +0 -57
  499. package/dist/transaction/schema/openapi.js +0 -340
  500. package/dist/transaction/schema/relation.d.ts +0 -199
  501. package/dist/transaction/schema/relation.js +0 -104
  502. package/dist/transaction/schema/residency.d.ts +0 -29
  503. package/dist/transaction/schema/residency.js +0 -25
  504. package/dist/transaction/schema/roles.d.ts +0 -249
  505. package/dist/transaction/schema/roles.js +0 -230
  506. package/dist/transaction/schema/schema.d.ts +0 -324
  507. package/dist/transaction/schema/schema.js +0 -305
  508. package/dist/transaction/schema/tenancy.d.ts +0 -139
  509. package/dist/transaction/schema/tenancy.js +0 -190
  510. package/dist/transaction/transactionLayer.d.ts +0 -82
  511. package/dist/transaction/transactionLayer.js +0 -24
  512. package/dist/transaction/transactions/settlement/commitEnvelope.d.ts +0 -143
  513. package/dist/transaction/transactions/settlement/commitEnvelope.js +0 -161
  514. package/dist/transaction/transactions/settlement/httpCommitEnvelope.d.ts +0 -53
  515. package/dist/transaction/transactions/settlement/httpCommitEnvelope.js +0 -207
  516. package/dist/transaction/transactions/settlement/idempotencyKey.d.ts +0 -10
  517. package/dist/transaction/transactions/settlement/idempotencyKey.js +0 -9
  518. package/dist/transaction/transactions/settlement/pendingWrite.d.ts +0 -112
  519. package/dist/transaction/transactions/settlement/pendingWrite.js +0 -20
  520. package/dist/transaction/transport/commitFrames.d.ts +0 -90
  521. package/dist/transaction/transport/commitFrames.js +0 -134
  522. package/dist/transaction/transport/connectionManager.d.ts +0 -215
  523. package/dist/transaction/transport/connectionManager.js +0 -673
  524. package/dist/transaction/transport/credentialLifecycle.d.ts +0 -177
  525. package/dist/transaction/transport/credentialLifecycle.js +0 -324
  526. package/dist/transaction/transport/heartbeat.d.ts +0 -65
  527. package/dist/transaction/transport/heartbeat.js +0 -93
  528. package/dist/transaction/transport/httpClient.d.ts +0 -123
  529. package/dist/transaction/transport/httpClient.js +0 -145
  530. package/dist/transaction/transport/httpOptions.d.ts +0 -33
  531. package/dist/transaction/transport/httpOptions.js +0 -12
  532. package/dist/transaction/transport/httpTransport.d.ts +0 -8
  533. package/dist/transaction/transport/httpTransport.js +0 -1276
  534. package/dist/transaction/transport/networkProbe.d.ts +0 -84
  535. package/dist/transaction/transport/networkProbe.js +0 -207
  536. package/dist/transaction/transport/wsFrameHandlers.d.ts +0 -128
  537. package/dist/transaction/transport/wsFrameHandlers.js +0 -429
  538. package/dist/transaction/transport/wsTransport.d.ts +0 -576
  539. package/dist/transaction/transport/wsTransport.js +0 -1017
  540. package/dist/transaction/types/assertExact.d.ts +0 -17
  541. package/dist/transaction/types/assertExact.js +0 -1
  542. package/dist/transaction/types/global.d.ts +0 -107
  543. package/dist/transaction/types/global.js +0 -40
  544. package/dist/transaction/types/index.d.ts +0 -205
  545. package/dist/transaction/types/index.js +0 -56
  546. package/dist/transaction/types/modelData.d.ts +0 -10
  547. package/dist/transaction/types/modelData.js +0 -9
  548. package/dist/transaction/types/participant.d.ts +0 -20
  549. package/dist/transaction/types/participant.js +0 -10
  550. package/dist/transaction/types/streams.d.ts +0 -540
  551. package/dist/transaction/types/streams.js +0 -11
  552. package/dist/transaction/utils/asyncIterator.d.ts +0 -34
  553. package/dist/transaction/utils/asyncIterator.js +0 -135
  554. package/dist/transaction/utils/duration.d.ts +0 -25
  555. package/dist/transaction/utils/duration.js +0 -45
  556. package/dist/transaction/utils/json.d.ts +0 -57
  557. package/dist/transaction/utils/json.js +0 -276
  558. package/dist/transaction/wire/accountResponses.d.ts +0 -351
  559. package/dist/transaction/wire/accountResponses.js +0 -255
  560. package/dist/transaction/wire/auth.d.ts +0 -49
  561. package/dist/transaction/wire/auth.js +0 -57
  562. package/dist/transaction/wire/bootstrapReason.d.ts +0 -9
  563. package/dist/transaction/wire/bootstrapReason.js +0 -8
  564. package/dist/transaction/wire/claimEvent.d.ts +0 -76
  565. package/dist/transaction/wire/claimEvent.js +0 -73
  566. package/dist/transaction/wire/claims.d.ts +0 -463
  567. package/dist/transaction/wire/claims.js +0 -229
  568. package/dist/transaction/wire/commit.d.ts +0 -603
  569. package/dist/transaction/wire/commit.js +0 -321
  570. package/dist/transaction/wire/delta.d.ts +0 -250
  571. package/dist/transaction/wire/delta.js +0 -147
  572. package/dist/transaction/wire/errorEnvelope.d.ts +0 -72
  573. package/dist/transaction/wire/errorEnvelope.js +0 -123
  574. package/dist/transaction/wire/feedCursor.d.ts +0 -60
  575. package/dist/transaction/wire/feedCursor.js +0 -82
  576. package/dist/transaction/wire/feedEvent.d.ts +0 -177
  577. package/dist/transaction/wire/feedEvent.js +0 -39
  578. package/dist/transaction/wire/frames.d.ts +0 -194
  579. package/dist/transaction/wire/frames.js +0 -50
  580. package/dist/transaction/wire/inboundFrames.d.ts +0 -552
  581. package/dist/transaction/wire/inboundFrames.js +0 -116
  582. package/dist/transaction/wire/index.d.ts +0 -50
  583. package/dist/transaction/wire/index.js +0 -74
  584. package/dist/transaction/wire/listEnvelope.d.ts +0 -37
  585. package/dist/transaction/wire/listEnvelope.js +0 -42
  586. package/dist/transaction/wire/modelResponses.d.ts +0 -85
  587. package/dist/transaction/wire/modelResponses.js +0 -43
  588. package/dist/transaction/wire/protocol.d.ts +0 -38
  589. package/dist/transaction/wire/protocol.js +0 -38
  590. package/dist/transaction/wire/protocolVersion.d.ts +0 -73
  591. package/dist/transaction/wire/protocolVersion.js +0 -83
  592. package/dist/transactions/mutations/MutationQueue.d.ts +0 -655
  593. package/dist/transactions/mutations/MutationQueue.js +0 -2797
  594. package/dist/transactions/mutations/MutationStore.d.ts +0 -20
  595. package/dist/transactions/mutations/MutationStore.js +0 -53
  596. package/dist/transactions/mutations/UnconfirmedWrites.d.ts +0 -82
  597. package/dist/transactions/mutations/UnconfirmedWrites.js +0 -104
  598. package/dist/transactions/mutations/coalesceRules.d.ts +0 -58
  599. package/dist/transactions/mutations/coalesceRules.js +0 -140
  600. package/dist/transactions/mutations/commitLatency.d.ts +0 -52
  601. package/dist/transactions/mutations/commitLatency.js +0 -130
  602. package/dist/transactions/mutations/commitOutboxStore.d.ts +0 -28
  603. package/dist/transactions/mutations/commitOutboxStore.js +0 -26
  604. package/dist/transactions/mutations/commitPayload.d.ts +0 -164
  605. package/dist/transactions/mutations/commitPayload.js +0 -152
  606. package/dist/transactions/mutations/deltaConfirmation.d.ts +0 -59
  607. package/dist/transactions/mutations/deltaConfirmation.js +0 -233
  608. package/dist/transactions/mutations/durableWriteStore.d.ts +0 -14
  609. package/dist/transactions/mutations/durableWriteStore.js +0 -12
  610. package/dist/transactions/mutations/optimisticApply.d.ts +0 -49
  611. package/dist/transactions/mutations/optimisticApply.js +0 -65
  612. package/dist/transactions/mutations/replayValidation.d.ts +0 -186
  613. package/dist/transactions/mutations/replayValidation.js +0 -163
  614. package/dist/utils/mobxSetup.d.ts +0 -53
  615. package/dist/utils/mobxSetup.js +0 -330
  616. package/dist/webhooks/events.d.ts +0 -43
  617. package/dist/webhooks/events.js +0 -42
  618. package/dist/webhooks/index.d.ts +0 -8
  619. package/dist/webhooks/index.js +0 -8
  620. package/dist/wire/index.d.ts +0 -1
  621. package/dist/wire/index.js +0 -8
  622. package/docs/interaction-model.md +0 -99
@@ -0,0 +1,91 @@
1
+ # Commit identifiers: the two axes
2
+
3
+ Maintainer reference. A commit carries several ids, and they are easy to
4
+ conflate because they travel together and are all "some number attached to a
5
+ write." They are not interchangeable. Each answers a different question, and
6
+ they split cleanly along **one line**: does this id help decide whether the
7
+ write *wins*, or does it only help *identify* the write after the fact?
8
+
9
+ Keeping the two axes separate is what lets each id be reasoned about — and
10
+ audited — on its own. This doc is the single place they sit side by side.
11
+
12
+ ## The line
13
+
14
+ | axis | the question it answers | when it acts | if it's absent |
15
+ |---|---|---|---|
16
+ | **Conflict resolution** | *should this write land, given what else happened?* | at the commit chokepoint, before the row is written | the write is unguarded (last-writer-wins) |
17
+ | **Correlation / audit** | *which write is this, and have I seen it before?* | on receipt (dedup) and after the fact (attribution) | the write still lands; you just can't dedup or trace it as precisely |
18
+
19
+ A conflict-resolution id can **reject** a commit. A correlation id never does —
20
+ at most it makes a retried commit a no-op (idempotency). Never reach for one to
21
+ do the other's job: a correlation id can't fence a stale write, and a fence
22
+ can't dedup a retry.
23
+
24
+ ## Axis 1: conflict resolution (does this write win?)
25
+
26
+ Evaluated inside `executeCommit`'s transaction, atomic with the delta write.
27
+ Three independent fences, each catching what the others can't; the full
28
+ narrative is [ADR 0009 §6](../../../../docs/decisions/0009-claim-durability-two-reclaim-clocks.md)
29
+ and the [coordination reference](../coordination.md).
30
+
31
+ | id | wire field | persisted to | what it asserts | rejects when |
32
+ |---|---|---|---|---|
33
+ | **read basis** | per-op `readAt` | `sync_deltas.read_at_sync_id` | "the state I reasoned **from**": a version watermark | the row moved since `readAt` (version-CAS), under `onStale: 'reject'` |
34
+ | **fencing token** | per-op `fenceToken` | `sync_deltas.fence_token` **and** `claim_fence_watermark.fence_token` | "the lease generation I was authorized **at**": a monotonic per-entity high-water | the token is below the entity's persisted high-water: a lapsed holder writing after its successor already claimed, wrote, and released |
35
+ | **claim / lease** | `claimId`, `heldBy` on the `WireClaim` | the coordination store (Redis), not `sync_deltas` | "I hold this row right now": live mutual exclusion | a non-holder writes a row another participant holds |
36
+
37
+ `onStale` (`notify` / `reject` / `overwrite`) is **not** an id — it's the
38
+ disposition that decides what a stale `readAt` *does*. It rides with the read
39
+ basis but is policy, not evidence, so it isn't persisted.
40
+
41
+ Why the token is a distinct id from `readAt`, and not just reused `sync_id`:
42
+ `readAt` advances on every **write** and asserts *from what data*; the token
43
+ advances on every **grant** and asserts *at what lease generation*. Their events
44
+ differ, so one can't stand in for the other — a lapsed holder that skips
45
+ version-CAS (no `readAt`, a blind write) is invisible to the read basis but
46
+ still carries a stale token. That is precisely fence (c) closing what (a) can't.
47
+ The reasoning in full lives in
48
+ [the fencing-token scope doc](../../../../docs/plans/claim-fencing-token-option-b-scope.md).
49
+
50
+ ## Axis 2: correlation / audit (which write is this?)
51
+
52
+ Never decides a conflict. These are how a write is recognized — as a duplicate,
53
+ as your own echo, or as one row in a signed history.
54
+
55
+ | id | wire field | persisted to | purpose |
56
+ |---|---|---|---|
57
+ | **idempotency key** | batch `clientTxId` (public alias `idempotencyKey`) | dedup ledger keyed by it | a retried batch commits **once**: the second attempt is recognized and folded to a no-op, not re-applied |
58
+ | **per-op transaction id** | per-op `transactionId` | `sync_deltas.transaction_id` | echo detection: the broadcast delta arrives at the originating client carrying the **same** id its queue marked pending, so it reconciles its optimistic write instead of double-applying |
59
+ | **sync id** | assigned server-side (`next_sync_id`) | `sync_deltas.id` | the monotonic total order: the serialization order every reader tails and every `readAt` names. It is *assigned*, never client-supplied |
60
+ | **attribution** | actor / capability / delegation on the frame | `sync_deltas` actor columns + the signed audit chain | who acted, on whose behalf, under which key: the [audit log](../audit.md)'s who/when |
61
+
62
+ The batch key and the per-op id are deliberately separate: a multi-row commit is
63
+ **one** idempotent unit (one `clientTxId`) made of **many** individually-echoable
64
+ ops (each its own `transactionId`). Collapsing them would make echo detection
65
+ batch-coarse and break optimistic reconciliation for multi-op commits.
66
+
67
+ ## The evidence tuple on a `sync_deltas` row
68
+
69
+ Both axes leave their mark on the delta, which is what makes a delta a complete,
70
+ self-describing audit record — you can reconstruct the full justification of a
71
+ write from the row alone, never from a live lease that has since vanished:
72
+
73
+ - **who:** `actor_id` / `capability_id` (+ the signed chain)
74
+ - **what:** `data` / `previous_data`
75
+ - **when:** `id` (`sync_id`) / `created_at`
76
+ - **from what known state:** `read_at_sync_id` (the read basis)
77
+ - **at what lease generation:** `fence_token` (the token the commit fenced)
78
+ - **as which client operation:** `transaction_id` (echo identity)
79
+
80
+ `read_at_sync_id` and `fence_token` are companions: the first records the data
81
+ version the write reasoned against, the second the lease generation it was
82
+ authorized at. Both are `NULL` when the write carried none (an unclaimed write, a
83
+ human `user` committer exempt under Law 7, or a legacy row) — **never fabricated
84
+ server-side**. The evidence derives only from what the write actually presented,
85
+ so the audit row can't drift from what the fence enforced.
86
+
87
+ ## One-line test for "which id is this?"
88
+
89
+ > If removing it could turn an accepted commit into a rejected one, it's
90
+ > **Axis 1**. If removing it only costs you dedup, echo reconciliation, or
91
+ > traceability — while the write still lands — it's **Axis 2**.
@@ -0,0 +1,37 @@
1
+ # Concurrency Convention: Open Decisions
2
+
3
+ Maintainer notes for [`concurrency-convention.md`](../concurrency-convention.md).
4
+ These are decisions the team has deliberately not made yet. They change public
5
+ behaviour, so they are tracked here rather than in the public contract, where an
6
+ unmade decision reads as an unsettled guarantee.
7
+
8
+ ## Default disposition for agents
9
+
10
+ Should an agent-participant guarded write default to `notify` (philosophy
11
+ aligned: surface, do not overwrite) instead of `reject` (back-compat)?
12
+
13
+ The trade-off is alignment against a behaviour change for existing agent
14
+ callers. Today a guarded write with `readAt` but no `onStale` defaults to
15
+ `reject` for every participant kind.
16
+
17
+ ## Batch premises through the policy seam
18
+
19
+ Should premise conflicts also pass through `ConflictPolicy`, or stay on the
20
+ direct `onStale` mapping?
21
+
22
+ Routing them through the seam requires a group-aware conflict shape, because a
23
+ batch premise can name a sync group rather than a row. Custom `ConflictPolicy`
24
+ functions currently see write-target conflicts only (`stale_context` /
25
+ `claim_held`); batch-premise conflicts resolve directly through each entry's
26
+ `onStale`.
27
+
28
+ ## The serializability floor
29
+
30
+ A batch premise is a sound check, not a full precedence-graph guarantee: it
31
+ catches only what the caller declared. A caller that declares nothing gets no
32
+ check at all, because write-target checking needs a `readAt` to check against,
33
+ and a plain write is last-writer-wins.
34
+
35
+ The floor is therefore zero, and closing that gap is the subject of ADR 0018.
36
+ The public page states the resulting behaviour as a limit; the framing of it as
37
+ a gap to be closed belongs here.
@@ -0,0 +1,150 @@
1
+ # Data Source Reverse Channel (local-dev parity)
2
+
3
+ Maintainer scoping doc. Closes the one real day-one DX gap in Data Source
4
+ mode: the `commit`/`load`/`list` legs are inbound webhooks (Ablo → your
5
+ endpoint), so on `localhost` they need a tunnel (ngrok/cloudflared). This
6
+ scopes a built-in reverse channel so Data Source works on localhost the way
7
+ managed mode already does — the Stripe-CLI `stripe listen` pattern.
8
+
9
+ ## The gap, precisely
10
+
11
+ Data Source has two directions today:
12
+
13
+ | Leg | Direction | Transport(s) today | localhost-friendly? |
14
+ |---|---|---|---|
15
+ | `events` (external writes → Ablo) | customer → Ablo | **poll** (`events` handler, Ablo calls you) **+ push** (`createPushQueue` → `POST /api/source/events`, you call Ablo) | ✅ yes, via push |
16
+ | `commit` / `load` / `list` | Ablo → customer | **inbound webhook only** (`dataSource()` route) | ❌ no: needs public URL |
17
+
18
+ The asymmetry is the whole bug. `events` already ships an outbound transport
19
+ (`src/source/pushQueue.ts`), so external writes reach Ablo from localhost
20
+ without a tunnel. The `commit`/`load`/`list` leg never got one, so Ablo Cloud
21
+ has no way to reach a `localhost:3000` dev server.
22
+
23
+ Inbound is exactly what localhost cannot receive. Managed mode has no inbound
24
+ leg (the browser/SDK opens the only connection, outbound to Ablo), which is why
25
+ managed mode "just works" locally and Data Source doesn't.
26
+
27
+ ## Prior art
28
+
29
+ - **Stripe CLI `stripe listen`:** the canonical fix. The CLI opens an
30
+ *outbound* WebSocket to Stripe; Stripe drains webhook events down it and the
31
+ CLI forwards them to `localhost`. No public URL, no tunnel. We want the same
32
+ for the `commit`/`load`/`list` leg.
33
+ - **Our own `createPushQueue`:** already proves the outbound-from-customer
34
+ pattern for the `events` leg. The reverse channel is the symmetric primitive
35
+ for the other direction.
36
+
37
+ ## Design
38
+
39
+ A customer-run **source connector** dials out to Ablo Cloud and serves
40
+ `commit`/`load`/`list` over the open connection, instead of Ablo making inbound
41
+ HTTP calls.
42
+
43
+ ```
44
+ LOCAL DEV (no public URL)
45
+
46
+ ablo client ──ws──▶ Ablo Cloud ──┐
47
+ │ (no inbound HTTP to localhost)
48
+ customer connector ──ws (dial-out)──▶ Ablo Cloud
49
+ │ drains pending commit/load/list for this source
50
+
51
+ dataSource(options) ← UNCHANGED handler, fed a synthesized Request
52
+
53
+ local Postgres
54
+ │ signed response posted back up the same ws
55
+ └──────────────────────────────────────▶ Ablo Cloud ──ws──▶ ablo client
56
+ ```
57
+
58
+ ### Why it's small
59
+
60
+ `dataSource(options)` is already `(request: Request) => Promise<Response>` in
61
+ `src/source/factory.ts`. The connector does not reimplement any handler logic —
62
+ it:
63
+
64
+ 1. Opens a WS to a new Ablo Cloud endpoint (e.g. `/v1/source/listen`),
65
+ authenticating with the project API key.
66
+ 2. Registers which source/org it serves. Ablo Cloud routes that source's
67
+ `commit`/`load`/`list` requests to this socket instead of the configured
68
+ webhook URL (when a live connector is attached).
69
+ 3. For each drained request frame: synthesize a `Request` with the same signed
70
+ headers Ablo would have sent, call the customer's existing `dataSource`
71
+ handler, and post the `Response` back up the socket.
72
+
73
+ Customer-side surface is one wrapper around the handler they already wrote:
74
+
75
+ ```ts
76
+ // dev only — same handler object as the deployed route
77
+ import { dataSource, createSourceConnector } from '@abloatai/ablo';
78
+ import { sourceOptions } from './ablo.source'; // shared with route.ts
79
+
80
+ const connector = createSourceConnector({
81
+ apiKey: process.env.ABLO_API_KEY!, // sk_test_*
82
+ handler: dataSource(sourceOptions), // the unchanged (Request)=>Response
83
+ });
84
+ await connector.run(abortSignal);
85
+ ```
86
+
87
+ `route.ts` (deployed) and the connector (local) share the same
88
+ `sourceOptions` — zero handler drift.
89
+
90
+ ### Server side (sync-server)
91
+
92
+ - New WS endpoint `/v1/source/listen`. Auth: project API key → resolves the
93
+ source. Reject if the key isn't `sk_test_*` unless the source explicitly
94
+ opts into reverse-channel for production (see "Production" below).
95
+ - Per-source request queue. When a `commit`/`load`/`list` needs the customer
96
+ and a connector is attached, enqueue + drain down the socket instead of
97
+ POSTing the webhook URL. Reuse the same signed-envelope shape so the
98
+ customer handler verifies identically (`verifyAbloSourceRequest` unchanged).
99
+ - Fallback: no connector attached → existing inbound webhook path. The
100
+ reverse channel is purely additive; nothing changes for deployed apps.
101
+
102
+ ### Signature / security
103
+
104
+ - The drained frames carry the **same** Standard Webhooks signature
105
+ (`webhook-id`/`webhook-timestamp`/`webhook-signature`) computed with the
106
+ project key, so the connector verifies them through the existing
107
+ `verifyAbloSourceRequest` with no special-casing. The transport changes; the
108
+ trust model does not.
109
+ - Gate to `sk_test_*` by default. The DB still stays canonical in the
110
+ customer's process; nothing here gives Ablo the `DATABASE_URL`.
111
+
112
+ ## Test-mode interplay
113
+
114
+ `SourceRequestContext.mode` (`src/source/types.ts`) already distinguishes
115
+ `test`/`live`. The reverse channel is the natural home for `mode: 'test'`
116
+ traffic: a local connector attached with an `sk_test_*` key receives the
117
+ source's test commits, runs them against the customer's test DB, and the SDK
118
+ sees confirmed rows + fan-out exactly as in production. This is the missing
119
+ piece that makes `sk_test_*` a complete local loop rather than just a data
120
+ namespace.
121
+
122
+ ## Production stance
123
+
124
+ Keep the inbound webhook as the default deployed transport — it's lower
125
+ latency (no long-lived socket to babysit) and stateless. The reverse channel
126
+ is primarily the **dev** affordance. A secondary, opt-in use is a
127
+ "no-public-URL deploy" mode for customers who cannot expose an inbound
128
+ endpoint at all (locked-down VPCs); that's a follow-on, not the initial scope.
129
+
130
+ ## Scope boundary (what this is NOT)
131
+
132
+ - Not a generic tunnel — it forwards only signed Ablo source frames for one
133
+ source, not arbitrary traffic.
134
+ - Not a change to the handler contract — `dataSource` is untouched; the
135
+ connector wraps its handler.
136
+ - Not a managed-mode change — managed mode has no inbound leg and is unaffected.
137
+
138
+ ## Touch list (when built)
139
+
140
+ - `packages/transaction/src/source/connector.ts` — `createSourceConnector`
141
+ (dial-out WS client; synthesize Request → existing handler → post Response).
142
+ - `packages/transaction` export surface — expose `createSourceConnector` next to
143
+ `createPushQueue`.
144
+ - `apps/sync-server` — `/v1/source/listen` WS endpoint + per-source request
145
+ queue + "drain to connector if attached, else webhook" branch in the source
146
+ dispatch path.
147
+ - `docs/data-sources.md` — document the local-dev loop (the current docs only
148
+ describe the public-HTTPS webhook).
149
+ - Tests: connector round-trip (drained commit → handler → response), signature
150
+ parity with the webhook path, fallback-to-webhook when no connector attached.
@@ -0,0 +1,165 @@
1
+ # Per-Field Conflict Detection (Track A)
2
+
3
+ Maintainer decision doc. Scopes the move from entity-level to field-level stale
4
+ detection in `executeCommit`. Library-free; restores Linear parity for the
5
+ disjoint-field case while keeping the agent-specific `readAt` reject.
6
+
7
+ ## Problem
8
+
9
+ `executeCommit` Step 0 (`apps/sync-server/src/mutators/commit.ts`) detects stale
10
+ writes at **entity granularity**:
11
+
12
+ ```sql
13
+ SELECT MAX(id) FROM sync_deltas WHERE model_name = ? AND model_id = ?
14
+ ```
15
+
16
+ If `observed > op.readAt`, the op conflicts. This means two writers touching
17
+ **different fields** of the same row collide falsely: agent A sets
18
+ `report.status`, human B sets `report.reviewer`, B carried a `readAt` from before A's
19
+ write → B is rejected with `AbloStaleContextError`, even though the edits never
20
+ overlapped.
21
+
22
+ This is an over-rejection. It is also stricter than the system we
23
+ reverse-engineered from (Linear), which never had this problem.
24
+
25
+ ## Why Linear says this is right
26
+
27
+ The model is reverse-engineered from Linear's sync engine. Linear's design
28
+ confirms every load-bearing choice here:
29
+
30
+ - **Transactions are property-level.** Linear's `UpdateTransaction` records
31
+ "the name of the changed property and its previous value" — it carries only
32
+ the changed properties, not a whole-object snapshot. Our `changed_fields`
33
+ column re-derives exactly that.
34
+ - **Resolution is last-writer-wins, per property, by total order:** `syncId`
35
+ (our `sync_id_seq`) is the total order. A partial transaction applies only its
36
+ properties, so two clients editing **different** properties both win — LWW
37
+ only bites on the **same** property.
38
+ - **CRDT is used only for issue descriptions.** Linear keeps LWW-per-property
39
+ for structured fields and reserves a CRDT for the one rich-text body. That is
40
+ the same Track A / Track B line we draw: this doc is Track A; rich-text bodies
41
+ (TipTap `content_json`) are out of scope and belong to a separate CRDT track.
42
+
43
+ So Track A is not a new feature — it **restores Linear parity** at the property
44
+ level we had flattened to entity level.
45
+
46
+ Sources: [reverse-linear-sync-engine (CTO-endorsed)](https://github.com/wzhudev/reverse-linear-sync-engine/blob/main/SUMMARY.md),
47
+ [Architectures for Central Server Collaboration — Weidner](https://mattweidner.com/2024/06/04/server-architectures.html).
48
+
49
+ ## The constraint that shapes the design
50
+
51
+ `sync_deltas.data` stores the **full post-update row**, not the changed columns.
52
+ This was a deliberate change (see `feedback_partial_update_delta_ui_drift`) so
53
+ the live-pool update path fires MobX reactivity for nested fields. Consequence:
54
+ we **cannot** recover "which fields did this delta change" from `data` — a
55
+ full-row snapshot does not tell you what moved.
56
+
57
+ We do have the changed set for free at write time: `Object.keys(snakeInput)` at
58
+ `commit.ts` UPDATE branch, after the unknown-column strip and before the
59
+ `updated_at` injection. So this is a write-side capture + a read-side
60
+ intersection, not a diff-the-snapshots problem.
61
+
62
+ ## Design
63
+
64
+ ### 1. Schema: `sync_deltas.changed_fields text[]` (nullable)
65
+
66
+ - Populate **only for UPDATE** with the real changed columns
67
+ (`Object.keys(snakeInput)` after strip, before `updated_at`).
68
+ - Leave `null` for CREATE / DELETE / ARCHIVE / UNARCHIVE.
69
+
70
+ `null` is semantically "whole-entity change" and always conflicts. This gives a
71
+ **safe migration**: every pre-migration delta is `null`, so detection falls back
72
+ to exactly today's entity-level behavior. Field granularity phases in only as
73
+ new deltas land — no risky backfill.
74
+
75
+ We store **field names only**, not previous values. LWW needs no value
76
+ comparison (latest `sync_id` wins); names are sufficient for the overlap check
77
+ that drives the optional reject. Prev-values would only matter for
78
+ "same field, same value ⇒ not a conflict" tie-breaking — defer it.
79
+
80
+ ### 2. Detection rewrite: Step 0 (`commit.ts`)
81
+
82
+ Replace the scalar `MAX(id)` with a field-aware scan:
83
+
84
+ ```ts
85
+ // op's own field set (snake-cased, framework cols excluded)
86
+ const opFields = new Set(Object.keys(op.input ?? {}).map(toSnakeCase));
87
+
88
+ const rows = await tx.unsafe(
89
+ `SELECT id, changed_fields FROM sync_deltas
90
+ WHERE model_name = $1 AND model_id = $2 AND id > $3
91
+ ORDER BY id DESC`,
92
+ [mapping.modelName, op.id, op.readAt] as never[],
93
+ );
94
+
95
+ // Conflict iff a newer delta touched a field this op also writes,
96
+ // OR a newer delta is whole-entity (changed_fields IS NULL → CREATE/DELETE).
97
+ const overlap = rows.find(
98
+ (r) => r.changedFields === null || r.changedFields.some((f) => opFields.has(f)),
99
+ );
100
+ if (overlap) {
101
+ conflicts.push({
102
+ /* ...existing fields... */
103
+ observedSyncId: overlap.id,
104
+ conflictingFields: intersect(overlap.changedFields, opFields),
105
+ });
106
+ }
107
+ ```
108
+
109
+ Disjoint-field concurrent writes now produce **no conflict** — they both apply.
110
+ That is LWW-per-field achieved by *not rejecting*; no merge code.
111
+
112
+ ### 3. Policy type: additive (`packages/transaction/src/policy/types.ts`)
113
+
114
+ Extend `StaleContextConflict` with:
115
+
116
+ ```ts
117
+ readonly conflictingFields?: readonly string[];
118
+ ```
119
+
120
+ Pure addition. `defaultPolicy` still rejects; existing policies compile
121
+ unchanged. A policy can now reason at field granularity, e.g. allow when the
122
+ only conflicting field is cosmetic.
123
+
124
+ ### 4. Scope boundary (honest)
125
+
126
+ Granularity is **column-level**, not JSON-path. Two writers editing different
127
+ keys *inside* one `content_json` column still conflict — that is the rich-text
128
+ case (Track B / CRDT), not this. JSON Merge Patch (RFC 7386) sub-column
129
+ granularity is a later refinement on the same column; v1 stops at columns.
130
+
131
+ ## Relationship to the existing `readAt` reject
132
+
133
+ Linear is pure LWW-per-property with no stale check. We keep `readAt` /
134
+ `onStale: 'reject'` as an **opt-in** for the agent-reasoned-against-stale-state
135
+ case (an LLM that read a stale value and reasoned on it is a real failure mode
136
+ humans rarely hit). After Track A:
137
+
138
+ - **No `readAt`** → LWW-per-field, Linear parity: disjoint fields never conflict.
139
+ - **`readAt` set** → reject only if a newer delta touched a field this op also
140
+ writes. The `changed_fields` column makes that intersection computable.
141
+ - **`onStale: 'overwrite'`** → unchanged; still skips detection entirely.
142
+
143
+ ## Index
144
+
145
+ Verify a `(model_name, model_id, id)` index exists on `sync_deltas` (the old
146
+ `MAX(id)` relied on it too). The new query is a bounded range scan (`id >
147
+ readAt`, usually a small recent window) on the same index prefix.
148
+
149
+ ## Tests (vitest, sync-server)
150
+
151
+ - Disjoint fields (A: `status`, B: `assignee`, same row, both stale `readAt`) →
152
+ **both commit, no `AbloStaleContextError`** — the regression that proves the win.
153
+ - Same field, both stale → still rejects (default policy unchanged).
154
+ - DELETE after `readAt` → conflicts regardless of op fields (`null`).
155
+ - Pre-migration delta (`null`) in the window → conflicts (back-compat).
156
+ - `onStale: 'overwrite'` → still skips detection.
157
+
158
+ ## Touch list
159
+
160
+ - `sync_deltas` migration — add `changed_fields text[]`.
161
+ - `commit.ts` — Step 0 detect rewrite + UPDATE write path populates
162
+ `changed_fields` + `deltaInfos` shape.
163
+ - `apps/sync-server/src/db/deltas.ts` — insert path carries `changed_fields`.
164
+ - `packages/transaction/src/policy/types.ts` — additive `conflictingFields`.
165
+ - vitest in sync-server.
@@ -0,0 +1,64 @@
1
+ # Postgres replication: internal architecture
2
+
3
+ > **Status: wired and covered by unit and real-Postgres journeys.** This is server-internal code under `apps/sync-server/src/replication/postgres/`; it is not an SDK surface.
4
+
5
+ ## Why this exists
6
+
7
+ Ablo observes a customer's Postgres through a publication and logical-replication slot. The customer owns the schema and write path. Ablo decodes committed changes, appends them to its control-plane log, and serves sync from that log. The low-level decoder and lifecycle draw on Zero and PowerSync patterns; ADR 0002 governs the product boundary.
8
+
9
+ ## The one job
10
+
11
+ **Postgres `pgoutput` messages → `PreparedDelta[]` + a confirmed LSN.** The consumer writes through `appendExternalDeltas`; deltas land in the control-plane `sync_deltas` log and use the normal fan-out pipeline.
12
+
13
+ ## Module map (`apps/sync-server/src/replication/postgres/`)
14
+
15
+ | file | role | provenance |
16
+ | -------------------------------------------------------- | ---------------------------------------------------------- | ----------------------------- |
17
+ | `binaryReader.ts` | big-endian protocol reader | modeled on Zero |
18
+ | `pgoutputTypes.ts`, `pgoutput.ts` | typed `pgoutput` messages and decoder | modeled on Zero |
19
+ | `lsn.ts` | `LSN` string ↔ `bigint` (`toBigInt`/`fromBigInt`) | ported subset ← Zero `lsn.ts` |
20
+ | `connection.ts`, `stream.ts`, `streamAdapter.ts` | dedicated query/replication connections and stream adapter | Zero/PowerSync patterns |
21
+ | `assembler.ts` | buffers a transaction and maps changes to `PreparedDelta` | Ablo adapter |
22
+ | `consumer.ts` | persist-before-ack consume loop with retry | PowerSync/Ablo patterns |
23
+ | `slot.ts`, `slotLease.ts`, `backfill.ts`, `watermark.ts` | slot ownership, initial snapshot, and durable progress | Ablo |
24
+ | `sources.ts`, `fleet.ts`, `start.ts` | registry resolution, reconciliation, start/stop lifecycle | Ablo |
25
+ | `preflight.ts`, `readiness.ts`, `publicationDrift.ts` | registration checks and runtime diagnostics | Ablo |
26
+
27
+ ## Data flow
28
+
29
+ ```
30
+ registered Postgres source
31
+ → replication slot + initial snapshot
32
+ → pgoutput stream
33
+ → TransactionAssembler
34
+ → WalConsumer
35
+ → appendExternalDeltas(controlSql, deltas, context)
36
+ → persist watermark
37
+ → acknowledge commit LSN
38
+ ```
39
+
40
+ ### Mapping (in `TransactionAssembler`, mirrors `events.ts:eventsToDeltas`)
41
+
42
+ - `actionType`: `insert→'I'`, `update→'U'`, `delete→'D'` (the 1:1 pgoutput↔Ablo coincidence).
43
+ - `modelName`: `mapping.tableToModel(schema, table)` — `null` skips the change.
44
+ - `modelId`: `mapping.rowToModelId(key)` over the replica-identity key.
45
+ - `data`: the row bound as an **object, never pre-stringified** (the jsonb double-encode trap at `deltaAppend.ts`).
46
+ - `transactionId`: `String(xid)`.
47
+
48
+ ## Load-bearing invariants
49
+
50
+ - **Persist-before-ack** (`WalConsumer`): `appendExternalDeltas` and the watermark transaction resolve before `ack(commitLsn)`. A crash between them replays work instead of losing it.
51
+ - **Keepalive watermark** (`streamAdapter.ts`, `stream.ts`): every reply carries the last confirmed LSN, never the server's live position — the timed status update included.
52
+ - **Liveness off the socket** (`stream.ts`): a status update goes out every 75% of the upstream's `wal_sender_timeout` whether or not the consumer is reading, so backpressure cannot get the connection terminated for silence; inbound silence on a stream we are reading for twice that long destroys it and falls into the per-source backoff. A `wal_sender_timeout` of 0 runs untimed.
53
+ - **Failover-capable slot** (`slot.ts`): from PostgreSQL 17 the slot is created with `FAILOVER true`, so a customer failover leaves our position intact instead of forcing the re-snapshot path. It only takes effect where the standby has `sync_replication_slots = on`, which the preflight recommends and never requires.
54
+ - **Fresh subscription per retry** (`WalConsumer`): every backoff iteration opens a new subscription so a half-dead socket / stale relation cache never carries into the retry.
55
+ - **Per-source isolation** (`start.ts`, `fleet.ts`): one broken source reports and retries without stopping healthy sources.
56
+ - **Runtime reconciliation** (`fleet.ts`): registrations, removals, schema changes, and secret rotations converge without a server restart.
57
+
58
+ ## Tests
59
+
60
+ Unit tests live beside the implementation in `replication/postgres/__tests__`. Real-Postgres coverage is grouped under `src/__journeys__/postgres-replication-*.journey.test.ts`: registration, registry migration, backfill, live streaming, source changes, bootstrap, query serving, read cutover, and customer-database isolation.
61
+
62
+ ## Operations
63
+
64
+ Registration is the enable signal. `startPostgresReplication` starts the fleet after the server begins listening; `postgresReplicationReady` is drained during graceful shutdown. Use `docs/runbooks/connect-customer-database-postgres-replication.md` for source setup and live verification.
@@ -0,0 +1,119 @@
1
+ # A `Schema` is serializable
2
+
3
+ A `Schema` (output of `defineSchema`) is JSON-serializable except for two
4
+ things, both of which are client-only:
5
+
6
+ - **Zod validators:** `model().schema` / `.shape`, `Schema.validators`. Used
7
+ by the client for type inference + validation. The server never reads them
8
+ (it checks `information_schema.columns` and does no field-shape validation in
9
+ the commit path).
10
+
11
+ Everything the server reads — `typename`, `tableName`, `mutable`, `load`, the
12
+ canonical `tenancy` descriptor (the `policy` authoring option is normalized away
13
+ at build), bootstrap hints, `relations` (`foreignKeyColumn`), field names, and
14
+ `identityRoles` — is plain data.
15
+
16
+ ## Identity roles are pure data
17
+
18
+ ```ts
19
+ interface IdentityRole {
20
+ kind: string;
21
+ template: string; // 'org:{id}'
22
+ source: IdentityRoleSource; // { field: 'organizationId', multi: false }
23
+ }
24
+ ```
25
+
26
+ The runtime behaviour lives in `extractIdentityIds(identity, source)`, a pure
27
+ function `composeIdentitySyncGroups` calls once per role. `identityRole({ kind,
28
+ template, source, multi? })` is the factory. Absent/falsy fields yield `[]`, so
29
+ a role whose field isn't present (a user with no `teamIds`) is a silent no-op —
30
+ org-only, org+user, and org+team are just different `identityRoles` arrays, not
31
+ different code paths. The engine ships zero prefixes; `org:`/`user:`/`team:`
32
+ live only in `ablo.schema.ts`.
33
+
34
+ ## Why this matters
35
+
36
+ Because a `Schema` carries no closures, the same object works in-process and,
37
+ for a hosted multi-tenant server, after being reconstructed from JSON over the
38
+ control plane (the GraphQL `printSchema` / `buildSchema` model). One type,
39
+ both places — no separate server-side schema type.
40
+
41
+ `apps/sync-server` reads the live `schema` directly today
42
+ (`buildModelMap(schema)`, `composeIdentitySyncGroups` via the `@ablo/schema`
43
+ wrapper).
44
+
45
+ ## Trust boundary
46
+
47
+ Never trust a client-connection schema for authz (Zero/Convex/Instant). A
48
+ client connection may carry only the schema **version** for compatibility
49
+ gating; the authoritative `Schema` arrives over an authenticated control-plane
50
+ path. The identity passed to `composeIdentitySyncGroups` is server-resolved
51
+ trusted claims.
52
+
53
+ ## Wire form (`serialize.ts`)
54
+
55
+ `serializeSchema(schema): string` / `parseSchema(json): Schema` are the
56
+ control-plane transport — the GraphQL `printSchema`/`buildSchema` model. The
57
+ JSON (`SchemaJSON`, envelope `{ v, models, identityRoles }`) carries every
58
+ model's routing/scoping metadata, relations (incl. resolved
59
+ `foreignKeyColumn`), field metadata, and identity roles. `parseSchema` rebuilds
60
+ each model's Zod permissively from `FieldMeta` (the server does no field-shape
61
+ validation) and drops `computed` closures. `schemaHash(schema)` is the stable
62
+ FNV-1a content hash used for connect-time gating. Round-trip tested in
63
+ `__tests__/serialize.test.ts`.
64
+
65
+ ## Storage + runtime resolution (`apps/sync-server/src/schema/`): built
66
+
67
+ - **`ablo_schemas` table** (`packages/database/prisma/models/sync.prisma`,
68
+ `SchemaArtifact`) — `(organizationId, version, schemaJson, schemaHash, state,
69
+ error, createdBy, createdAt, activatedAt)`, unique `(orgId, version)`. State
70
+ `pending|validated|active|overwritten|failed`, ≤1 active per tenant (Convex
71
+ `_schemas` machine; Zero's "row in the operational DB"). *Migration written,
72
+ not applied — 0 users, Neon direct-endpoint rule.*
73
+ - **`pgSchemaStore` / `memorySchemaStore`** (`schemaStore.ts`) — mirrors
74
+ `pgApiKeyStore`. `insertPending` assigns `MAX(version)+1`; `activate` is a
75
+ transaction that demotes the current active → `overwritten` then promotes the
76
+ target. State-machine invariants tested.
77
+ - **`createSchemaRegistry(store)`** (`schemaRegistry.ts`) — `load(orgId)` parses
78
+ the active artifact's `schemaJson` to a `Schema` and caches it (shared
79
+ in-flight promise across concurrent cold loads); `invalidate(orgId)` busts it
80
+ on activation (Convex `schema_registry`). This is the seam that turns the
81
+ boot-time `import { schema }` into per-tenant runtime resolution.
82
+
83
+ ## Push route (`apps/sync-server/src/routes/schema.ts`): built
84
+
85
+ `POST /api/schema`, mounted in `index.ts` (`schemaRoutes({ provider, store,
86
+ registry })`). Auth: secret `sk_` key carrying the `schema:push` scope —
87
+ `Identity.scopes` was added and `apiKeyProvider` now populates it from the key
88
+ row's `scopes` column (restricted `rk_` keys get no `scopes`, so they're
89
+ excluded). Tenant comes from `identity.organizationId`, never the body. Flow:
90
+ read `{ schema, force? }` → validate via `parseSchema` (throws → 400) →
91
+ authoritative hash via `schemaHash(parsed)` → reject removed-model changes (409)
92
+ unless `force` → no-op fast path on identical hash (200) → `insertPending` →
93
+ `activate` → `registry.invalidate(org)` → 201 `{ schemaId, version, hash }`. 6
94
+ route tests.
95
+
96
+ ## CLI (`packages/ablo-cli`): built
97
+
98
+ `ablo push` (`src/push.ts`, dispatched from `index.ts`). Imports the
99
+ user's `sync/schema.ts` at runtime via tsx's `tsImport` (the real object —
100
+ `migrate`'s regex parse can't produce a faithful AST), then `serializeSchema`
101
+ + `schemaHash` and POSTs `{ schema, force, renames }` to `POST /api/schema`
102
+ with `Authorization: Bearer $ABLO_API_KEY`. Flags: `--schema`, `--export`,
103
+ `--url` (`$ABLO_API_URL`, default `https://api.abloatai.com`), `--force`,
104
+ `--rename old:new` (repeatable). The route honors `renames` so a renamed model
105
+ isn't flagged as a removed-model incompatibility. `parsePushArgs` unit-tested.
106
+
107
+ ## Schema drift is advisory
108
+
109
+ Bootstrap includes the tenant's active schema hash. The client compares it to
110
+ its built-in hash and warns once when they differ. Hash drift never closes the
111
+ WebSocket: an additive rollout must allow old and new clients to overlap while
112
+ data is expanded, dual-read/written, backfilled, verified, and finally
113
+ contracted. Breaking wire shapes use the protocol-version codec registry
114
+ instead.
115
+
116
+ ## Not built yet
117
+ - **Switch the hot paths to per-tenant `registry.load(org)`:** boot still does
118
+ the single-tenant `import { schema }`; `buildModelMap`/bootstrap/commit
119
+ reading the registry per request is the final multi-tenant wiring.
@@ -0,0 +1,32 @@
1
+ # Repository Structure
2
+
3
+ The public repository preserves the same ownership boundaries as the main
4
+ monorepo. `@abloatai/ablo` is the product package; the packages beneath it are
5
+ implementation owners and first-party extension surfaces.
6
+
7
+ | Workspace | Responsibility |
8
+ | --- | --- |
9
+ | `packages/ablo` | Branded SDK, public entrypoints, docs, examples, release assets |
10
+ | `packages/transaction` | Headless HTTP client, canonical contracts, reads, commits, settlement, claims, durable observation |
11
+ | `packages/humans` | Reactive materializer, WebSocket transport, presence, browser persistence, React |
12
+ | `packages/agent` | Agent behavior, perception, and coordination helpers |
13
+ | `packages/cli` | Project setup, database connection, schema operations, and diagnostics |
14
+ | `packages/tsconfig` | Private shared compiler configuration |
15
+
16
+ Applications install and import `@abloatai/ablo`. The root entrypoint is the
17
+ headless HTTP API. Human-facing reactive behavior is explicit:
18
+
19
+ ```ts
20
+ import Ablo from '@abloatai/ablo';
21
+ import ReactiveAblo from '@abloatai/ablo/client';
22
+ import { AbloProvider, useAblo } from '@abloatai/ablo/react';
23
+ ```
24
+
25
+ The backend implementation remains in `apps/sync-server` in the private
26
+ monorepo. It consumes transaction contracts but is not part of the public SDK
27
+ repository.
28
+
29
+ Internal packages must not import the branded facade. Dependencies point from
30
+ the facade to the owners, from humans and agents to transaction, and never back
31
+ upward. The public mirror copies these workspaces as workspaces; it does not
32
+ flatten them or generate compatibility source.