@consiliency/agent-board-schema 1.60.0 → 1.64.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 (100) hide show
  1. package/README.md +86 -0
  2. package/fixtures/already-applied.json +16 -0
  3. package/migrations/20261011000100_agent_board_session_identities.sql +1964 -0
  4. package/migrations/20261012000100_agent_board_sidowner.sql +209 -0
  5. package/migrations/20261014000100_agent_board_grant_target_role.sql +218 -0
  6. package/migrations/20261014000200_agent_board_last_owner.sql +239 -0
  7. package/package.json +1 -1
  8. package/schema-manifest.json +39 -3
  9. package/src/tooling.test.mjs +10 -2
  10. package/tests/agent_board_atomic_fleet_onboarding.test.sql +15 -4
  11. package/tests/agent_board_atomic_fleet_onboarding_runtime.test.sql +7 -0
  12. package/tests/agent_board_consent_emission.test.sql +30 -11
  13. package/tests/agent_board_consent_gating.test.sql +16 -2
  14. package/tests/agent_board_delegate_role_class.test.sql +1 -1
  15. package/tests/agent_board_delegation_cap_ceiling.test.sql +26 -15
  16. package/tests/agent_board_delegation_enforcement.test.sql +1 -1
  17. package/tests/agent_board_enroll_resumption.test.sql +22 -4
  18. package/tests/agent_board_enroll_token_single_use.test.sql +18 -3
  19. package/tests/agent_board_federated_lock_order_class.test.sql +2 -2
  20. package/tests/agent_board_fk_lock_edges.test.sql +77 -16
  21. package/tests/agent_board_grant_target_role.test.sql +1118 -0
  22. package/tests/agent_board_hosted_delegates.test.sql +5 -3
  23. package/tests/agent_board_invite_no_downgrade.test.sql +15 -11
  24. package/tests/agent_board_ledger_authority.test.sql +77 -12
  25. package/tests/agent_board_membership_current_row_class.test.sql +9 -1
  26. package/tests/agent_board_mint_fleet_agent_session.test.sql +2 -2
  27. package/tests/agent_board_mint_harden.test.sql +38 -27
  28. package/tests/agent_board_role_guard.test.sql +319 -0
  29. package/tests/agent_board_rolereset.test.sql +25 -29
  30. package/tests/agent_board_roomdeleg.test.sql +8 -0
  31. package/tests/agent_board_roomdeleg_lane_b.test.sql +6 -6
  32. package/tests/agent_board_roomown.test.sql +3 -1
  33. package/tests/agent_board_rowid.test.sql +7 -2
  34. package/tests/agent_board_session_identity_approval_revocation.test.sql +334 -0
  35. package/tests/agent_board_session_identity_backfill.test.sql +82 -0
  36. package/tests/agent_board_session_identity_backfill_refusal.test.sql +14 -0
  37. package/tests/agent_board_session_identity_behavior_classes.test.sql +176 -0
  38. package/tests/agent_board_session_identity_class.test.sql +118 -0
  39. package/tests/agent_board_session_identity_fingerprint.test.sql +271 -0
  40. package/tests/agent_board_session_identity_labels_limits.test.sql +205 -0
  41. package/tests/agent_board_session_identity_lease.test.sql +235 -0
  42. package/tests/agent_board_session_identity_lifetime.test.sql +230 -0
  43. package/tests/agent_board_session_identity_limits.test.sql +208 -0
  44. package/tests/agent_board_session_identity_lineage.test.sql +64 -0
  45. package/tests/agent_board_session_identity_link.test.sql +201 -0
  46. package/tests/agent_board_session_identity_mint.test.sql +233 -0
  47. package/tests/agent_board_session_identity_panel_r1.test.sql +194 -0
  48. package/tests/agent_board_session_identity_panel_r2.test.sql +174 -0
  49. package/tests/agent_board_session_identity_protected_actions.test.sql +156 -0
  50. package/tests/agent_board_session_identity_revoke_closure.test.sql +131 -0
  51. package/tests/agent_board_sidowner_leases.test.sql +107 -0
  52. package/tests/agent_board_sidowner_owner_required.test.sql +97 -0
  53. package/tests/agent_board_sidowner_read_class.test.sql +171 -0
  54. package/tests/agent_board_sidowner_reads.test.sql +136 -0
  55. package/tests/agent_board_signer_lifecycle.test.sql +26 -4
  56. package/tests/agent_board_signing_challenge_pending_cap.test.sql +1 -1
  57. package/tests/agent_board_write_enforcement_class.test.sql +74 -15
  58. package/tests/catalog/function_execute_privileges.json +227 -2
  59. package/tests/catalog/p_metadata_parameters.json +102 -2
  60. package/tests/isolation/expected/grant_target_role_race.out +348 -0
  61. package/tests/isolation/expected/last_owner_departure.out +562 -0
  62. package/tests/isolation/expected/session_identity_limits.out +165 -0
  63. package/tests/isolation/expected/session_identity_links.out +235 -0
  64. package/tests/isolation/expected/session_identity_reclaim.out +253 -0
  65. package/tests/isolation/expected/session_identity_renew_revoke.out +115 -0
  66. package/tests/isolation/expected/session_identity_renew_revoke_key_gate.out +138 -0
  67. package/tests/isolation/expected/session_identity_tombstone_onboarding.out +247 -0
  68. package/tests/isolation/specs/atomic_fleet_authority_races.spec +8 -3
  69. package/tests/isolation/specs/atomic_fleet_binding_revoke.spec +0 -3
  70. package/tests/isolation/specs/atomic_fleet_board_archive.spec +0 -3
  71. package/tests/isolation/specs/atomic_fleet_human_fresh_revoke.spec +0 -3
  72. package/tests/isolation/specs/atomic_fleet_identity_disposition.spec +0 -3
  73. package/tests/isolation/specs/atomic_fleet_lifecycle_races.spec +0 -3
  74. package/tests/isolation/specs/atomic_fleet_subtree_lock_order.spec +0 -3
  75. package/tests/isolation/specs/ceiling_setter_lock_before_upsert.spec +0 -3
  76. package/tests/isolation/specs/claimpolicy_acquire_revert_race.spec +0 -2
  77. package/tests/isolation/specs/claimpolicy_board_scope.spec +0 -2
  78. package/tests/isolation/specs/claimpolicy_claimrow_lock_order.spec +0 -2
  79. package/tests/isolation/specs/claimpolicy_isolation_refusal.spec +0 -2
  80. package/tests/isolation/specs/claimpolicy_renew_revert_race.spec +0 -2
  81. package/tests/isolation/specs/claimpolicy_writer_keyshare.spec +0 -2
  82. package/tests/isolation/specs/consent_approval_lock.spec +0 -1
  83. package/tests/isolation/specs/consent_emit_actual_state.spec +0 -1
  84. package/tests/isolation/specs/grant_target_role_race.spec +206 -0
  85. package/tests/isolation/specs/hosted_delegate_add_tombstone_race.spec +0 -1
  86. package/tests/isolation/specs/hosted_delegate_tombstone_revoke_race.spec +0 -1
  87. package/tests/isolation/specs/hosted_delegate_withdraw_evict_race.spec +0 -1
  88. package/tests/isolation/specs/last_owner_departure.spec +137 -0
  89. package/tests/isolation/specs/ledger_authority_revoke.spec +0 -3
  90. package/tests/isolation/specs/onboarding_ceiling_lock_order.spec +0 -3
  91. package/tests/isolation/specs/outbox_wake_ordering.spec +0 -1
  92. package/tests/isolation/specs/session_identity_limits.spec +81 -0
  93. package/tests/isolation/specs/session_identity_links.spec +82 -0
  94. package/tests/isolation/specs/session_identity_reclaim.spec +125 -0
  95. package/tests/isolation/specs/session_identity_renew_revoke.spec +84 -0
  96. package/tests/isolation/specs/session_identity_renew_revoke_key_gate.spec +94 -0
  97. package/tests/isolation/specs/session_identity_tombstone_onboarding.spec +107 -0
  98. package/tests/isolation/specs/signed_grant_onboarding_deadlock.spec +0 -1
  99. package/tests/isolation/specs/signer_lifecycle_revoke.spec +0 -1
  100. package/tests/seeds/20261011000100_agent_board_session_identities.sql.pre.sql +47 -0
@@ -40,7 +40,7 @@ set search_path = extensions, public, agent_board;
40
40
 
41
41
  begin;
42
42
 
43
- select plan(18);
43
+ select plan(21);
44
44
 
45
45
  -- ---------------------------------------------------------------------------
46
46
  -- The derivation, materialized once. `first_path` is the position PATH of the
@@ -49,7 +49,46 @@ select plan(18);
49
49
  -- function takes on that relation anywhere, which over-reports conflicts rather
50
50
  -- than under-reporting them.
51
51
  -- ---------------------------------------------------------------------------
52
- create temp table _le on commit drop as select * from agent_board_test.lock_edges();
52
+ create temp table _le_raw on commit drop as select * from agent_board_test.lock_edges();
53
+ -- The fixture conservatively assigns every target-table FK to every INSERT.
54
+ -- Bound these two exemptions by the executable column lists AND the catalog:
55
+ -- every INSERT must use an explicit list, omit all nullable/default-free FK
56
+ -- columns, and have no BEFORE INSERT trigger that could populate them. UPDATE
57
+ -- edges and any new writer/constructor remain in the graph.
58
+ create temp table _null_fk_insert on commit drop as
59
+ with candidates as (
60
+ select distinct e.origin_oid, e.origin_name, e.rel_oid, e.path[1] as pos,
61
+ c.oid as constraint_oid, c.conname, c.conrelid, c.conkey,
62
+ cl.relname, agent_board_test.stripped_body(e.origin_oid) as body
63
+ from _le_raw e join pg_constraint c on e.via='fk-insert:' || c.conname and c.confrelid=e.rel_oid
64
+ join pg_class cl on cl.oid=c.conrelid
65
+ where e.own and e.origin_name in ('_session_identity_agent_lineage','_revoke_agent_principal')
66
+ ), proven as (
67
+ select c.*, (select count(*) from regexp_matches(c.body,
68
+ agent_board_test.re(agent_board_test.stmt_frag('insert into', c.relname, true)
69
+ || '\s*(\([^)]*\))?'), 'g')) as insert_count
70
+ from candidates c
71
+ where not exists (select 1 from unnest(c.conkey) k join pg_attribute a
72
+ on a.attrelid=c.conrelid and a.attnum=k where a.attnotnull or a.atthasdef)
73
+ and not exists (select 1 from pg_trigger t where t.tgrelid=c.conrelid
74
+ and not t.tgisinternal and (t.tgtype & 2)=2 and (t.tgtype & 4)=4)
75
+ and not exists (select 1 from regexp_matches(c.body,
76
+ agent_board_test.re(agent_board_test.stmt_frag('insert into', c.relname, true)
77
+ || '\s*(\([^)]*\))?'), 'g') m
78
+ where m[1] is null or exists (select 1 from unnest(c.conkey) k join pg_attribute a
79
+ on a.attrelid=c.conrelid and a.attnum=k
80
+ where a.attname = any(string_to_array(trim(both '()' from
81
+ translate(lower(m[1]), ' "' || chr(9) || chr(10) || chr(13), '')), ','))))
82
+ )
83
+ select * from proven where insert_count>0;
84
+ create temp table _le on commit drop as
85
+ select e.* from _le_raw e where not exists (
86
+ select 1 from _null_fk_insert n where n.origin_oid=e.origin_oid and n.rel_oid=e.rel_oid
87
+ and n.pos=e.path[cardinality(e.path)]
88
+ and not exists (select 1 from _le_raw kept where kept.own and kept.origin_oid=e.origin_oid
89
+ and kept.rel_oid=e.rel_oid and kept.path[1]=n.pos
90
+ and not exists (select 1 from _null_fk_insert omitted where omitted.origin_oid=kept.origin_oid
91
+ and omitted.rel_oid=kept.rel_oid and omitted.pos=kept.path[1] and kept.via='fk-insert:' || omitted.conname)));
53
92
  create temp table _cl on commit drop as select * from agent_board_test.fn_call_closure();
54
93
 
55
94
  create temp table _acq on commit drop as
@@ -187,8 +226,28 @@ select is(
187
226
  join pg_class cl on cl.oid = t.tgrelid
188
227
  join pg_namespace n on n.oid = cl.relnamespace
189
228
  where not t.tgisinternal and n.nspname = 'agent_board')),
190
- '',
191
- 'FKL-8: no trigger function on an agent_board table takes a lock edge -- trigger locks are NOT folded in');
229
+ '_session_identity_agent_lineage',
230
+ 'FKL-8: the catalog-derived locking trigger class is exactly the positively bounded fresh legacy-identity constructor; new trigger locks name themselves');
231
+
232
+ -- This trigger may INSERT only the identity for NEW.id on an agents INSERT,
233
+ -- never another ancestor and never on UPDATE. Its own-agent FK is already
234
+ -- owned by the insert; parent/root KEY SHARE locks are the same FK targets the
235
+ -- agents INSERT already acquired. The constructor is exercised positively by
236
+ -- agent_board_session_identity_lineage.test.sql, including session-kind retention.
237
+ select is((select string_agg(distinct e.rel_name || ' ' || e.lock_mode || ' via ' || e.via, ' | '
238
+ order by e.rel_name || ' ' || e.lock_mode || ' via ' || e.via)
239
+ from _le e where e.fn_name='_session_identity_agent_lineage' and e.own),
240
+ 'agent_board.agents key share via fk-insert:session_identities_agent_id_fkey | agent_board.agents key share via fk-insert:session_identities_parent_agent_id_fkey | agent_board.agents key share via fk-insert:session_identities_root_agent_id_fkey',
241
+ 'FKL-8-constructor-positive: every actual constructor FK lock is on its already inserted agent or the same parent/root agents targets; it has no independent identity update lock');
242
+
243
+ select is((select string_agg(conname || ':' || insert_count, ', ' order by conname)
244
+ from _null_fk_insert where origin_name='_session_identity_agent_lineage'),
245
+ 'session_identities_forked_from_fkey:1, session_identities_lease_alias_fkey:1, session_identities_readmitted_from_fkey:1',
246
+ 'FKL-8-constructor-null-positive: all three nullable/default-free continuity columns are omitted from every constructor INSERT and no BEFORE INSERT trigger can populate them');
247
+ select is((select string_agg(conname || ':' || insert_count, ', ' order by conname)
248
+ from _null_fk_insert where origin_name='_revoke_agent_principal'),
249
+ 'fleet_revocation_events_harness_registration_id_fkey:2, fleet_revocation_events_machine_id_fkey:2, fleet_revocation_events_repo_scope_id_fkey:2, fleet_revocation_events_trust_domain_id_fkey:2',
250
+ 'FKL-8-revoke-null-positive: every principal ledger INSERT omits all four nullable/default-free fleet links with no BEFORE INSERT trigger; real FK edges are retained');
192
251
 
193
252
  -- ===========================================================================
194
253
  -- FKL-9: THE CLASS section 9 could not compute. Every function whose lock graph
@@ -238,9 +297,15 @@ select is(
238
297
  'grant_board_role: agents(share) then memberships(update) [advisory]',
239
298
  'invite_board_member: memberships(update) then agents(key share) [unserialised]',
240
299
  'join_board: memberships(update) then agents(key share) [unserialised]',
241
- 'leave_board: memberships(update) then agents(key share) [unserialised]',
300
+ -- ROLEGUARD: the departure RPC serializes on the board key before its sink.
301
+ 'leave_board: memberships(update) then agents(key share) [advisory]',
302
+ -- SIDSCHEMA role writers use the existing consent sink and board mutex.
303
+ -- Their installed lock order is asserted, not exempted from the class.
304
+ 'link_session_alias: agents(update) then memberships(update) [advisory]',
305
+ 'mint_delegated_child: agents(update) then memberships(update) [advisory]',
242
306
  'onboard_federated_fleet_agent: agents(update) then memberships(update) [advisory]',
243
307
  'post_board_message: agents(key share) then memberships(key share) [unserialised]',
308
+ 'readmit_session_identity: agents(update) then memberships(update) [advisory]',
244
309
  'redeem_invite_link: memberships(key share) then agents(key share) [unserialised]',
245
310
  'request_join_board: memberships(update) then agents(key share) [unserialised]',
246
311
  'resolve_board_request: memberships(update) then agents(key share) [unserialised]',
@@ -252,13 +317,10 @@ select is(
252
317
  'signed_send_direct_message: agents(key share) then memberships(key share) [unserialised]',
253
318
  'signed_send_multicast_message: agents(key share) then memberships(key share) [unserialised]',
254
319
  'subscribe_callback_endpoint: memberships(key share) then agents(key share) [unserialised]',
255
- -- HOSTED (20261008000100). Unserialised, and deliberately NOT given the advisory lock:
256
- -- add_delegate_to_board takes the instance row and THEN the advisory lock, so a tombstone
257
- -- (instance row FOR UPDATE first) taking the advisory lock after it would be the ABBA this
258
- -- file exists to catch. Every row it locks belongs to the tombstoned delegate -- its agents
259
- -- row, its credentials, its memberships -- which onboarding never locks.
260
- 'tombstone_delegate_instance: agents(update) then memberships(update) [unserialised]',
261
- 'update_board_membership: memberships(update) then agents(key share) [unserialised]'
320
+ -- SIDSCHEMA integrity repair: board mutexes precede owner/instance and family row locks,
321
+ -- matching add_delegate_to_board's board-before-instance order. No withdrawal exemption.
322
+ 'tombstone_delegate_instance: agents(update) then memberships(update) [advisory]',
323
+ 'update_board_membership: memberships(update) then agents(key share) [advisory]'
262
324
  ]::text[],
263
325
  'FKL-9: the agents+board_memberships lock class, FK edges included (section 9''s text scan sees 3 of these)');
264
326
 
@@ -267,6 +329,9 @@ select is(
267
329
  -- orders with CONFLICTING modes and no shared advisory lock. Pinned at the
268
330
  -- relation-pair level rather than the function-pair level (146 of those today)
269
331
  -- because a pair of tables entering this set is the reviewable event.
332
+ -- SIDSCHEMA's positively proved NULL-insert filtering above removes four
333
+ -- previously inferred fleet-machine/trust-domain pairs: PostgreSQL cannot take
334
+ -- an FK row lock for a NULL reference. No new relation pair is exempted.
270
335
  --
271
336
  -- KNOWN-BAD and deliberately not fixed -- see the file header. ONE pair has been
272
337
  -- removed since: #281 R1 fixed `agent_board.tasks <-> agent_board.threads` by
@@ -340,14 +405,12 @@ select is(
340
405
  'agent_board.agent_signing_keys <-> agent_board.agents',
341
406
  'agent_board.agent_signing_keys <-> agent_board.board_memberships',
342
407
  'agent_board.agent_signing_keys <-> agent_board.fleet_machine_agents',
343
- 'agent_board.agent_signing_keys <-> agent_board.fleet_machines',
344
408
  'agent_board.agent_signing_keys <-> agent_board.fleet_trust_domains',
345
409
  'agent_board.agents <-> agent_board.board_invite_tokens',
346
410
  'agent_board.agents <-> agent_board.board_memberships',
347
411
  'agent_board.agents <-> agent_board.boards',
348
412
  'agent_board.agents <-> agent_board.enroll_token_uses',
349
413
  'agent_board.agents <-> agent_board.fleet_machine_agents',
350
- 'agent_board.agents <-> agent_board.fleet_machines',
351
414
  'agent_board.agents <-> agent_board.fleet_trust_domains',
352
415
  'agent_board.agents <-> agent_board.identity_providers',
353
416
  'agent_board.agents <-> auth.users',
@@ -361,8 +424,6 @@ select is(
361
424
  'agent_board.fleet_harness_registrations <-> agent_board.fleet_machine_agents',
362
425
  'agent_board.fleet_machine_agents <-> agent_board.fleet_repo_scopes',
363
426
  'agent_board.fleet_machine_agents <-> agent_board.fleet_trust_domains',
364
- 'agent_board.fleet_machines <-> auth.users',
365
- 'agent_board.fleet_trust_domains <-> auth.users',
366
427
  'agent_board.identity_providers <-> auth.users'
367
428
  ]::text[],
368
429
  'FKL-10: the relation pairs held in opposite orders with conflicting modes and no shared advisory lock');