@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,484 @@
1
+ grant usage on schema agent_board to authenticated;
2
+
3
+ grant select on agent_board.schema_version to authenticated;
4
+
5
+ grant select on agent_board.agents,
6
+ agent_board.threads,
7
+ agent_board.messages,
8
+ agent_board.tasks,
9
+ agent_board.task_events,
10
+ agent_board.claims,
11
+ agent_board.delegations,
12
+ agent_board.artifacts,
13
+ agent_board.conflicts,
14
+ agent_board.escalations,
15
+ agent_board.read_receipts,
16
+ agent_board.presence,
17
+ agent_board.boards,
18
+ agent_board.board_memberships,
19
+ agent_board.delivery_recipients,
20
+ agent_board.inbox_items,
21
+ agent_board.callback_endpoints,
22
+ agent_board.callback_subscriptions,
23
+ agent_board.callback_outbox_events,
24
+ agent_board.callback_delivery_attempts
25
+ to authenticated;
26
+
27
+ alter table agent_board.agents enable row level security;
28
+ alter table agent_board.threads enable row level security;
29
+ alter table agent_board.messages enable row level security;
30
+ alter table agent_board.tasks enable row level security;
31
+ alter table agent_board.task_events enable row level security;
32
+ alter table agent_board.claims enable row level security;
33
+ alter table agent_board.delegations enable row level security;
34
+ alter table agent_board.artifacts enable row level security;
35
+ alter table agent_board.conflicts enable row level security;
36
+ alter table agent_board.escalations enable row level security;
37
+ alter table agent_board.read_receipts enable row level security;
38
+ alter table agent_board.presence enable row level security;
39
+ alter table agent_board.boards enable row level security;
40
+ alter table agent_board.board_memberships enable row level security;
41
+ alter table agent_board.delivery_recipients enable row level security;
42
+ alter table agent_board.inbox_items enable row level security;
43
+ alter table agent_board.callback_endpoints enable row level security;
44
+ alter table agent_board.callback_subscriptions enable row level security;
45
+ alter table agent_board.callback_outbox_events enable row level security;
46
+ alter table agent_board.callback_delivery_attempts enable row level security;
47
+
48
+ drop policy if exists agent_board_authenticated_select_agents on agent_board.agents;
49
+ create policy agent_board_authenticated_select_agents
50
+ on agent_board.agents
51
+ as permissive
52
+ for select
53
+ to authenticated
54
+ using (agent_board.current_agent_id() is not null);
55
+
56
+ drop policy if exists agent_board_authenticated_select_threads on agent_board.threads;
57
+ create policy agent_board_authenticated_select_threads
58
+ on agent_board.threads
59
+ as permissive
60
+ for select
61
+ to authenticated
62
+ using (
63
+ created_by_agent_id = agent_board.current_agent_id()
64
+ or exists (
65
+ select 1
66
+ from agent_board.messages m
67
+ where m.thread_id = id
68
+ and (
69
+ m.author_agent_id = agent_board.current_agent_id()
70
+ or exists (
71
+ select 1
72
+ from agent_board.delivery_recipients dr
73
+ where dr.source_message_id = m.id
74
+ and (
75
+ dr.recipient_agent_id = agent_board.current_agent_id()
76
+ or (
77
+ dr.board_id is not null
78
+ and exists (
79
+ select 1
80
+ from agent_board.board_memberships bm
81
+ where bm.board_id = dr.board_id
82
+ and bm.agent_id = agent_board.current_agent_id()
83
+ and bm.membership_state in ('joined', 'subscribed')
84
+ )
85
+ )
86
+ )
87
+ )
88
+ )
89
+ )
90
+ or exists (
91
+ select 1
92
+ from agent_board.tasks t
93
+ where t.thread_id = id
94
+ and (
95
+ t.created_by_agent_id = agent_board.current_agent_id()
96
+ or t.assigned_agent_id = agent_board.current_agent_id()
97
+ or exists (
98
+ select 1
99
+ from agent_board.delivery_recipients dr
100
+ where dr.source_task_id = t.id
101
+ and (
102
+ dr.recipient_agent_id = agent_board.current_agent_id()
103
+ or (
104
+ dr.board_id is not null
105
+ and exists (
106
+ select 1
107
+ from agent_board.board_memberships bm
108
+ where bm.board_id = dr.board_id
109
+ and bm.agent_id = agent_board.current_agent_id()
110
+ and bm.membership_state in ('joined', 'subscribed')
111
+ )
112
+ )
113
+ )
114
+ )
115
+ )
116
+ )
117
+ or exists (
118
+ select 1
119
+ from agent_board.read_receipts rr
120
+ where rr.thread_id = id
121
+ and rr.agent_id = agent_board.current_agent_id()
122
+ )
123
+ );
124
+
125
+ drop policy if exists agent_board_authenticated_select_messages on agent_board.messages;
126
+ create policy agent_board_authenticated_select_messages
127
+ on agent_board.messages
128
+ as permissive
129
+ for select
130
+ to authenticated
131
+ using (
132
+ author_agent_id = agent_board.current_agent_id()
133
+ or exists (
134
+ select 1
135
+ from agent_board.delivery_recipients dr
136
+ where dr.source_message_id = id
137
+ and (
138
+ dr.recipient_agent_id = agent_board.current_agent_id()
139
+ or (
140
+ dr.board_id is not null
141
+ and exists (
142
+ select 1
143
+ from agent_board.board_memberships bm
144
+ where bm.board_id = dr.board_id
145
+ and bm.agent_id = agent_board.current_agent_id()
146
+ and bm.membership_state in ('joined', 'subscribed')
147
+ )
148
+ )
149
+ )
150
+ )
151
+ );
152
+
153
+ drop policy if exists agent_board_authenticated_select_tasks on agent_board.tasks;
154
+ create policy agent_board_authenticated_select_tasks
155
+ on agent_board.tasks
156
+ as permissive
157
+ for select
158
+ to authenticated
159
+ using (
160
+ created_by_agent_id = agent_board.current_agent_id()
161
+ or assigned_agent_id = agent_board.current_agent_id()
162
+ or exists (
163
+ select 1
164
+ from agent_board.delivery_recipients dr
165
+ where dr.source_task_id = id
166
+ and (
167
+ dr.recipient_agent_id = agent_board.current_agent_id()
168
+ or (
169
+ dr.board_id is not null
170
+ and exists (
171
+ select 1
172
+ from agent_board.board_memberships bm
173
+ where bm.board_id = dr.board_id
174
+ and bm.agent_id = agent_board.current_agent_id()
175
+ and bm.membership_state in ('joined', 'subscribed')
176
+ )
177
+ )
178
+ )
179
+ )
180
+ );
181
+
182
+ drop policy if exists agent_board_authenticated_select_task_events on agent_board.task_events;
183
+ create policy agent_board_authenticated_select_task_events
184
+ on agent_board.task_events
185
+ as permissive
186
+ for select
187
+ to authenticated
188
+ using (
189
+ actor_agent_id = agent_board.current_agent_id()
190
+ or exists (
191
+ select 1
192
+ from agent_board.tasks t
193
+ where t.id = task_id
194
+ and (
195
+ t.created_by_agent_id = agent_board.current_agent_id()
196
+ or t.assigned_agent_id = agent_board.current_agent_id()
197
+ )
198
+ )
199
+ );
200
+
201
+ drop policy if exists agent_board_authenticated_select_claims on agent_board.claims;
202
+ create policy agent_board_authenticated_select_claims
203
+ on agent_board.claims
204
+ as permissive
205
+ for select
206
+ to authenticated
207
+ using (
208
+ agent_id = agent_board.current_agent_id()
209
+ or exists (
210
+ select 1
211
+ from agent_board.tasks t
212
+ where t.id = task_id
213
+ and (
214
+ t.created_by_agent_id = agent_board.current_agent_id()
215
+ or t.assigned_agent_id = agent_board.current_agent_id()
216
+ )
217
+ )
218
+ );
219
+
220
+ drop policy if exists agent_board_authenticated_select_delegations on agent_board.delegations;
221
+ create policy agent_board_authenticated_select_delegations
222
+ on agent_board.delegations
223
+ as permissive
224
+ for select
225
+ to authenticated
226
+ using (
227
+ delegated_by_agent_id = agent_board.current_agent_id()
228
+ or delegated_to_agent_id = agent_board.current_agent_id()
229
+ or exists (
230
+ select 1
231
+ from agent_board.tasks t
232
+ where t.id in (parent_task_id, child_task_id)
233
+ and (
234
+ t.created_by_agent_id = agent_board.current_agent_id()
235
+ or t.assigned_agent_id = agent_board.current_agent_id()
236
+ )
237
+ )
238
+ );
239
+
240
+ drop policy if exists agent_board_authenticated_select_artifacts on agent_board.artifacts;
241
+ create policy agent_board_authenticated_select_artifacts
242
+ on agent_board.artifacts
243
+ as permissive
244
+ for select
245
+ to authenticated
246
+ using (
247
+ created_by_agent_id = agent_board.current_agent_id()
248
+ or exists (
249
+ select 1
250
+ from agent_board.tasks t
251
+ where t.id = task_id
252
+ and (
253
+ t.created_by_agent_id = agent_board.current_agent_id()
254
+ or t.assigned_agent_id = agent_board.current_agent_id()
255
+ )
256
+ )
257
+ or exists (
258
+ select 1
259
+ from agent_board.messages m
260
+ where m.id = message_id
261
+ and m.author_agent_id = agent_board.current_agent_id()
262
+ )
263
+ or exists (
264
+ select 1
265
+ from agent_board.messages m
266
+ where m.id = message_id
267
+ and exists (
268
+ select 1
269
+ from agent_board.delivery_recipients dr
270
+ where dr.source_message_id = m.id
271
+ and dr.recipient_agent_id = agent_board.current_agent_id()
272
+ )
273
+ )
274
+ or exists (
275
+ select 1
276
+ from agent_board.task_events te
277
+ where te.id = task_event_id
278
+ and te.actor_agent_id = agent_board.current_agent_id()
279
+ )
280
+ );
281
+
282
+ drop policy if exists agent_board_authenticated_select_conflicts on agent_board.conflicts;
283
+ create policy agent_board_authenticated_select_conflicts
284
+ on agent_board.conflicts
285
+ as permissive
286
+ for select
287
+ to authenticated
288
+ using (
289
+ raised_by_agent_id = agent_board.current_agent_id()
290
+ or resolved_by_agent_id = agent_board.current_agent_id()
291
+ or exists (
292
+ select 1
293
+ from agent_board.tasks t
294
+ where t.id = task_id
295
+ and (
296
+ t.created_by_agent_id = agent_board.current_agent_id()
297
+ or t.assigned_agent_id = agent_board.current_agent_id()
298
+ )
299
+ )
300
+ );
301
+
302
+ drop policy if exists agent_board_authenticated_select_escalations on agent_board.escalations;
303
+ create policy agent_board_authenticated_select_escalations
304
+ on agent_board.escalations
305
+ as permissive
306
+ for select
307
+ to authenticated
308
+ using (
309
+ raised_by_agent_id = agent_board.current_agent_id()
310
+ or resolved_by_agent_id = agent_board.current_agent_id()
311
+ or assigned_agent_id = agent_board.current_agent_id()
312
+ or exists (
313
+ select 1
314
+ from agent_board.tasks t
315
+ where t.id = task_id
316
+ and (
317
+ t.created_by_agent_id = agent_board.current_agent_id()
318
+ or t.assigned_agent_id = agent_board.current_agent_id()
319
+ )
320
+ )
321
+ );
322
+
323
+ drop policy if exists agent_board_authenticated_select_read_receipts on agent_board.read_receipts;
324
+ create policy agent_board_authenticated_select_read_receipts
325
+ on agent_board.read_receipts
326
+ as permissive
327
+ for select
328
+ to authenticated
329
+ using (
330
+ agent_id = agent_board.current_agent_id()
331
+ );
332
+
333
+ drop policy if exists agent_board_authenticated_select_presence on agent_board.presence;
334
+ create policy agent_board_authenticated_select_presence
335
+ on agent_board.presence
336
+ as permissive
337
+ for select
338
+ to authenticated
339
+ using (
340
+ agent_id = agent_board.current_agent_id()
341
+ );
342
+
343
+ drop policy if exists agent_board_authenticated_select_boards on agent_board.boards;
344
+ create policy agent_board_authenticated_select_boards
345
+ on agent_board.boards
346
+ as permissive
347
+ for select
348
+ to authenticated
349
+ using (
350
+ created_by_agent_id = agent_board.current_agent_id()
351
+ or exists (
352
+ select 1
353
+ from agent_board.board_memberships bm
354
+ where bm.board_id = id
355
+ and bm.agent_id = agent_board.current_agent_id()
356
+ and bm.membership_state in ('joined', 'subscribed')
357
+ )
358
+ );
359
+
360
+ drop policy if exists agent_board_authenticated_select_board_memberships on agent_board.board_memberships;
361
+ create policy agent_board_authenticated_select_board_memberships
362
+ on agent_board.board_memberships
363
+ as permissive
364
+ for select
365
+ to authenticated
366
+ using (
367
+ agent_id = agent_board.current_agent_id()
368
+ );
369
+
370
+ drop policy if exists agent_board_authenticated_select_delivery_recipients on agent_board.delivery_recipients;
371
+ create policy agent_board_authenticated_select_delivery_recipients
372
+ on agent_board.delivery_recipients
373
+ as permissive
374
+ for select
375
+ to authenticated
376
+ using (
377
+ recipient_agent_id = agent_board.current_agent_id()
378
+ or (
379
+ board_id is not null
380
+ and exists (
381
+ select 1
382
+ from agent_board.board_memberships bm
383
+ where bm.board_id = board_id
384
+ and bm.agent_id = agent_board.current_agent_id()
385
+ and bm.membership_state in ('joined', 'subscribed')
386
+ )
387
+ )
388
+ );
389
+
390
+ drop policy if exists agent_board_authenticated_select_inbox_items on agent_board.inbox_items;
391
+ create policy agent_board_authenticated_select_inbox_items
392
+ on agent_board.inbox_items
393
+ as permissive
394
+ for select
395
+ to authenticated
396
+ using (
397
+ recipient_agent_id = agent_board.current_agent_id()
398
+ or (
399
+ board_id is not null
400
+ and visible_in_board_feed = true
401
+ and exists (
402
+ select 1
403
+ from agent_board.board_memberships bm
404
+ where bm.board_id = board_id
405
+ and bm.agent_id = agent_board.current_agent_id()
406
+ and bm.membership_state in ('joined', 'subscribed')
407
+ )
408
+ )
409
+ );
410
+
411
+ drop policy if exists agent_board_authenticated_select_callback_endpoints on agent_board.callback_endpoints;
412
+ create policy agent_board_authenticated_select_callback_endpoints
413
+ on agent_board.callback_endpoints
414
+ as permissive
415
+ for select
416
+ to authenticated
417
+ using (owner_agent_id = agent_board.current_agent_id());
418
+
419
+ drop policy if exists agent_board_authenticated_select_callback_subscriptions on agent_board.callback_subscriptions;
420
+ create policy agent_board_authenticated_select_callback_subscriptions
421
+ on agent_board.callback_subscriptions
422
+ as permissive
423
+ for select
424
+ to authenticated
425
+ using (
426
+ agent_id = agent_board.current_agent_id()
427
+ or exists (
428
+ select 1
429
+ from agent_board.callback_endpoints ce
430
+ where ce.id = endpoint_id
431
+ and ce.owner_agent_id = agent_board.current_agent_id()
432
+ )
433
+ );
434
+
435
+ drop policy if exists agent_board_authenticated_select_callback_outbox_events on agent_board.callback_outbox_events;
436
+ create policy agent_board_authenticated_select_callback_outbox_events
437
+ on agent_board.callback_outbox_events
438
+ as permissive
439
+ for select
440
+ to authenticated
441
+ using (
442
+ recipient_agent_id = agent_board.current_agent_id()
443
+ or exists (
444
+ select 1
445
+ from agent_board.callback_subscriptions cs
446
+ join agent_board.callback_endpoints ce on ce.id = cs.endpoint_id
447
+ where cs.id = subscription_id
448
+ and (
449
+ cs.agent_id = agent_board.current_agent_id()
450
+ or ce.owner_agent_id = agent_board.current_agent_id()
451
+ )
452
+ )
453
+ );
454
+
455
+ drop policy if exists agent_board_authenticated_select_callback_delivery_attempts on agent_board.callback_delivery_attempts;
456
+ create policy agent_board_authenticated_select_callback_delivery_attempts
457
+ on agent_board.callback_delivery_attempts
458
+ as permissive
459
+ for select
460
+ to authenticated
461
+ using (
462
+ exists (
463
+ select 1
464
+ from agent_board.callback_outbox_events coe
465
+ where coe.id = outbox_event_id
466
+ and (
467
+ coe.recipient_agent_id = agent_board.current_agent_id()
468
+ or exists (
469
+ select 1
470
+ from agent_board.callback_subscriptions cs
471
+ join agent_board.callback_endpoints ce on ce.id = cs.endpoint_id
472
+ where cs.id = coe.subscription_id
473
+ and (
474
+ cs.agent_id = agent_board.current_agent_id()
475
+ or ce.owner_agent_id = agent_board.current_agent_id()
476
+ )
477
+ )
478
+ )
479
+ )
480
+ );
481
+
482
+ insert into agent_board.schema_version (version)
483
+ values (3)
484
+ on conflict ((true)) do update set version = excluded.version;
@@ -0,0 +1,34 @@
1
+ create table if not exists agent_board.ops_backup_runs (
2
+ id uuid primary key default gen_random_uuid(),
3
+ backup_scope text not null default 'schema' check (backup_scope in ('schema')),
4
+ schema_name text not null default 'agent_board',
5
+ backup_status text not null check (backup_status in ('started', 'completed', 'failed')),
6
+ started_at timestamptz not null default now(),
7
+ completed_at timestamptz,
8
+ duration_seconds integer,
9
+ artifact_storage_uri text,
10
+ artifact_checksum_sha256 text,
11
+ artifact_size_bytes bigint,
12
+ triggered_by text not null default 'scheduler',
13
+ project_ref text,
14
+ restore_target_project_ref text,
15
+ restore_status text not null default 'not_run' check (restore_status in ('not_run', 'script_ready', 'completed', 'failed')),
16
+ restore_checked_at timestamptz,
17
+ metadata jsonb not null default '{}'::jsonb
18
+ );
19
+
20
+ comment on table agent_board.ops_backup_runs is 'Schema-scoped agent_board backup evidence for scheduled backups and restore drills.';
21
+
22
+ create index if not exists idx_agent_board_ops_backup_runs_started_at
23
+ on agent_board.ops_backup_runs(started_at desc);
24
+
25
+ grant select on agent_board.ops_backup_runs to service_role;
26
+
27
+ alter table agent_board.ops_backup_runs enable row level security;
28
+
29
+ drop policy if exists "agent_board_service_all_ops_backup_runs" on agent_board.ops_backup_runs;
30
+ create policy "agent_board_service_all_ops_backup_runs"
31
+ on agent_board.ops_backup_runs
32
+ to service_role
33
+ using (true)
34
+ with check (true);
@@ -0,0 +1,160 @@
1
+ create table if not exists agent_board.ops_retention_runs (
2
+ id uuid primary key default gen_random_uuid(),
3
+ retention_status text not null check (retention_status in ('started', 'completed', 'failed')),
4
+ triggered_by text not null default 'scheduler',
5
+ started_at timestamptz not null default now(),
6
+ completed_at timestamptz,
7
+ summary jsonb not null default '{}'::jsonb
8
+ );
9
+
10
+ comment on table agent_board.ops_retention_runs is 'Soft-delete retention evidence for agent_board scheduled retention passes.';
11
+
12
+ create index if not exists idx_agent_board_ops_retention_runs_started_at
13
+ on agent_board.ops_retention_runs(started_at desc);
14
+
15
+ grant select on agent_board.ops_retention_runs to service_role;
16
+
17
+ alter table agent_board.ops_retention_runs enable row level security;
18
+
19
+ drop policy if exists "agent_board_service_all_ops_retention_runs" on agent_board.ops_retention_runs;
20
+ create policy "agent_board_service_all_ops_retention_runs"
21
+ on agent_board.ops_retention_runs
22
+ to service_role
23
+ using (true)
24
+ with check (true);
25
+
26
+ insert into public.app_settings(key, value)
27
+ values
28
+ ('agent_board_retention_messages_days', to_jsonb(365)),
29
+ ('agent_board_retention_task_events_days', to_jsonb(365)),
30
+ ('agent_board_retention_presence_days', to_jsonb(30)),
31
+ ('agent_board_retention_callback_outbox_days', to_jsonb(90)),
32
+ ('agent_board_retention_callback_attempts_days', to_jsonb(180)),
33
+ ('agent_board_retention_inbox_items_days', to_jsonb(180)),
34
+ ('agent_board_retention_delivery_recipients_days', to_jsonb(180)),
35
+ ('agent_board_retention_board_memberships_days', to_jsonb(180))
36
+ on conflict (key) do nothing;
37
+
38
+ create or replace function agent_board.mark_retention_metadata(
39
+ p_table_name text,
40
+ p_cutoff timestamptz,
41
+ p_run_id uuid,
42
+ p_reason text default 'scheduled_soft_delete'
43
+ )
44
+ returns bigint
45
+ language plpgsql
46
+ security definer
47
+ set search_path = agent_board, public
48
+ as $$
49
+ declare
50
+ v_sql text;
51
+ v_count bigint;
52
+ begin
53
+ if p_table_name not in (
54
+ 'messages',
55
+ 'task_events',
56
+ 'presence',
57
+ 'callback_outbox_events',
58
+ 'callback_delivery_attempts',
59
+ 'inbox_items',
60
+ 'delivery_recipients',
61
+ 'board_memberships'
62
+ ) then
63
+ raise exception 'unsupported retention table %', p_table_name using errcode = '22023';
64
+ end if;
65
+
66
+ if p_table_name = 'presence' then
67
+ v_sql := format(
68
+ $retention$
69
+ with target_rows as (
70
+ update agent_board.%1$I
71
+ set metadata = coalesce(metadata, '{}'::jsonb) || jsonb_build_object(
72
+ 'ops_retained_at', now(),
73
+ 'ops_retention_run_id', %2$L,
74
+ 'ops_retention_reason', %3$L
75
+ ),
76
+ updated_at = now()
77
+ where expires_at < %4$L::timestamptz
78
+ and coalesce(metadata->>'ops_retained_at', '') = ''
79
+ returning 1
80
+ )
81
+ select count(*)::bigint from target_rows
82
+ $retention$,
83
+ p_table_name,
84
+ p_run_id::text,
85
+ p_reason,
86
+ p_cutoff::text
87
+ );
88
+ elsif p_table_name = 'task_events' then
89
+ v_sql := format(
90
+ $retention$
91
+ with target_rows as (
92
+ update agent_board.%1$I
93
+ set payload = coalesce(payload, '{}'::jsonb) || jsonb_build_object(
94
+ 'ops_retained_at', now(),
95
+ 'ops_retention_run_id', %2$L,
96
+ 'ops_retention_reason', %3$L
97
+ )
98
+ where created_at < %4$L::timestamptz
99
+ and coalesce(payload->>'ops_retained_at', '') = ''
100
+ returning 1
101
+ )
102
+ select count(*)::bigint from target_rows
103
+ $retention$,
104
+ p_table_name,
105
+ p_run_id::text,
106
+ p_reason,
107
+ p_cutoff::text
108
+ );
109
+ elsif p_table_name = 'board_memberships' then
110
+ v_sql := format(
111
+ $retention$
112
+ with target_rows as (
113
+ update agent_board.%1$I
114
+ set metadata = coalesce(metadata, '{}'::jsonb) || jsonb_build_object(
115
+ 'ops_retained_at', now(),
116
+ 'ops_retention_run_id', %2$L,
117
+ 'ops_retention_reason', %3$L
118
+ ),
119
+ updated_at = now()
120
+ where membership_state in ('left', 'removed')
121
+ and left_at < %4$L::timestamptz
122
+ and coalesce(metadata->>'ops_retained_at', '') = ''
123
+ returning 1
124
+ )
125
+ select count(*)::bigint from target_rows
126
+ $retention$,
127
+ p_table_name,
128
+ p_run_id::text,
129
+ p_reason,
130
+ p_cutoff::text
131
+ );
132
+ else
133
+ v_sql := format(
134
+ $retention$
135
+ with target_rows as (
136
+ update agent_board.%1$I
137
+ set metadata = coalesce(metadata, '{}'::jsonb) || jsonb_build_object(
138
+ 'ops_retained_at', now(),
139
+ 'ops_retention_run_id', %2$L,
140
+ 'ops_retention_reason', %3$L
141
+ )
142
+ where created_at < %4$L::timestamptz
143
+ and coalesce(metadata->>'ops_retained_at', '') = ''
144
+ returning 1
145
+ )
146
+ select count(*)::bigint from target_rows
147
+ $retention$,
148
+ p_table_name,
149
+ p_run_id::text,
150
+ p_reason,
151
+ p_cutoff::text
152
+ );
153
+ end if;
154
+
155
+ execute v_sql into v_count;
156
+ return coalesce(v_count, 0);
157
+ end;
158
+ $$;
159
+
160
+ grant execute on function agent_board.mark_retention_metadata(text, timestamptz, uuid, text) to service_role;