@abloatai/transaction 0.47.0 → 0.49.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 (270) hide show
  1. package/CONVENTIONS.md +7 -7
  2. package/README.md +1 -1
  3. package/dist/ai-sdk/modelTools.d.ts +1 -1
  4. package/dist/ai-sdk/modelTools.js +1 -1
  5. package/dist/ai-sdk/updateTool.d.ts +2 -2
  6. package/dist/ai-sdk/updateTool.d.ts.map +1 -1
  7. package/dist/auth/bootstrapScope.d.ts +1 -1
  8. package/dist/auth/capability.d.ts +32 -10
  9. package/dist/auth/capability.d.ts.map +1 -1
  10. package/dist/auth/capability.js +9 -3
  11. package/dist/auth/capability.js.map +1 -1
  12. package/dist/auth/capabilityLifecycle.d.ts +2 -2
  13. package/dist/auth/capabilityLifecycle.d.ts.map +1 -1
  14. package/dist/auth/capabilityLifecycle.js +3 -2
  15. package/dist/auth/capabilityLifecycle.js.map +1 -1
  16. package/dist/auth/identity.d.ts +3 -0
  17. package/dist/auth/identity.d.ts.map +1 -1
  18. package/dist/auth/identity.js +13 -1
  19. package/dist/auth/identity.js.map +1 -1
  20. package/dist/auth/index.d.ts +2 -2
  21. package/dist/auth/index.d.ts.map +1 -1
  22. package/dist/auth/index.js +1 -1
  23. package/dist/auth/index.js.map +1 -1
  24. package/dist/auth/schemas.d.ts +18 -1
  25. package/dist/auth/schemas.d.ts.map +1 -1
  26. package/dist/auth/schemas.js +3 -1
  27. package/dist/auth/schemas.js.map +1 -1
  28. package/dist/auth/sessionMint.d.ts.map +1 -1
  29. package/dist/auth/sessionMint.js +0 -1
  30. package/dist/auth/sessionMint.js.map +1 -1
  31. package/dist/branches.d.ts +61 -12
  32. package/dist/branches.d.ts.map +1 -1
  33. package/dist/branches.js +25 -3
  34. package/dist/branches.js.map +1 -1
  35. package/dist/commitRecordRuntime.d.ts +30 -0
  36. package/dist/commitRecordRuntime.d.ts.map +1 -0
  37. package/dist/commitRecordRuntime.js +188 -0
  38. package/dist/commitRecordRuntime.js.map +1 -0
  39. package/dist/coordination/index.d.ts +3 -3
  40. package/dist/coordination/index.d.ts.map +1 -1
  41. package/dist/coordination/index.js +2 -2
  42. package/dist/coordination/index.js.map +1 -1
  43. package/dist/coordination/locator.d.ts +5 -0
  44. package/dist/coordination/locator.d.ts.map +1 -1
  45. package/dist/coordination/locator.js +12 -0
  46. package/dist/coordination/locator.js.map +1 -1
  47. package/dist/coordination/schema.d.ts +205 -15
  48. package/dist/coordination/schema.d.ts.map +1 -1
  49. package/dist/coordination/schema.js +88 -47
  50. package/dist/coordination/schema.js.map +1 -1
  51. package/dist/coordination/targetConflict.js +1 -1
  52. package/dist/durableWrites.d.ts +3 -3
  53. package/dist/durableWrites.d.ts.map +1 -1
  54. package/dist/durableWrites.js +1 -1
  55. package/dist/errorCodes.d.ts +35 -2
  56. package/dist/errorCodes.d.ts.map +1 -1
  57. package/dist/errorCodes.js +67 -19
  58. package/dist/errorCodes.js.map +1 -1
  59. package/dist/errorObservation.d.ts +82 -0
  60. package/dist/errorObservation.d.ts.map +1 -0
  61. package/dist/errorObservation.js +101 -0
  62. package/dist/errorObservation.js.map +1 -0
  63. package/dist/errors.d.ts +2 -0
  64. package/dist/errors.d.ts.map +1 -1
  65. package/dist/errors.js +6 -1
  66. package/dist/errors.js.map +1 -1
  67. package/dist/footprint.d.ts +10 -22
  68. package/dist/footprint.d.ts.map +1 -1
  69. package/dist/footprint.js +12 -24
  70. package/dist/footprint.js.map +1 -1
  71. package/dist/index.d.ts +5 -2
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.js +1 -1
  74. package/dist/index.js.map +1 -1
  75. package/dist/internal/read-set.d.ts +8 -0
  76. package/dist/internal/read-set.d.ts.map +1 -0
  77. package/dist/internal/read-set.js +7 -0
  78. package/dist/internal/read-set.js.map +1 -0
  79. package/dist/logger.d.ts +1 -1
  80. package/dist/observability.d.ts +1 -1
  81. package/dist/observability.js +1 -1
  82. package/dist/readSetContext.d.ts +54 -0
  83. package/dist/readSetContext.d.ts.map +1 -0
  84. package/dist/readSetContext.js +98 -0
  85. package/dist/readSetContext.js.map +1 -0
  86. package/dist/resources/functionalUpdate.d.ts +13 -3
  87. package/dist/resources/functionalUpdate.d.ts.map +1 -1
  88. package/dist/resources/functionalUpdate.js.map +1 -1
  89. package/dist/resources/httpResources.d.ts +23 -7
  90. package/dist/resources/httpResources.d.ts.map +1 -1
  91. package/dist/resources/modelOperations.d.ts +2 -2
  92. package/dist/resources/modelOperations.js +1 -1
  93. package/dist/resources/mutationOptions.d.ts +1 -1
  94. package/dist/resources/mutationOptions.js +1 -1
  95. package/dist/resources/where.d.ts +1 -1
  96. package/dist/resources/where.js +1 -1
  97. package/dist/resources/writeOptionsSchema.d.ts +39 -4
  98. package/dist/resources/writeOptionsSchema.d.ts.map +1 -1
  99. package/dist/resources/writeOptionsSchema.js +12 -6
  100. package/dist/resources/writeOptionsSchema.js.map +1 -1
  101. package/dist/schema/index.js +1 -1
  102. package/dist/schema/index.js.map +1 -1
  103. package/dist/schema/openapi.d.ts.map +1 -1
  104. package/dist/schema/openapi.js +45 -11
  105. package/dist/schema/openapi.js.map +1 -1
  106. package/dist/server/commit.d.ts +7 -1
  107. package/dist/server/commit.d.ts.map +1 -1
  108. package/dist/server/index.d.ts +1 -1
  109. package/dist/server/index.d.ts.map +1 -1
  110. package/dist/server/index.js +1 -1
  111. package/dist/server/index.js.map +1 -1
  112. package/dist/source/adapters/kysely.d.ts.map +1 -1
  113. package/dist/source/adapters/kysely.js +11 -4
  114. package/dist/source/adapters/kysely.js.map +1 -1
  115. package/dist/source/adapters/kyselyMutationCore.d.ts.map +1 -1
  116. package/dist/source/adapters/kyselyMutationCore.js +14 -2
  117. package/dist/source/adapters/kyselyMutationCore.js.map +1 -1
  118. package/dist/source/contract.d.ts +2 -0
  119. package/dist/source/contract.d.ts.map +1 -1
  120. package/dist/source/index.d.ts +1 -1
  121. package/dist/source/index.d.ts.map +1 -1
  122. package/dist/source/types.d.ts +1 -1
  123. package/dist/testing/fixtures/httpResponses.d.ts +30 -1
  124. package/dist/testing/fixtures/httpResponses.d.ts.map +1 -1
  125. package/dist/testing/fixtures/httpResponses.js +48 -1
  126. package/dist/testing/fixtures/httpResponses.js.map +1 -1
  127. package/dist/transactionLayer.d.ts +1 -1
  128. package/dist/transactions/{settlement → confirmation}/commitEnvelope.d.ts +4 -2
  129. package/dist/transactions/confirmation/commitEnvelope.d.ts.map +1 -0
  130. package/dist/transactions/{settlement → confirmation}/commitEnvelope.js +8 -3
  131. package/dist/transactions/confirmation/commitEnvelope.js.map +1 -0
  132. package/dist/transactions/confirmation/httpCommitEnvelope.d.ts.map +1 -0
  133. package/dist/transactions/confirmation/httpCommitEnvelope.js.map +1 -0
  134. package/dist/transactions/confirmation/idempotencyKey.d.ts.map +1 -0
  135. package/dist/transactions/confirmation/idempotencyKey.js.map +1 -0
  136. package/dist/transactions/{settlement → confirmation}/pendingWrite.d.ts +3 -2
  137. package/dist/transactions/confirmation/pendingWrite.d.ts.map +1 -0
  138. package/dist/transactions/confirmation/pendingWrite.js.map +1 -0
  139. package/dist/transport/commitFrames.d.ts +1 -0
  140. package/dist/transport/commitFrames.d.ts.map +1 -1
  141. package/dist/transport/commitFrames.js +8 -7
  142. package/dist/transport/commitFrames.js.map +1 -1
  143. package/dist/transport/connectionManager.d.ts +1 -1
  144. package/dist/transport/connectionManager.js +1 -1
  145. package/dist/transport/httpClient.d.ts +23 -9
  146. package/dist/transport/httpClient.d.ts.map +1 -1
  147. package/dist/transport/httpClient.js +134 -15
  148. package/dist/transport/httpClient.js.map +1 -1
  149. package/dist/transport/httpOptions.d.ts +9 -1
  150. package/dist/transport/httpOptions.d.ts.map +1 -1
  151. package/dist/transport/httpTransport.d.ts +3 -0
  152. package/dist/transport/httpTransport.d.ts.map +1 -1
  153. package/dist/transport/httpTransport.js +109 -22
  154. package/dist/transport/httpTransport.js.map +1 -1
  155. package/dist/transport/wsFrameHandlers.d.ts +3 -1
  156. package/dist/transport/wsFrameHandlers.d.ts.map +1 -1
  157. package/dist/transport/wsFrameHandlers.js +38 -17
  158. package/dist/transport/wsFrameHandlers.js.map +1 -1
  159. package/dist/transport/wsTransport.d.ts +3 -0
  160. package/dist/transport/wsTransport.d.ts.map +1 -1
  161. package/dist/transport/wsTransport.js +32 -1
  162. package/dist/transport/wsTransport.js.map +1 -1
  163. package/dist/utils/json.d.ts +1 -1
  164. package/dist/utils/json.js +1 -1
  165. package/dist/wire/accountResponses.d.ts +1 -1
  166. package/dist/wire/auth.d.ts +2 -2
  167. package/dist/wire/commit.d.ts +1652 -213
  168. package/dist/wire/commit.d.ts.map +1 -1
  169. package/dist/wire/commit.js +196 -132
  170. package/dist/wire/commit.js.map +1 -1
  171. package/dist/wire/delta.d.ts +1 -1
  172. package/dist/wire/errorEnvelope.d.ts +1 -0
  173. package/dist/wire/errorEnvelope.d.ts.map +1 -1
  174. package/dist/wire/errorEnvelope.js +1 -0
  175. package/dist/wire/errorEnvelope.js.map +1 -1
  176. package/dist/wire/feedCursor.d.ts +1 -1
  177. package/dist/wire/feedCursor.js +1 -1
  178. package/dist/wire/feedEvent.d.ts +3 -3
  179. package/dist/wire/frames.d.ts +8 -4
  180. package/dist/wire/frames.d.ts.map +1 -1
  181. package/dist/wire/frames.js +6 -3
  182. package/dist/wire/frames.js.map +1 -1
  183. package/dist/wire/inboundFrames.d.ts +44 -4
  184. package/dist/wire/inboundFrames.d.ts.map +1 -1
  185. package/dist/wire/index.d.ts +6 -4
  186. package/dist/wire/index.d.ts.map +1 -1
  187. package/dist/wire/index.js +6 -5
  188. package/dist/wire/index.js.map +1 -1
  189. package/dist/wire/modelMutations.d.ts +35 -0
  190. package/dist/wire/modelMutations.d.ts.map +1 -1
  191. package/dist/wire/modelMutations.js +9 -2
  192. package/dist/wire/modelMutations.js.map +1 -1
  193. package/dist/wire/modelResponses.d.ts +12 -1
  194. package/dist/wire/modelResponses.d.ts.map +1 -1
  195. package/dist/wire/modelResponses.js +12 -1
  196. package/dist/wire/modelResponses.js.map +1 -1
  197. package/package.json +12 -2
  198. package/src/ai-sdk/modelTools.ts +1 -1
  199. package/src/ai-sdk/updateTool.ts +2 -2
  200. package/src/auth/bootstrapScope.ts +1 -1
  201. package/src/auth/capability.ts +13 -4
  202. package/src/auth/capabilityLifecycle.ts +3 -2
  203. package/src/auth/identity.ts +16 -1
  204. package/src/auth/index.ts +2 -0
  205. package/src/auth/schemas.ts +3 -0
  206. package/src/auth/sessionMint.ts +0 -1
  207. package/src/branches.ts +27 -3
  208. package/src/commitRecordRuntime.ts +245 -0
  209. package/src/coordination/index.ts +24 -1
  210. package/src/coordination/locator.ts +17 -0
  211. package/src/coordination/schema.ts +114 -49
  212. package/src/coordination/targetConflict.ts +1 -1
  213. package/src/durableWrites.ts +3 -3
  214. package/src/errorCodes.ts +205 -25
  215. package/src/errorObservation.ts +121 -0
  216. package/src/errors.ts +5 -1
  217. package/src/footprint.ts +16 -27
  218. package/src/index.ts +8 -1
  219. package/src/internal/read-set.ts +18 -0
  220. package/src/logger.ts +1 -1
  221. package/src/observability.ts +1 -1
  222. package/src/readSetContext.ts +164 -0
  223. package/src/resources/functionalUpdate.ts +16 -4
  224. package/src/resources/httpResources.ts +27 -8
  225. package/src/resources/modelOperations.ts +2 -2
  226. package/src/resources/mutationOptions.ts +1 -1
  227. package/src/resources/where.ts +1 -1
  228. package/src/resources/writeOptionsSchema.ts +30 -12
  229. package/src/schema/index.ts +1 -1
  230. package/src/schema/openapi.ts +52 -11
  231. package/src/server/commit.ts +7 -1
  232. package/src/server/index.ts +5 -1
  233. package/src/source/adapters/kysely.ts +12 -4
  234. package/src/source/adapters/kyselyMutationCore.ts +14 -2
  235. package/src/source/index.ts +1 -1
  236. package/src/source/types.ts +1 -1
  237. package/src/testing/fixtures/httpResponses.ts +60 -1
  238. package/src/transactionLayer.ts +1 -1
  239. package/src/transactions/{settlement → confirmation}/commitEnvelope.ts +13 -3
  240. package/src/transport/commitFrames.ts +7 -5
  241. package/src/transport/connectionManager.ts +1 -1
  242. package/src/transport/httpClient.ts +256 -29
  243. package/src/transport/httpOptions.ts +9 -1
  244. package/src/transport/httpTransport.ts +139 -27
  245. package/src/transport/wsFrameHandlers.ts +46 -8
  246. package/src/transport/wsTransport.ts +42 -1
  247. package/src/utils/json.ts +1 -1
  248. package/src/wire/commit.ts +248 -147
  249. package/src/wire/errorEnvelope.ts +1 -0
  250. package/src/wire/feedCursor.ts +1 -1
  251. package/src/wire/frames.ts +9 -4
  252. package/src/wire/index.ts +38 -5
  253. package/src/wire/modelMutations.ts +16 -2
  254. package/src/wire/modelResponses.ts +14 -1
  255. package/dist/transactions/settlement/commitEnvelope.d.ts.map +0 -1
  256. package/dist/transactions/settlement/commitEnvelope.js.map +0 -1
  257. package/dist/transactions/settlement/httpCommitEnvelope.d.ts.map +0 -1
  258. package/dist/transactions/settlement/httpCommitEnvelope.js.map +0 -1
  259. package/dist/transactions/settlement/idempotencyKey.d.ts.map +0 -1
  260. package/dist/transactions/settlement/idempotencyKey.js.map +0 -1
  261. package/dist/transactions/settlement/pendingWrite.d.ts.map +0 -1
  262. package/dist/transactions/settlement/pendingWrite.js.map +0 -1
  263. /package/dist/transactions/{settlement → confirmation}/httpCommitEnvelope.d.ts +0 -0
  264. /package/dist/transactions/{settlement → confirmation}/httpCommitEnvelope.js +0 -0
  265. /package/dist/transactions/{settlement → confirmation}/idempotencyKey.d.ts +0 -0
  266. /package/dist/transactions/{settlement → confirmation}/idempotencyKey.js +0 -0
  267. /package/dist/transactions/{settlement → confirmation}/pendingWrite.js +0 -0
  268. /package/src/transactions/{settlement → confirmation}/httpCommitEnvelope.ts +0 -0
  269. /package/src/transactions/{settlement → confirmation}/idempotencyKey.ts +0 -0
  270. /package/src/transactions/{settlement → confirmation}/pendingWrite.ts +0 -0
@@ -267,6 +267,17 @@ const streamTargetSchema = targetRefSchema
267
267
  export const onStaleModeSchema = z.enum(['reject', 'overwrite', 'notify']);
268
268
  export type OnStaleMode = z.infer<typeof onStaleModeSchema>;
269
269
 
270
+ /** The exact authoritative position retained for one ReadSet entry. */
271
+ export const readSetWatermarkSchema = z.number().int().nonnegative();
272
+ export type ReadSetWatermark = z.infer<typeof readSetWatermarkSchema>;
273
+
274
+ /** Maximum decision-input entries one logical commit may ask the server to scan. */
275
+ export const MAX_READ_SET_ENTRIES = 500;
276
+
277
+ /** A persisted entry cannot overwrite: it observes future writes, not its own. */
278
+ export const trackOnStaleSchema = onStaleModeSchema.exclude(['overwrite']);
279
+ export type TrackOnStale = z.infer<typeof trackOnStaleSchema>;
280
+
270
281
  /**
271
282
  * The optimistic guard carried on a commit operation. `readAt` is the
272
283
  * snapshot watermark from `context.capture` (null/absent ⇒ unguarded write).
@@ -274,7 +285,7 @@ export type OnStaleMode = z.infer<typeof onStaleModeSchema>;
274
285
  * claim — see the claim layer below.
275
286
  */
276
287
  export const writeGuardSchema = z.object({
277
- readAt: z.number().nullish(),
288
+ readAt: readSetWatermarkSchema.nullish(),
278
289
  onStale: onStaleModeSchema.nullish(),
279
290
  bypass: z.boolean().optional(),
280
291
  });
@@ -445,42 +456,79 @@ export const staleNotificationSchema = z.discriminatedUnion('scope', [
445
456
  ]);
446
457
  export type StaleNotification = z.infer<typeof staleNotificationSchema>;
447
458
 
448
- /**
449
- * One entry in a commit's batch premise — a read it was based on, so the
450
- * server can ask "did anything I looked at change?" — broader than the
451
- * write-target check, which only validates the rows being written. The server
452
- * re-runs stale detection against each entry at its `readAt`; a moved premise
453
- * fires the entry's `onStale` disposition (default `reject`) across the whole
454
- * batch (`notify` holds every write and notifies, `reject` aborts, `overwrite`
455
- * proceeds silently). An entry comes at one of two granularities:
456
- *
457
- * • Row — `{ model, id, readAt, fields? }`: did this specific row, or these
458
- * specific fields, change?
459
- * • Group — `{ group, readAt }`: did anything in this sync group change?
460
- * `group` is a sync-group key such as `report:abc` or `section:s1`, the
461
- * same unit a participant watches and claims.
462
- *
463
- * See `packages/ablo/docs/concurrency-convention.md` (§4) for the
464
- * governing convention and the receive → reconcile loop.
465
- */
466
- const readRowDependencySchema = z.object({
459
+ /** One row named by a commit-lifetime ReadSet entry. */
460
+ export const readSetRowTargetSchema = z.object({
461
+ scope: z.literal('row'),
467
462
  model: z.string(),
468
463
  id: z.string(),
469
- readAt: z.number(),
470
464
  fields: z.array(z.string()).readonly().optional(),
471
- onStale: onStaleModeSchema.optional(),
472
465
  });
466
+ export type ReadSetRowTarget = z.infer<typeof readSetRowTargetSchema>;
473
467
 
474
- const readGroupDependencySchema = z.object({
475
- /**
476
- * The caller-facing view of the format: `reads: [{ group: 'report:abc' }]`
477
- * type-checks inline, `'nonsense'` does not. The
478
- * {@link groupStaleNotificationSchema} this premise fires uses the branded
479
- * view, because there Ablo is the author.
480
- */
468
+ /** One sync group named by a ReadSet entry. */
469
+ export const readSetGroupTargetSchema = z.object({
470
+ scope: z.literal('group'),
481
471
  group: syncGroupRefSchema,
482
- readAt: z.number(),
483
- onStale: onStaleModeSchema.optional(),
472
+ });
473
+ export type ReadSetGroupTarget = z.infer<typeof readSetGroupTargetSchema>;
474
+
475
+ export const commitReadSetTargetSchema = z.discriminatedUnion('scope', [
476
+ readSetRowTargetSchema,
477
+ readSetGroupTargetSchema,
478
+ ]);
479
+ export type CommitReadSetTarget = z.infer<typeof commitReadSetTargetSchema>;
480
+
481
+ /** Persisted row tracking is row-grained; it has no field storage axis. */
482
+ export const persistedReadSetRowTargetSchema = readSetRowTargetSchema.omit({ fields: true });
483
+ export const persistedReadSetTargetSchema = z.discriminatedUnion('scope', [
484
+ persistedReadSetRowTargetSchema,
485
+ readSetGroupTargetSchema,
486
+ ]);
487
+ export type PersistedReadSetTarget = z.infer<typeof persistedReadSetTargetSchema>;
488
+
489
+ /** One exact input to the decision made by this commit. */
490
+ export const commitReadSetEntrySchema = z.object({
491
+ target: commitReadSetTargetSchema,
492
+ watermark: readSetWatermarkSchema,
493
+ lifetime: z.literal('commit'),
494
+ onStale: onStaleModeSchema,
495
+ });
496
+ export type CommitReadSetEntry = z.infer<typeof commitReadSetEntrySchema>;
497
+
498
+ /** One exact input retained after the declaring commit settles. */
499
+ export const persistedReadSetEntrySchema = z.object({
500
+ target: persistedReadSetTargetSchema,
501
+ watermark: readSetWatermarkSchema,
502
+ lifetime: z.literal('persisted'),
503
+ onStale: trackOnStaleSchema,
504
+ });
505
+ export type PersistedReadSetEntry = z.infer<typeof persistedReadSetEntrySchema>;
506
+
507
+ export const readSetEntrySchema = z.discriminatedUnion('lifetime', [
508
+ commitReadSetEntrySchema,
509
+ persistedReadSetEntrySchema,
510
+ ]);
511
+ export type ReadSetEntry = z.infer<typeof readSetEntrySchema>;
512
+
513
+ export const readSetSchema = z
514
+ .array(readSetEntrySchema)
515
+ .max(MAX_READ_SET_ENTRIES)
516
+ .readonly();
517
+ export type ReadSet = z.infer<typeof readSetSchema>;
518
+
519
+ /**
520
+ * Wire projection of a commit-lifetime ReadSet entry. The public field names
521
+ * stay compatible (`readAt`, flat row/group target); every member is selected
522
+ * from the canonical entry rather than restated beside it.
523
+ */
524
+ const readRowDependencySchema = readSetRowTargetSchema.omit({ scope: true }).extend({
525
+ readAt: commitReadSetEntrySchema.shape.watermark,
526
+ onStale: commitReadSetEntrySchema.shape.onStale.optional(),
527
+ });
528
+
529
+ const readGroupDependencySchema = readSetGroupTargetSchema.omit({ scope: true }).extend({
530
+ readAt: commitReadSetEntrySchema.shape.watermark,
531
+ onStale: commitReadSetEntrySchema.shape.onStale.optional(),
484
532
  });
485
533
 
486
534
  export const readDependencySchema = z.union([
@@ -490,14 +538,14 @@ export const readDependencySchema = z.union([
490
538
  export type ReadDependency = z.infer<typeof readDependencySchema>;
491
539
 
492
540
  /**
493
- * A durable premise — what a participant is watching so that a later
541
+ * A durable ReadSet entry — what a participant is watching so that a later
494
542
  * change to it opens a {@link StaleNotification}. Where a {@link ReadDependency}
495
543
  * is checked once at commit and discarded, a `TrackDependency` is kept and
496
544
  * re-checked against every future delta. The row form watches one object; the
497
545
  * group form watches a whole sync group ("anything in `report:abc`"). See
498
546
  * `packages/ablo/docs/groups.md` for how it drives change propagation.
499
547
  *
500
- * It is a PROJECTION of the ephemeral premise, not a second declaration of the
548
+ * It is a projection of the canonical persisted entry, not a second declaration of the
501
549
  * same reference: a track names its target exactly as a read does, and the
502
550
  * three ways it differs are stated here as omissions the compiler holds.
503
551
  *
@@ -514,18 +562,10 @@ export type ReadDependency = z.infer<typeof readDependencySchema>;
514
562
  * registered the track, so a caller with nothing to say about when it last
515
563
  * looked gets "from here on".
516
564
  *
517
- * A field added to the premise reaches both halves; a field the durable half
565
+ * A field added to the ReadSet target reaches both halves; a field the durable half
518
566
  * genuinely cannot carry has to be omitted here on purpose, in one line, rather
519
567
  * than by being quietly left out of a copy.
520
568
  */
521
- /**
522
- * A track's disposition — the premise enum with `overwrite` subtracted, derived
523
- * from it rather than spelled again, so a mode added to the convention reaches
524
- * a track unless it is deliberately excluded here.
525
- */
526
- export const trackOnStaleSchema = onStaleModeSchema.exclude(['overwrite']);
527
- export type TrackOnStale = z.infer<typeof trackOnStaleSchema>;
528
-
529
569
  /**
530
570
  * What a track does when the caller says nothing — reporting, the behavior every
531
571
  * track had before the disposition existed, so an existing registration is
@@ -539,18 +579,32 @@ export type TrackOnStale = z.infer<typeof trackOnStaleSchema>;
539
579
  export const DEFAULT_TRACK_ON_STALE: TrackOnStale = 'notify';
540
580
 
541
581
  const trackBaseSchema = {
542
- readAt: z.number().optional(),
543
- onStale: trackOnStaleSchema.optional(),
582
+ readAt: persistedReadSetEntrySchema.shape.watermark.optional(),
583
+ onStale: persistedReadSetEntrySchema.shape.onStale.optional(),
544
584
  } as const;
545
585
 
546
586
  export const trackDependencySchema = z.union([
547
- readRowDependencySchema
548
- .omit({ onStale: true, fields: true, readAt: true })
549
- .extend(trackBaseSchema),
550
- readGroupDependencySchema.omit({ onStale: true, readAt: true }).extend(trackBaseSchema),
587
+ persistedReadSetRowTargetSchema.omit({ scope: true }).extend(trackBaseSchema),
588
+ readSetGroupTargetSchema.omit({ scope: true }).extend(trackBaseSchema),
551
589
  ]);
552
590
  export type TrackDependency = z.infer<typeof trackDependencySchema>;
553
591
 
592
+ /** Bounded wire projections of the two ReadSet lifetimes. */
593
+ export const readDependencyListSchema = z
594
+ .array(readDependencySchema)
595
+ .max(MAX_READ_SET_ENTRIES);
596
+ export const trackDependencyListSchema = z
597
+ .array(trackDependencySchema)
598
+ .max(MAX_READ_SET_ENTRIES);
599
+
600
+ /** Shared combined-cardinality rule for envelopes carrying both projections. */
601
+ export function readSetProjectionEntryCount(value: {
602
+ readonly reads?: readonly unknown[] | null;
603
+ readonly track?: readonly unknown[] | null;
604
+ }): number {
605
+ return (value.reads?.length ?? 0) + (value.track?.length ?? 0);
606
+ }
607
+
554
608
  // ─────────────────────────────────────────────────────────────────────────
555
609
  // Layer 2 — pessimistic claims and leases
556
610
  // ─────────────────────────────────────────────────────────────────────────
@@ -1328,7 +1382,12 @@ export type UpdateSubscriptionPayload = z.infer<
1328
1382
  export const subscriptionAckPayloadSchema = z.object({
1329
1383
  success: z.boolean(),
1330
1384
  syncGroups: z.array(z.string()),
1331
- error: z.object({ code: z.string(), message: z.string() }).optional(),
1385
+ error: z.object({
1386
+ code: z.string(),
1387
+ message: z.string(),
1388
+ request_id: z.string().optional(),
1389
+ event_id: z.string().optional(),
1390
+ }).optional(),
1332
1391
  });
1333
1392
  export type SubscriptionAckPayload = z.infer<
1334
1393
  typeof subscriptionAckPayloadSchema
@@ -1360,6 +1419,12 @@ export const commitOperationSchema = writeGuardSchema.extend({
1360
1419
  input: z.record(z.string(), z.unknown()).nullish(),
1361
1420
  /** Per-op client tx id, echoed on the broadcast delta. */
1362
1421
  transactionId: z.string().nullish(),
1422
+ /**
1423
+ * The server-issued claim identity this operation was performed under.
1424
+ * Distinct from `transactionId`: a claim attributes and fences coordinated
1425
+ * ownership, while a transaction id correlates the resulting source echo.
1426
+ */
1427
+ claimId: z.string().min(1).nullish(),
1363
1428
  /**
1364
1429
  * The fencing token from the held claim this write belongs to (Option B).
1365
1430
  * Present only on a write issued under a claim that was granted one; the
@@ -12,7 +12,7 @@ import type { ClaimTargetDetails } from './locator.js';
12
12
  * two places to land and no way to notice when it landed in one. It is one
13
13
  * definition here, and both authorities import it.
14
14
  *
15
- * It lives in the settlement core rather than the server because it is the rule
15
+ * It lives in the confirmation core rather than the server because it is the rule
16
16
  * itself, not a deployment of it: a claim authority running against a
17
17
  * developer's own database has to answer this question exactly the same way,
18
18
  * and a client that wants to predict a refusal before paying for the round trip
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Owns the persistence contract behind `Ablo({ durableWrites })` — a behavior
5
5
  * contract of methods, not a serialized shape, which is why it lives here
6
- * rather than in `transactions/settlement/`. The records that cross this port
6
+ * rather than in `transactions/confirmation/`. The records that cross this port
7
7
  * are owned by `pendingWriteSchema` there; this module never redescribes them.
8
8
  *
9
9
  * The engine consumes the port through `commitOutboxStore`, and the HTTP client
@@ -12,8 +12,8 @@
12
12
 
13
13
  import { z } from 'zod';
14
14
  import { AbloValidationError } from './errors.js';
15
- import type { CommitOutboxScope } from './transactions/settlement/commitEnvelope.js';
16
- import type { PendingWrite } from './transactions/settlement/pendingWrite.js';
15
+ import type { CommitOutboxScope } from './transactions/confirmation/commitEnvelope.js';
16
+ import type { PendingWrite } from './transactions/confirmation/pendingWrite.js';
17
17
 
18
18
  /**
19
19
  * Persistence port used by `Ablo({ durableWrites })`.
package/src/errorCodes.ts CHANGED
@@ -39,7 +39,7 @@ import { z } from 'zod';
39
39
  * error documentation and returned on the `Ablo-Version` response header, so a
40
40
  * consumer can detect when its expected contract has drifted from the server's.
41
41
  */
42
- export const ERROR_CONTRACT_VERSION = '2026-07-30';
42
+ export const ERROR_CONTRACT_VERSION = '2026-08-04';
43
43
 
44
44
  /** A coarse grouping of error codes, used to organize metrics and documentation. */
45
45
  export type ErrorCategory =
@@ -129,6 +129,36 @@ export interface ErrorCodeSpec {
129
129
  * this field can be left unset.
130
130
  */
131
131
  readonly recovery?: RecoveryClass;
132
+ /** Exhaustive sink and alert policy for this code. */
133
+ readonly observability: ErrorObservabilityPolicy;
134
+ }
135
+
136
+ export interface ErrorObservabilityPolicy {
137
+ readonly severity: 'info' | 'warning' | 'error' | 'fatal';
138
+ readonly sentry: 'log' | 'issue';
139
+ readonly pagingEligible: boolean;
140
+ readonly expectedVolume: 'low' | 'normal' | 'high';
141
+ readonly owner: 'platform' | 'product';
142
+ }
143
+
144
+ function observabilityPolicy(
145
+ category: ErrorCategory,
146
+ httpStatus: number | undefined,
147
+ operational = false,
148
+ ): ErrorObservabilityPolicy {
149
+ if ((httpStatus ?? 0) >= 500) {
150
+ return { severity: 'error', sentry: 'issue', pagingEligible: true, expectedVolume: 'low', owner: 'platform' };
151
+ }
152
+ if (operational) {
153
+ return { severity: 'warning', sentry: 'issue', pagingEligible: false, expectedVolume: 'low', owner: 'platform' };
154
+ }
155
+ return {
156
+ severity: category === 'auth' ? 'info' : 'warning',
157
+ sentry: 'log',
158
+ pagingEligible: false,
159
+ expectedVolume: category === 'auth' ? 'high' : 'normal',
160
+ owner: 'product',
161
+ };
132
162
  }
133
163
 
134
164
  const wire = (
@@ -136,8 +166,17 @@ const wire = (
136
166
  httpStatus: number,
137
167
  retryable: boolean,
138
168
  message: string,
139
- recovery?: RecoveryClass
140
- ): ErrorCodeSpec => ({ category, surface: 'wire', httpStatus, retryable, message, recovery });
169
+ recovery?: RecoveryClass,
170
+ operational = false,
171
+ ): ErrorCodeSpec => ({
172
+ category,
173
+ surface: 'wire',
174
+ httpStatus,
175
+ retryable,
176
+ message,
177
+ recovery,
178
+ observability: observabilityPolicy(category, httpStatus, operational),
179
+ });
141
180
 
142
181
  const client = (
143
182
  category: ErrorCategory,
@@ -147,6 +186,7 @@ const client = (
147
186
  category,
148
187
  surface: 'client',
149
188
  retryable: false,
189
+ observability: observabilityPolicy(category, undefined),
150
190
  message,
151
191
  ...(recovery !== undefined ? { recovery } : {}),
152
192
  });
@@ -234,6 +274,18 @@ export const ERROR_CODES = {
234
274
  'Your session has expired or is no longer valid. Sign in again to continue.',
235
275
  'session_expiry'
236
276
  ),
277
+ better_auth_request_failed: wire(
278
+ 'auth',
279
+ 400,
280
+ false,
281
+ 'The authentication request was rejected. Check the submitted credentials or request and try again.'
282
+ ),
283
+ better_auth_upstream_failed: wire(
284
+ 'server',
285
+ 500,
286
+ true,
287
+ 'The authentication service could not complete the request. Retry the request; if it continues to fail, contact support.'
288
+ ),
237
289
  // `jwt_invalid` is the general fallback; the codes below it split out specific
238
290
  // failure modes, so an integrator can tell a wrong JWKS registration from a
239
291
  // token with no organization claim from a wrong audience, instead of getting
@@ -319,6 +371,18 @@ export const ERROR_CODES = {
319
371
  false,
320
372
  'This deployment does not accept direct connection-string data sources. Register a signed Data Source endpoint instead.'
321
373
  ),
374
+ source_connector_localhost_required: wire(
375
+ 'validation',
376
+ 400,
377
+ false,
378
+ 'A connector-only development Data Source must use a localhost descriptor. Use `ablo dev --local`; use the ordinary signed HTTPS endpoint registration for a deployed handler.'
379
+ ),
380
+ source_connector_unauthenticated: wire(
381
+ 'auth',
382
+ 401,
383
+ false,
384
+ 'The localhost Data Source connector could not authenticate. Rerun `ablo dev --local` so it mints and uses a fresh branch-bound secret key.'
385
+ ),
322
386
 
323
387
  // ── permission / capability (403) ──────────────────────────────────
324
388
  capability_scope_denied: wire(
@@ -339,12 +403,6 @@ export const ERROR_CODES = {
339
403
  false,
340
404
  'This capability cannot be used — it is unknown, revoked, or expired. Request a fresh grant.'
341
405
  ),
342
- test_database_not_registered: wire(
343
- 'permission',
344
- 403,
345
- false,
346
- 'Test mode requires a registered dev database for this org — run `npx ablo init` to register one for your test key.'
347
- ),
348
406
  tenant_routing_failed: wire(
349
407
  'server',
350
408
  500,
@@ -355,25 +413,37 @@ export const ERROR_CODES = {
355
413
  'permission',
356
414
  403,
357
415
  false,
358
- 'The database role Ablo connects with is a superuser or has `BYPASSRLS`, so Postgres will not enforce row-level security for it. Connect with a role that is subject to RLS.'
416
+ 'The database role Ablo connects with is a superuser or has `BYPASSRLS`, so Postgres will not enforce row-level security for it. Connect with a role that is subject to RLS.',
417
+ undefined,
418
+ true,
359
419
  ),
360
420
  database_role_unreadable: wire(
361
421
  'permission',
362
422
  403,
363
423
  false,
364
- 'Ablo could not introspect the database role it connects with, so it cannot verify that row-level security is enforced.'
424
+ 'Ablo could not introspect the database role it connects with, so it cannot verify that row-level security is enforced.',
425
+ undefined,
426
+ true,
365
427
  ),
366
428
  database_tables_unforced_rls: wire(
367
429
  'permission',
368
430
  403,
369
431
  false,
370
- 'Some synced tables do not have `FORCE ROW LEVEL SECURITY` applied, so the table owner can bypass row isolation. Run `ALTER TABLE ... FORCE ROW LEVEL SECURITY` on each synced table.'
432
+ 'Some synced tables do not have `FORCE ROW LEVEL SECURITY` applied, so the table owner can bypass row isolation. Run `ALTER TABLE ... FORCE ROW LEVEL SECURITY` on each synced table.',
433
+ undefined,
434
+ true,
371
435
  ),
372
436
  database_host_not_allowed: wire(
373
437
  'permission',
374
438
  403,
375
439
  false,
376
- "The database host resolves to a private, loopback, or link-local address, which Ablo's servers will not connect to. Use a publicly resolvable host."
440
+ "The database host did not resolve exclusively to an allowed address for this route. Use a publicly resolvable direct endpoint, or configure the matching PrivateLink, peering, or VPN route. Localhost has its own `database_loopback_requires_connector` workflow."
441
+ ),
442
+ database_loopback_requires_connector: wire(
443
+ 'validation',
444
+ 400,
445
+ false,
446
+ 'Ablo Cloud cannot open a direct PostgreSQL connection to localhost on your machine. For localhost-first development, run `ablo migrate` once and keep `ablo dev --local` running; use a network-reachable direct route only when Ablo must observe arbitrary SQL writes through WAL.'
377
447
  ),
378
448
  connected_database_unreachable: wire(
379
449
  'tenant',
@@ -430,7 +500,7 @@ export const ERROR_CODES = {
430
500
  'claim',
431
501
  409,
432
502
  false,
433
- 'This write carried a fencing token below the row’s current high-water: a later holder claimed the row, wrote, and moved on while this claim was lapsed, so applying the write would silently overwrite their work. The claim is gone — re-claim the row and retry from the current state.'
503
+ 'This claim is no longer current because another participant completed newer work on the row. Re-claim the row and retry from the fresh state.'
434
504
  ),
435
505
  entity_claimed: wire(
436
506
  'claim',
@@ -486,7 +556,7 @@ export const ERROR_CODES = {
486
556
  ),
487
557
  model_claim_not_configured: client(
488
558
  'claim',
489
- 'Claiming requires the collaboration runtime, which the standard Ablo({ schema, apiKey }) client wires up for every model automatically — there is no per-model claim configuration to add. This appears only when a model proxy is constructed directly without that runtime (an internal/advanced path).'
559
+ 'Claiming is unavailable on this model client. Construct it through the standard Ablo({ schema, apiKey }) client and retry.'
490
560
  ),
491
561
  model_join_not_configured: client(
492
562
  'claim',
@@ -699,6 +769,18 @@ export const ERROR_CODES = {
699
769
  false,
700
770
  'The participant is not allowed to execute this mutator.'
701
771
  ),
772
+ source_connector_requires_secret_key: wire(
773
+ 'permission',
774
+ 403,
775
+ false,
776
+ 'A Data Source reverse channel must authenticate with the branch-bound secret (`sk_`) key created by `ablo dev`; browser, ephemeral, restricted, and management keys cannot serve a database.'
777
+ ),
778
+ source_connector_production_not_enabled: wire(
779
+ 'permission',
780
+ 403,
781
+ false,
782
+ 'This production Data Source has not opted into reverse-channel transport. `ablo dev --local` is for child branches; use a direct production endpoint or explicitly enable the supported production reverse-channel route.'
783
+ ),
702
784
 
703
785
  // ── not found (404) ────────────────────────────────────────────────
704
786
  entity_not_found: wire(
@@ -723,7 +805,15 @@ export const ERROR_CODES = {
723
805
  'not_found',
724
806
  404,
725
807
  false,
726
- 'No database is connected to this plane yet, so there is nothing to check. Connect one with `ablo connect apply`, then run the check again.'
808
+ 'This branch is not connected to your database yet. Run `ablo connect` for a cloud-reachable direct Postgres endpoint, or `ablo dev --local` to register and serve a localhost Data Source, then retry.',
809
+ undefined,
810
+ true,
811
+ ),
812
+ source_connector_no_source_registered: wire(
813
+ 'not_found',
814
+ 404,
815
+ false,
816
+ 'The branch has no endpoint Data Source for this connector to serve. Run `ablo dev --local` with the current CLI, which registers the connector-only source before opening the socket.'
727
817
  ),
728
818
  task_id_missing: wire(
729
819
  'server',
@@ -978,7 +1068,55 @@ export const ERROR_CODES = {
978
1068
  'transport',
979
1069
  503,
980
1070
  true,
981
- 'A network error occurred while talking to the data source. Check connectivity and retry.'
1071
+ 'A network error occurred while talking to the data source. For localhost mode, keep `ablo dev --local` running and check its connector output; for a direct source, check database connectivity and retry.'
1072
+ ),
1073
+ source_request_failed: wire(
1074
+ 'transport',
1075
+ 502,
1076
+ true,
1077
+ 'The signed Data Source returned a failure without a more specific code. Inspect the `ablo dev --local` process or deployed endpoint logs, then retry with the same idempotency key.'
1078
+ ),
1079
+ source_connector_not_attached: wire(
1080
+ 'transport',
1081
+ 503,
1082
+ true,
1083
+ 'This branch uses localhost connector-only storage, but no connector is attached. Start or restart `ablo dev --local` and keep that process running.'
1084
+ ),
1085
+ source_connector_timeout: wire(
1086
+ 'transport',
1087
+ 504,
1088
+ true,
1089
+ 'The local Data Source handler did not answer before the connector deadline. Check the local Postgres connection and handler logs; the same idempotent request may be retried.'
1090
+ ),
1091
+ source_connector_send_failed: wire(
1092
+ 'transport',
1093
+ 502,
1094
+ true,
1095
+ 'The connector socket closed while Ablo was forwarding a signed Data Source request. Keep `ablo dev --local` running; Ablo retries after the connector reconnects.'
1096
+ ),
1097
+ source_connector_disconnected: wire(
1098
+ 'transport',
1099
+ 503,
1100
+ true,
1101
+ 'The localhost Data Source connector disconnected with a request in flight. The connector reconnects automatically; retry after it reports ready.'
1102
+ ),
1103
+ source_connector_superseded: wire(
1104
+ 'transport',
1105
+ 503,
1106
+ true,
1107
+ 'A newer localhost Data Source connector replaced this one. Stop duplicate `ablo dev --local` processes and use the newest process; in-flight requests are safe to retry.'
1108
+ ),
1109
+ source_connector_shutdown: wire(
1110
+ 'transport',
1111
+ 503,
1112
+ true,
1113
+ 'The Ablo service shut down the connector while a request was in flight. The connector reconnects automatically after the service returns.'
1114
+ ),
1115
+ source_connector_protocol_error: wire(
1116
+ 'transport',
1117
+ 400,
1118
+ false,
1119
+ 'The CLI and Ablo service disagreed on the Data Source connector frame protocol, or a malformed frame arrived. Upgrade the Ablo CLI and SDK together, then restart `ablo dev --local`.'
982
1120
  ),
983
1121
  source_unreachable: wire(
984
1122
  'transport',
@@ -990,7 +1128,19 @@ export const ERROR_CODES = {
990
1128
  'transport',
991
1129
  503,
992
1130
  false,
993
- "The plane's data source is not in a verified, resolvable state. Fix its registration, secret, or configuration; Ablo will not fall through to hosted storage."
1131
+ "This branch's database connection is not ready. Check its credentials and configuration; Ablo will not send reads or writes anywhere else."
1132
+ ),
1133
+ source_connector_handler_error: wire(
1134
+ 'server',
1135
+ 500,
1136
+ true,
1137
+ 'The local signed Data Source handler threw while processing a request. Read the `ablo dev --local` error immediately above it, fix the database or adapter failure, and retry.'
1138
+ ),
1139
+ source_connector_internal_error: wire(
1140
+ 'server',
1141
+ 500,
1142
+ true,
1143
+ 'Ablo could not establish the Data Source reverse channel because of an internal service failure. Retry; if it persists, report the request id.'
994
1144
  ),
995
1145
  identity_network_error: wire(
996
1146
  'transport',
@@ -1103,6 +1253,30 @@ export const ERROR_CODES = {
1103
1253
  ),
1104
1254
 
1105
1255
  // ── server (5xx) ───────────────────────────────────────────────────
1256
+ session_check_failed: wire(
1257
+ 'server',
1258
+ 503,
1259
+ true,
1260
+ 'Ablo could not verify the current login session. Retry shortly.'
1261
+ ),
1262
+ mint_failed: wire(
1263
+ 'server',
1264
+ 502,
1265
+ true,
1266
+ 'Ablo could not mint the requested runtime credential. Retry shortly.'
1267
+ ),
1268
+ server_side_only: wire(
1269
+ 'permission', 403, false, 'This endpoint is available only to trusted server-side callers.'
1270
+ ),
1271
+ unknown_auth_host: wire(
1272
+ 'auth', 400, false, 'The authentication host is not recognized.'
1273
+ ),
1274
+ no_active_organization: wire(
1275
+ 'tenant', 400, false, 'Select an active organization before continuing.'
1276
+ ),
1277
+ data_session_unsupported: wire(
1278
+ 'validation', 400, false, 'This sign-in host cannot create the requested data session.'
1279
+ ),
1106
1280
  internal_error: wire(
1107
1281
  'server',
1108
1282
  500,
@@ -1140,7 +1314,7 @@ export const ERROR_CODES = {
1140
1314
  ),
1141
1315
  db_identity_mismatch: client(
1142
1316
  'client',
1143
- 'The local database belongs to a different authenticated identity or data plane.'
1317
+ 'The local database is connected to a different organization, project, or branch.'
1144
1318
  ),
1145
1319
  db_cleanup_failed: client(
1146
1320
  'client',
@@ -1306,19 +1480,19 @@ export const ERROR_CODES = {
1306
1480
  'tenant',
1307
1481
  400,
1308
1482
  false,
1309
- "This model is scoped by its connected data source (`policy: { by: 'source' }`), so its tenant is resolved from the source registration rather than a row column. Enforcing that resolution requires the write-through connect path, which is not enabled on this plane yet so the model cannot be served through the tenant-scoped read or bootstrap path without risking a cross-tenant read. If this model lives on a log plane, scope it with `by: 'column'` or `by: 'parent'` instead."
1483
+ "This model gets its tenant identity from the connected Data Source, but that connection is not ready for scoped reads yet. Ablo refused the read to prevent data from crossing tenant boundaries. Connect the branch and retry; for internally logged models, use `by: 'column'` or `by: 'parent'`."
1310
1484
  ),
1311
1485
  user_scope_not_enforced: wire(
1312
1486
  'tenant',
1313
1487
  400,
1314
1488
  false,
1315
- "Rows in this model belong to one person rather than to the whole organization, and that boundary is kept where the rows are stored. This plane is currently served from its log, which carries the organization and the project on every row but not the owner — so the boundary cannot be applied here, and serving the read would show one member another member's private records. It was declined whole and nothing was returned. Serve the plane from its tables, or read with a credential that acts for the organization rather than for a person."
1489
+ "Rows in this model belong to one person rather than the whole organization, but the current read source does not carry the owner identity needed to enforce that boundary. Ablo returned nothing to prevent one member from seeing another member's private records. Use a read source that preserves owner identity, or a credential that acts for the organization."
1316
1490
  ),
1317
1491
  model_not_provisioned: wire(
1318
1492
  'tenant',
1319
1493
  409,
1320
1494
  false,
1321
- "This model is in the plane's registered schema, but its table has not been provisioned yet. Registering a schema with `ablo push` records the model; a plane's physical tables are created separately, out of band, so a model can appear in the schema before its table exists. Provision the plane's tables, then retry the read."
1495
+ "This model is in the branch's registered schema, but its table does not exist in the connected database yet. `ablo push` records the model but does not change a customer's database. Apply the table migration in your database, then retry the read."
1322
1496
  ),
1323
1497
  schema_table_invalid: wire('schema', 500, false, "The model's table identifier is invalid."),
1324
1498
  schema_scope_invalid: wire(
@@ -1355,7 +1529,9 @@ export const ERROR_CODES = {
1355
1529
  'permission',
1356
1530
  403,
1357
1531
  false,
1358
- 'Schema registration could not create tables in the target database: the engine is not permitted to run DDL there.'
1532
+ 'Schema registration could not create tables in the target database: the engine is not permitted to run DDL there.',
1533
+ undefined,
1534
+ true,
1359
1535
  ),
1360
1536
  model_query_failed: wire(
1361
1537
  'validation',
@@ -1492,7 +1668,7 @@ export const ERROR_CODES = {
1492
1668
  'not_found',
1493
1669
  404,
1494
1670
  false,
1495
- 'No claim of yours exists with the given id. It was released, it expired, or it was never acquired on this plane.'
1671
+ 'No claim of yours exists with the given id. It was released, expired, or belongs to a different branch.'
1496
1672
  ),
1497
1673
  invalid_participant_kind: wire(
1498
1674
  'validation',
@@ -1549,6 +1725,10 @@ export const ERROR_CODES = {
1549
1725
  ),
1550
1726
  request_too_large: wire('validation', 413, false, 'The request body exceeds the maximum size.'),
1551
1727
  invalid_schema: wire('validation', 400, false, 'The submitted schema could not be parsed.'),
1728
+ operational_warning: client(
1729
+ 'server',
1730
+ 'Ablo handled an operational degradation that remains searchable for diagnosis.'
1731
+ ),
1552
1732
  incompatible_change: wire(
1553
1733
  'conflict',
1554
1734
  409,
@@ -1559,7 +1739,7 @@ export const ERROR_CODES = {
1559
1739
  'conflict',
1560
1740
  409,
1561
1741
  false,
1562
- 'A connected log plane has live rows for a mapped model that disappeared. Declare a rename or an explicit drop/reset before advancing the schema.'
1742
+ 'This branch has live replicated rows for a mapped model that disappeared. Declare a rename or an explicit drop/reset before advancing the schema.'
1563
1743
  ),
1564
1744
  } as const satisfies Record<string, ErrorCodeSpec>;
1565
1745