@consiliency/agent-board-schema 1.2.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 (92) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +11 -0
  3. package/README.md +55 -0
  4. package/bin/agent-board-schema.js +97 -0
  5. package/fixtures/already-applied.json +181 -0
  6. package/fixtures/divergent-history.json +173 -0
  7. package/fixtures/production-history.json +173 -0
  8. package/migrations/20260501000001_agent_board_schema_version.sql +12 -0
  9. package/migrations/20260502000000_agent_board_schema.sql +180 -0
  10. package/migrations/20260503000000_agent_board_rpcs.sql +422 -0
  11. package/migrations/20260503000001_agent_board_coordination_rpcs.sql +229 -0
  12. package/migrations/20260504000000_agent_board_read_models.sql +206 -0
  13. package/migrations/20260505000000_agent_board_inbox_schema.sql +341 -0
  14. package/migrations/20260506000000_agent_board_inbox_delivery_rpcs.sql +1200 -0
  15. package/migrations/20260506000001_agent_board_inbox_callback_rpcs.sql +330 -0
  16. package/migrations/20260507000000_agent_board_inbox_reads.sql +334 -0
  17. package/migrations/20260508000000_agent_board_callback_runtime.sql +453 -0
  18. package/migrations/20260508000001_agent_board_inbox_service_role_reads.sql +10 -0
  19. package/migrations/20260510000000_agent_board_dblint_repairs.sql +413 -0
  20. package/migrations/20260511000000_agent_board_auth_schema.sql +146 -0
  21. package/migrations/20260511000001_agent_board_auth_credentials.sql +39 -0
  22. package/migrations/20260512000000_agent_board_auth_provisioning_rpcs.sql +622 -0
  23. package/migrations/20260512000001_agent_board_auth_v1_rpcs.sql +798 -0
  24. package/migrations/20260512000002_agent_board_auth_v2_rpcs.sql +1243 -0
  25. package/migrations/20260512000003_agent_board_auth_rls.sql +484 -0
  26. package/migrations/20260513000000_agent_board_backup_ops.sql +34 -0
  27. package/migrations/20260513000001_agent_board_retention.sql +160 -0
  28. package/migrations/20260513000002_agent_board_monitoring_views.sql +227 -0
  29. package/migrations/20260514000000_agent_board_auth_rls_id_qualification.sql +150 -0
  30. package/migrations/20260515000000_agent_board_boardopt.sql +493 -0
  31. package/migrations/20260516000000_agent_board_callbackcap.sql +113 -0
  32. package/migrations/20260517000000_agent_board_addressing.sql +542 -0
  33. package/migrations/20260628000000_agent_board_signing_store.sql +174 -0
  34. package/migrations/20260628000001_agent_board_signing_proof_rpcs.sql +215 -0
  35. package/migrations/20260628000002_agent_board_signature_rls_reads.sql +300 -0
  36. package/migrations/20260628000003_agent_board_signed_write_gateway.sql +288 -0
  37. package/migrations/20260628000004_agent_board_fleet_registry.sql +411 -0
  38. package/migrations/20260628000005_agent_board_fleet_policy.sql +509 -0
  39. package/migrations/20260628000006_agent_board_fleet_read_models.sql +170 -0
  40. package/migrations/20260629000100_agent_board_fleet_registry_repair.sql +411 -0
  41. package/migrations/20260629000101_agent_board_fleet_policy_refresh.sql +509 -0
  42. package/migrations/20260629000102_agent_board_fleet_read_models_refresh.sql +170 -0
  43. package/migrations/20260630000100_agent_board_harness_kind_open_vocab.sql +15 -0
  44. package/migrations/20260710000000_agent_board_authbound_boundary.sql +155 -0
  45. package/migrations/20260710000100_agent_board_schema_version_rpc.sql +14 -0
  46. package/migrations/20260710000400_agent_board_nonfleet_identity.sql +87 -0
  47. package/migrations/20260710000401_agent_board_workload_providers.sql +39 -0
  48. package/migrations/20260711000000_agent_board_provision_system_fail_closed.sql +242 -0
  49. package/migrations/20260711000100_canary_signed_fleet_enforcement.sql +234 -0
  50. package/migrations/20260712000000_agent_board_bundle_endpoint_compat.sql +97 -0
  51. package/migrations/20260713000000_agent_board_rls_reassert_board_scoping.sql +90 -0
  52. package/package.json +42 -0
  53. package/provenance.json +580 -0
  54. package/schema-manifest.json +458 -0
  55. package/src/check.js +23 -0
  56. package/src/index.js +50 -0
  57. package/src/materialize.js +73 -0
  58. package/src/plan.js +44 -0
  59. package/src/tooling.test.mjs +97 -0
  60. package/tests/agent_board_addressing.test.sql +166 -0
  61. package/tests/agent_board_auth_rls.test.sql +243 -0
  62. package/tests/agent_board_auth_rls_id_qualification.test.sql +225 -0
  63. package/tests/agent_board_auth_rpcs.test.sql +311 -0
  64. package/tests/agent_board_auth_schema.test.sql +411 -0
  65. package/tests/agent_board_authbound_boundary.test.sql +148 -0
  66. package/tests/agent_board_boardopt.test.sql +170 -0
  67. package/tests/agent_board_bundle_endpoint_compat.test.sql +49 -0
  68. package/tests/agent_board_callback_runtime.test.sql +425 -0
  69. package/tests/agent_board_callbackcap.test.sql +222 -0
  70. package/tests/agent_board_canary_signed_fleet_enforcement.test.sql +60 -0
  71. package/tests/agent_board_dblint_rpcs.test.sql +247 -0
  72. package/tests/agent_board_fleet_policy.test.sql +370 -0
  73. package/tests/agent_board_fleet_read_models.test.sql +158 -0
  74. package/tests/agent_board_fleet_registry.test.sql +149 -0
  75. package/tests/agent_board_inbox_reads.test.sql +471 -0
  76. package/tests/agent_board_inbox_rpcs.test.sql +452 -0
  77. package/tests/agent_board_inbox_schema.test.sql +355 -0
  78. package/tests/agent_board_nonfleet_identity.test.sql +26 -0
  79. package/tests/agent_board_rls_reassert_board_scoping.test.sql +39 -0
  80. package/tests/agent_board_schema_version.test.sql +24 -0
  81. package/tests/agent_board_schema_version_rpc.test.sql +40 -0
  82. package/tests/agent_board_signature_rls_reads.test.sql +133 -0
  83. package/tests/agent_board_signed_write_gateway.test.sql +219 -0
  84. package/tests/agent_board_signing_proof_rpcs.test.sql +143 -0
  85. package/tests/agent_board_signing_store.test.sql +183 -0
  86. package/tests/agent_board_smoke.test.sql +12 -0
  87. package/tests/agent_board_v1_invariants.test.sql +298 -0
  88. package/tests/agent_board_v2_invariants.test.sql +753 -0
  89. package/tests/agent_board_workload_providers.test.sql +15 -0
  90. package/tests/fixtures/adopter-base.sql +19 -0
  91. package/tests/fixtures/operator-hardening.sql +27 -0
  92. package/tests/fixtures/test-prereqs.sql +84 -0
@@ -0,0 +1,753 @@
1
+ create extension if not exists pgtap with schema extensions;
2
+ set search_path = extensions, public, agent_board;
3
+
4
+ begin;
5
+
6
+ select plan(20);
7
+
8
+ insert into agent_board.agents(id, agent_type, handle, display_name)
9
+ values
10
+ ('20000000-0000-0000-0000-000000000101', 'agent', 'v2_sender', 'V2 Sender'),
11
+ ('20000000-0000-0000-0000-000000000102', 'agent', 'v2_direct_nonmember', 'V2 Direct Nonmember'),
12
+ ('20000000-0000-0000-0000-000000000103', 'agent', 'v2_board_member', 'V2 Board Member'),
13
+ ('20000000-0000-0000-0000-000000000104', 'agent', 'v2_board_subscriber', 'V2 Board Subscriber'),
14
+ ('20000000-0000-0000-0000-000000000105', 'agent', 'v2_left_member', 'V2 Left Member'),
15
+ ('20000000-0000-0000-0000-000000000106', 'agent', 'v2_claim_recipient', 'V2 Claim Recipient')
16
+ on conflict (id) do nothing;
17
+
18
+ insert into agent_board.threads(id, thread_type, created_by_agent_id, subject)
19
+ values (
20
+ '20000000-0000-0000-0000-000000000201',
21
+ 'coordination',
22
+ '20000000-0000-0000-0000-000000000101',
23
+ 'V2 invariant thread'
24
+ );
25
+
26
+ create temporary table v2_board on commit drop as
27
+ select *
28
+ from agent_board.create_board(
29
+ '20000000-0000-0000-0000-000000000101',
30
+ 'v2-invariant-board',
31
+ 'V2 Invariant Board',
32
+ 'Board for delivery invariants',
33
+ '{}'::jsonb
34
+ );
35
+
36
+ create temporary table v2_board_member_join on commit drop as
37
+ select *
38
+ from agent_board.join_board(
39
+ (select board_id from v2_board),
40
+ '20000000-0000-0000-0000-000000000103',
41
+ 'joined',
42
+ '20000000-0000-0000-0000-000000000101',
43
+ '{}'::jsonb
44
+ );
45
+
46
+ create temporary table v2_board_subscriber_join on commit drop as
47
+ select *
48
+ from agent_board.join_board(
49
+ (select board_id from v2_board),
50
+ '20000000-0000-0000-0000-000000000104',
51
+ 'subscribed',
52
+ '20000000-0000-0000-0000-000000000101',
53
+ '{}'::jsonb
54
+ );
55
+
56
+ create temporary table v2_left_member_join on commit drop as
57
+ select *
58
+ from agent_board.join_board(
59
+ (select board_id from v2_board),
60
+ '20000000-0000-0000-0000-000000000105',
61
+ 'joined',
62
+ '20000000-0000-0000-0000-000000000101',
63
+ '{}'::jsonb
64
+ );
65
+
66
+ create temporary table v2_left_member_leave on commit drop as
67
+ select *
68
+ from agent_board.leave_board(
69
+ (select board_id from v2_board),
70
+ '20000000-0000-0000-0000-000000000105',
71
+ '{}'::jsonb
72
+ );
73
+
74
+ create temporary table v2_direct_endpoint on commit drop as
75
+ select *
76
+ from agent_board.register_callback_endpoint(
77
+ '20000000-0000-0000-0000-000000000102',
78
+ 'https://example.test/v2-direct',
79
+ '{}'::jsonb
80
+ );
81
+
82
+ create temporary table v2_direct_endpoint_verified on commit drop as
83
+ select *
84
+ from agent_board.verify_callback_endpoint(
85
+ (select endpoint_id from v2_direct_endpoint),
86
+ '20000000-0000-0000-0000-000000000102',
87
+ '{"controlProofRef":"v2/direct"}'::jsonb
88
+ );
89
+
90
+ create temporary table v2_direct_subscription on commit drop as
91
+ select *
92
+ from agent_board.subscribe_callback_endpoint(
93
+ (select endpoint_id from v2_direct_endpoint),
94
+ '20000000-0000-0000-0000-000000000102',
95
+ 'own_inbox',
96
+ 'inbox.item.delivered',
97
+ null,
98
+ '{}'::jsonb
99
+ );
100
+
101
+ create temporary table v2_board_member_endpoint on commit drop as
102
+ select *
103
+ from agent_board.register_callback_endpoint(
104
+ '20000000-0000-0000-0000-000000000103',
105
+ 'https://example.test/v2-board-member',
106
+ '{}'::jsonb
107
+ );
108
+
109
+ create temporary table v2_board_member_verified on commit drop as
110
+ select *
111
+ from agent_board.verify_callback_endpoint(
112
+ (select endpoint_id from v2_board_member_endpoint),
113
+ '20000000-0000-0000-0000-000000000103',
114
+ '{"controlProofRef":"v2/board-member"}'::jsonb
115
+ );
116
+
117
+ create temporary table v2_board_member_subscription on commit drop as
118
+ select *
119
+ from agent_board.subscribe_callback_endpoint(
120
+ (select endpoint_id from v2_board_member_endpoint),
121
+ '20000000-0000-0000-0000-000000000103',
122
+ 'board',
123
+ 'board.post.delivered',
124
+ (select board_id from v2_board),
125
+ '{}'::jsonb
126
+ );
127
+
128
+ create temporary table v2_board_subscriber_endpoint on commit drop as
129
+ select *
130
+ from agent_board.register_callback_endpoint(
131
+ '20000000-0000-0000-0000-000000000104',
132
+ 'https://example.test/v2-board-subscriber',
133
+ '{}'::jsonb
134
+ );
135
+
136
+ create temporary table v2_board_subscriber_verified on commit drop as
137
+ select *
138
+ from agent_board.verify_callback_endpoint(
139
+ (select endpoint_id from v2_board_subscriber_endpoint),
140
+ '20000000-0000-0000-0000-000000000104',
141
+ '{"controlProofRef":"v2/board-subscriber"}'::jsonb
142
+ );
143
+
144
+ create temporary table v2_board_subscriber_subscription on commit drop as
145
+ select *
146
+ from agent_board.subscribe_callback_endpoint(
147
+ (select endpoint_id from v2_board_subscriber_endpoint),
148
+ '20000000-0000-0000-0000-000000000104',
149
+ 'board',
150
+ 'board.post.delivered',
151
+ (select board_id from v2_board),
152
+ '{}'::jsonb
153
+ );
154
+
155
+ create temporary table v2_claim_seed_delivery on commit drop as
156
+ select *
157
+ from agent_board.send_direct_message(
158
+ '20000000-0000-0000-0000-000000000201',
159
+ '20000000-0000-0000-0000-000000000101',
160
+ '20000000-0000-0000-0000-000000000106',
161
+ 'Claim seed subject',
162
+ 'Claim seed body',
163
+ 'comment',
164
+ 'medium',
165
+ 'v2-claim-seed',
166
+ '{}'::jsonb
167
+ );
168
+
169
+ create temporary table v2_claim_endpoint on commit drop as
170
+ select *
171
+ from agent_board.register_callback_endpoint(
172
+ '20000000-0000-0000-0000-000000000106',
173
+ 'https://example.test/v2-claim',
174
+ '{}'::jsonb
175
+ );
176
+
177
+ create temporary table v2_claim_endpoint_verified on commit drop as
178
+ select *
179
+ from agent_board.verify_callback_endpoint(
180
+ (select endpoint_id from v2_claim_endpoint),
181
+ '20000000-0000-0000-0000-000000000106',
182
+ '{"controlProofRef":"v2/claim"}'::jsonb
183
+ );
184
+
185
+ create temporary table v2_claim_subscription on commit drop as
186
+ select *
187
+ from agent_board.subscribe_callback_endpoint(
188
+ (select endpoint_id from v2_claim_endpoint),
189
+ '20000000-0000-0000-0000-000000000106',
190
+ 'own_inbox',
191
+ 'inbox.item.delivered',
192
+ null,
193
+ '{}'::jsonb
194
+ );
195
+
196
+ create temporary table v2_disable_endpoint on commit drop as
197
+ select *
198
+ from agent_board.register_callback_endpoint(
199
+ '20000000-0000-0000-0000-000000000106',
200
+ 'https://example.test/v2-disable',
201
+ '{}'::jsonb
202
+ );
203
+
204
+ create temporary table v2_disable_endpoint_verified on commit drop as
205
+ select *
206
+ from agent_board.verify_callback_endpoint(
207
+ (select endpoint_id from v2_disable_endpoint),
208
+ '20000000-0000-0000-0000-000000000106',
209
+ '{"controlProofRef":"v2/disable"}'::jsonb
210
+ );
211
+
212
+ create temporary table v2_disable_subscription on commit drop as
213
+ select *
214
+ from agent_board.subscribe_callback_endpoint(
215
+ (select endpoint_id from v2_disable_endpoint),
216
+ '20000000-0000-0000-0000-000000000106',
217
+ 'own_inbox',
218
+ 'inbox.item.delivered',
219
+ null,
220
+ '{}'::jsonb
221
+ );
222
+
223
+ create temporary table v2_direct_delivery on commit drop as
224
+ select *
225
+ from agent_board.send_direct_message(
226
+ '20000000-0000-0000-0000-000000000201',
227
+ '20000000-0000-0000-0000-000000000101',
228
+ '20000000-0000-0000-0000-000000000102',
229
+ 'Direct subject',
230
+ 'Direct body to a non-member',
231
+ 'comment',
232
+ 'high',
233
+ 'v2-direct',
234
+ '{}'::jsonb
235
+ );
236
+
237
+ select is(
238
+ (select count(*)::integer from agent_board.delivery_recipients where source_message_id = (select message_id from v2_direct_delivery)),
239
+ 1,
240
+ 'send_direct_message delivers to explicit recipients without board membership'
241
+ );
242
+
243
+ select is(
244
+ (select count(*)::integer from agent_board.inbox_items where source_message_id = (select message_id from v2_direct_delivery)),
245
+ 1,
246
+ 'send_direct_message creates one inbox row for a direct non-member recipient'
247
+ );
248
+
249
+ create temporary table v2_direct_replay on commit drop as
250
+ select *
251
+ from agent_board.send_direct_message(
252
+ '20000000-0000-0000-0000-000000000201',
253
+ '20000000-0000-0000-0000-000000000101',
254
+ '20000000-0000-0000-0000-000000000102',
255
+ 'Direct subject',
256
+ 'Direct body to a non-member',
257
+ 'comment',
258
+ 'high',
259
+ 'v2-direct',
260
+ '{}'::jsonb
261
+ );
262
+
263
+ select is(
264
+ (select message_id::text from v2_direct_replay),
265
+ (select message_id::text from v2_direct_delivery),
266
+ 'direct idempotency reuses the canonical message row'
267
+ );
268
+
269
+ select is(
270
+ (
271
+ select count(*)::integer
272
+ from agent_board.callback_outbox_events
273
+ where source_message_id = (select message_id from v2_direct_delivery)
274
+ and event_category = 'inbox.item.delivered'
275
+ ),
276
+ 1,
277
+ 'direct idempotency does not duplicate callback outbox rows'
278
+ );
279
+
280
+ create temporary table v2_multicast_delivery on commit drop as
281
+ select *
282
+ from agent_board.send_multicast_message(
283
+ '20000000-0000-0000-0000-000000000201',
284
+ '20000000-0000-0000-0000-000000000101',
285
+ array[
286
+ '20000000-0000-0000-0000-000000000102'::uuid,
287
+ '20000000-0000-0000-0000-000000000103'::uuid,
288
+ '20000000-0000-0000-0000-000000000102'::uuid
289
+ ],
290
+ 'Multicast subject',
291
+ 'Multicast body',
292
+ 'comment',
293
+ 'medium',
294
+ 'v2-multicast',
295
+ '{}'::jsonb
296
+ );
297
+
298
+ select is(
299
+ (select delivered_recipient_count from v2_multicast_delivery),
300
+ 2,
301
+ 'send_multicast_message collapses duplicate explicit recipients'
302
+ );
303
+
304
+ select is(
305
+ (select count(*)::integer from agent_board.delivery_recipients where source_message_id = (select message_id from v2_multicast_delivery)),
306
+ 2,
307
+ 'multicast delivery writes one recipient row per unique recipient'
308
+ );
309
+
310
+ create temporary table v2_board_delivery on commit drop as
311
+ select *
312
+ from agent_board.post_board_message(
313
+ '20000000-0000-0000-0000-000000000201',
314
+ '20000000-0000-0000-0000-000000000101',
315
+ (select board_id from v2_board),
316
+ 'Board subject',
317
+ 'Board body',
318
+ 'comment',
319
+ 'urgent',
320
+ 'v2-board',
321
+ '{}'::jsonb
322
+ );
323
+
324
+ select is(
325
+ (select delivered_recipient_count from v2_board_delivery),
326
+ 2,
327
+ 'post_board_message only reaches active joined or subscribed board members'
328
+ );
329
+
330
+ select is(
331
+ (
332
+ select count(*)::integer
333
+ from agent_board.delivery_recipients
334
+ where source_message_id = (select message_id from v2_board_delivery)
335
+ and recipient_agent_id in (
336
+ '20000000-0000-0000-0000-000000000102'::uuid,
337
+ '20000000-0000-0000-0000-000000000105'::uuid
338
+ )
339
+ ),
340
+ 0,
341
+ 'board delivery excludes non-members and agents who already left the board'
342
+ );
343
+
344
+ select is(
345
+ (
346
+ select count(*)::integer
347
+ from agent_board.callback_outbox_events
348
+ where source_message_id = (select message_id from v2_board_delivery)
349
+ and event_category = 'board.post.delivered'
350
+ ),
351
+ 2,
352
+ 'board post callbacks queue exactly one row per active board subscription'
353
+ );
354
+
355
+ create temporary table v2_tasking_delivery on commit drop as
356
+ select *
357
+ from agent_board.create_tasking_delivery(
358
+ '20000000-0000-0000-0000-000000000201',
359
+ '20000000-0000-0000-0000-000000000101',
360
+ 'Tasking invariant title',
361
+ 'Tasking invariant details',
362
+ array['20000000-0000-0000-0000-000000000103'::uuid],
363
+ (select board_id from v2_board),
364
+ 'high',
365
+ 'distributed',
366
+ 'v2-task',
367
+ '{}'::jsonb
368
+ );
369
+
370
+ select is(
371
+ (select delivered_recipient_count from v2_tasking_delivery),
372
+ 3,
373
+ 'tasking counts explicit and board fan-out attempts before duplicate collapse'
374
+ );
375
+
376
+ select is(
377
+ (select count(*)::integer from agent_board.delivery_recipients where source_task_id = (select task_id from v2_tasking_delivery)),
378
+ 2,
379
+ 'tasking suppresses duplicate durable recipient rows across explicit and board fan-out'
380
+ );
381
+
382
+ select *
383
+ into temporary table v2_disable_direct_callbacks
384
+ from agent_board.disable_callback_endpoint(
385
+ (select endpoint_id from v2_direct_endpoint),
386
+ '20000000-0000-0000-0000-000000000102',
387
+ 'claim isolation',
388
+ '{}'::jsonb
389
+ );
390
+
391
+ select *
392
+ into temporary table v2_disable_board_member_callbacks
393
+ from agent_board.disable_callback_endpoint(
394
+ (select endpoint_id from v2_board_member_endpoint),
395
+ '20000000-0000-0000-0000-000000000103',
396
+ 'claim isolation',
397
+ '{}'::jsonb
398
+ );
399
+
400
+ select *
401
+ into temporary table v2_disable_board_subscriber_callbacks
402
+ from agent_board.disable_callback_endpoint(
403
+ (select endpoint_id from v2_board_subscriber_endpoint),
404
+ '20000000-0000-0000-0000-000000000104',
405
+ 'claim isolation',
406
+ '{}'::jsonb
407
+ );
408
+
409
+ -- claim_callback_outbox_events polls globally. Neutralize any preexisting
410
+ -- ready rows before the claim-specific fixture so reused local databases do
411
+ -- not change the expected batch size; rollback restores the original rows.
412
+ update agent_board.callback_outbox_events
413
+ set outbox_status = 'delivered',
414
+ delivered_at = coalesce(delivered_at, now()),
415
+ updated_at = now()
416
+ where outbox_status in ('pending', 'retryable')
417
+ and available_at <= now();
418
+
419
+ insert into agent_board.callback_outbox_events(
420
+ id,
421
+ endpoint_id,
422
+ subscription_id,
423
+ recipient_agent_id,
424
+ inbox_item_id,
425
+ event_category,
426
+ delivery_mode,
427
+ source_kind,
428
+ source_message_id,
429
+ idempotency_key,
430
+ subject,
431
+ preview,
432
+ priority,
433
+ fetch_descriptor,
434
+ outbox_status,
435
+ available_at
436
+ )
437
+ values
438
+ (
439
+ '20000000-0000-0000-0000-000000000901',
440
+ (select endpoint_id from v2_claim_endpoint),
441
+ (select subscription_id from v2_claim_subscription),
442
+ '20000000-0000-0000-0000-000000000106',
443
+ (select inbox_item_id from v2_claim_seed_delivery),
444
+ 'inbox.item.delivered',
445
+ 'direct',
446
+ 'message',
447
+ (select message_id from v2_claim_seed_delivery),
448
+ 'v2-claim-pending',
449
+ 'Claim pending',
450
+ 'Pending callback',
451
+ 'medium',
452
+ jsonb_build_object('surface', 'thread', 'targetId', '20000000-0000-0000-0000-000000000201', 'view', 'agent_board.v_thread_feed', 'filters', jsonb_build_object(), 'reason', 'claim invariant'),
453
+ 'pending',
454
+ now() - interval '5 minutes'
455
+ ),
456
+ (
457
+ '20000000-0000-0000-0000-000000000902',
458
+ (select endpoint_id from v2_claim_endpoint),
459
+ (select subscription_id from v2_claim_subscription),
460
+ '20000000-0000-0000-0000-000000000106',
461
+ (select inbox_item_id from v2_claim_seed_delivery),
462
+ 'inbox.item.delivered',
463
+ 'direct',
464
+ 'message',
465
+ (select message_id from v2_claim_seed_delivery),
466
+ 'v2-claim-retryable',
467
+ 'Claim retryable',
468
+ 'Retryable callback',
469
+ 'medium',
470
+ jsonb_build_object('surface', 'thread', 'targetId', '20000000-0000-0000-0000-000000000201', 'view', 'agent_board.v_thread_feed', 'filters', jsonb_build_object(), 'reason', 'claim invariant'),
471
+ 'retryable',
472
+ now() - interval '2 minutes'
473
+ ),
474
+ (
475
+ '20000000-0000-0000-0000-000000000903',
476
+ (select endpoint_id from v2_claim_endpoint),
477
+ (select subscription_id from v2_claim_subscription),
478
+ '20000000-0000-0000-0000-000000000106',
479
+ (select inbox_item_id from v2_claim_seed_delivery),
480
+ 'inbox.item.delivered',
481
+ 'direct',
482
+ 'message',
483
+ (select message_id from v2_claim_seed_delivery),
484
+ 'v2-claim-delivered',
485
+ 'Claim delivered',
486
+ 'Delivered callback',
487
+ 'medium',
488
+ jsonb_build_object('surface', 'thread', 'targetId', '20000000-0000-0000-0000-000000000201', 'view', 'agent_board.v_thread_feed', 'filters', jsonb_build_object(), 'reason', 'claim invariant'),
489
+ 'delivered',
490
+ now() - interval '1 minute'
491
+ ),
492
+ (
493
+ '20000000-0000-0000-0000-000000000904',
494
+ (select endpoint_id from v2_claim_endpoint),
495
+ (select subscription_id from v2_claim_subscription),
496
+ '20000000-0000-0000-0000-000000000106',
497
+ (select inbox_item_id from v2_claim_seed_delivery),
498
+ 'inbox.item.delivered',
499
+ 'direct',
500
+ 'message',
501
+ (select message_id from v2_claim_seed_delivery),
502
+ 'v2-claim-failed',
503
+ 'Claim failed',
504
+ 'Failed callback',
505
+ 'medium',
506
+ jsonb_build_object('surface', 'thread', 'targetId', '20000000-0000-0000-0000-000000000201', 'view', 'agent_board.v_thread_feed', 'filters', jsonb_build_object(), 'reason', 'claim invariant'),
507
+ 'failed',
508
+ now() - interval '1 minute'
509
+ ),
510
+ (
511
+ '20000000-0000-0000-0000-000000000905',
512
+ (select endpoint_id from v2_claim_endpoint),
513
+ (select subscription_id from v2_claim_subscription),
514
+ '20000000-0000-0000-0000-000000000106',
515
+ (select inbox_item_id from v2_claim_seed_delivery),
516
+ 'inbox.item.delivered',
517
+ 'direct',
518
+ 'message',
519
+ (select message_id from v2_claim_seed_delivery),
520
+ 'v2-claim-disabled',
521
+ 'Claim disabled',
522
+ 'Disabled callback',
523
+ 'medium',
524
+ jsonb_build_object('surface', 'thread', 'targetId', '20000000-0000-0000-0000-000000000201', 'view', 'agent_board.v_thread_feed', 'filters', jsonb_build_object(), 'reason', 'claim invariant'),
525
+ 'disabled',
526
+ now() - interval '1 minute'
527
+ );
528
+
529
+ create temporary table v2_claimed_once on commit drop as
530
+ select *
531
+ from agent_board.claim_callback_outbox_events(10);
532
+
533
+ select is(
534
+ (select count(*)::integer from v2_claimed_once),
535
+ 2,
536
+ 'claim_callback_outbox_events only claims pending and retryable rows that are ready now'
537
+ );
538
+
539
+ select is(
540
+ (
541
+ select count(*)::integer
542
+ from v2_claimed_once
543
+ where outbox_event_id in (
544
+ '20000000-0000-0000-0000-000000000903'::uuid,
545
+ '20000000-0000-0000-0000-000000000904'::uuid,
546
+ '20000000-0000-0000-0000-000000000905'::uuid
547
+ )
548
+ ),
549
+ 0,
550
+ 'claim_callback_outbox_events skips delivered, failed, and disabled stop states'
551
+ );
552
+
553
+ create temporary table v2_claimed_twice on commit drop as
554
+ select *
555
+ from agent_board.claim_callback_outbox_events(10);
556
+
557
+ select is(
558
+ (select count(*)::integer from v2_claimed_twice),
559
+ 0,
560
+ 'claimed callback rows are not immediately reclaimable while already delivering'
561
+ );
562
+
563
+ insert into agent_board.callback_outbox_events(
564
+ id,
565
+ endpoint_id,
566
+ subscription_id,
567
+ recipient_agent_id,
568
+ inbox_item_id,
569
+ event_category,
570
+ delivery_mode,
571
+ source_kind,
572
+ source_message_id,
573
+ idempotency_key,
574
+ subject,
575
+ preview,
576
+ priority,
577
+ fetch_descriptor,
578
+ outbox_status,
579
+ available_at
580
+ )
581
+ values (
582
+ '20000000-0000-0000-0000-000000000906',
583
+ (select endpoint_id from v2_claim_endpoint),
584
+ (select subscription_id from v2_claim_subscription),
585
+ '20000000-0000-0000-0000-000000000106',
586
+ (select inbox_item_id from v2_claim_seed_delivery),
587
+ 'inbox.item.delivered',
588
+ 'direct',
589
+ 'message',
590
+ (select message_id from v2_claim_seed_delivery),
591
+ 'v2-claim-past-retry',
592
+ 'Past retry',
593
+ 'Past retry callback',
594
+ 'medium',
595
+ jsonb_build_object('surface', 'thread', 'targetId', '20000000-0000-0000-0000-000000000201', 'view', 'agent_board.v_thread_feed', 'filters', jsonb_build_object(), 'reason', 'claim invariant'),
596
+ 'pending',
597
+ now() - interval '1 minute'
598
+ );
599
+
600
+ select throws_ok(
601
+ $$select *
602
+ from agent_board.record_callback_delivery_retry(
603
+ '20000000-0000-0000-0000-000000000906'::uuid,
604
+ (
605
+ select id
606
+ from agent_board.callback_endpoints
607
+ where endpoint_url = 'https://example.test/v2-claim'
608
+ ),
609
+ 503,
610
+ 'temporary outage',
611
+ now() - interval '1 minute',
612
+ '{}'::jsonb
613
+ )$$,
614
+ '22023',
615
+ 'next retry timestamp must be in the future',
616
+ 'record_callback_delivery_retry requires a future next_retry_at'
617
+ );
618
+
619
+ insert into agent_board.callback_outbox_events(
620
+ id,
621
+ endpoint_id,
622
+ subscription_id,
623
+ recipient_agent_id,
624
+ inbox_item_id,
625
+ event_category,
626
+ delivery_mode,
627
+ source_kind,
628
+ source_message_id,
629
+ idempotency_key,
630
+ subject,
631
+ preview,
632
+ priority,
633
+ fetch_descriptor,
634
+ outbox_status,
635
+ available_at
636
+ )
637
+ values (
638
+ '20000000-0000-0000-0000-000000000907',
639
+ (select endpoint_id from v2_claim_endpoint),
640
+ (select subscription_id from v2_claim_subscription),
641
+ '20000000-0000-0000-0000-000000000106',
642
+ (select inbox_item_id from v2_claim_seed_delivery),
643
+ 'inbox.item.delivered',
644
+ 'direct',
645
+ 'message',
646
+ (select message_id from v2_claim_seed_delivery),
647
+ 'v2-terminal',
648
+ 'Terminal failure',
649
+ 'Terminal failure callback',
650
+ 'medium',
651
+ jsonb_build_object('surface', 'thread', 'targetId', '20000000-0000-0000-0000-000000000201', 'view', 'agent_board.v_thread_feed', 'filters', jsonb_build_object(), 'reason', 'claim invariant'),
652
+ 'pending',
653
+ now() - interval '1 minute'
654
+ );
655
+
656
+ select *
657
+ into temporary table v2_terminal_failure_result
658
+ from agent_board.record_callback_delivery_terminal_failure(
659
+ '20000000-0000-0000-0000-000000000907',
660
+ (select endpoint_id from v2_claim_endpoint),
661
+ 410,
662
+ 'gone',
663
+ '{}'::jsonb
664
+ );
665
+
666
+ select is(
667
+ (select outbox_status from v2_terminal_failure_result),
668
+ 'failed',
669
+ 'record_callback_delivery_terminal_failure moves rows into the failed stop state'
670
+ );
671
+
672
+ select is(
673
+ (
674
+ select count(*)::integer
675
+ from agent_board.claim_callback_outbox_events(10)
676
+ where outbox_event_id = '20000000-0000-0000-0000-000000000907'::uuid
677
+ ),
678
+ 0,
679
+ 'failed callback rows are not claimable again'
680
+ );
681
+
682
+ insert into agent_board.callback_outbox_events(
683
+ id,
684
+ endpoint_id,
685
+ subscription_id,
686
+ recipient_agent_id,
687
+ inbox_item_id,
688
+ event_category,
689
+ delivery_mode,
690
+ source_kind,
691
+ source_message_id,
692
+ idempotency_key,
693
+ subject,
694
+ preview,
695
+ priority,
696
+ fetch_descriptor,
697
+ outbox_status,
698
+ available_at
699
+ )
700
+ values (
701
+ '20000000-0000-0000-0000-000000000908',
702
+ (select endpoint_id from v2_disable_endpoint),
703
+ (select subscription_id from v2_disable_subscription),
704
+ '20000000-0000-0000-0000-000000000106',
705
+ (select inbox_item_id from v2_claim_seed_delivery),
706
+ 'inbox.item.delivered',
707
+ 'direct',
708
+ 'message',
709
+ (select message_id from v2_claim_seed_delivery),
710
+ 'v2-disable',
711
+ 'Disable endpoint',
712
+ 'Disable endpoint callback',
713
+ 'medium',
714
+ jsonb_build_object('surface', 'thread', 'targetId', '20000000-0000-0000-0000-000000000201', 'view', 'agent_board.v_thread_feed', 'filters', jsonb_build_object(), 'reason', 'disable invariant'),
715
+ 'pending',
716
+ now() - interval '1 minute'
717
+ );
718
+
719
+ select *
720
+ into temporary table v2_disable_result
721
+ from agent_board.disable_callback_delivery_endpoint(
722
+ (select endpoint_id from v2_disable_endpoint),
723
+ '20000000-0000-0000-0000-000000000908',
724
+ 'disablement threshold reached',
725
+ 'endpoint disabled',
726
+ '{}'::jsonb
727
+ );
728
+
729
+ select is(
730
+ (select endpoint_status from v2_disable_result),
731
+ 'disabled',
732
+ 'disable_callback_delivery_endpoint disables the endpoint'
733
+ );
734
+
735
+ select is(
736
+ (select outbox_status from agent_board.callback_outbox_events where id = '20000000-0000-0000-0000-000000000908'::uuid),
737
+ 'disabled',
738
+ 'disable_callback_delivery_endpoint marks pending outbox rows disabled'
739
+ );
740
+
741
+ select is(
742
+ (
743
+ select count(*)::integer
744
+ from agent_board.claim_callback_outbox_events(10)
745
+ where outbox_event_id = '20000000-0000-0000-0000-000000000908'::uuid
746
+ ),
747
+ 0,
748
+ 'disabled callback rows are not claimable again'
749
+ );
750
+
751
+ select * from finish();
752
+
753
+ rollback;