@consiliency/agent-board-schema 1.56.0 → 1.58.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 (33) hide show
  1. package/fixtures/already-applied.json +8 -0
  2. package/migrations/20261007000100_agent_board_onboard_requests.sql +139 -0
  3. package/migrations/20261008000100_agent_board_hosted_delegates.sql +3152 -0
  4. package/package.json +1 -1
  5. package/schema-manifest.json +21 -3
  6. package/src/tooling.test.mjs +8 -2
  7. package/tests/agent_board_atomic_fleet_onboarding.test.sql +13 -0
  8. package/tests/agent_board_consent_emission.test.sql +4 -2
  9. package/tests/agent_board_delegate_role_class.test.sql +736 -0
  10. package/tests/agent_board_federated_identity_injective_slug.test.sql +5 -2
  11. package/tests/agent_board_fedsession_stored_identity_gate.test.sql +71 -14
  12. package/tests/agent_board_fk_lock_edges.test.sql +9 -0
  13. package/tests/agent_board_hosted_delegates.test.sql +1134 -0
  14. package/tests/agent_board_ledger_authority.test.sql +34 -5
  15. package/tests/agent_board_membership_current_row_class.test.sql +3 -1
  16. package/tests/agent_board_onboard_requests.test.sql +353 -0
  17. package/tests/agent_board_rowid.test.sql +18 -5
  18. package/tests/agent_board_signer_lifecycle.test.sql +3 -0
  19. package/tests/agent_board_write_enforcement_class.test.sql +8 -3
  20. package/tests/catalog/function_execute_privileges.json +278 -2
  21. package/tests/catalog/p_metadata_parameters.json +48 -0
  22. package/tests/isolation/expected/hosted_delegate_exchange_race.out +93 -0
  23. package/tests/isolation/expected/hosted_delegate_first_exchange_race.out +129 -0
  24. package/tests/isolation/expected/hosted_delegate_owner_provisioning_race.out +91 -0
  25. package/tests/isolation/expected/hosted_delegate_owner_revoke_race.out +137 -0
  26. package/tests/isolation/expected/hosted_delegate_revoke_race.out +87 -0
  27. package/tests/isolation/expected/hosted_delegate_withdraw_evict_race.out +235 -0
  28. package/tests/isolation/specs/hosted_delegate_exchange_race.spec +64 -0
  29. package/tests/isolation/specs/hosted_delegate_first_exchange_race.spec +72 -0
  30. package/tests/isolation/specs/hosted_delegate_owner_provisioning_race.spec +73 -0
  31. package/tests/isolation/specs/hosted_delegate_owner_revoke_race.spec +93 -0
  32. package/tests/isolation/specs/hosted_delegate_revoke_race.spec +59 -0
  33. package/tests/isolation/specs/hosted_delegate_withdraw_evict_race.spec +145 -0
@@ -0,0 +1,91 @@
1
+ Parsed test spec with 3 sessions
2
+
3
+ starting permutation: h_begin h_enroll x_begin x_exchange h_commit x_commit check
4
+ step h_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
5
+ step h_enroll:
6
+ select status from agent_board.enroll_federated_human(
7
+ 'https://iso-prov.test', 'message-board', 'iso-prov-owner',
8
+ jsonb_build_object('sub', 'iso-prov-owner'), 'Iso Prov Owner');
9
+
10
+ status
11
+ ------
12
+ active
13
+ (1 row)
14
+
15
+ step x_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
16
+ step x_exchange:
17
+ select count(*) as issued from agent_board.exchange_delegate_credential(jsonb_build_object(
18
+ 'iss', 'https://iso-prov.test', 'sub', 'iso-prov-owner', 'slot_resource', null,
19
+ 'agent_board_delegate_instance', jsonb_build_object(
20
+ 'instance_id', 'iso_prov_instance_0001', 'grant_id', 'iso_grant_0000000001', 'generation', 1,
21
+ 'owner_sub', 'iso-prov-owner', 'client_id', 'iso-client', 'status', 'active', 'client_change', null)));
22
+ <waiting ...>
23
+ step h_commit: COMMIT;
24
+ step x_exchange: <... completed>
25
+ issued
26
+ ------
27
+ 1
28
+ (1 row)
29
+
30
+ step x_commit: COMMIT;
31
+ step check:
32
+ select (select count(*) from auth.users
33
+ where email = 'agent+' || agent_board._federated_identity_slug('https://iso-prov.test', 'iso-prov-owner')
34
+ || '@agent-board.local') as owner_auth_users,
35
+ (select count(*) from agent_board.agents
36
+ where handle = agent_board._federated_identity_slug('https://iso-prov.test', 'iso-prov-owner')) as human_rows,
37
+ (select h.auth_user_id = di.owner_user_id
38
+ from agent_board.agents h, agent_board.delegate_instances di
39
+ where h.handle = agent_board._federated_identity_slug('https://iso-prov.test', 'iso-prov-owner')
40
+ and di.instance_id = 'iso_prov_instance_0001') as delegate_owned_by_human;
41
+
42
+ owner_auth_users|human_rows|delegate_owned_by_human
43
+ ----------------+----------+-----------------------
44
+ 1| 1|t
45
+ (1 row)
46
+
47
+
48
+ starting permutation: x_begin x_exchange h_begin h_enroll x_commit h_commit check
49
+ step x_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
50
+ step x_exchange:
51
+ select count(*) as issued from agent_board.exchange_delegate_credential(jsonb_build_object(
52
+ 'iss', 'https://iso-prov.test', 'sub', 'iso-prov-owner', 'slot_resource', null,
53
+ 'agent_board_delegate_instance', jsonb_build_object(
54
+ 'instance_id', 'iso_prov_instance_0001', 'grant_id', 'iso_grant_0000000001', 'generation', 1,
55
+ 'owner_sub', 'iso-prov-owner', 'client_id', 'iso-client', 'status', 'active', 'client_change', null)));
56
+
57
+ issued
58
+ ------
59
+ 1
60
+ (1 row)
61
+
62
+ step h_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
63
+ step h_enroll:
64
+ select status from agent_board.enroll_federated_human(
65
+ 'https://iso-prov.test', 'message-board', 'iso-prov-owner',
66
+ jsonb_build_object('sub', 'iso-prov-owner'), 'Iso Prov Owner');
67
+ <waiting ...>
68
+ step x_commit: COMMIT;
69
+ step h_enroll: <... completed>
70
+ status
71
+ ------
72
+ active
73
+ (1 row)
74
+
75
+ step h_commit: COMMIT;
76
+ step check:
77
+ select (select count(*) from auth.users
78
+ where email = 'agent+' || agent_board._federated_identity_slug('https://iso-prov.test', 'iso-prov-owner')
79
+ || '@agent-board.local') as owner_auth_users,
80
+ (select count(*) from agent_board.agents
81
+ where handle = agent_board._federated_identity_slug('https://iso-prov.test', 'iso-prov-owner')) as human_rows,
82
+ (select h.auth_user_id = di.owner_user_id
83
+ from agent_board.agents h, agent_board.delegate_instances di
84
+ where h.handle = agent_board._federated_identity_slug('https://iso-prov.test', 'iso-prov-owner')
85
+ and di.instance_id = 'iso_prov_instance_0001') as delegate_owned_by_human;
86
+
87
+ owner_auth_users|human_rows|delegate_owned_by_human
88
+ ----------------+----------+-----------------------
89
+ 1| 1|t
90
+ (1 row)
91
+
@@ -0,0 +1,137 @@
1
+ Parsed test spec with 3 sessions
2
+
3
+ starting permutation: x_begin x_new r_begin r_revoke x_commit r_commit check
4
+ count
5
+ -----
6
+ 1
7
+ (1 row)
8
+
9
+ step x_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
10
+ step x_new:
11
+ select count(*) as issued from agent_board.exchange_delegate_credential(jsonb_build_object(
12
+ 'iss', 'https://iso-rev.test', 'sub', 'iso-rev-owner', 'slot_resource', null,
13
+ 'agent_board_delegate_instance', jsonb_build_object(
14
+ 'instance_id', 'iso_rev_new_000000001', 'grant_id', 'iso_grant_0000000001', 'generation', 1,
15
+ 'owner_sub', 'iso-rev-owner', 'client_id', 'iso-client', 'status', 'active', 'client_change', null)));
16
+
17
+ issued
18
+ ------
19
+ 1
20
+ (1 row)
21
+
22
+ step r_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
23
+ step r_revoke:
24
+ select revoked_agent_count from agent_board.revoke_human_subtree(
25
+ (select auth_user_id from agent_board.agents
26
+ where handle = agent_board._federated_identity_slug('https://iso-rev.test', 'iso-rev-owner')),
27
+ '16310000-0000-0000-0000-000000000001', 'iso owner revoke race');
28
+ <waiting ...>
29
+ step x_commit: COMMIT;
30
+ step r_revoke: <... completed>
31
+ revoked_agent_count
32
+ -------------------
33
+ 3
34
+ (1 row)
35
+
36
+ step r_commit: COMMIT;
37
+ step check:
38
+ select (select count(*) from agent_board.fleet_human_revocations where issuer = 'https://iso-rev.test') as markers,
39
+ (select count(*) from agent_board.delegate_instances where instance_id like 'iso\_rev\_%') as instances,
40
+ (select count(*) from agent_board.agents where handle like 'delegate-%' and status = 'active') as active_delegates,
41
+ (select count(*) from agent_board.agent_credentials ac
42
+ join agent_board.delegate_instances di on di.agent_id = ac.agent_id
43
+ where di.instance_id like 'iso\_rev\_%' and ac.revoked_at is null) as live_delegate_credentials;
44
+
45
+ markers|instances|active_delegates|live_delegate_credentials
46
+ -------+---------+----------------+-------------------------
47
+ 1| 2| 0| 0
48
+ (1 row)
49
+
50
+
51
+ starting permutation: r_begin r_revoke x_begin x_new r_commit x_commit check
52
+ count
53
+ -----
54
+ 1
55
+ (1 row)
56
+
57
+ step r_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
58
+ step r_revoke:
59
+ select revoked_agent_count from agent_board.revoke_human_subtree(
60
+ (select auth_user_id from agent_board.agents
61
+ where handle = agent_board._federated_identity_slug('https://iso-rev.test', 'iso-rev-owner')),
62
+ '16310000-0000-0000-0000-000000000001', 'iso owner revoke race');
63
+
64
+ revoked_agent_count
65
+ -------------------
66
+ 2
67
+ (1 row)
68
+
69
+ step x_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
70
+ step x_new:
71
+ select count(*) as issued from agent_board.exchange_delegate_credential(jsonb_build_object(
72
+ 'iss', 'https://iso-rev.test', 'sub', 'iso-rev-owner', 'slot_resource', null,
73
+ 'agent_board_delegate_instance', jsonb_build_object(
74
+ 'instance_id', 'iso_rev_new_000000001', 'grant_id', 'iso_grant_0000000001', 'generation', 1,
75
+ 'owner_sub', 'iso-rev-owner', 'client_id', 'iso-client', 'status', 'active', 'client_change', null)));
76
+ <waiting ...>
77
+ step r_commit: COMMIT;
78
+ step x_new: <... completed>
79
+ ERROR: owner_inactive
80
+ step x_commit: COMMIT;
81
+ step check:
82
+ select (select count(*) from agent_board.fleet_human_revocations where issuer = 'https://iso-rev.test') as markers,
83
+ (select count(*) from agent_board.delegate_instances where instance_id like 'iso\_rev\_%') as instances,
84
+ (select count(*) from agent_board.agents where handle like 'delegate-%' and status = 'active') as active_delegates,
85
+ (select count(*) from agent_board.agent_credentials ac
86
+ join agent_board.delegate_instances di on di.agent_id = ac.agent_id
87
+ where di.instance_id like 'iso\_rev\_%' and ac.revoked_at is null) as live_delegate_credentials;
88
+
89
+ markers|instances|active_delegates|live_delegate_credentials
90
+ -------+---------+----------------+-------------------------
91
+ 1| 1| 0| 0
92
+ (1 row)
93
+
94
+
95
+ starting permutation: r_begin r_revoke x_begin x_existing r_commit x_commit check
96
+ count
97
+ -----
98
+ 1
99
+ (1 row)
100
+
101
+ step r_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
102
+ step r_revoke:
103
+ select revoked_agent_count from agent_board.revoke_human_subtree(
104
+ (select auth_user_id from agent_board.agents
105
+ where handle = agent_board._federated_identity_slug('https://iso-rev.test', 'iso-rev-owner')),
106
+ '16310000-0000-0000-0000-000000000001', 'iso owner revoke race');
107
+
108
+ revoked_agent_count
109
+ -------------------
110
+ 2
111
+ (1 row)
112
+
113
+ step x_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
114
+ step x_existing:
115
+ select count(*) as issued from agent_board.exchange_delegate_credential(jsonb_build_object(
116
+ 'iss', 'https://iso-rev.test', 'sub', 'iso-rev-owner', 'slot_resource', null,
117
+ 'agent_board_delegate_instance', jsonb_build_object(
118
+ 'instance_id', 'iso_rev_existing_00001', 'grant_id', 'iso_grant_0000000001', 'generation', 1,
119
+ 'owner_sub', 'iso-rev-owner', 'client_id', 'iso-client', 'status', 'active', 'client_change', null)));
120
+ <waiting ...>
121
+ step r_commit: COMMIT;
122
+ step x_existing: <... completed>
123
+ ERROR: owner_inactive
124
+ step x_commit: COMMIT;
125
+ step check:
126
+ select (select count(*) from agent_board.fleet_human_revocations where issuer = 'https://iso-rev.test') as markers,
127
+ (select count(*) from agent_board.delegate_instances where instance_id like 'iso\_rev\_%') as instances,
128
+ (select count(*) from agent_board.agents where handle like 'delegate-%' and status = 'active') as active_delegates,
129
+ (select count(*) from agent_board.agent_credentials ac
130
+ join agent_board.delegate_instances di on di.agent_id = ac.agent_id
131
+ where di.instance_id like 'iso\_rev\_%' and ac.revoked_at is null) as live_delegate_credentials;
132
+
133
+ markers|instances|active_delegates|live_delegate_credentials
134
+ -------+---------+----------------+-------------------------
135
+ 1| 1| 0| 0
136
+ (1 row)
137
+
@@ -0,0 +1,87 @@
1
+ Parsed test spec with 3 sessions
2
+
3
+ starting permutation: ex_begin ex_exchange rv_begin rv_tombstone ex_commit rv_commit check
4
+ count
5
+ -----
6
+ 1
7
+ (1 row)
8
+
9
+ step ex_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
10
+ step ex_exchange:
11
+ select count(*) as issued from agent_board.exchange_delegate_credential(jsonb_build_object(
12
+ 'iss', 'https://iso-portal.test', 'sub', 'iso-owner', 'slot_resource', null,
13
+ 'agent_board_delegate_instance', jsonb_build_object(
14
+ 'instance_id', 'iso_revoke_race_00001', 'grant_id', 'iso_grant_0000000002', 'generation', 2,
15
+ 'owner_sub', 'iso-owner', 'client_id', 'iso-client', 'status', 'active', 'client_change', null)));
16
+
17
+ issued
18
+ ------
19
+ 1
20
+ (1 row)
21
+
22
+ step rv_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
23
+ step rv_tombstone:
24
+ select first from agent_board.tombstone_delegate_instance(
25
+ 'iso_revoke_race_00001', 'https://iso-portal.test', 'iso-owner', 'owner_revoked');
26
+ <waiting ...>
27
+ step ex_commit: COMMIT;
28
+ step rv_tombstone: <... completed>
29
+ first
30
+ -----
31
+ t
32
+ (1 row)
33
+
34
+ step rv_commit: COMMIT;
35
+ step check:
36
+ select (di.tombstoned_at is not null) as tombstoned,
37
+ (select count(*) from agent_board.agent_credentials ac where ac.agent_id = di.agent_id and ac.revoked_at is null) as live_credentials,
38
+ (select count(*) from agent_board.agent_credentials ac where ac.agent_id = di.agent_id) as all_credentials,
39
+ (select a.status from agent_board.agents a where a.id = di.agent_id) as agent_status
40
+ from agent_board.delegate_instances di where di.instance_id = 'iso_revoke_race_00001';
41
+
42
+ tombstoned|live_credentials|all_credentials|agent_status
43
+ ----------+----------------+---------------+------------
44
+ t | 0| 2|revoked
45
+ (1 row)
46
+
47
+
48
+ starting permutation: rv_begin rv_tombstone ex_begin ex_exchange rv_commit ex_commit check
49
+ count
50
+ -----
51
+ 1
52
+ (1 row)
53
+
54
+ step rv_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
55
+ step rv_tombstone:
56
+ select first from agent_board.tombstone_delegate_instance(
57
+ 'iso_revoke_race_00001', 'https://iso-portal.test', 'iso-owner', 'owner_revoked');
58
+
59
+ first
60
+ -----
61
+ t
62
+ (1 row)
63
+
64
+ step ex_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
65
+ step ex_exchange:
66
+ select count(*) as issued from agent_board.exchange_delegate_credential(jsonb_build_object(
67
+ 'iss', 'https://iso-portal.test', 'sub', 'iso-owner', 'slot_resource', null,
68
+ 'agent_board_delegate_instance', jsonb_build_object(
69
+ 'instance_id', 'iso_revoke_race_00001', 'grant_id', 'iso_grant_0000000002', 'generation', 2,
70
+ 'owner_sub', 'iso-owner', 'client_id', 'iso-client', 'status', 'active', 'client_change', null)));
71
+ <waiting ...>
72
+ step rv_commit: COMMIT;
73
+ step ex_exchange: <... completed>
74
+ ERROR: instance_revoked
75
+ step ex_commit: COMMIT;
76
+ step check:
77
+ select (di.tombstoned_at is not null) as tombstoned,
78
+ (select count(*) from agent_board.agent_credentials ac where ac.agent_id = di.agent_id and ac.revoked_at is null) as live_credentials,
79
+ (select count(*) from agent_board.agent_credentials ac where ac.agent_id = di.agent_id) as all_credentials,
80
+ (select a.status from agent_board.agents a where a.id = di.agent_id) as agent_status
81
+ from agent_board.delegate_instances di where di.instance_id = 'iso_revoke_race_00001';
82
+
83
+ tombstoned|live_credentials|all_credentials|agent_status
84
+ ----------+----------------+---------------+------------
85
+ t | 0| 1|revoked
86
+ (1 row)
87
+
@@ -0,0 +1,235 @@
1
+ Parsed test spec with 4 sessions
2
+
3
+ starting permutation: a_begin a_evict o_begin o_withdraw a_commit o_commit check o_readd check
4
+ step a_begin:
5
+ BEGIN;
6
+ select set_config('request.jwt.claims',
7
+ json_build_object('role', 'authenticated', 'sub', '16320000-0000-4000-8000-000000000001')::text, true) is not null as admin_session;
8
+
9
+ admin_session
10
+ -------------
11
+ t
12
+ (1 row)
13
+
14
+ step a_evict:
15
+ select membership_state from agent_board.update_board_membership(
16
+ '16322000-0000-4000-8000-000000000001',
17
+ (select agent_id from agent_board.delegate_instances where instance_id = 'iso_wd_instance_00001'),
18
+ 'removed');
19
+
20
+ membership_state
21
+ ----------------
22
+ removed
23
+ (1 row)
24
+
25
+ step o_begin:
26
+ BEGIN;
27
+ select set_config('request.jwt.claims', json_build_object('role', 'authenticated', 'sub',
28
+ (select auth_user_id from agent_board.agents
29
+ where handle = agent_board._federated_identity_slug('https://iso-wd.test', 'iso-wd-owner')))::text, true) is not null as owner_session;
30
+
31
+ owner_session
32
+ -------------
33
+ t
34
+ (1 row)
35
+
36
+ step o_withdraw:
37
+ select membership_state from agent_board.update_board_membership(
38
+ '16322000-0000-4000-8000-000000000001',
39
+ (select agent_id from agent_board.delegate_instances where instance_id = 'iso_wd_instance_00001'),
40
+ 'left');
41
+ <waiting ...>
42
+ step a_commit: COMMIT;
43
+ step o_withdraw: <... completed>
44
+ ERROR: a principal may withdraw its delegate only from an active or pending membership
45
+ step o_commit: COMMIT;
46
+ step check:
47
+ select (select bm.membership_state from agent_board.board_memberships bm
48
+ join agent_board.delegate_instances di on di.agent_id = bm.agent_id
49
+ where bm.board_id = '16322000-0000-4000-8000-000000000001' and di.instance_id = 'iso_wd_instance_00001'
50
+ order by bm.created_at desc, bm.id desc limit 1) as delegate_state,
51
+ (select bm.membership_state from agent_board.board_memberships bm
52
+ where bm.board_id = '16322000-0000-4000-8000-000000000001'
53
+ and bm.agent_id = '16321000-0000-4000-8000-000000000002'
54
+ order by bm.created_at desc, bm.id desc limit 1) as member_state;
55
+
56
+ delegate_state|member_state
57
+ --------------+------------
58
+ removed |joined
59
+ (1 row)
60
+
61
+ step o_readd:
62
+ select set_config('request.jwt.claims', json_build_object('role', 'authenticated', 'sub',
63
+ (select auth_user_id from agent_board.agents
64
+ where handle = agent_board._federated_identity_slug('https://iso-wd.test', 'iso-wd-owner')))::text, false) is not null as owner_session;
65
+ select membership_state from agent_board.add_delegate_to_board(
66
+ (select agent_id from agent_board.delegate_instances where instance_id = 'iso_wd_instance_00001'),
67
+ '16322000-0000-4000-8000-000000000001');
68
+
69
+ owner_session
70
+ -------------
71
+ t
72
+ (1 row)
73
+
74
+ ERROR: a removed delegate stays removed; a board admin must lift the removal
75
+ step check:
76
+ select (select bm.membership_state from agent_board.board_memberships bm
77
+ join agent_board.delegate_instances di on di.agent_id = bm.agent_id
78
+ where bm.board_id = '16322000-0000-4000-8000-000000000001' and di.instance_id = 'iso_wd_instance_00001'
79
+ order by bm.created_at desc, bm.id desc limit 1) as delegate_state,
80
+ (select bm.membership_state from agent_board.board_memberships bm
81
+ where bm.board_id = '16322000-0000-4000-8000-000000000001'
82
+ and bm.agent_id = '16321000-0000-4000-8000-000000000002'
83
+ order by bm.created_at desc, bm.id desc limit 1) as member_state;
84
+
85
+ delegate_state|member_state
86
+ --------------+------------
87
+ removed |joined
88
+ (1 row)
89
+
90
+
91
+ starting permutation: o_begin o_withdraw a_begin a_evict o_commit a_commit check o_readd check
92
+ step o_begin:
93
+ BEGIN;
94
+ select set_config('request.jwt.claims', json_build_object('role', 'authenticated', 'sub',
95
+ (select auth_user_id from agent_board.agents
96
+ where handle = agent_board._federated_identity_slug('https://iso-wd.test', 'iso-wd-owner')))::text, true) is not null as owner_session;
97
+
98
+ owner_session
99
+ -------------
100
+ t
101
+ (1 row)
102
+
103
+ step o_withdraw:
104
+ select membership_state from agent_board.update_board_membership(
105
+ '16322000-0000-4000-8000-000000000001',
106
+ (select agent_id from agent_board.delegate_instances where instance_id = 'iso_wd_instance_00001'),
107
+ 'left');
108
+
109
+ membership_state
110
+ ----------------
111
+ left
112
+ (1 row)
113
+
114
+ step a_begin:
115
+ BEGIN;
116
+ select set_config('request.jwt.claims',
117
+ json_build_object('role', 'authenticated', 'sub', '16320000-0000-4000-8000-000000000001')::text, true) is not null as admin_session;
118
+
119
+ admin_session
120
+ -------------
121
+ t
122
+ (1 row)
123
+
124
+ step a_evict:
125
+ select membership_state from agent_board.update_board_membership(
126
+ '16322000-0000-4000-8000-000000000001',
127
+ (select agent_id from agent_board.delegate_instances where instance_id = 'iso_wd_instance_00001'),
128
+ 'removed');
129
+ <waiting ...>
130
+ step o_commit: COMMIT;
131
+ step a_evict: <... completed>
132
+ membership_state
133
+ ----------------
134
+ removed
135
+ (1 row)
136
+
137
+ step a_commit: COMMIT;
138
+ step check:
139
+ select (select bm.membership_state from agent_board.board_memberships bm
140
+ join agent_board.delegate_instances di on di.agent_id = bm.agent_id
141
+ where bm.board_id = '16322000-0000-4000-8000-000000000001' and di.instance_id = 'iso_wd_instance_00001'
142
+ order by bm.created_at desc, bm.id desc limit 1) as delegate_state,
143
+ (select bm.membership_state from agent_board.board_memberships bm
144
+ where bm.board_id = '16322000-0000-4000-8000-000000000001'
145
+ and bm.agent_id = '16321000-0000-4000-8000-000000000002'
146
+ order by bm.created_at desc, bm.id desc limit 1) as member_state;
147
+
148
+ delegate_state|member_state
149
+ --------------+------------
150
+ removed |joined
151
+ (1 row)
152
+
153
+ step o_readd:
154
+ select set_config('request.jwt.claims', json_build_object('role', 'authenticated', 'sub',
155
+ (select auth_user_id from agent_board.agents
156
+ where handle = agent_board._federated_identity_slug('https://iso-wd.test', 'iso-wd-owner')))::text, false) is not null as owner_session;
157
+ select membership_state from agent_board.add_delegate_to_board(
158
+ (select agent_id from agent_board.delegate_instances where instance_id = 'iso_wd_instance_00001'),
159
+ '16322000-0000-4000-8000-000000000001');
160
+
161
+ owner_session
162
+ -------------
163
+ t
164
+ (1 row)
165
+
166
+ ERROR: a removed delegate stays removed; a board admin must lift the removal
167
+ step check:
168
+ select (select bm.membership_state from agent_board.board_memberships bm
169
+ join agent_board.delegate_instances di on di.agent_id = bm.agent_id
170
+ where bm.board_id = '16322000-0000-4000-8000-000000000001' and di.instance_id = 'iso_wd_instance_00001'
171
+ order by bm.created_at desc, bm.id desc limit 1) as delegate_state,
172
+ (select bm.membership_state from agent_board.board_memberships bm
173
+ where bm.board_id = '16322000-0000-4000-8000-000000000001'
174
+ and bm.agent_id = '16321000-0000-4000-8000-000000000002'
175
+ order by bm.created_at desc, bm.id desc limit 1) as member_state;
176
+
177
+ delegate_state|member_state
178
+ --------------+------------
179
+ removed |joined
180
+ (1 row)
181
+
182
+
183
+ starting permutation: a_begin a_evict_member m_begin m_leave a_commit m_commit check
184
+ step a_begin:
185
+ BEGIN;
186
+ select set_config('request.jwt.claims',
187
+ json_build_object('role', 'authenticated', 'sub', '16320000-0000-4000-8000-000000000001')::text, true) is not null as admin_session;
188
+
189
+ admin_session
190
+ -------------
191
+ t
192
+ (1 row)
193
+
194
+ step a_evict_member:
195
+ select membership_state from agent_board.update_board_membership(
196
+ '16322000-0000-4000-8000-000000000001', '16321000-0000-4000-8000-000000000002', 'removed');
197
+
198
+ membership_state
199
+ ----------------
200
+ removed
201
+ (1 row)
202
+
203
+ step m_begin:
204
+ BEGIN;
205
+ select set_config('request.jwt.claims',
206
+ json_build_object('role', 'authenticated', 'sub', '16320000-0000-4000-8000-000000000002')::text, true) is not null as member_session;
207
+
208
+ member_session
209
+ --------------
210
+ t
211
+ (1 row)
212
+
213
+ step m_leave:
214
+ select membership_state from agent_board.update_board_membership(
215
+ '16322000-0000-4000-8000-000000000001', '16321000-0000-4000-8000-000000000002', 'left');
216
+ <waiting ...>
217
+ step a_commit: COMMIT;
218
+ step m_leave: <... completed>
219
+ ERROR: self membership change not permitted; use the join/approval path
220
+ step m_commit: COMMIT;
221
+ step check:
222
+ select (select bm.membership_state from agent_board.board_memberships bm
223
+ join agent_board.delegate_instances di on di.agent_id = bm.agent_id
224
+ where bm.board_id = '16322000-0000-4000-8000-000000000001' and di.instance_id = 'iso_wd_instance_00001'
225
+ order by bm.created_at desc, bm.id desc limit 1) as delegate_state,
226
+ (select bm.membership_state from agent_board.board_memberships bm
227
+ where bm.board_id = '16322000-0000-4000-8000-000000000001'
228
+ and bm.agent_id = '16321000-0000-4000-8000-000000000002'
229
+ order by bm.created_at desc, bm.id desc limit 1) as member_state;
230
+
231
+ delegate_state|member_state
232
+ --------------+------------
233
+ joined |removed
234
+ (1 row)
235
+
@@ -0,0 +1,64 @@
1
+ # HOSTED (v11) SL-2, IF-0-HOSTED-7: two concurrent exchanges of ONE instance at different
2
+ # generations serialise on the instance row lock (`select ... for update`), in both orders.
3
+ #
4
+ # new first: the generation-3 exchange commits; the generation-2 exchange, blocked on the
5
+ # row, then sees r = 3 and is refused generation_superseded.
6
+ # old first: the generation-2 exchange commits; the generation-3 exchange, blocked, then
7
+ # sees r = 2, records 3 and revokes every lower-generation credential.
8
+ #
9
+ # Either way the recorded generation is 3 and the only live credential is generation 3.
10
+ # Without the row lock the generation-2 exchange does not wait for the read, decides against
11
+ # r = 1, and the trace and the final generation both change (the mutation receipt).
12
+
13
+ setup
14
+ {
15
+ select count(*) from agent_board.exchange_delegate_credential(jsonb_build_object(
16
+ 'iss', 'https://iso-portal.test', 'sub', 'iso-owner', 'slot_resource', null,
17
+ 'agent_board_delegate_instance', jsonb_build_object(
18
+ 'instance_id', 'iso_exchange_race_0001', 'grant_id', 'iso_grant_0000000001', 'generation', 1,
19
+ 'owner_sub', 'iso-owner', 'client_id', 'iso-client', 'status', 'active', 'client_change', null)));
20
+ }
21
+
22
+ teardown
23
+ {
24
+ delete from agent_board.agent_credentials
25
+ where agent_id in (select agent_id from agent_board.delegate_instances where instance_id like 'iso\_%');
26
+ delete from agent_board.delegate_instances where instance_id like 'iso\_%';
27
+ delete from agent_board.agents where handle like 'delegate-%';
28
+ delete from auth.identities where provider_id like 'delegate-%' or provider_id like 'fed-human-%';
29
+ delete from auth.users where email like 'agent+delegate-%' or email like 'agent+fed-human-%';
30
+ }
31
+
32
+ session "new"
33
+ step "new_begin" { BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role'; }
34
+ step "new_exchange" {
35
+ select count(*) as issued from agent_board.exchange_delegate_credential(jsonb_build_object(
36
+ 'iss', 'https://iso-portal.test', 'sub', 'iso-owner', 'slot_resource', null,
37
+ 'agent_board_delegate_instance', jsonb_build_object(
38
+ 'instance_id', 'iso_exchange_race_0001', 'grant_id', 'iso_grant_0000000003', 'generation', 3,
39
+ 'owner_sub', 'iso-owner', 'client_id', 'iso-client', 'status', 'active', 'client_change', null)));
40
+ }
41
+ step "new_commit" { COMMIT; }
42
+
43
+ session "old"
44
+ step "old_begin" { BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role'; }
45
+ step "old_exchange" {
46
+ select count(*) as issued from agent_board.exchange_delegate_credential(jsonb_build_object(
47
+ 'iss', 'https://iso-portal.test', 'sub', 'iso-owner', 'slot_resource', null,
48
+ 'agent_board_delegate_instance', jsonb_build_object(
49
+ 'instance_id', 'iso_exchange_race_0001', 'grant_id', 'iso_grant_0000000002', 'generation', 2,
50
+ 'owner_sub', 'iso-owner', 'client_id', 'iso-client', 'status', 'active', 'client_change', null)));
51
+ }
52
+ step "old_commit" { COMMIT; }
53
+
54
+ session "check"
55
+ step "check" {
56
+ select di.generation as recorded_generation, di.grant_id as recorded_grant,
57
+ (select string_agg(ac.generation::text, ',' order by ac.generation)
58
+ from agent_board.agent_credentials ac where ac.agent_id = di.agent_id and ac.revoked_at is null) as live_generations,
59
+ (select count(*) from agent_board.agents a where a.handle like 'delegate-%') as delegate_agents
60
+ from agent_board.delegate_instances di where di.instance_id = 'iso_exchange_race_0001';
61
+ }
62
+
63
+ permutation "new_begin" "new_exchange" "old_begin" "old_exchange" "new_commit" "old_commit" "check"
64
+ permutation "old_begin" "old_exchange" "new_begin" "new_exchange" "old_commit" "new_commit" "check"