@consiliency/agent-board-schema 1.57.0 → 1.59.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.
- package/fixtures/already-applied.json +8 -0
- package/migrations/20261008000100_agent_board_hosted_delegates.sql +3152 -0
- package/migrations/20261009000100_agent_board_hosted_followups.sql +665 -0
- package/package.json +1 -1
- package/schema-manifest.json +21 -3
- package/src/tooling.test.mjs +9 -2
- package/tests/agent_board_atomic_fleet_onboarding.test.sql +13 -0
- package/tests/agent_board_consent_emission.test.sql +4 -2
- package/tests/agent_board_delegate_role_class.test.sql +736 -0
- package/tests/agent_board_federated_identity_injective_slug.test.sql +5 -2
- package/tests/agent_board_fedsession_stored_identity_gate.test.sql +71 -14
- package/tests/agent_board_fk_lock_edges.test.sql +9 -0
- package/tests/agent_board_hosted_delegates.test.sql +1278 -0
- package/tests/agent_board_ledger_authority.test.sql +34 -5
- package/tests/agent_board_signer_lifecycle.test.sql +3 -0
- package/tests/agent_board_write_enforcement_class.test.sql +8 -3
- package/tests/catalog/function_execute_privileges.json +269 -2
- package/tests/catalog/p_metadata_parameters.json +44 -0
- package/tests/isolation/expected/hosted_delegate_add_tombstone_race.out +105 -0
- package/tests/isolation/expected/hosted_delegate_exchange_agent_revoke_race.out +66 -0
- package/tests/isolation/expected/hosted_delegate_exchange_race.out +93 -0
- package/tests/isolation/expected/hosted_delegate_first_exchange_race.out +129 -0
- package/tests/isolation/expected/hosted_delegate_owner_provisioning_race.out +91 -0
- package/tests/isolation/expected/hosted_delegate_owner_revoke_race.out +137 -0
- package/tests/isolation/expected/hosted_delegate_revoke_race.out +87 -0
- package/tests/isolation/expected/hosted_delegate_tombstone_revoke_race.out +170 -0
- package/tests/isolation/expected/hosted_delegate_withdraw_evict_race.out +235 -0
- package/tests/isolation/specs/hosted_delegate_add_tombstone_race.spec +102 -0
- package/tests/isolation/specs/hosted_delegate_exchange_agent_revoke_race.spec +92 -0
- package/tests/isolation/specs/hosted_delegate_exchange_race.spec +64 -0
- package/tests/isolation/specs/hosted_delegate_first_exchange_race.spec +72 -0
- package/tests/isolation/specs/hosted_delegate_owner_provisioning_race.spec +73 -0
- package/tests/isolation/specs/hosted_delegate_owner_revoke_race.spec +93 -0
- package/tests/isolation/specs/hosted_delegate_revoke_race.spec +59 -0
- package/tests/isolation/specs/hosted_delegate_tombstone_revoke_race.spec +158 -0
- package/tests/isolation/specs/hosted_delegate_withdraw_evict_race.spec +145 -0
|
@@ -353,8 +353,11 @@ select is(
|
|
|
353
353
|
where p.pronamespace = 'agent_board'::regnamespace
|
|
354
354
|
and p.proname <> '_federated_identity_slug'
|
|
355
355
|
and agent_board_test.stripped_body(p.oid, true) like '%\_federated\_identity\_slug(%') x),
|
|
356
|
-
|
|
357
|
-
|
|
356
|
+
-- HOSTED (20261008000100) added two sites: exchange_delegate_credential and
|
|
357
|
+
-- tombstone_delegate_instance derive the hosted delegate's OWNER uid exactly as
|
|
358
|
+
-- enroll_federated_human derives the human's (IF-0-HOSTED-12's frozen owner identity).
|
|
359
|
+
'_enroll_federated_human_after_revocation_gate=1, _enroll_federated_identity_once=1, enroll_federated_human=1, enroll_federated_identity=2, exchange_delegate_credential=1, onboard_federated_fleet_agent=1, tombstone_delegate_instance=1',
|
|
360
|
+
'CLS-3: exactly these seven functions route through the helper, each with its own call count');
|
|
358
361
|
|
|
359
362
|
-- CLS-4. The helper is not a public lever. It is pure and mints nothing, but it is the
|
|
360
363
|
-- function that decides which account a pair maps to, and CREATE FUNCTION grants EXECUTE
|
|
@@ -44,7 +44,10 @@ set search_path = extensions, public, agent_board;
|
|
|
44
44
|
|
|
45
45
|
begin;
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
-- 31 with HOSTED (20261008000100): two derivation sites, each exempt positively (13, 14).
|
|
48
|
+
-- 33 with the HOSTED fix lane (PR #590): the exchange joins the class through its owner-liveness
|
|
49
|
+
-- read and is exempted from the gate positively (assertions 2b, 2c).
|
|
50
|
+
select plan(33);
|
|
48
51
|
|
|
49
52
|
-- ---------------------------------------------------------------------------
|
|
50
53
|
-- Section A -- the CLASS, derived from pg_proc, not from a list written here
|
|
@@ -84,15 +87,48 @@ select p.oid, p.proname, agent_board_test.stripped_body(p.oid, true) as body
|
|
|
84
87
|
~ agent_board_test.re(agent_board_test.qname_frag('agents', agent_board_test.bare_ok(p.oid)) || agent_board_test.same_stmt()
|
|
85
88
|
|| agent_board_test.col_ref_frag('auth_user_id') || '\s*=');
|
|
86
89
|
|
|
90
|
+
-- HOSTED fix lane (PR #590, finding N2): exchange_delegate_credential now reads the owner's
|
|
91
|
+
-- human row on auth_user_id, to REFUSE an inactive owner (owner_inactive). It never hands that
|
|
92
|
+
-- row back, so the stored-identity gate (which guards a RETURN of a row found by the derivation)
|
|
93
|
+
-- has nothing to gate there. It is a class member exempted POSITIVELY, in two halves: the view
|
|
94
|
+
-- below admits it only while its body holds exactly ONE agents-on-auth_user_id read (counted with
|
|
95
|
+
-- the class query's own matcher, so a second read anywhere drops it back into the gated set and
|
|
96
|
+
-- reds 3-7 by name), and 2c asserts, over that one body, that the read is the owner_inactive
|
|
97
|
+
-- refusal. The exemption is keyed by name, a list of one; a new member is gated by default.
|
|
98
|
+
create temp view _c246_refusal_only as
|
|
99
|
+
select c.oid, c.proname
|
|
100
|
+
from _c246 c
|
|
101
|
+
where c.proname = 'exchange_delegate_credential'
|
|
102
|
+
and regexp_count(c.body, agent_board_test.re(agent_board_test.qname_frag('agents', agent_board_test.bare_ok(c.oid))
|
|
103
|
+
|| agent_board_test.same_stmt() || agent_board_test.col_ref_frag('auth_user_id') || '\s*=')) = 1;
|
|
104
|
+
-- The members the gate applies to: the class, less the positively exempted refusal-only read.
|
|
105
|
+
create temp view _c246g as
|
|
106
|
+
select c.* from _c246 c where c.oid not in (select oid from _c246_refusal_only);
|
|
107
|
+
|
|
87
108
|
-- 1 -- the class is NOT EMPTY. A class scan that finds nothing passes every
|
|
88
109
|
-- "every member has X" assertion vacuously; this is the assertion that stops that.
|
|
89
|
-
select is((select count(*)::int from _c246),
|
|
90
|
-
'exactly
|
|
110
|
+
select is((select count(*)::int from _c246), 2,
|
|
111
|
+
'exactly two agent_board functions both derive a fed-human owner uid and read agents on auth_user_id');
|
|
91
112
|
|
|
92
|
-
-- 2 -- and
|
|
113
|
+
-- 2 -- and they are the ones we think they are, named POSITIVELY (not "no unexpected members").
|
|
93
114
|
select is((select string_agg(proname, ',' order by proname) from _c246),
|
|
94
|
-
'_enroll_federated_human_after_revocation_gate',
|
|
95
|
-
'
|
|
115
|
+
'_enroll_federated_human_after_revocation_gate,exchange_delegate_credential',
|
|
116
|
+
'those functions are _enroll_federated_human_after_revocation_gate and exchange_delegate_credential');
|
|
117
|
+
|
|
118
|
+
-- 2b (TAP ok 3; every later "-- N --" comment in this file is two BEHIND its TAP number) --
|
|
119
|
+
-- the exempt and gated sets, by name.
|
|
120
|
+
select is(
|
|
121
|
+
(select string_agg(proname, ',' order by proname) from _c246_refusal_only)
|
|
122
|
+
|| ' | ' || (select string_agg(proname, ',' order by proname) from _c246g),
|
|
123
|
+
'exchange_delegate_credential | _enroll_federated_human_after_revocation_gate',
|
|
124
|
+
'EXEMPT: exchange_delegate_credential has exactly one agents-on-auth_user_id read; the gated set is the one function that returns a found row');
|
|
125
|
+
-- 2c (TAP ok 4) -- and that one read is the owner_inactive refusal, over its one body.
|
|
126
|
+
select ok(
|
|
127
|
+
(select agent_board_test.stripped_body(p.oid, true)
|
|
128
|
+
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
129
|
+
where n.nspname = 'agent_board' and p.proname = 'exchange_delegate_credential')
|
|
130
|
+
~ 'exists\s*\(\s*select\s+1\s+from\s+agent_board\.agents\s+a\s+where\s+a\.auth_user_id\s*=\s*v_owner\s+and\s+a\.status\s*<>\s*''active''\s*\)\s*then\s+raise\s+exception\s+''owner_inactive''',
|
|
131
|
+
'EXEMPT: exchange_delegate_credential''s agents-on-auth_user_id read only refuses: exists(... status <> active) then raise owner_inactive');
|
|
96
132
|
|
|
97
133
|
-- BOTH return sites carry the gate -- asserted PER SITE, not as a count.
|
|
98
134
|
--
|
|
@@ -120,14 +156,14 @@ select proname,
|
|
|
120
156
|
(regexp_split_to_array(body, agent_board_test.re('\m' || agent_board_test.ci_re('exception when unique_violation') || '\M')))[1] as pre_handler,
|
|
121
157
|
(regexp_split_to_array(body, agent_board_test.re('\m' || agent_board_test.ci_re('exception when unique_violation') || '\M')))[2] as in_handler,
|
|
122
158
|
array_length(regexp_split_to_array(body, agent_board_test.re('\m' || agent_board_test.ci_re('exception when unique_violation') || '\M')), 1) as parts
|
|
123
|
-
from
|
|
159
|
+
from _c246g;
|
|
124
160
|
|
|
125
161
|
-- 3 -- the split boundary is UNAMBIGUOUS for every member. With a second `exception
|
|
126
162
|
-- when unique_violation` the two assertions below would be reading arbitrary halves,
|
|
127
163
|
-- so this is asserted before anything is read from them.
|
|
128
164
|
select is(
|
|
129
165
|
(select string_agg(proname, ',' order by proname) from _c246parts where parts = 2),
|
|
130
|
-
(select string_agg(proname, ',' order by proname) from
|
|
166
|
+
(select string_agg(proname, ',' order by proname) from _c246g),
|
|
131
167
|
'every class member has exactly one `exception when unique_violation`, so the per-site split is unambiguous');
|
|
132
168
|
|
|
133
169
|
-- 4 -- SITE (7a): the idempotent branch, everything before the handler.
|
|
@@ -138,7 +174,7 @@ select is(
|
|
|
138
174
|
|| '\s+' || agent_board_test.ci_re('and') || '\s+'
|
|
139
175
|
|| agent_board_test.ident_re('v_existing') || '\s*\.\s*' || agent_board_test.ident_re('metadata') || '\s*#>>\s*''\{federation,subject\}''\s*=\s*' || agent_board_test.ident_re('p_subject')
|
|
140
176
|
|| '\s*,\s*' || agent_board_test.ci_re('false') || '\s*\)')),
|
|
141
|
-
(select string_agg(proname, ',' order by proname) from
|
|
177
|
+
(select string_agg(proname, ',' order by proname) from _c246g),
|
|
142
178
|
'every class member carries the gate at return site (7a) -- the idempotent branch, BEFORE the unique_violation handler');
|
|
143
179
|
|
|
144
180
|
-- 5 -- SITE (8a): the race handler, everything after. This is the site a single
|
|
@@ -151,7 +187,7 @@ select is(
|
|
|
151
187
|
|| '\s+' || agent_board_test.ci_re('and') || '\s+'
|
|
152
188
|
|| agent_board_test.ident_re('v_existing') || '\s*\.\s*' || agent_board_test.ident_re('metadata') || '\s*#>>\s*''\{federation,subject\}''\s*=\s*' || agent_board_test.ident_re('p_subject')
|
|
153
189
|
|| '\s*,\s*' || agent_board_test.ci_re('false') || '\s*\)')),
|
|
154
|
-
(select string_agg(proname, ',' order by proname) from
|
|
190
|
+
(select string_agg(proname, ',' order by proname) from _c246g),
|
|
155
191
|
'every class member carries the gate at return site (8a) -- INSIDE the unique_violation race handler');
|
|
156
192
|
|
|
157
193
|
-- 6 and 7 -- both sites raise the SAME refusal, so neither becomes a
|
|
@@ -168,14 +204,14 @@ select is(
|
|
|
168
204
|
select is(
|
|
169
205
|
(select string_agg(proname, ',' order by proname) from _c246parts
|
|
170
206
|
where pre_handler ~ 'federated identity mismatch'),
|
|
171
|
-
(select string_agg(proname, ',' order by proname) from
|
|
207
|
+
(select string_agg(proname, ',' order by proname) from _c246g),
|
|
172
208
|
'every class member raises the "federated identity mismatch" refusal at site (7a) -- the idempotent branch');
|
|
173
209
|
|
|
174
210
|
-- 7
|
|
175
211
|
select is(
|
|
176
212
|
(select string_agg(proname, ',' order by proname) from _c246parts
|
|
177
213
|
where in_handler ~ 'federated identity mismatch'),
|
|
178
|
-
(select string_agg(proname, ',' order by proname) from
|
|
214
|
+
(select string_agg(proname, ',' order by proname) from _c246g),
|
|
179
215
|
'every class member raises the "federated identity mismatch" refusal at site (8a) -- INSIDE the race handler');
|
|
180
216
|
|
|
181
217
|
-- ---------------------------------------------------------------------------
|
|
@@ -203,8 +239,29 @@ select is(
|
|
|
203
239
|
where n.nspname = 'agent_board' and p.prokind in ('f', 'p')
|
|
204
240
|
and p.proname <> '_federated_identity_slug'
|
|
205
241
|
and agent_board_test.stripped_body(p.oid, true) like '%\_federated\_identity\_slug(%'),
|
|
206
|
-
'_enroll_federated_human_after_revocation_gate,_enroll_federated_identity_once,enroll_federated_human,enroll_federated_identity,onboard_federated_fleet_agent',
|
|
207
|
-
'the fed-human derivation set is exactly these
|
|
242
|
+
'_enroll_federated_human_after_revocation_gate,_enroll_federated_identity_once,enroll_federated_human,enroll_federated_identity,exchange_delegate_credential,onboard_federated_fleet_agent,tombstone_delegate_instance',
|
|
243
|
+
'the fed-human derivation set is exactly these seven functions (HOSTED added the exchange and the tombstone)');
|
|
244
|
+
|
|
245
|
+
-- 13, 14 -- HOSTED (20261008000100): exchange_delegate_credential and
|
|
246
|
+
-- tombstone_delegate_instance derive the OWNER's uid from the verified (issuer, subject),
|
|
247
|
+
-- exactly as enroll_federated_human does (IF-0-HOSTED-12's frozen owner identity). They are
|
|
248
|
+
-- exempt because neither returns an agent found by that uid: each uses it only as the
|
|
249
|
+
-- instance's owner, inserted as delegate_instances.owner_user_id and compared with the stored
|
|
250
|
+
-- one (owner_mismatch). The comparison is what is pinned, positively. The tombstone's absence
|
|
251
|
+
-- of an agents read on auth_user_id is the class query's half (assertions 1-2); the exchange
|
|
252
|
+
-- has one such read since the fix lane, the owner_inactive refusal, exempted by 2b and 2c.
|
|
253
|
+
select ok(
|
|
254
|
+
(select agent_board_test.stripped_body(p.oid, true)
|
|
255
|
+
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
256
|
+
where n.nspname = 'agent_board' and p.proname = 'exchange_delegate_credential')
|
|
257
|
+
~ 'v_row\.owner_user_id\s+is\s+distinct\s+from\s+v_owner',
|
|
258
|
+
'EXEMPT: exchange_delegate_credential compares the stored instance owner with the derived uid (owner_mismatch)');
|
|
259
|
+
select ok(
|
|
260
|
+
(select agent_board_test.stripped_body(p.oid, true)
|
|
261
|
+
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
262
|
+
where n.nspname = 'agent_board' and p.proname = 'tombstone_delegate_instance')
|
|
263
|
+
~ 'v_row\.owner_user_id\s+is\s+distinct\s+from\s+v_owner',
|
|
264
|
+
'EXEMPT: tombstone_delegate_instance compares the stored instance owner with the derived uid (owner_mismatch)');
|
|
208
265
|
|
|
209
266
|
-- 9 -- enroll_federated_identity is exempt because it ALREADY compares the caller's
|
|
210
267
|
-- pair against the creation-time stamp (#176/#244). Asserted on BOTH conjuncts: a
|
|
@@ -230,6 +230,9 @@ select is(
|
|
|
230
230
|
'_redeem_unbounded_invite_for_agent: memberships(key share) then agents(key share) [unserialised]',
|
|
231
231
|
'_upsert_board_membership_state: memberships(update) then agents(key share) [unserialised]',
|
|
232
232
|
'_upsert_delivery_recipient: memberships(key share) then agents(key share) [unserialised]',
|
|
233
|
+
-- HOSTED (20261008000100). Advisory-serialised, as grant_board_role is: the fleet-ledger gate
|
|
234
|
+
-- row-locks the owner's agents row before the sink writes the delegate's membership.
|
|
235
|
+
'add_delegate_to_board: agents(share) then memberships(update) [advisory]',
|
|
233
236
|
'create_tasking_delivery: agents(key share) then memberships(key share) [unserialised]',
|
|
234
237
|
'enroll_federated_identity: agents(update) then memberships(key share) [unserialised]',
|
|
235
238
|
'grant_board_role: agents(share) then memberships(update) [advisory]',
|
|
@@ -249,6 +252,12 @@ select is(
|
|
|
249
252
|
'signed_send_direct_message: agents(key share) then memberships(key share) [unserialised]',
|
|
250
253
|
'signed_send_multicast_message: agents(key share) then memberships(key share) [unserialised]',
|
|
251
254
|
'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]',
|
|
252
261
|
'update_board_membership: memberships(update) then agents(key share) [unserialised]'
|
|
253
262
|
]::text[],
|
|
254
263
|
'FKL-9: the agents+board_memberships lock class, FK edges included (section 9''s text scan sees 3 of these)');
|