@consiliency/agent-board-schema 1.54.0 → 1.56.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 (68) hide show
  1. package/fixtures/already-applied.json +8 -0
  2. package/migrations/20261005000100_agent_board_enroll_token_single_use.sql +886 -0
  3. package/migrations/20261006000100_agent_board_room_roster_active_first.sql +118 -0
  4. package/package.json +1 -1
  5. package/schema-manifest.json +21 -3
  6. package/src/tooling.test.mjs +7 -2
  7. package/tests/agent_board_atomic_fleet_onboarding.test.sql +35 -35
  8. package/tests/agent_board_atomic_fleet_onboarding_backfill.test.sql +5 -5
  9. package/tests/agent_board_atomic_fleet_onboarding_runtime.test.sql +32 -32
  10. package/tests/agent_board_enroll_federated_identity.test.sql +11 -10
  11. package/tests/agent_board_enroll_pop_binding.test.sql +12 -12
  12. package/tests/agent_board_enroll_resumption.test.sql +1 -1
  13. package/tests/agent_board_enroll_token_single_use.test.sql +538 -0
  14. package/tests/agent_board_enrollment_audit_content.test.sql +3 -3
  15. package/tests/agent_board_federated_lock_order_class.test.sql +5 -5
  16. package/tests/agent_board_fedsession.test.sql +1 -1
  17. package/tests/agent_board_fk_lock_edges.test.sql +23 -0
  18. package/tests/agent_board_identity_providers.test.sql +3 -2
  19. package/tests/agent_board_membership_current_row_class.test.sql +460 -0
  20. package/tests/agent_board_portal_offline_invariant.test.sql +1 -1
  21. package/tests/agent_board_room_roster.test.sql +262 -24
  22. package/tests/agent_board_roomlink.test.sql +2 -2
  23. package/tests/agent_board_rowid.test.sql +15 -2
  24. package/tests/agent_board_write_enforcement_class.test.sql +5 -2
  25. package/tests/catalog/function_execute_privileges.json +12 -3
  26. package/tests/catalog/p_metadata_parameters.json +6 -2
  27. package/tests/isolation/expected/atomic_fedsession_fleet_lock_order.out +1 -1
  28. package/tests/isolation/expected/atomic_fleet_authority_races.out +8 -8
  29. package/tests/isolation/expected/atomic_fleet_binding_revoke.out +2 -2
  30. package/tests/isolation/expected/atomic_fleet_board_archive.out +2 -2
  31. package/tests/isolation/expected/atomic_fleet_human_fresh_revoke.out +2 -2
  32. package/tests/isolation/expected/atomic_fleet_identity_disposition.out +2 -2
  33. package/tests/isolation/expected/atomic_fleet_lifecycle_races.out +22 -22
  34. package/tests/isolation/expected/atomic_fleet_onboarding.out +2 -2
  35. package/tests/isolation/expected/atomic_fleet_subtree_lock_order.out +1 -1
  36. package/tests/isolation/expected/enroll_identity_board_owner_lock_order.out +1 -1
  37. package/tests/isolation/expected/enroll_token_fleet_abort.out +81 -0
  38. package/tests/isolation/expected/enroll_token_pk_backstop.out +36 -0
  39. package/tests/isolation/expected/enroll_token_placement.out +86 -0
  40. package/tests/isolation/expected/enroll_token_rollback_unstrands.out +59 -0
  41. package/tests/isolation/expected/enroll_token_route_race_fleet_first.out +72 -0
  42. package/tests/isolation/expected/enroll_token_route_race_identity_first.out +72 -0
  43. package/tests/isolation/expected/enroll_token_same_key_fleet_then_identity.out +73 -0
  44. package/tests/isolation/expected/enroll_token_same_key_identity_then_fleet.out +73 -0
  45. package/tests/isolation/expected/enroll_token_same_key_resume.out +59 -0
  46. package/tests/isolation/expected/enroll_token_same_token_race.out +58 -0
  47. package/tests/isolation/expected/onboarding_ceiling_lock_order.out +2 -2
  48. package/tests/isolation/specs/atomic_fedsession_fleet_lock_order.spec +1 -1
  49. package/tests/isolation/specs/atomic_fleet_authority_races.spec +2 -2
  50. package/tests/isolation/specs/atomic_fleet_binding_revoke.spec +2 -2
  51. package/tests/isolation/specs/atomic_fleet_board_archive.spec +2 -2
  52. package/tests/isolation/specs/atomic_fleet_human_fresh_revoke.spec +2 -2
  53. package/tests/isolation/specs/atomic_fleet_identity_disposition.spec +2 -2
  54. package/tests/isolation/specs/atomic_fleet_lifecycle_races.spec +2 -2
  55. package/tests/isolation/specs/atomic_fleet_onboarding.spec +2 -2
  56. package/tests/isolation/specs/atomic_fleet_subtree_lock_order.spec +2 -2
  57. package/tests/isolation/specs/enroll_identity_board_owner_lock_order.spec +1 -1
  58. package/tests/isolation/specs/enroll_token_fleet_abort.spec +138 -0
  59. package/tests/isolation/specs/enroll_token_pk_backstop.spec +37 -0
  60. package/tests/isolation/specs/enroll_token_placement.spec +136 -0
  61. package/tests/isolation/specs/enroll_token_rollback_unstrands.spec +71 -0
  62. package/tests/isolation/specs/enroll_token_route_race_fleet_first.spec +119 -0
  63. package/tests/isolation/specs/enroll_token_route_race_identity_first.spec +120 -0
  64. package/tests/isolation/specs/enroll_token_same_key_fleet_then_identity.spec +112 -0
  65. package/tests/isolation/specs/enroll_token_same_key_identity_then_fleet.spec +113 -0
  66. package/tests/isolation/specs/enroll_token_same_key_resume.spec +70 -0
  67. package/tests/isolation/specs/enroll_token_same_token_race.spec +80 -0
  68. package/tests/isolation/specs/onboarding_ceiling_lock_order.spec +1 -1
@@ -0,0 +1,118 @@
1
+ -- ROOMCONTRACT-ACTIVEFIRST (message-board#542): the owner/admin roster chooses current
2
+ -- standing ACTIVE-FIRST, as every other acting membership reader does (MBROWID 3a).
3
+ --
4
+ -- THE DEFECT. 20261003000100 chose the caller's standing (authorization) and each
5
+ -- member's inclusion (projection) from the LATEST board_memberships row alone:
6
+ -- `order by created_at desc, id desc`. The active-membership unique index is PARTIAL
7
+ -- (idx_agent_board_board_memberships_active_unique, WHERE membership_state in
8
+ -- ('joined','subscribed')), so one active row can coexist with newer non-active rows
9
+ -- (removed / left / requested / invited). MBROWID 3a (20260803000100) ruled that every
10
+ -- reader acting on "current standing" prefers the active row when one exists; join_board,
11
+ -- update_board_membership, _upsert_board_membership_state and
12
+ -- list_board_pending_memberships all do. The roster did not, so an active owner or admin
13
+ -- with a newer non-active row was refused with 42501 and omitted from every roster —
14
+ -- while the rest of MB (and Portal's self view, which reduces active-first as MBROWID
15
+ -- requires) treated them as active.
16
+ --
17
+ -- THE FIX. Both selection sites take the MBROWID 3a key, then the existing total order:
18
+ -- order by case when membership_state in ('joined','subscribed') then 0 else 1 end,
19
+ -- created_at desc, id desc
20
+ -- Everything else is unchanged: signature, projection, STABLE, SECURITY DEFINER,
21
+ -- search_path, the 42501 message, the result order, and the grants.
22
+ --
23
+ -- The class this belongs to is pinned from the catalog, not from this list:
24
+ -- tests/agent_board_membership_current_row_class.test.sql derives every statement that
25
+ -- picks ONE board_memberships row by recency and requires the active-first key, or a
26
+ -- named exemption whose filter it asserts exactly. This migration fixes the only two
27
+ -- sites that class found without the key; the exemptions are recorded there.
28
+ --
29
+ -- CREATE OR REPLACE keeps the ACL and the COMMENT, but both are restated below anyway:
30
+ -- the COMMENT's text described latest-row semantics and must change, and the grants are
31
+ -- restated so this file alone says what the function's posture is.
32
+
33
+ create or replace function agent_board.list_board_members(p_board_id uuid)
34
+ returns table(
35
+ membership_id uuid,
36
+ board_id uuid,
37
+ agent_id uuid,
38
+ handle text,
39
+ display_name text,
40
+ agent_type text,
41
+ membership_state text,
42
+ role text,
43
+ created_by_agent_id uuid,
44
+ membership_created_at timestamptz,
45
+ membership_updated_at timestamptz
46
+ )
47
+ language plpgsql
48
+ stable
49
+ security definer
50
+ set search_path = agent_board, public
51
+ as $$
52
+ declare
53
+ v_actor uuid := agent_board.current_agent_id();
54
+ begin
55
+ -- Authorization precedes disclosure. Creator identity is deliberately absent:
56
+ -- only the caller's current membership row can establish authority, and the
57
+ -- current row is the ACTIVE one when one exists (MBROWID 3a), else the latest.
58
+ if v_actor is null
59
+ or not exists (
60
+ select 1
61
+ from (
62
+ select bm.membership_state, bm.role
63
+ from agent_board.board_memberships bm
64
+ where bm.board_id = p_board_id
65
+ and bm.agent_id = v_actor
66
+ order by case when bm.membership_state in ('joined', 'subscribed') then 0 else 1 end,
67
+ bm.created_at desc, bm.id desc
68
+ limit 1
69
+ ) actor_standing
70
+ where actor_standing.membership_state in ('joined', 'subscribed')
71
+ and actor_standing.role in ('owner', 'admin')
72
+ ) then
73
+ raise exception 'only a board owner or admin may list board members'
74
+ using errcode = '42501';
75
+ end if;
76
+
77
+ -- Choose exactly one current row per agent (active-first, MBROWID 3a), then
78
+ -- keep only agents whose current row is active.
79
+ return query
80
+ select
81
+ latest.id,
82
+ latest.board_id,
83
+ latest.agent_id,
84
+ a.handle,
85
+ a.display_name,
86
+ a.agent_type,
87
+ latest.membership_state,
88
+ latest.role,
89
+ latest.created_by_agent_id,
90
+ latest.created_at,
91
+ latest.updated_at
92
+ from (
93
+ select distinct on (bm.agent_id) bm.*
94
+ from agent_board.board_memberships bm
95
+ where bm.board_id = p_board_id
96
+ order by bm.agent_id,
97
+ case when bm.membership_state in ('joined', 'subscribed') then 0 else 1 end,
98
+ bm.created_at desc, bm.id desc
99
+ ) latest
100
+ join agent_board.agents a on a.id = latest.agent_id
101
+ where latest.membership_state in ('joined', 'subscribed')
102
+ order by
103
+ case latest.role
104
+ when 'owner' then 0
105
+ when 'admin' then 1
106
+ when 'member' then 2
107
+ when 'observer' then 3
108
+ end,
109
+ lower(a.handle),
110
+ latest.agent_id;
111
+ end;
112
+ $$;
113
+
114
+ comment on function agent_board.list_board_members(uuid) is
115
+ 'Owner/admin-gated active board roster. Fails 42501 for a null actor (including service_role through this user-facing path), creator without current standing, or any caller whose current board membership is not active owner/admin. The current row is chosen active-first (MBROWID 3a): case when membership_state in (joined, subscribed) then 0 else 1 end, created_at desc, id desc — so an active row outranks a newer removed/left/requested/invited row. Returns each agent''s current row only when joined/subscribed, ordered owner/admin/member/observer then lower(handle), agent_id. Board-scoped and metadata-free.';
116
+
117
+ revoke all on function agent_board.list_board_members(uuid) from public, anon;
118
+ grant execute on function agent_board.list_board_members(uuid) to authenticated, service_role;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@consiliency/agent-board-schema",
3
- "version": "1.54.0",
3
+ "version": "1.56.0",
4
4
  "description": "Canonical, immutable, exact-pinnable agent_board schema bundle: ordered SQL migrations, digest + compatibility manifest, and deterministic materialize/plan/check tooling for adopters.",
5
5
  "repository": "https://github.com/Consiliency/message-board.git",
6
6
  "type": "module",
@@ -2,7 +2,7 @@
2
2
  "schema": "agent_board_schema_manifest.v1",
3
3
  "interface": "IF-0-MBPLANE-BUNDLE-1",
4
4
  "package": "@consiliency/agent-board-schema",
5
- "packageVersion": "1.54.0",
5
+ "packageVersion": "1.56.0",
6
6
  "schemaVersion": 8,
7
7
  "minClientVersion": "1.1.0",
8
8
  "source": {
@@ -46,8 +46,8 @@
46
46
  "usage": "upsert (retention/monitoring defaults; on conflict do nothing)"
47
47
  }
48
48
  ],
49
- "migrationCount": 152,
50
- "aggregateSha256": "a98fe8541f1689748da03753076ff5e7d8d8af5bcda5857c9c5a1dce0358d8c4",
49
+ "migrationCount": 154,
50
+ "aggregateSha256": "d0b74be9b41032a78ad45227706a48283f931b40cd6350c63c619a0e153c0cee",
51
51
  "migrations": [
52
52
  {
53
53
  "order": 0,
@@ -1416,6 +1416,24 @@
1416
1416
  "sourceRef": "message-board",
1417
1417
  "bytes": 3403,
1418
1418
  "sha256": "cd1815a58978261be143426eaac2dfccb6bb86b003487e4d1866aba60e7a6eb6"
1419
+ },
1420
+ {
1421
+ "order": 152,
1422
+ "id": "20261005000100_agent_board_enroll_token_single_use",
1423
+ "filename": "20261005000100_agent_board_enroll_token_single_use.sql",
1424
+ "origin": "message-board-forward",
1425
+ "sourceRef": "message-board",
1426
+ "bytes": 55964,
1427
+ "sha256": "317d7b41432b36a2e38cc64940838e173e425014e3e256a96d832202b250e82d"
1428
+ },
1429
+ {
1430
+ "order": 153,
1431
+ "id": "20261006000100_agent_board_room_roster_active_first",
1432
+ "filename": "20261006000100_agent_board_room_roster_active_first.sql",
1433
+ "origin": "message-board-forward",
1434
+ "sourceRef": "message-board",
1435
+ "bytes": 5470,
1436
+ "sha256": "637480feeca07dbb2fe7a4624dc6d063d0c1f23e6ea3c3ee9a946ad43de4e34e"
1419
1437
  }
1420
1438
  ],
1421
1439
  "excludedBridge": {
@@ -38,9 +38,9 @@ describe('manifest integrity', () => {
38
38
  expect(manifest.schemaVersion).toBe(8);
39
39
  expect(manifest.minClientVersion).toMatch(/^\d+\.\d+\.\d+$/);
40
40
  });
41
- it('registers the four MBAUTHZ + four MBSENDAUTHZ + four MBSESSION + four MBDELEG + three MBFED + three MBHARDEN + two MBPORTALGATE + two MBPORTAL-PR32 + two ROOMOWN + two ROOMLINK + one ROOMDELEG + one LOCALAUTH + one ROOMUIAPI + one FEDSESSION + one FEDSESSION-AUTOJOIN + one GATENULL + one MEMBSTATE + one AGENTSDIR + one LEAVEIDEM + one ROLERESET + one MBROWID + one MBHYGIENE + one MBCONSENT-A + one MBCONSENT-B(a) + one MBCONSENT-B(i) + one MBCONSENT-B(ii) + one MBCONSENT-B(iii) + one MBHARDEN-#97 + one MBHARDEN-#118 + one INVITEPOLICY + one OBSGATE + one OBSGATE-2 + one OBSGATE-3 + one OBSGATE-4 + one FLEETMACHINERLS + one RESOLVENOOP + one ROOMDELEG-LANEB + one CLAIMKEY + two MINTHARDEN + one FLEETMACHINEHIJACK + one CLAIMGATE-SHADOW + one CLAIMGATE-ENFORCE + one FLEETMACHINEREACTIVATION + one DELEGCAPCEILING + one FLEETMACHINERECLAIM + one DELEGSOLEWRITER + one FLEETDOMAINRECLAIM + five ATOMICFLEETONBOARDING (onboarding + ceiling-lock + revocation-evidence + fedsession-gate + fedsession-lock-order) + one OUTBOXWAKE + one ENROLLLOCKORDER + one FEDPROVIDERABSENCE + one WAKERECOVERY + one CONSENTRECONCILE + one ENROLLRESUMPTION + one SIGNEDAUTHORITY + one SIGNERLIFECYCLE + one LEDGERAUTHORITY + one CEILINGLOCKCOMMENT + one LEDGERBOARDCREATE + one RECEIPTSUBJECTGATE + one TASKTHREADLOCKORDER + one CLAIMSUBJECTGATE + one FEDSESSIONIDENTITYGATE + one FEDIDENTITYSLUG + one INVITENODOWNGRADE + one SIGNEDWRITEFLAG + one SIGNEDWRITEMODEL + one RTCREDCLAIM + one ORIGINPOSTURE + one BOARDCLAIMPOLICY + one HEARTBEATPRESERVE + one LIVENESSDENOM + one OWNERLESSRENEWTTL + one OWNERLESSQUOTACENSUS + one FLEETDOMAINNODOWNGRADE + one OWNERLESSQUOTACONVERGE + one ADVISORYREVERTRESET + one CLAIMPOLICYRACE + one CLAIMPOLICYLOCKGRANT + one SESSIONCHALLENGESUPERSEDE + one ROOMCONTRACT + one CALLBACKCONSUMER forward migrations (migrationCount = prior 113 + 5 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 = 152)', () => {
41
+ it('registers the four MBAUTHZ + four MBSENDAUTHZ + four MBSESSION + four MBDELEG + three MBFED + three MBHARDEN + two MBPORTALGATE + two MBPORTAL-PR32 + two ROOMOWN + two ROOMLINK + one ROOMDELEG + one LOCALAUTH + one ROOMUIAPI + one FEDSESSION + one FEDSESSION-AUTOJOIN + one GATENULL + one MEMBSTATE + one AGENTSDIR + one LEAVEIDEM + one ROLERESET + one MBROWID + one MBHYGIENE + one MBCONSENT-A + one MBCONSENT-B(a) + one MBCONSENT-B(i) + one MBCONSENT-B(ii) + one MBCONSENT-B(iii) + one MBHARDEN-#97 + one MBHARDEN-#118 + one INVITEPOLICY + one OBSGATE + one OBSGATE-2 + one OBSGATE-3 + one OBSGATE-4 + one FLEETMACHINERLS + one RESOLVENOOP + one ROOMDELEG-LANEB + one CLAIMKEY + two MINTHARDEN + one FLEETMACHINEHIJACK + one CLAIMGATE-SHADOW + one CLAIMGATE-ENFORCE + one FLEETMACHINEREACTIVATION + one DELEGCAPCEILING + one FLEETMACHINERECLAIM + one DELEGSOLEWRITER + one FLEETDOMAINRECLAIM + five ATOMICFLEETONBOARDING (onboarding + ceiling-lock + revocation-evidence + fedsession-gate + fedsession-lock-order) + one OUTBOXWAKE + one ENROLLLOCKORDER + one FEDPROVIDERABSENCE + one WAKERECOVERY + one CONSENTRECONCILE + one ENROLLRESUMPTION + one SIGNEDAUTHORITY + one SIGNERLIFECYCLE + one LEDGERAUTHORITY + one CEILINGLOCKCOMMENT + one LEDGERBOARDCREATE + one RECEIPTSUBJECTGATE + one TASKTHREADLOCKORDER + one CLAIMSUBJECTGATE + one FEDSESSIONIDENTITYGATE + one FEDIDENTITYSLUG + one INVITENODOWNGRADE + one SIGNEDWRITEFLAG + one SIGNEDWRITEMODEL + one RTCREDCLAIM + one ORIGINPOSTURE + one BOARDCLAIMPOLICY + one HEARTBEATPRESERVE + one LIVENESSDENOM + one OWNERLESSRENEWTTL + one OWNERLESSQUOTACENSUS + one FLEETDOMAINNODOWNGRADE + one OWNERLESSQUOTACONVERGE + one ADVISORYREVERTRESET + one CLAIMPOLICYRACE + one CLAIMPOLICYLOCKGRANT + one SESSIONCHALLENGESUPERSEDE + one ROOMCONTRACT + one CALLBACKCONSUMER + one ENROLLTOKENSINGLEUSE + one ROOMCONTRACT-ACTIVEFIRST forward migrations (migrationCount = prior 113 + 5 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 = 154)', () => {
42
42
  expect(manifest.migrationCount).toBe(manifest.migrations.length);
43
- expect(manifest.migrationCount).toBe(152);
43
+ expect(manifest.migrationCount).toBe(154);
44
44
  // MBSESSION switched enforcement on (scoped-send re-grant): the client-visible
45
45
  // schema version bumped to 8. MBDELEG tightens delegation and MBFED adds only
46
46
  // enrollment-only additive surface (identity_providers registry, enrollment audit,
@@ -338,6 +338,11 @@ describe('manifest integrity', () => {
338
338
  // ROOMCONTRACT: owner/admin active roster RPC plus self-membership role.
339
339
  '20261003000100_agent_board_room_roster.sql',
340
340
  '20261004000100_agent_board_callback_consumer_intent.sql',
341
+ // ENROLLTOKENSINGLEUSE (#521): a verified Portal token binds to at most one key.
342
+ '20261005000100_agent_board_enroll_token_single_use.sql',
343
+ // ROOMCONTRACT-ACTIVEFIRST (#542): list_board_members chooses current standing
344
+ // active-first (MBROWID 3a) at both its authorization and projection sites.
345
+ '20261006000100_agent_board_room_roster_active_first.sql',
341
346
  ]) {
342
347
  expect(names.has(f)).toBe(true);
343
348
  }
@@ -156,7 +156,7 @@ select has_function(
156
156
  );
157
157
  select has_function(
158
158
  'agent_board', 'onboard_federated_fleet_agent',
159
- array['text','text','text','jsonb','text','text','text','text','boolean','text','text','text[]','text[]','text','uuid','uuid','text','text','text','text','text','text','text'],
159
+ array['text','text','text','jsonb','text','text','text','text','boolean','text','timestamp with time zone','text','text','text[]','text[]','text','uuid','uuid','text','text','text','text','text','text','text'],
160
160
  'atomic fleet onboarding RPC exists'
161
161
  );
162
162
  select has_function('agent_board', '_redeem_fleet_invite_for_agent', array['text','uuid'], 'fleet-only redemption helper exists');
@@ -168,7 +168,7 @@ select has_function(
168
168
  );
169
169
  select function_privs_are(
170
170
  'agent_board', 'onboard_federated_fleet_agent',
171
- array['text','text','text','jsonb','text','text','text','text','boolean','text','text','text[]','text[]','text','uuid','uuid','text','text','text','text','text','text','text'],
171
+ array['text','text','text','jsonb','text','text','text','text','boolean','text','timestamp with time zone','text','text','text[]','text[]','text','uuid','uuid','text','text','text','text','text','text','text'],
172
172
  'service_role', array['EXECUTE'],
173
173
  'only service_role can execute atomic onboarding'
174
174
  );
@@ -178,27 +178,27 @@ select function_privs_are(
178
178
  'fleet redemption helper is service-role-only'
179
179
  );
180
180
  select ok(
181
- agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
181
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
182
182
  like '%fleet_onboarding.v1%',
183
183
  'atomic onboarding returns the frozen contract version'
184
184
  );
185
185
  select ok(
186
- agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
186
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
187
187
  like '%for update%',
188
188
  'atomic onboarding catalog-pins row locking'
189
189
  );
190
190
  select ok(
191
- agent_board_test.stripped_body('agent_board.enroll_federated_identity(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text)'::regprocedure, true)
191
+ agent_board_test.stripped_body('agent_board.enroll_federated_identity(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text)'::regprocedure, true)
192
192
  like '%pg_advisory_xact_lock%',
193
193
  'exact-key create-or-reuse is serialized by fingerprint'
194
194
  );
195
195
  select ok(
196
- agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
196
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
197
197
  like '%pg_advisory_xact_lock(hashtextextended(p_agent_public_key_fingerprint, 0))%',
198
198
  'composed onboarding serializes identity disposition by fingerprint'
199
199
  );
200
200
  select ok(
201
- agent_board_test.stripped_body('agent_board.enroll_federated_identity(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text)'::regprocedure, true)
201
+ agent_board_test.stripped_body('agent_board.enroll_federated_identity(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text)'::regprocedure, true)
202
202
  like '%fleet invite requires atomic fleet onboarding%',
203
203
  'legacy enrollment rejects bounded invites before writes'
204
204
  );
@@ -208,42 +208,42 @@ select ok(
208
208
  'generic ROOMLINK redemption rejects bounded invites'
209
209
  );
210
210
  select ok(
211
- agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
211
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
212
212
  like '%phase_worker%worker%reviewer%reviewer%',
213
213
  'only bounded worker and reviewer role pairs are admitted'
214
214
  );
215
215
  select ok(
216
- agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
216
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
217
217
  like '%signing_key_id is distinct from v_enroll.signing_key_id%',
218
218
  'binding replay pins the exact proven signing key'
219
219
  );
220
220
  select ok(
221
- agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
221
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
222
222
  like '%v_inv.board_id is distinct from p_board_id%',
223
223
  'requested board is checked against the locked invite before writes'
224
224
  );
225
225
  select ok(
226
- agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
226
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
227
227
  like '%not v_completed_replay%v_inv.expires_at <= now()%v_inv.uses_count >= v_inv.max_uses%',
228
228
  'only a complete active replay may bypass expiry and exhaustion'
229
229
  );
230
230
  select ok(
231
- agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
231
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
232
232
  like '%v_inv.revoked_at is not null%a.status = ''active''%_grant_within_ceiling%',
233
233
  'revocation and current creator authority gate every fleet replay'
234
234
  );
235
235
  select ok(
236
- agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
236
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
237
237
  like '%a.id = any(array[v_inv.created_by_agent_id, v_existing_agent_id])%order by a.id for update%bm.agent_id = v_inv.created_by_agent_id%order by bm.id for update%_grant_within_ceiling%',
238
238
  'creator and enrolled principals lock in UUID order before creator authority'
239
239
  );
240
240
  select ok(
241
- agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
241
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
242
242
  like '%board_invite_redemptions red%order by red.id for update%bm.agent_id = v_enroll.agent_id%order by bm.id for update%select exists%',
243
243
  'complete-replay proof locks redemption and membership first'
244
244
  );
245
245
  select ok(
246
- agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
246
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
247
247
  like '%perform 1 from agent_board.fleet_harness_registrations%perform 1 from agent_board.fleet_repo_scopes%perform 1 from agent_board.fleet_machine_agents%',
248
248
  'every matching harness, repo, and binding history row is locked before reuse'
249
249
  );
@@ -263,12 +263,12 @@ select ok(
263
263
  'exact binding revocation locks the machine before the binding [SOLE PIN: no isolation spec discriminates this ordering -- measured against all 20]'
264
264
  );
265
265
  select ok(
266
- strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'where fm.id = p_machine_id for update'
266
+ strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'where fm.id = p_machine_id for update'
267
267
  ) > 0
268
- and strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'from agent_board.agents a where a.id = v_enroll.agent_id for update'
268
+ and strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'from agent_board.agents a where a.id = v_enroll.agent_id for update'
269
269
  ) > 0
270
- and strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'where fm.id = p_machine_id for update'
271
- ) < strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'from agent_board.agents a where a.id = v_enroll.agent_id for update'
270
+ and strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'where fm.id = p_machine_id for update'
271
+ ) < strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'from agent_board.agents a where a.id = v_enroll.agent_id for update'
272
272
  ),
273
273
  'atomic onboarding locks the machine before its enrolled agent [CONFIRMATORY: atomic_fleet_binding_revoke.spec and atomic_fleet_lifecycle_races.spec both discriminate the inversion]'
274
274
  );
@@ -368,12 +368,12 @@ select ok(
368
368
  'bounded invite mint locks and requires an active board'
369
369
  );
370
370
  select ok(
371
- agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
371
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
372
372
  like '%from agent_board.boards b%where b.id = v_inv.board_id%for update%v_board.status <> ''active''%',
373
373
  'atomic onboarding locks and requires the invite board to remain active'
374
374
  );
375
375
  select ok(
376
- agent_board_test.stripped_body('agent_board.enroll_federated_identity(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text)'::regprocedure, true)
376
+ agent_board_test.stripped_body('agent_board.enroll_federated_identity(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text)'::regprocedure, true)
377
377
  like '%from auth.users u where u.id = v_owner_user_id for update%fleet_human_revocations%r.issuer = p_issuer and r.subject = p_subject%',
378
378
  'identity enrollment locks the human owner and rejects durable total revocation'
379
379
  );
@@ -404,7 +404,7 @@ select ok(
404
404
  select ok(
405
405
  not has_function_privilege(
406
406
  'authenticated',
407
- 'agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure,
407
+ 'agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure,
408
408
  'EXECUTE'
409
409
  ),
410
410
  'authenticated CANNOT execute atomic onboarding'
@@ -412,7 +412,7 @@ select ok(
412
412
  select ok(
413
413
  not has_function_privilege(
414
414
  'anon',
415
- 'agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure,
415
+ 'agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure,
416
416
  'EXECUTE'
417
417
  ),
418
418
  'anon CANNOT execute atomic onboarding'
@@ -439,10 +439,10 @@ select ok(
439
439
  -- Ordering, not mere presence: the lock has to be held BEFORE the authorization
440
440
  -- decision reads the (possibly absent) ceiling, and BEFORE the writer upserts it.
441
441
  select ok(
442
- strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'perform agent_board._lock_board_authority_ceiling(v_inv.board_id)'
442
+ strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'perform agent_board._lock_board_authority_ceiling(v_inv.board_id)'
443
443
  ) > 0
444
- and strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'perform agent_board._lock_board_authority_ceiling(v_inv.board_id)'
445
- ) < strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'agent_board._grant_within_ceiling('
444
+ and strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'perform agent_board._lock_board_authority_ceiling(v_inv.board_id)'
445
+ ) < strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'agent_board._grant_within_ceiling('
446
446
  ),
447
447
  'atomic onboarding holds the board ceiling lock before evaluating creator authority [REDUNDANT: implied by the advisory-before-boards and boards-before-ceiling-read claims below; an advisory moved between those two points violates the real ordering and still passes THIS one]'
448
448
  );
@@ -454,10 +454,10 @@ select ok(
454
454
  -- that catches the inversion; it is deliberately separate from the "before the
455
455
  -- ceiling read" assertion below, because that one passes in BOTH orders.
456
456
  select ok(
457
- strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'perform agent_board._lock_board_authority_ceiling(v_inv.board_id)'
457
+ strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'perform agent_board._lock_board_authority_ceiling(v_inv.board_id)'
458
458
  ) > 0
459
- and strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'perform agent_board._lock_board_authority_ceiling(v_inv.board_id)'
460
- ) < strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'from agent_board.boards b
459
+ and strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'perform agent_board._lock_board_authority_ceiling(v_inv.board_id)'
460
+ ) < strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'from agent_board.boards b
461
461
  where b.id = v_inv.board_id
462
462
  for update'
463
463
  ),
@@ -477,14 +477,14 @@ select ok(
477
477
  -- ordering nothing else states, because the boards lock is taken for board liveness,
478
478
  -- not for the ceiling. Pin the ordering so a refactor that moves it is not silent.
479
479
  select ok(
480
- strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'from agent_board.boards b
480
+ strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'from agent_board.boards b
481
481
  where b.id = v_inv.board_id
482
482
  for update'
483
483
  ) > 0
484
- and strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'from agent_board.boards b
484
+ and strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'from agent_board.boards b
485
485
  where b.id = v_inv.board_id
486
486
  for update'
487
- ) < strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'agent_board._grant_within_ceiling('
487
+ ) < strpos(lower(agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure)), 'agent_board._grant_within_ceiling('
488
488
  ),
489
489
  'atomic onboarding holds the boards row FOR UPDATE before it reads the ceiling [CONFIRMATORY FOR FLAT MASKS ONLY: agent_board_federated_lock_order_class.test.sql EC-3 fails by name on the comment/literal mutants, but it carries the SAME strip, so a NESTED block comment takes this assertion and EC-3 green together -- see #199 in the header; atomic_fleet_authority_races.spec pins the OUTCOME only when this lock and the advisory are BOTH gone, and atomic_fleet_board_archive.spec BLOCKS rather than failing by name]'
490
490
  );
@@ -553,7 +553,7 @@ select is( -- pgtap-allow-raise-checked: catalog read only; the RPC names below
553
553
  -- lock also closes this reader's exposure to the ceiling row's ABSENCE, one of the three
554
554
  -- 20260901000110 left explicitly undetermined.
555
555
  'agent_board.grant_board_role(uuid,uuid,text,uuid) => serialized',
556
- 'agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text) => serialized'
556
+ 'agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text) => serialized'
557
557
  ]::text[],
558
558
  'every ceiling-authorization reader in pg_proc is declared with its serialization state'
559
559
  );
@@ -698,14 +698,14 @@ select is( -- pgtap-allow-raise-checked: catalog read only; the RPC names below
698
698
  'agent_board._revoke_agent_principal(uuid,uuid,text) [definer] => service_role',
699
699
  'agent_board.create_fleet_onboarding_invite(uuid,uuid,text,text,text,text,text,text,text,integer,timestamp with time zone,uuid,jsonb) [definer] => authenticated,service_role',
700
700
  'agent_board.enroll_federated_human(text,text,text,jsonb,text) [definer] => service_role',
701
- 'agent_board.enroll_federated_identity(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text) [definer] => service_role',
701
+ 'agent_board.enroll_federated_identity(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text) [definer] => service_role',
702
702
  'agent_board.evaluate_fleet_sender_policy(uuid,uuid,uuid,uuid,text,uuid,boolean) [definer] => authenticated,service_role',
703
703
  -- Joined this class in #276: the live grant chokepoint now takes _lock_board_authority_ceiling
704
704
  -- (forced by the section-9 agents/board_memberships class in agent_board_signer_lifecycle.test.sql
705
705
  -- once the ledger gate made it lock both). Still authenticated,service_role — #276 removes
706
706
  -- authority from a revoked principal, it does not change who may call the RPC.
707
707
  'agent_board.grant_board_role(uuid,uuid,text,uuid) [definer] => authenticated,service_role',
708
- 'agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text) [definer] => service_role',
708
+ 'agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text) [definer] => service_role',
709
709
  'agent_board.reactivate_fleet_machine(uuid,uuid,text) [definer] => service_role',
710
710
  'agent_board.revoke_fleet_subject(text,uuid,uuid,text,jsonb) [definer] => authenticated,service_role',
711
711
  'agent_board.revoke_human_subtree(uuid,uuid,text) [definer] => service_role',
@@ -83,7 +83,7 @@ select * from agent_board.enroll_federated_identity(
83
83
  'TauOMxwU-TW-deqQjil1DLDy_AvJrfoG_eXGwwvaRUk',
84
84
  'raw_ed25519_base64url',
85
85
  'sha256:QnDuPiA99wAIcCJlk4ZS_ZJV4gQPW8cX0LVmKQJNl0I',
86
- true, null, 'agent', ARRAY['board:read','board:send'], ARRAY[]::text[], null
86
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:read','board:send'], ARRAY[]::text[], null
87
87
  );
88
88
  select is((select agent_id from _legacy_replay),
89
89
  '16200000-0000-0000-0000-000000000001'::uuid,
@@ -107,7 +107,7 @@ select throws_ok(
107
107
  '{"iss":"https://legacy.atomic.test","aud":["message-board","new-audience"],"uid":"legacy-complete"}'::jsonb,
108
108
  'atomic-backfill-complete','TauOMxwU-TW-deqQjil1DLDy_AvJrfoG_eXGwwvaRUk',
109
109
  'raw_ed25519_base64url','sha256:QnDuPiA99wAIcCJlk4ZS_ZJV4gQPW8cX0LVmKQJNl0I',
110
- true,null,'agent',ARRAY['board:read','board:send'],ARRAY[]::text[],null)$$,
110
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1),null,'agent',ARRAY['board:read','board:send'],ARRAY[]::text[],null)$$,
111
111
  '23505', null, 'changed normalized audience projection conflicts'
112
112
  );
113
113
  select throws_ok(
@@ -116,7 +116,7 @@ select throws_ok(
116
116
  '{"iss":"https://legacy.atomic.test","aud":["message-board","secondary-audience"],"uid":"changed-subject"}'::jsonb,
117
117
  'atomic-backfill-complete','TauOMxwU-TW-deqQjil1DLDy_AvJrfoG_eXGwwvaRUk',
118
118
  'raw_ed25519_base64url','sha256:QnDuPiA99wAIcCJlk4ZS_ZJV4gQPW8cX0LVmKQJNl0I',
119
- true,null,'agent',ARRAY['board:read','board:send'],ARRAY[]::text[],null)$$,
119
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1),null,'agent',ARRAY['board:read','board:send'],ARRAY[]::text[],null)$$,
120
120
  '23505', null, 'changed stable subject conflicts'
121
121
  );
122
122
  select throws_ok(
@@ -125,7 +125,7 @@ select throws_ok(
125
125
  '{"iss":"https://legacy.atomic.test","aud":["message-board","secondary-audience"],"uid":"legacy-complete"}'::jsonb,
126
126
  'changed-slug','TauOMxwU-TW-deqQjil1DLDy_AvJrfoG_eXGwwvaRUk',
127
127
  'raw_ed25519_base64url','sha256:QnDuPiA99wAIcCJlk4ZS_ZJV4gQPW8cX0LVmKQJNl0I',
128
- true,null,'agent',ARRAY['board:read','board:send'],ARRAY[]::text[],null)$$,
128
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1),null,'agent',ARRAY['board:read','board:send'],ARRAY[]::text[],null)$$,
129
129
  '23505', null, 'changed requested slug conflicts'
130
130
  );
131
131
  select throws_ok(
@@ -134,7 +134,7 @@ select throws_ok(
134
134
  '{"iss":"https://legacy.atomic.test","aud":["message-board","secondary-audience"],"uid":"legacy-complete"}'::jsonb,
135
135
  'atomic-backfill-complete','TauOMxwU-TW-deqQjil1DLDy_AvJrfoG_eXGwwvaRUk',
136
136
  'raw_ed25519_base64url','sha256:QnDuPiA99wAIcCJlk4ZS_ZJV4gQPW8cX0LVmKQJNl0I',
137
- true,'new-actor','agent',ARRAY['board:read','board:send'],ARRAY[]::text[],null)$$,
137
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1),'new-actor','agent',ARRAY['board:read','board:send'],ARRAY[]::text[],null)$$,
138
138
  '23505', null, 'changed independent actor subject conflicts'
139
139
  );
140
140