@consiliency/agent-board-schema 1.24.0 → 1.27.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 +12 -0
- package/migrations/20260906000100_agent_board_enroll_resumption.sql +749 -0
- package/migrations/20260907000100_agent_board_signed_authority_provenance.sql +243 -0
- package/migrations/20260908000100_agent_board_signer_lifecycle_revalidation.sql +451 -0
- package/package.json +1 -1
- package/schema-manifest.json +30 -3
- package/src/tooling.test.mjs +3 -3
- package/tests/agent_board_atomic_fleet_onboarding.test.sql +129 -112
- package/tests/agent_board_claimkey.test.sql +25 -3
- package/tests/agent_board_consent_emission.test.sql +17 -13
- package/tests/agent_board_consent_gating.test.sql +55 -11
- package/tests/agent_board_delegation_policies.test.sql +3 -1
- package/tests/agent_board_enroll_resumption.test.sql +412 -0
- package/tests/agent_board_federated_lock_order_class.test.sql +1 -3
- package/tests/agent_board_fedsession_provider_absence_gate.test.sql +9 -6
- package/tests/agent_board_fleet_machine_reactivation.test.sql +56 -5
- package/tests/agent_board_mint_harden.test.sql +39 -4
- package/tests/agent_board_outbox_wake_ordering.test.sql +27 -12
- package/tests/agent_board_rejectsem.test.sql +1 -1
- package/tests/agent_board_rolereset.test.sql +23 -2
- package/tests/agent_board_rowid.test.sql +24 -13
- package/tests/agent_board_scoped_send_regrant.test.sql +8 -4
- package/tests/agent_board_signed_authority_provenance.test.sql +477 -0
- package/tests/agent_board_signer_lifecycle.test.sql +709 -0
- package/tests/agent_board_source_strip_helper.test.sql +285 -0
- package/tests/agent_board_wake_recovery.test.sql +58 -40
- package/tests/catalog/p_metadata_parameters.json +693 -0
- package/tests/fixtures/test-prereqs.sql +313 -0
- package/tests/isolation/expected/atomic_fleet_lifecycle_races.out +3 -3
- package/tests/isolation/expected/signed_grant_onboarding_deadlock.out +220 -0
- package/tests/isolation/expected/signer_lifecycle_revoke.out +307 -0
- package/tests/isolation/specs/signed_grant_onboarding_deadlock.spec +159 -0
- package/tests/isolation/specs/signer_lifecycle_revoke.spec +176 -0
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
-- runtime backstop at all.
|
|
13
13
|
--
|
|
14
14
|
-- WHAT THE FIX IS. Each positional assertion now locates its markers on
|
|
15
|
-
-- `
|
|
15
|
+
-- `agent_board_test.stripped_body(<fn>)` -- prosrc with dollar-quoted blocks, single-quoted
|
|
16
16
|
-- literals, line comments and block comments removed in ONE alternation pass. The
|
|
17
17
|
-- regex is copied BYTE-FOR-BYTE from agent_board_federated_lock_order_class.test.sql,
|
|
18
18
|
-- where its three failure modes were found the hard way: a block comment whose body
|
|
@@ -123,24 +123,23 @@ create extension if not exists pgtap with schema extensions;
|
|
|
123
123
|
set search_path = extensions, public, agent_board;
|
|
124
124
|
|
|
125
125
|
begin;
|
|
126
|
-
select plan(
|
|
126
|
+
select plan(54);
|
|
127
127
|
|
|
128
128
|
-- ---------------------------------------------------------------------------
|
|
129
|
-
-- The stripped-body reader every positional assertion below uses
|
|
130
|
-
--
|
|
131
|
-
--
|
|
132
|
-
--
|
|
133
|
-
--
|
|
134
|
-
--
|
|
129
|
+
-- The stripped-body reader every positional assertion below uses is
|
|
130
|
+
-- agent_board_test.stripped_body(), defined ONCE for the whole suite in
|
|
131
|
+
-- tests/fixtures/test-prereqs.sql (the runner applies that fixture before any
|
|
132
|
+
-- test, so it needs no include path).
|
|
133
|
+
--
|
|
134
|
+
-- It used to be a local regex copied here and kept byte-identical to
|
|
135
|
+
-- agent_board_federated_lock_order_class.test.sql so the two could not drift.
|
|
136
|
+
-- They could not drift, and they were both WRONG: no regular expression can
|
|
137
|
+
-- match a NESTED block comment, and `/* note /* x */ <code> */` therefore hid
|
|
138
|
+
-- <code> from both files at once (message-board#199) -- 52/52 and 15/15 green
|
|
139
|
+
-- with the boards lock genuinely relocated. The shared helper is a character
|
|
140
|
+
-- scanner for that reason; see its header for the full enumeration of escape
|
|
141
|
+
-- hatches and which ones a regex can and cannot close.
|
|
135
142
|
-- ---------------------------------------------------------------------------
|
|
136
|
-
create function pg_temp._stripped_body(p_fn regprocedure) returns text
|
|
137
|
-
language sql stable as $strip$
|
|
138
|
-
select regexp_replace(p.prosrc,
|
|
139
|
-
$re$(\$[A-Za-z_0-9]*\$).*?\1|('([^']|'')*')|(--[^\n]*)|(/\*([^*]|\*+[^*/])*\*+/)$re$,
|
|
140
|
-
' ', 'g')
|
|
141
|
-
from pg_proc p
|
|
142
|
-
where p.oid = p_fn;
|
|
143
|
-
$strip$;
|
|
144
143
|
|
|
145
144
|
select has_table('agent_board', 'fleet_onboarding_invite_bounds', 'immutable fleet invite bounds table exists');
|
|
146
145
|
select has_pk('agent_board', 'fleet_onboarding_invite_bounds', 'fleet invite bounds are keyed by invite id');
|
|
@@ -248,38 +247,22 @@ select ok(
|
|
|
248
247
|
'binding revocation is a permanent tombstone'
|
|
249
248
|
);
|
|
250
249
|
select ok(
|
|
251
|
-
strpos(
|
|
252
|
-
pg_temp._stripped_body('agent_board.revoke_fleet_machine_agent_binding(uuid,uuid,text,jsonb)'::regprocedure),
|
|
253
|
-
'from agent_board.fleet_machines m'
|
|
250
|
+
strpos(lower(agent_board_test.stripped_body('agent_board.revoke_fleet_machine_agent_binding(uuid,uuid,text,jsonb)'::regprocedure)), 'from agent_board.fleet_machines m'
|
|
254
251
|
) > 0
|
|
255
|
-
and strpos(
|
|
256
|
-
pg_temp._stripped_body('agent_board.revoke_fleet_machine_agent_binding(uuid,uuid,text,jsonb)'::regprocedure),
|
|
257
|
-
'from agent_board.fleet_machine_agents b where b.id = p_binding_id for update'
|
|
252
|
+
and strpos(lower(agent_board_test.stripped_body('agent_board.revoke_fleet_machine_agent_binding(uuid,uuid,text,jsonb)'::regprocedure)), 'from agent_board.fleet_machine_agents b where b.id = p_binding_id for update'
|
|
258
253
|
) > 0
|
|
259
|
-
and strpos(
|
|
260
|
-
|
|
261
|
-
'from agent_board.fleet_machines m'
|
|
262
|
-
) < strpos(
|
|
263
|
-
pg_temp._stripped_body('agent_board.revoke_fleet_machine_agent_binding(uuid,uuid,text,jsonb)'::regprocedure),
|
|
264
|
-
'from agent_board.fleet_machine_agents b where b.id = p_binding_id for update'
|
|
254
|
+
and strpos(lower(agent_board_test.stripped_body('agent_board.revoke_fleet_machine_agent_binding(uuid,uuid,text,jsonb)'::regprocedure)), 'from agent_board.fleet_machines m'
|
|
255
|
+
) < strpos(lower(agent_board_test.stripped_body('agent_board.revoke_fleet_machine_agent_binding(uuid,uuid,text,jsonb)'::regprocedure)), 'from agent_board.fleet_machine_agents b where b.id = p_binding_id for update'
|
|
265
256
|
),
|
|
266
257
|
'exact binding revocation locks the machine before the binding [SOLE PIN: no isolation spec discriminates this ordering -- measured against all 20]'
|
|
267
258
|
);
|
|
268
259
|
select ok(
|
|
269
|
-
strpos(
|
|
270
|
-
pg_temp._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),
|
|
271
|
-
'where fm.id = p_machine_id for update'
|
|
260
|
+
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'
|
|
272
261
|
) > 0
|
|
273
|
-
and strpos(
|
|
274
|
-
pg_temp._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),
|
|
275
|
-
'from agent_board.agents a where a.id = v_enroll.agent_id for update'
|
|
262
|
+
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'
|
|
276
263
|
) > 0
|
|
277
|
-
and strpos(
|
|
278
|
-
|
|
279
|
-
'where fm.id = p_machine_id for update'
|
|
280
|
-
) < strpos(
|
|
281
|
-
pg_temp._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),
|
|
282
|
-
'from agent_board.agents a where a.id = v_enroll.agent_id for update'
|
|
264
|
+
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'
|
|
265
|
+
) < 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'
|
|
283
266
|
),
|
|
284
267
|
'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]'
|
|
285
268
|
);
|
|
@@ -299,7 +282,7 @@ select ok(
|
|
|
299
282
|
-- does not fail the claim.
|
|
300
283
|
select ok(
|
|
301
284
|
(with b as (
|
|
302
|
-
select
|
|
285
|
+
select agent_board_test.stripped_body(
|
|
303
286
|
'agent_board.revoke_fleet_subject(text,uuid,uuid,text,jsonb)'::regprocedure) as t),
|
|
304
287
|
p as (
|
|
305
288
|
select t,
|
|
@@ -318,20 +301,12 @@ select ok(
|
|
|
318
301
|
'agent fleet revocation takes its machine locks before the agent lock [SOLE PIN: no isolation spec discriminates this ordering -- measured against all 20]'
|
|
319
302
|
);
|
|
320
303
|
select ok(
|
|
321
|
-
strpos(
|
|
322
|
-
pg_temp._stripped_body('agent_board._revoke_agent_principal(uuid,uuid,text)'::regprocedure),
|
|
323
|
-
'from agent_board.agents a where a.id = p_agent_id for update'
|
|
304
|
+
strpos(lower(agent_board_test.stripped_body('agent_board._revoke_agent_principal(uuid,uuid,text)'::regprocedure)), 'from agent_board.agents a where a.id = p_agent_id for update'
|
|
324
305
|
) > 0
|
|
325
|
-
and strpos(
|
|
326
|
-
pg_temp._stripped_body('agent_board._revoke_agent_principal(uuid,uuid,text)'::regprocedure),
|
|
327
|
-
'from agent_board.agent_signing_keys'
|
|
306
|
+
and strpos(lower(agent_board_test.stripped_body('agent_board._revoke_agent_principal(uuid,uuid,text)'::regprocedure)), 'from agent_board.agent_signing_keys'
|
|
328
307
|
) > 0
|
|
329
|
-
and strpos(
|
|
330
|
-
|
|
331
|
-
'from agent_board.agents a where a.id = p_agent_id for update'
|
|
332
|
-
) < strpos(
|
|
333
|
-
pg_temp._stripped_body('agent_board._revoke_agent_principal(uuid,uuid,text)'::regprocedure),
|
|
334
|
-
'from agent_board.agent_signing_keys'
|
|
308
|
+
and strpos(lower(agent_board_test.stripped_body('agent_board._revoke_agent_principal(uuid,uuid,text)'::regprocedure)), 'from agent_board.agents a where a.id = p_agent_id for update'
|
|
309
|
+
) < strpos(lower(agent_board_test.stripped_body('agent_board._revoke_agent_principal(uuid,uuid,text)'::regprocedure)), 'from agent_board.agent_signing_keys'
|
|
335
310
|
),
|
|
336
311
|
'subtree primitive locks the agent before signing keys [SOLE PIN: no isolation spec discriminates this ordering -- measured against all 20]'
|
|
337
312
|
);
|
|
@@ -359,20 +334,12 @@ select ok(
|
|
|
359
334
|
-- not-found case fail instead of pass; the other eight positional claims in this
|
|
360
335
|
-- file already carried them.
|
|
361
336
|
select ok(
|
|
362
|
-
strpos(
|
|
363
|
-
pg_temp._stripped_body('agent_board.revoke_agent(uuid,text)'::regprocedure),
|
|
364
|
-
'update agent_board.agents'
|
|
337
|
+
strpos(lower(agent_board_test.stripped_body('agent_board.revoke_agent(uuid,text)'::regprocedure)), 'update agent_board.agents'
|
|
365
338
|
) > 0
|
|
366
|
-
and strpos(
|
|
367
|
-
pg_temp._stripped_body('agent_board.revoke_agent(uuid,text)'::regprocedure),
|
|
368
|
-
'update agent_board.agent_credentials'
|
|
339
|
+
and strpos(lower(agent_board_test.stripped_body('agent_board.revoke_agent(uuid,text)'::regprocedure)), 'update agent_board.agent_credentials'
|
|
369
340
|
) > 0
|
|
370
|
-
and strpos(
|
|
371
|
-
|
|
372
|
-
'update agent_board.agents'
|
|
373
|
-
) < strpos(
|
|
374
|
-
pg_temp._stripped_body('agent_board.revoke_agent(uuid,text)'::regprocedure),
|
|
375
|
-
'update agent_board.agent_credentials'
|
|
341
|
+
and strpos(lower(agent_board_test.stripped_body('agent_board.revoke_agent(uuid,text)'::regprocedure)), 'update agent_board.agents'
|
|
342
|
+
) < strpos(lower(agent_board_test.stripped_body('agent_board.revoke_agent(uuid,text)'::regprocedure)), 'update agent_board.agent_credentials'
|
|
376
343
|
),
|
|
377
344
|
'legacy authenticated revoke_agent preserves agent-before-credential ordering [HISTORICAL: legacy platform-admin path, no live fleet counterparty and no isolation spec]'
|
|
378
345
|
);
|
|
@@ -404,10 +371,18 @@ select ok(
|
|
|
404
371
|
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%',
|
|
405
372
|
'identity enrollment locks the human owner and rejects durable total revocation'
|
|
406
373
|
);
|
|
374
|
+
-- LITERALS KEPT (the `true` second argument): every marker below is itself a
|
|
375
|
+
-- string literal in the body -- `''federation''`, `''object''`, the
|
|
376
|
+
-- `'{federation,replay,issuer}'` path, the raise message. Stripping literals here
|
|
377
|
+
-- would delete the very text being located and make the `not like` vacuously true
|
|
378
|
+
-- forever. Comments (all forms, INCLUDING NESTED) are still removed, so the
|
|
379
|
+
-- message-board#196 comment masks are closed; a literal mask is inherent to a
|
|
380
|
+
-- literal marker, which is why the isolation spec and not this line is the
|
|
381
|
+
-- load-bearing guard.
|
|
407
382
|
select ok(
|
|
408
|
-
|
|
383
|
+
agent_board_test.stripped_body('agent_board.revoke_human_subtree(uuid,uuid,text)'::regprocedure, true)
|
|
409
384
|
like '%from auth.users u where u.id = p_owner_user_id for update%where a.owner_user_id = p_owner_user_id%jsonb_typeof(a.metadata -> ''federation'') = ''object''%not exists (%from agent_board.enrollment_federation_audit e%where e.agent_id = a.id%stable federation evidence not found for every linked agent%insert into agent_board.fleet_human_revocations%issuer, subject, owner_user_id%on conflict (issuer, subject) do nothing%where owner_user_id = p_owner_user_id%order by id%'
|
|
410
|
-
and
|
|
385
|
+
and agent_board_test.stripped_body('agent_board.revoke_human_subtree(uuid,uuid,text)'::regprocedure, true)
|
|
411
386
|
not like '%metadata #>> ''{federation,replay,issuer}''%',
|
|
412
387
|
'total human revocation requires per-agent audit evidence before marking and enumeration'
|
|
413
388
|
);
|
|
@@ -458,16 +433,10 @@ select ok(
|
|
|
458
433
|
-- Ordering, not mere presence: the lock has to be held BEFORE the authorization
|
|
459
434
|
-- decision reads the (possibly absent) ceiling, and BEFORE the writer upserts it.
|
|
460
435
|
select ok(
|
|
461
|
-
strpos(
|
|
462
|
-
pg_temp._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),
|
|
463
|
-
'perform agent_board._lock_board_authority_ceiling(v_inv.board_id)'
|
|
436
|
+
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)'
|
|
464
437
|
) > 0
|
|
465
|
-
and strpos(
|
|
466
|
-
|
|
467
|
-
'perform agent_board._lock_board_authority_ceiling(v_inv.board_id)'
|
|
468
|
-
) < strpos(
|
|
469
|
-
pg_temp._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),
|
|
470
|
-
'agent_board._grant_within_ceiling('
|
|
438
|
+
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)'
|
|
439
|
+
) < 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('
|
|
471
440
|
),
|
|
472
441
|
'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]'
|
|
473
442
|
);
|
|
@@ -479,16 +448,10 @@ select ok(
|
|
|
479
448
|
-- that catches the inversion; it is deliberately separate from the "before the
|
|
480
449
|
-- ceiling read" assertion below, because that one passes in BOTH orders.
|
|
481
450
|
select ok(
|
|
482
|
-
strpos(
|
|
483
|
-
pg_temp._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),
|
|
484
|
-
'perform agent_board._lock_board_authority_ceiling(v_inv.board_id)'
|
|
451
|
+
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)'
|
|
485
452
|
) > 0
|
|
486
|
-
and strpos(
|
|
487
|
-
|
|
488
|
-
'perform agent_board._lock_board_authority_ceiling(v_inv.board_id)'
|
|
489
|
-
) < strpos(
|
|
490
|
-
pg_temp._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),
|
|
491
|
-
'from agent_board.boards b
|
|
453
|
+
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)'
|
|
454
|
+
) < 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
|
|
492
455
|
where b.id = v_inv.board_id
|
|
493
456
|
for update'
|
|
494
457
|
),
|
|
@@ -508,34 +471,22 @@ select ok(
|
|
|
508
471
|
-- ordering nothing else states, because the boards lock is taken for board liveness,
|
|
509
472
|
-- not for the ceiling. Pin the ordering so a refactor that moves it is not silent.
|
|
510
473
|
select ok(
|
|
511
|
-
strpos(
|
|
512
|
-
pg_temp._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),
|
|
513
|
-
'from agent_board.boards b
|
|
474
|
+
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
|
|
514
475
|
where b.id = v_inv.board_id
|
|
515
476
|
for update'
|
|
516
477
|
) > 0
|
|
517
|
-
and strpos(
|
|
518
|
-
pg_temp._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),
|
|
519
|
-
'from agent_board.boards b
|
|
478
|
+
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
|
|
520
479
|
where b.id = v_inv.board_id
|
|
521
480
|
for update'
|
|
522
|
-
) < strpos(
|
|
523
|
-
pg_temp._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),
|
|
524
|
-
'agent_board._grant_within_ceiling('
|
|
481
|
+
) < 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('
|
|
525
482
|
),
|
|
526
483
|
'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]'
|
|
527
484
|
);
|
|
528
485
|
select ok(
|
|
529
|
-
strpos(
|
|
530
|
-
pg_temp._stripped_body('agent_board.set_board_authority_ceiling(uuid,text)'::regprocedure),
|
|
531
|
-
'perform agent_board._lock_board_authority_ceiling(p_board_id)'
|
|
486
|
+
strpos(lower(agent_board_test.stripped_body('agent_board.set_board_authority_ceiling(uuid,text,uuid)'::regprocedure)), 'perform agent_board._lock_board_authority_ceiling(p_board_id)'
|
|
532
487
|
) > 0
|
|
533
|
-
and strpos(
|
|
534
|
-
|
|
535
|
-
'perform agent_board._lock_board_authority_ceiling(p_board_id)'
|
|
536
|
-
) < strpos(
|
|
537
|
-
pg_temp._stripped_body('agent_board.set_board_authority_ceiling(uuid,text)'::regprocedure),
|
|
538
|
-
'insert into agent_board.board_authority_ceilings'
|
|
488
|
+
and strpos(lower(agent_board_test.stripped_body('agent_board.set_board_authority_ceiling(uuid,text,uuid)'::regprocedure)), 'perform agent_board._lock_board_authority_ceiling(p_board_id)'
|
|
489
|
+
) < strpos(lower(agent_board_test.stripped_body('agent_board.set_board_authority_ceiling(uuid,text,uuid)'::regprocedure)), 'insert into agent_board.board_authority_ceilings'
|
|
539
490
|
),
|
|
540
491
|
'the ceiling setter holds the same lock before its owner gate and upsert [BACKSTOP: tests/isolation/specs/ceiling_setter_lock_before_upsert.spec]'
|
|
541
492
|
);
|
|
@@ -550,12 +501,22 @@ select ok(
|
|
|
550
501
|
select is( -- pgtap-allow-raise-checked: catalog read only; the RPC names below are string literals in the expected array, never invoked
|
|
551
502
|
(select array_agg(entry order by entry) from (
|
|
552
503
|
select 'agent_board.' || regexp_replace(p.oid::regprocedure::text, '^([^(]*\.)?', '') || ' => ' ||
|
|
553
|
-
|
|
504
|
+
-- CALL SHAPE, not strpos on a bare name: this is the dangerous
|
|
505
|
+
-- direction -- a reader that DROPS the lock but keeps a variable named
|
|
506
|
+
-- after it would still print "serialized".
|
|
507
|
+
-- SCHEMA-QUALIFIED, because a bare-name anchor proves call SYNTAX, not callee
|
|
508
|
+
-- IDENTITY: `\m_lock_board_authority_ceiling\s*\(` also matches
|
|
509
|
+
-- `zz_other._lock_board_authority_ceiling(...)` (measured true). Every live call
|
|
510
|
+
-- site in agent_board is `agent_board.`-qualified, so requiring the
|
|
511
|
+
-- qualification costs nothing today and is fail-CLOSED if someone later writes
|
|
512
|
+
-- an unqualified call (it reads as NOT calling). Overload count is pinned
|
|
513
|
+
-- separately, so schema + name is the identity.
|
|
514
|
+
case when agent_board_test.stripped_body(p.oid) ~* '\magent_board\._lock_board_authority_ceiling\s*\('
|
|
554
515
|
then 'serialized' else 'UNSERIALIZED' end as entry
|
|
555
516
|
from pg_proc p
|
|
556
517
|
join pg_namespace n on n.oid = p.pronamespace
|
|
557
518
|
where n.nspname = 'agent_board'
|
|
558
|
-
and strpos(p.
|
|
519
|
+
and strpos(lower(agent_board_test.stripped_body(p.oid)), '_grant_within_ceiling') > 0
|
|
559
520
|
) readers),
|
|
560
521
|
array[
|
|
561
522
|
'agent_board._redeem_unbounded_invite_for_agent(text,uuid) => UNSERIALIZED',
|
|
@@ -594,11 +555,18 @@ select is( -- pgtap-allow-raise-checked: catalog read only; the RPC names below
|
|
|
594
555
|
from pg_proc p
|
|
595
556
|
join pg_namespace n on n.oid = p.pronamespace
|
|
596
557
|
where n.nspname = 'agent_board'
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
558
|
+
-- The first four markers are CODE (relation and function names), so the
|
|
559
|
+
-- body is read with literals stripped. The fifth is the advisory-lock key
|
|
560
|
+
-- prefix, which exists ONLY as a string literal
|
|
561
|
+
-- (`hashtextextended('agent_board.board_authority_ceilings:' || ...)`), so
|
|
562
|
+
-- that one predicate must KEEP literals or _lock_board_authority_ceiling --
|
|
563
|
+
-- the single function this migration creates -- silently leaves the class.
|
|
564
|
+
-- Comments are stripped in both modes.
|
|
565
|
+
and (strpos(lower(agent_board_test.stripped_body(p.oid)), 'fleet_onboarding_invite_bounds') > 0
|
|
566
|
+
or strpos(lower(agent_board_test.stripped_body(p.oid)), 'fleet_human_revocations') > 0
|
|
567
|
+
or strpos(lower(agent_board_test.stripped_body(p.oid)), 'fleet_revocation_events') > 0
|
|
568
|
+
or strpos(lower(agent_board_test.stripped_body(p.oid)), '_lock_board_authority_ceiling') > 0
|
|
569
|
+
or strpos(agent_board_test.stripped_body(p.oid, true), 'board_authority_ceilings:') > 0)
|
|
602
570
|
) definers),
|
|
603
571
|
array[
|
|
604
572
|
'agent_board._lock_board_authority_ceiling(uuid) [invoker] => service_role',
|
|
@@ -611,13 +579,62 @@ select is( -- pgtap-allow-raise-checked: catalog read only; the RPC names below
|
|
|
611
579
|
'agent_board.evaluate_fleet_sender_policy(uuid,uuid,uuid,uuid,text,uuid,boolean) [definer] => authenticated,service_role',
|
|
612
580
|
'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',
|
|
613
581
|
'agent_board.reactivate_fleet_machine(uuid,uuid,text) [definer] => service_role',
|
|
614
|
-
'agent_board.reclaim_fleet_machine(uuid,uuid,uuid,text,uuid) [definer] => service_role',
|
|
615
582
|
'agent_board.revoke_fleet_subject(text,uuid,uuid,text,jsonb) [definer] => authenticated,service_role',
|
|
616
583
|
'agent_board.revoke_human_subtree(uuid,uuid,text) [definer] => service_role',
|
|
617
|
-
'agent_board.set_board_authority_ceiling(uuid,text) [definer] => authenticated,service_role'
|
|
584
|
+
'agent_board.set_board_authority_ceiling(uuid,text,uuid) [definer] => authenticated,service_role',
|
|
585
|
+
-- Joined this class in #269 F4: the signed grant wrapper now takes the board advisory lock
|
|
586
|
+
-- FIRST, because its order was board_memberships(grantee) -> agents(signer) against
|
|
587
|
+
-- onboard_federated_fleet_agent's agents -> board_memberships -- a reproduced ABBA between
|
|
588
|
+
-- two honest callers. It is service_role-only like every other signed_* wrapper (#118).
|
|
589
|
+
'agent_board.signed_grant_board_role(jsonb,jsonb) [definer] => service_role'
|
|
618
590
|
]::text[],
|
|
619
591
|
'every function on this migration set''s surface is declared with its definer flag and exact EXECUTE grantees'
|
|
620
592
|
);
|
|
621
593
|
|
|
594
|
+
-- WHAT THE message-board#196 CONVERSION REMOVED FROM THE CLASS ABOVE, and why
|
|
595
|
+
-- it is restated here instead of being silently dropped.
|
|
596
|
+
--
|
|
597
|
+
-- reclaim_fleet_machine was a member of that class while the predicate read RAW
|
|
598
|
+
-- prosrc -- and ONLY because a `--` comment in its body says "no
|
|
599
|
+
-- fleet_revocation_events row is written per binding". It was in the surface
|
|
600
|
+
-- class for SAYING IT IS NOT ON THE SURFACE. Reading the stripped body removes
|
|
601
|
+
-- it, which is correct: it touches none of the five surfaces.
|
|
602
|
+
--
|
|
603
|
+
-- Its exact-grantee pin was real coverage, though, and an accidental member's
|
|
604
|
+
-- coverage is still coverage. Per CLAUDE.md rule 1 an exemption is asserted
|
|
605
|
+
-- POSITIVELY, never by absence of a match, so the claim it used to carry is
|
|
606
|
+
-- restated as its own assertion.
|
|
607
|
+
select is(
|
|
608
|
+
(select coalesce((
|
|
609
|
+
select string_agg(distinct coalesce(pg_get_userbyid(nullif(a.grantee, 0)), 'PUBLIC'), ','
|
|
610
|
+
order by coalesce(pg_get_userbyid(nullif(a.grantee, 0)), 'PUBLIC'))
|
|
611
|
+
from aclexplode(p.proacl) a
|
|
612
|
+
where a.privilege_type = 'EXECUTE'
|
|
613
|
+
and a.grantee <> p.proowner), '(none)')
|
|
614
|
+
|| case when p.prosecdef then ' [definer]' else ' [invoker]' end
|
|
615
|
+
from pg_proc p
|
|
616
|
+
join pg_namespace n on n.oid = p.pronamespace
|
|
617
|
+
where n.nspname = 'agent_board'
|
|
618
|
+
-- identity by OID + identity-arguments, never by a rendered regprocedure:
|
|
619
|
+
-- this file puts agent_board on search_path, so `oid::regprocedure::text`
|
|
620
|
+
-- renders UNQUALIFIED and an 'agent_board.…' comparison silently matches
|
|
621
|
+
-- nothing (measured: this assertion read NULL until it was written this way).
|
|
622
|
+
and 'agent_board.' || regexp_replace(p.oid::regprocedure::text, '^([^(]*\.)?', '')
|
|
623
|
+
= 'agent_board.reclaim_fleet_machine(uuid,uuid,uuid,text,uuid)'),
|
|
624
|
+
'service_role [definer]',
|
|
625
|
+
'reclaim_fleet_machine -- off this surface once comments are stripped -- keeps its service_role-only definer grant'
|
|
626
|
+
);
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
-- The schema-qualified call anchor above treats `agent_board._lock_board_authority_ceiling` as an
|
|
630
|
+
-- IDENTITY. That is only true while the name resolves to exactly one function:
|
|
631
|
+
-- a second overload would be a different implementation the same anchor accepts.
|
|
632
|
+
-- Catalog-derived, so a new overload fails here and the count names itself.
|
|
633
|
+
select is(
|
|
634
|
+
(select count(*)::int from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
635
|
+
where n.nspname = 'agent_board' and p.proname = '_lock_board_authority_ceiling'),
|
|
636
|
+
1,
|
|
637
|
+
'agent_board._lock_board_authority_ceiling has exactly ONE overload, so the schema-qualified call anchor identifies it uniquely');
|
|
638
|
+
|
|
622
639
|
select * from finish();
|
|
623
640
|
rollback;
|
|
@@ -16,7 +16,7 @@ set search_path = extensions, public, agent_board;
|
|
|
16
16
|
|
|
17
17
|
begin;
|
|
18
18
|
|
|
19
|
-
select plan(
|
|
19
|
+
select plan(26);
|
|
20
20
|
|
|
21
21
|
select set_config('request.jwt.claim.role', 'service_role', true);
|
|
22
22
|
select set_config('request.jwt.claim.sub', '', true);
|
|
@@ -219,11 +219,33 @@ select is(
|
|
|
219
219
|
join pg_namespace n on n.oid = p.pronamespace
|
|
220
220
|
where n.nspname = 'agent_board'
|
|
221
221
|
and p.prokind = 'f'
|
|
222
|
-
and
|
|
223
|
-
|
|
222
|
+
and agent_board_test.stripped_body(p.oid) ~* '(insert\s+into|update)\s+(agent_board\.)?claims\M'
|
|
223
|
+
-- message-board#196 Bucket B: read RAW, one comment naming _canonical_claim_key
|
|
224
|
+
-- dropped a bypass writer out of this set and the count went to 0 green.
|
|
225
|
+
-- anchored to the CALL shape: a bare-substring `!~` is satisfied by any
|
|
226
|
+
-- identifier containing the name, e.g. an argument `p_canonical_claim_key`.
|
|
227
|
+
-- SCHEMA-QUALIFIED, because a bare-name anchor proves call SYNTAX, not callee
|
|
228
|
+
-- IDENTITY: `\m_canonical_claim_key\s*\(` also matches
|
|
229
|
+
-- `zz_other._canonical_claim_key(...)` (measured true). Every live call
|
|
230
|
+
-- site in agent_board is `agent_board.`-qualified, so requiring the
|
|
231
|
+
-- qualification costs nothing today and is fail-CLOSED if someone later writes
|
|
232
|
+
-- an unqualified call (it reads as NOT calling). Overload count is pinned
|
|
233
|
+
-- separately, so schema + name is the identity.
|
|
234
|
+
and agent_board_test.stripped_body(p.oid) !~* '\magent_board\._canonical_claim_key\s*\('),
|
|
224
235
|
0,
|
|
225
236
|
'every catalog-enumerated claims writer routes its resource_key through _canonical_claim_key');
|
|
226
237
|
|
|
238
|
+
|
|
239
|
+
-- The schema-qualified call anchor above treats `agent_board._canonical_claim_key` as an
|
|
240
|
+
-- IDENTITY. That is only true while the name resolves to exactly one function:
|
|
241
|
+
-- a second overload would be a different implementation the same anchor accepts.
|
|
242
|
+
-- Catalog-derived, so a new overload fails here and the count names itself.
|
|
243
|
+
select is(
|
|
244
|
+
(select count(*)::int from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
245
|
+
where n.nspname = 'agent_board' and p.proname = '_canonical_claim_key'),
|
|
246
|
+
1,
|
|
247
|
+
'agent_board._canonical_claim_key has exactly ONE overload, so the schema-qualified call anchor identifies it uniquely');
|
|
248
|
+
|
|
227
249
|
select * from finish();
|
|
228
250
|
|
|
229
251
|
rollback;
|
|
@@ -222,21 +222,18 @@ select is(
|
|
|
222
222
|
-- anywhere in the body is fail-open twice over: the name in a comment or a dead branch would
|
|
223
223
|
-- satisfy it, and calling the emitter TWICE would too -- while this suite claims exactly one
|
|
224
224
|
-- event per transition.
|
|
225
|
-
--
|
|
226
|
-
--
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
regexp_replace(pg_get_functiondef(p_oid), '/\*.*?\*/', '', 'gs'),
|
|
230
|
-
'--[^\n]*', '', 'g');
|
|
231
|
-
$$;
|
|
225
|
+
-- The stripped reader is agent_board_test.stripped_body(), shared by the whole
|
|
226
|
+
-- suite from tests/fixtures/test-prereqs.sql. The local version this replaces
|
|
227
|
+
-- stripped both comment forms with a regex -- which still could not see a NESTED
|
|
228
|
+
-- block comment (message-board#199) or a string literal.
|
|
232
229
|
|
|
233
230
|
select is(
|
|
234
231
|
(select coalesce(string_agg(p.proname, ', ' order by p.proname), '')
|
|
235
232
|
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
236
233
|
where n.nspname = 'agent_board' and p.prokind = 'f'
|
|
237
234
|
and p.prolang <> (select oid from pg_language where lanname = 'internal')
|
|
238
|
-
and
|
|
239
|
-
and
|
|
235
|
+
and agent_board_test.stripped_body(p.oid) ~* '(insert into|update)[[:space:]]+agent_board\.board_memberships'
|
|
236
|
+
and agent_board_test.stripped_body(p.oid) !~* 'perform agent_board\._emit_membership_event'
|
|
240
237
|
and p.proname <> 'grant_board_role'),
|
|
241
238
|
'',
|
|
242
239
|
'COMPLETENESS: every function writing board_memberships emits, or is a stated exemption');
|
|
@@ -244,14 +241,21 @@ select is(
|
|
|
244
241
|
-- ...and none of them emits MORE than once, which would break one-event-per-transition.
|
|
245
242
|
select is(
|
|
246
243
|
(select coalesce(string_agg(p.proname || '=' ||
|
|
247
|
-
(length(
|
|
244
|
+
(length(agent_board_test.stripped_body(p.oid)) - length(replace(lower(agent_board_test.stripped_body(p.oid)),
|
|
248
245
|
'perform agent_board._emit_membership_event', '')))
|
|
249
246
|
/ length('perform agent_board._emit_membership_event'), ', ' order by p.proname), '')
|
|
250
247
|
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
251
248
|
where n.nspname = 'agent_board' and p.prokind = 'f'
|
|
252
249
|
and p.prolang <> (select oid from pg_language where lanname = 'internal')
|
|
253
|
-
and
|
|
254
|
-
|
|
250
|
+
-- CASE-INSENSITIVE, and that is load-bearing rather than tidiness. SQL folds
|
|
251
|
+
-- unquoted identifiers, but `prosrc` is stored VERBATIM, so a writer spelling
|
|
252
|
+
-- the same call `AGENT_BOARD.…` or the same column `FINGERPRINT` is identical
|
|
253
|
+
-- to PostgreSQL and invisible to a case-sensitive scan. For a MEMBERSHIP
|
|
254
|
+
-- filter that is the fail-OPEN direction: the offender simply leaves the
|
|
255
|
+
-- class and the expected-empty assertion stays green. (Same mechanism
|
|
256
|
+
-- agent_board_wake_recovery.test.sql records for its own WR-7 filter.)
|
|
257
|
+
and agent_board_test.stripped_body(p.oid) ~* 'perform agent_board\._emit_membership_event'
|
|
258
|
+
and (length(agent_board_test.stripped_body(p.oid)) - length(replace(lower(agent_board_test.stripped_body(p.oid)),
|
|
255
259
|
'perform agent_board._emit_membership_event', '')))
|
|
256
260
|
/ length('perform agent_board._emit_membership_event') <> 1),
|
|
257
261
|
'',
|
|
@@ -259,7 +263,7 @@ select is(
|
|
|
259
263
|
|
|
260
264
|
-- ...and the one exemption is genuinely state-blind: membership_state never appears in a SET.
|
|
261
265
|
select ok(
|
|
262
|
-
(select
|
|
266
|
+
(select agent_board_test.stripped_body(p.oid) !~* 'set[^;]*membership_state[[:space:]]*='
|
|
263
267
|
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
264
268
|
where n.nspname = 'agent_board' and p.proname = 'grant_board_role'),
|
|
265
269
|
'COMPLETENESS: the exemption holds -- grant_board_role never SETs membership_state');
|
|
@@ -17,7 +17,7 @@ set search_path = extensions, public, agent_board;
|
|
|
17
17
|
|
|
18
18
|
begin;
|
|
19
19
|
|
|
20
|
-
select plan(
|
|
20
|
+
select plan(54);
|
|
21
21
|
|
|
22
22
|
create function pg_temp.claims(p_sub text) returns void language plpgsql as $$
|
|
23
23
|
begin
|
|
@@ -298,14 +298,44 @@ select is(
|
|
|
298
298
|
and p.prolang <> (select oid from pg_language where lanname = 'internal')
|
|
299
299
|
and has_function_privilege('authenticated', p.oid, 'EXECUTE')
|
|
300
300
|
-- reaches an active membership, directly or through the shared sink
|
|
301
|
-
and
|
|
301
|
+
and agent_board_test.stripped_body(p.oid)
|
|
302
302
|
-- Includes update_board_membership: a one-hop WRAPPER calling it with 'joined'
|
|
303
303
|
-- reaches an active membership without ever naming the sink, and would otherwise
|
|
304
304
|
-- evade this class assertion entirely.
|
|
305
|
-
|
|
306
|
-
--
|
|
307
|
-
|
|
308
|
-
|
|
305
|
+
-- CASE-INSENSITIVE, and that is load-bearing rather than tidiness. SQL folds
|
|
306
|
+
-- unquoted identifiers, but `prosrc` is stored VERBATIM, so a writer spelling
|
|
307
|
+
-- the same call `AGENT_BOARD.…` or the same column `FINGERPRINT` is identical
|
|
308
|
+
-- to PostgreSQL and invisible to a case-sensitive scan. For a MEMBERSHIP
|
|
309
|
+
-- filter that is the fail-OPEN direction: the offender simply leaves the
|
|
310
|
+
-- class and the expected-empty assertion stays green. (Same mechanism
|
|
311
|
+
-- agent_board_wake_recovery.test.sql records for its own WR-7 filter.)
|
|
312
|
+
~* '(_upsert_board_membership_state|_redeem_invite_for_agent|update_board_membership|insert into agent_board\.board_memberships)'
|
|
313
|
+
-- gated: consults the predicate, or refuses a non-subject actor structurally.
|
|
314
|
+
--
|
|
315
|
+
-- SPLIT IN TWO, by what kind of thing each marker IS. The single `!~` over a
|
|
316
|
+
-- three-way alternation could not be read correctly in either mode: the
|
|
317
|
+
-- helper name is CODE and the other two are raise MESSAGES, so
|
|
318
|
+
-- literal-stripping deleted the messages (every route then looked ungated)
|
|
319
|
+
-- and literal-keeping left the messages maskable by any string.
|
|
320
|
+
--
|
|
321
|
+
-- The message half is therefore ANCHORED TO THE STATEMENT SHAPE
|
|
322
|
+
-- (message-board#196 remedy 2) rather than to the bare message: the text has
|
|
323
|
+
-- to follow a `raise exception` to count. Measured before the split: a
|
|
324
|
+
-- `raise debug 'gate: %', '_membership_consent_present';` planted in an
|
|
325
|
+
-- ungated update_board_membership took this class assertion GREEN with the
|
|
326
|
+
-- gate genuinely removed. It does not any more.
|
|
327
|
+
-- anchored to the CALL shape, for the same reason the message half below is
|
|
328
|
+
-- anchored to the raise shape: a bare substring is satisfied by a NAME.
|
|
329
|
+
-- SCHEMA-QUALIFIED, because a bare-name anchor proves call SYNTAX, not callee
|
|
330
|
+
-- IDENTITY: `\m_membership_consent_present\s*\(` also matches
|
|
331
|
+
-- `zz_other._membership_consent_present(...)` (measured true). Every live call
|
|
332
|
+
-- site in agent_board is `agent_board.`-qualified, so requiring the
|
|
333
|
+
-- qualification costs nothing today and is fail-CLOSED if someone later writes
|
|
334
|
+
-- an unqualified call (it reads as NOT calling). Overload count is pinned
|
|
335
|
+
-- separately, so schema + name is the identity.
|
|
336
|
+
and agent_board_test.stripped_body(p.oid) !~* '\magent_board\._membership_consent_present\s*\('
|
|
337
|
+
and agent_board_test.stripped_body(p.oid, true)
|
|
338
|
+
!~ 'raise[[:space:]]+exception[^;]{0,250}(actor mismatch|requires an act by that agent)'
|
|
309
339
|
-- stated exemptions, each with a reason recorded in the migration header
|
|
310
340
|
and p.proname not in (
|
|
311
341
|
'create_board', -- the actor IS the subject: it seeds the creator's own seat
|
|
@@ -329,7 +359,9 @@ select is(
|
|
|
329
359
|
from pg_proc p
|
|
330
360
|
join pg_namespace n on n.oid = p.pronamespace
|
|
331
361
|
cross join lateral regexp_matches(
|
|
332
|
-
|
|
362
|
+
-- LITERALS KEPT: the marker is the ARGUMENT ''joined''/''subscribed'' passed
|
|
363
|
+
-- to the sink -- a string literal in the code being matched.
|
|
364
|
+
agent_board_test.stripped_body(p.oid, true),
|
|
333
365
|
'(?:_upsert_board_membership_state|update_board_membership)\s*\([^;]*?''(joined|subscribed)''',
|
|
334
366
|
'g') m
|
|
335
367
|
where n.nspname = 'agent_board'
|
|
@@ -343,7 +375,8 @@ select is(
|
|
|
343
375
|
from pg_proc p
|
|
344
376
|
join pg_namespace n on n.oid = p.pronamespace
|
|
345
377
|
cross join lateral regexp_matches(
|
|
346
|
-
|
|
378
|
+
-- LITERALS KEPT: same shape as above -- the state being written is a literal.
|
|
379
|
+
agent_board_test.stripped_body(p.oid, true),
|
|
347
380
|
'(?:_upsert_board_membership_state|update_board_membership)\s*\([^;]*?''(invited|requested|left)''',
|
|
348
381
|
'g') m
|
|
349
382
|
where n.nspname = 'agent_board'
|
|
@@ -389,7 +422,7 @@ select is(
|
|
|
389
422
|
-- silently reopening the race -- the classic wrong "fix" for lock-wait reports. `nowait`
|
|
390
423
|
-- is excluded too (it at least fails loudly).
|
|
391
424
|
select matches(
|
|
392
|
-
(select
|
|
425
|
+
(select agent_board_test.stripped_body(p.oid)
|
|
393
426
|
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
394
427
|
where n.nspname = 'agent_board' and p.proname = 'resolve_board_request'),
|
|
395
428
|
'from agent_board\.board_memberships[[:space:]]+bm[[:space:]]+where bm\.board_id = p_board_id[[:space:]]+and bm\.agent_id = p_agent_id[[:space:]]+order by bm\.id[[:space:]]+for update;',
|
|
@@ -399,14 +432,14 @@ select matches(
|
|
|
399
432
|
-- a lock taken after the check protects nothing.
|
|
400
433
|
select ok(
|
|
401
434
|
(select position('for update' in body) < position('_membership_consent_present' in body)
|
|
402
|
-
from (select
|
|
435
|
+
from (select agent_board_test.stripped_body(p.oid) as body
|
|
403
436
|
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
404
437
|
where n.nspname = 'agent_board' and p.proname = 'resolve_board_request') s),
|
|
405
438
|
'LOCK: ...and reads consent AFTER taking it, not before');
|
|
406
439
|
|
|
407
440
|
-- The sink acts on the row the CALLER named, rather than reselecting (#109).
|
|
408
441
|
select matches(
|
|
409
|
-
(select
|
|
442
|
+
(select agent_board_test.stripped_body(p.oid)
|
|
410
443
|
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
411
444
|
where n.nspname = 'agent_board' and p.proname = '_upsert_board_membership_state'),
|
|
412
445
|
'if p_membership_id is not null then',
|
|
@@ -485,5 +518,16 @@ select is(
|
|
|
485
518
|
select is((select version from agent_board.schema_version), 8,
|
|
486
519
|
'schema_version stays 8 -- additive');
|
|
487
520
|
|
|
521
|
+
|
|
522
|
+
-- The schema-qualified call anchor above treats `agent_board._membership_consent_present` as an
|
|
523
|
+
-- IDENTITY. That is only true while the name resolves to exactly one function:
|
|
524
|
+
-- a second overload would be a different implementation the same anchor accepts.
|
|
525
|
+
-- Catalog-derived, so a new overload fails here and the count names itself.
|
|
526
|
+
select is(
|
|
527
|
+
(select count(*)::int from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
528
|
+
where n.nspname = 'agent_board' and p.proname = '_membership_consent_present'),
|
|
529
|
+
1,
|
|
530
|
+
'agent_board._membership_consent_present has exactly ONE overload, so the schema-qualified call anchor identifies it uniquely');
|
|
531
|
+
|
|
488
532
|
select finish();
|
|
489
533
|
rollback;
|
|
@@ -94,7 +94,9 @@ select is(
|
|
|
94
94
|
false, 'over-ceiling grant is rejected (owner cannot grant admin when ceiling is member)');
|
|
95
95
|
|
|
96
96
|
-- #13 owner-settable ceiling: set_board_authority_ceiling is owner-gated. ---------
|
|
97
|
-
|
|
97
|
+
-- #247 added the delegable p_actor_agent_id (null default => the pre-#247 path for every
|
|
98
|
+
-- role), so the identity signature is (uuid, text, uuid).
|
|
99
|
+
select has_function('agent_board', 'set_board_authority_ceiling', array['uuid','text','uuid'],
|
|
98
100
|
'set_board_authority_ceiling exists');
|
|
99
101
|
|
|
100
102
|
set local role authenticated;
|