@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.
Files changed (33) hide show
  1. package/fixtures/already-applied.json +12 -0
  2. package/migrations/20260906000100_agent_board_enroll_resumption.sql +749 -0
  3. package/migrations/20260907000100_agent_board_signed_authority_provenance.sql +243 -0
  4. package/migrations/20260908000100_agent_board_signer_lifecycle_revalidation.sql +451 -0
  5. package/package.json +1 -1
  6. package/schema-manifest.json +30 -3
  7. package/src/tooling.test.mjs +3 -3
  8. package/tests/agent_board_atomic_fleet_onboarding.test.sql +129 -112
  9. package/tests/agent_board_claimkey.test.sql +25 -3
  10. package/tests/agent_board_consent_emission.test.sql +17 -13
  11. package/tests/agent_board_consent_gating.test.sql +55 -11
  12. package/tests/agent_board_delegation_policies.test.sql +3 -1
  13. package/tests/agent_board_enroll_resumption.test.sql +412 -0
  14. package/tests/agent_board_federated_lock_order_class.test.sql +1 -3
  15. package/tests/agent_board_fedsession_provider_absence_gate.test.sql +9 -6
  16. package/tests/agent_board_fleet_machine_reactivation.test.sql +56 -5
  17. package/tests/agent_board_mint_harden.test.sql +39 -4
  18. package/tests/agent_board_outbox_wake_ordering.test.sql +27 -12
  19. package/tests/agent_board_rejectsem.test.sql +1 -1
  20. package/tests/agent_board_rolereset.test.sql +23 -2
  21. package/tests/agent_board_rowid.test.sql +24 -13
  22. package/tests/agent_board_scoped_send_regrant.test.sql +8 -4
  23. package/tests/agent_board_signed_authority_provenance.test.sql +477 -0
  24. package/tests/agent_board_signer_lifecycle.test.sql +709 -0
  25. package/tests/agent_board_source_strip_helper.test.sql +285 -0
  26. package/tests/agent_board_wake_recovery.test.sql +58 -40
  27. package/tests/catalog/p_metadata_parameters.json +693 -0
  28. package/tests/fixtures/test-prereqs.sql +313 -0
  29. package/tests/isolation/expected/atomic_fleet_lifecycle_races.out +3 -3
  30. package/tests/isolation/expected/signed_grant_onboarding_deadlock.out +220 -0
  31. package/tests/isolation/expected/signer_lifecycle_revoke.out +307 -0
  32. package/tests/isolation/specs/signed_grant_onboarding_deadlock.spec +159 -0
  33. 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
- -- `pg_temp._stripped_body(<fn>)` -- prosrc with dollar-quoted blocks, single-quoted
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(52);
126
+ select plan(54);
127
127
 
128
128
  -- ---------------------------------------------------------------------------
129
- -- The stripped-body reader every positional assertion below uses. A shared SQL
130
- -- include is not workable in this harness -- the runner pipes
131
- -- `begin; \i <file>; rollback;` into psql, so there is no reliable relative
132
- -- include path -- so it is defined ONCE here instead of being copy-pasted into
133
- -- nine call sites, which would guarantee drift. The regex is byte-identical to
134
- -- agent_board_federated_lock_order_class.test.sql; see the header.
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
- pg_temp._stripped_body('agent_board.revoke_fleet_machine_agent_binding(uuid,uuid,text,jsonb)'::regprocedure),
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
- 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),
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 pg_temp._stripped_body(
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
- pg_temp._stripped_body('agent_board._revoke_agent_principal(uuid,uuid,text)'::regprocedure),
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
- pg_temp._stripped_body('agent_board.revoke_agent(uuid,text)'::regprocedure),
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
- pg_get_functiondef('agent_board.revoke_human_subtree(uuid,uuid,text)'::regprocedure)
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 pg_get_functiondef('agent_board.revoke_human_subtree(uuid,uuid,text)'::regprocedure)
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
- 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),
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
- 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),
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
- pg_temp._stripped_body('agent_board.set_board_authority_ceiling(uuid,text)'::regprocedure),
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
- case when strpos(p.prosrc, '_lock_board_authority_ceiling') > 0
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.prosrc, '_grant_within_ceiling') > 0
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
- and (strpos(p.prosrc, 'fleet_onboarding_invite_bounds') > 0
598
- or strpos(p.prosrc, 'fleet_human_revocations') > 0
599
- or strpos(p.prosrc, 'fleet_revocation_events') > 0
600
- or strpos(p.prosrc, '_lock_board_authority_ceiling') > 0
601
- or strpos(p.prosrc, 'board_authority_ceilings:') > 0)
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(25);
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 pg_get_functiondef(p.oid) ~* '(insert\s+into|update)\s+(agent_board\.)?claims\M'
223
- and pg_get_functiondef(p.oid) !~ '_canonical_claim_key'),
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
- -- Strips BOTH comment forms. The first version stripped only `--`, so a /* */ commented-out
226
- -- call still satisfied the completeness assertion.
227
- create function pg_temp.code(p_oid oid) returns text language sql stable as $$
228
- select regexp_replace(
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 pg_temp.code(p.oid) ~* '(insert into|update)[[:space:]]+agent_board\.board_memberships'
239
- and pg_temp.code(p.oid) !~ 'perform agent_board\._emit_membership_event'
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(pg_temp.code(p.oid)) - length(replace(pg_temp.code(p.oid),
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 pg_temp.code(p.oid) ~ 'perform agent_board\._emit_membership_event'
254
- and (length(pg_temp.code(p.oid)) - length(replace(pg_temp.code(p.oid),
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 pg_get_functiondef(p.oid) !~* 'set[^;]*membership_state[[:space:]]*='
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(53);
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 regexp_replace(pg_get_functiondef(p.oid), '--[^\n]*', '', 'g')
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
- ~ '(_upsert_board_membership_state|_redeem_invite_for_agent|update_board_membership|insert into agent_board\.board_memberships)'
306
- -- gated: consults the predicate, or refuses a non-subject actor structurally
307
- and regexp_replace(pg_get_functiondef(p.oid), '--[^\n]*', '', 'g')
308
- !~ '(_membership_consent_present|actor mismatch|requires an act by that agent)'
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
- regexp_replace(pg_get_functiondef(p.oid), '--[^\n]*', '', 'g'),
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
- regexp_replace(pg_get_functiondef(p.oid), '--[^\n]*', '', 'g'),
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 regexp_replace(regexp_replace(pg_get_functiondef(p.oid), '/\*.*?\*/', '', 'gs'), '--[^\n]*', '', 'g')
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 regexp_replace(regexp_replace(pg_get_functiondef(p.oid), '/\*.*?\*/', '', 'gs'), '--[^\n]*', '', 'g') as body
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 regexp_replace(pg_get_functiondef(p.oid), '--[^\n]*', '', 'g')
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
- select has_function('agent_board', 'set_board_authority_ceiling', array['uuid','text'],
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;