@consiliency/agent-board-schema 1.51.0 → 1.53.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 (44) hide show
  1. package/fixtures/already-applied.json +4 -0
  2. package/migrations/20261003000100_agent_board_room_roster.sql +115 -0
  3. package/package.json +1 -1
  4. package/schema-manifest.json +12 -3
  5. package/src/tooling.test.mjs +4 -2
  6. package/tests/agent_board_agent_liveness.test.sql +3 -1
  7. package/tests/agent_board_atomic_fleet_onboarding.test.sql +33 -36
  8. package/tests/agent_board_authbound_boundary.test.sql +4 -3
  9. package/tests/agent_board_board_claim_policy.test.sql +17 -6
  10. package/tests/agent_board_claim_subject_gate.test.sql +7 -8
  11. package/tests/agent_board_claimkey.test.sql +9 -6
  12. package/tests/agent_board_claimpolicy_race.test.sql +1466 -342
  13. package/tests/agent_board_consent_emission.test.sql +5 -4
  14. package/tests/agent_board_consent_gating.test.sql +20 -9
  15. package/tests/agent_board_enroll_resumption.test.sql +1 -1
  16. package/tests/agent_board_enrollment_audit_content.test.sql +5 -4
  17. package/tests/agent_board_federated_identity_injective_slug.test.sql +3 -1
  18. package/tests/agent_board_federated_lock_order_class.test.sql +69 -52
  19. package/tests/agent_board_fedsession.test.sql +3 -1
  20. package/tests/agent_board_fedsession_provider_absence_gate.test.sql +9 -4
  21. package/tests/agent_board_fedsession_stored_identity_gate.test.sql +18 -6
  22. package/tests/agent_board_fk_lock_edges.test.sql +64 -5
  23. package/tests/agent_board_fleet_domain_reclaim.test.sql +2 -4
  24. package/tests/agent_board_fleet_machine_hijack.test.sql +2 -4
  25. package/tests/agent_board_fleet_machine_reactivation.test.sql +5 -1
  26. package/tests/agent_board_fleet_machine_reclaim.test.sql +2 -4
  27. package/tests/agent_board_invite_no_downgrade.test.sql +24 -18
  28. package/tests/agent_board_ledger_authority.test.sql +29 -21
  29. package/tests/agent_board_mint_harden.test.sql +5 -5
  30. package/tests/agent_board_outbox_wake_ordering.test.sql +4 -3
  31. package/tests/agent_board_ownerless_renew_quota.test.sql +7 -2
  32. package/tests/agent_board_portal_offline_invariant.test.sql +5 -4
  33. package/tests/agent_board_receipt_subject_gate.test.sql +7 -8
  34. package/tests/agent_board_rolereset.test.sql +5 -3
  35. package/tests/agent_board_room_roster.test.sql +466 -0
  36. package/tests/agent_board_rowid.test.sql +39 -19
  37. package/tests/agent_board_signer_lifecycle.test.sql +24 -13
  38. package/tests/agent_board_source_strip_helper.test.sql +2 -1
  39. package/tests/agent_board_task_event_origin_posture.test.sql +21 -9
  40. package/tests/agent_board_wake_recovery.test.sql +53 -26
  41. package/tests/agent_board_write_enforcement_class.test.sql +130 -12
  42. package/tests/catalog/function_execute_privileges.json +1932 -0
  43. package/tests/catalog/p_metadata_parameters.json +4 -0
  44. package/tests/fixtures/test-prereqs.sql +644 -237
@@ -600,6 +600,10 @@
600
600
  {
601
601
  "id": "20261002000100_agent_board_session_challenge_supersede",
602
602
  "sha256": "f10d8f539538fcf63d5b3538650160e7a4f9e345ccb7476b21756675f2b6ab80"
603
+ },
604
+ {
605
+ "id": "20261003000100_agent_board_room_roster",
606
+ "sha256": "7bcb993b9943cd7e6bd3777c90c32e9cb32e53415262f8c58a8d75678a449853"
603
607
  }
604
608
  ]
605
609
  }
@@ -0,0 +1,115 @@
1
+ -- ROOMCONTRACT: owner/admin active roster plus self-membership role visibility.
2
+ --
3
+ -- The roster is a purpose-built SECURITY DEFINER read. The base table and the
4
+ -- security-invoker view remain self-scoped; authority is checked before any
5
+ -- roster query, and current standing is chosen before active-state filtering.
6
+
7
+ create or replace view agent_board.v_board_memberships
8
+ with (security_invoker = true)
9
+ as
10
+ select
11
+ bm.id as board_membership_id,
12
+ bm.board_id,
13
+ b.slug as board_slug,
14
+ b.name as board_name,
15
+ bm.agent_id,
16
+ a.handle as agent_handle,
17
+ a.display_name as agent_display_name,
18
+ bm.membership_state,
19
+ bm.created_by_agent_id,
20
+ creator.handle as created_by_handle,
21
+ bm.left_at,
22
+ bm.created_at,
23
+ bm.updated_at,
24
+ bm.role
25
+ from agent_board.board_memberships bm
26
+ join agent_board.boards b on b.id = bm.board_id
27
+ join agent_board.agents a on a.id = bm.agent_id
28
+ left join agent_board.agents creator on creator.id = bm.created_by_agent_id;
29
+
30
+ comment on view agent_board.v_board_memberships is
31
+ 'Self-scoped board membership read model. security_invoker preserves base-table RLS; role is appended after every pre-existing column.';
32
+
33
+ grant select on agent_board.v_board_memberships to authenticated, service_role;
34
+
35
+ create or replace function agent_board.list_board_members(p_board_id uuid)
36
+ returns table(
37
+ membership_id uuid,
38
+ board_id uuid,
39
+ agent_id uuid,
40
+ handle text,
41
+ display_name text,
42
+ agent_type text,
43
+ membership_state text,
44
+ role text,
45
+ created_by_agent_id uuid,
46
+ membership_created_at timestamptz,
47
+ membership_updated_at timestamptz
48
+ )
49
+ language plpgsql
50
+ stable
51
+ security definer
52
+ set search_path = agent_board, public
53
+ as $$
54
+ declare
55
+ v_actor uuid := agent_board.current_agent_id();
56
+ begin
57
+ -- Authorization precedes disclosure. Creator identity is deliberately absent:
58
+ -- only the caller's latest membership row can establish current authority.
59
+ if v_actor is null
60
+ or not exists (
61
+ select 1
62
+ from (
63
+ select bm.membership_state, bm.role
64
+ from agent_board.board_memberships bm
65
+ where bm.board_id = p_board_id
66
+ and bm.agent_id = v_actor
67
+ order by bm.created_at desc, bm.id desc
68
+ limit 1
69
+ ) actor_standing
70
+ where actor_standing.membership_state in ('joined', 'subscribed')
71
+ and actor_standing.role in ('owner', 'admin')
72
+ ) then
73
+ raise exception 'only a board owner or admin may list board members'
74
+ using errcode = '42501';
75
+ end if;
76
+
77
+ -- Choose exactly one current row per agent before filtering to active states.
78
+ return query
79
+ select
80
+ latest.id,
81
+ latest.board_id,
82
+ latest.agent_id,
83
+ a.handle,
84
+ a.display_name,
85
+ a.agent_type,
86
+ latest.membership_state,
87
+ latest.role,
88
+ latest.created_by_agent_id,
89
+ latest.created_at,
90
+ latest.updated_at
91
+ from (
92
+ select distinct on (bm.agent_id) bm.*
93
+ from agent_board.board_memberships bm
94
+ where bm.board_id = p_board_id
95
+ order by bm.agent_id, bm.created_at desc, bm.id desc
96
+ ) latest
97
+ join agent_board.agents a on a.id = latest.agent_id
98
+ where latest.membership_state in ('joined', 'subscribed')
99
+ order by
100
+ case latest.role
101
+ when 'owner' then 0
102
+ when 'admin' then 1
103
+ when 'member' then 2
104
+ when 'observer' then 3
105
+ end,
106
+ lower(a.handle),
107
+ latest.agent_id;
108
+ end;
109
+ $$;
110
+
111
+ comment on function agent_board.list_board_members(uuid) is
112
+ 'Owner/admin-gated active board roster. Fails 42501 for a null actor (including service_role through this user-facing path), creator without current standing, or any caller whose latest board membership is not active owner/admin. Returns each agent''s latest row by created_at desc, id desc only when joined/subscribed, ordered owner/admin/member/observer then lower(handle), agent_id. Board-scoped and metadata-free.';
113
+
114
+ revoke all on function agent_board.list_board_members(uuid) from public, anon;
115
+ grant execute on function agent_board.list_board_members(uuid) to authenticated, service_role;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@consiliency/agent-board-schema",
3
- "version": "1.51.0",
3
+ "version": "1.53.0",
4
4
  "description": "Canonical, immutable, exact-pinnable agent_board schema bundle: ordered SQL migrations, digest + compatibility manifest, and deterministic materialize/plan/check tooling for adopters.",
5
5
  "repository": "https://github.com/Consiliency/message-board.git",
6
6
  "type": "module",
@@ -2,7 +2,7 @@
2
2
  "schema": "agent_board_schema_manifest.v1",
3
3
  "interface": "IF-0-MBPLANE-BUNDLE-1",
4
4
  "package": "@consiliency/agent-board-schema",
5
- "packageVersion": "1.51.0",
5
+ "packageVersion": "1.53.0",
6
6
  "schemaVersion": 8,
7
7
  "minClientVersion": "1.1.0",
8
8
  "source": {
@@ -46,8 +46,8 @@
46
46
  "usage": "upsert (retention/monitoring defaults; on conflict do nothing)"
47
47
  }
48
48
  ],
49
- "migrationCount": 150,
50
- "aggregateSha256": "94b8945ab506a02a31ffd0ed6daad37d5655f209214558ca868dbb22693fb57d",
49
+ "migrationCount": 151,
50
+ "aggregateSha256": "332c33f67f08d8bcdbf71ef2568ea03f6cd16ccdeaac4d1944259ab28012900c",
51
51
  "migrations": [
52
52
  {
53
53
  "order": 0,
@@ -1398,6 +1398,15 @@
1398
1398
  "sourceRef": "message-board",
1399
1399
  "bytes": 19273,
1400
1400
  "sha256": "f10d8f539538fcf63d5b3538650160e7a4f9e345ccb7476b21756675f2b6ab80"
1401
+ },
1402
+ {
1403
+ "order": 150,
1404
+ "id": "20261003000100_agent_board_room_roster",
1405
+ "filename": "20261003000100_agent_board_room_roster.sql",
1406
+ "origin": "message-board-forward",
1407
+ "sourceRef": "message-board",
1408
+ "bytes": 3993,
1409
+ "sha256": "7bcb993b9943cd7e6bd3777c90c32e9cb32e53415262f8c58a8d75678a449853"
1401
1410
  }
1402
1411
  ],
1403
1412
  "excludedBridge": {
@@ -38,9 +38,9 @@ describe('manifest integrity', () => {
38
38
  expect(manifest.schemaVersion).toBe(8);
39
39
  expect(manifest.minClientVersion).toMatch(/^\d+\.\d+\.\d+$/);
40
40
  });
41
- it('registers the four MBAUTHZ + four MBSENDAUTHZ + four MBSESSION + four MBDELEG + three MBFED + three MBHARDEN + two MBPORTALGATE + two MBPORTAL-PR32 + two ROOMOWN + two ROOMLINK + one ROOMDELEG + one LOCALAUTH + one ROOMUIAPI + one FEDSESSION + one FEDSESSION-AUTOJOIN + one GATENULL + one MEMBSTATE + one AGENTSDIR + one LEAVEIDEM + one ROLERESET + one MBROWID + one MBHYGIENE + one MBCONSENT-A + one MBCONSENT-B(a) + one MBCONSENT-B(i) + one MBCONSENT-B(ii) + one MBCONSENT-B(iii) + one MBHARDEN-#97 + one MBHARDEN-#118 + one INVITEPOLICY + one OBSGATE + one OBSGATE-2 + one OBSGATE-3 + one OBSGATE-4 + one FLEETMACHINERLS + one RESOLVENOOP + one ROOMDELEG-LANEB + one CLAIMKEY + two MINTHARDEN + one FLEETMACHINEHIJACK + one CLAIMGATE-SHADOW + one CLAIMGATE-ENFORCE + one FLEETMACHINEREACTIVATION + one DELEGCAPCEILING + one FLEETMACHINERECLAIM + one DELEGSOLEWRITER + one FLEETDOMAINRECLAIM + five ATOMICFLEETONBOARDING (onboarding + ceiling-lock + revocation-evidence + fedsession-gate + fedsession-lock-order) + one OUTBOXWAKE + one ENROLLLOCKORDER + one FEDPROVIDERABSENCE + one WAKERECOVERY + one CONSENTRECONCILE + one ENROLLRESUMPTION + one SIGNEDAUTHORITY + one SIGNERLIFECYCLE + one LEDGERAUTHORITY + one CEILINGLOCKCOMMENT + one LEDGERBOARDCREATE + one RECEIPTSUBJECTGATE + one TASKTHREADLOCKORDER + one CLAIMSUBJECTGATE + one FEDSESSIONIDENTITYGATE + one FEDIDENTITYSLUG + one INVITENODOWNGRADE + one SIGNEDWRITEFLAG + one SIGNEDWRITEMODEL + one RTCREDCLAIM + one ORIGINPOSTURE + one BOARDCLAIMPOLICY + one HEARTBEATPRESERVE + one LIVENESSDENOM + one OWNERLESSRENEWTTL + one OWNERLESSQUOTACENSUS + one FLEETDOMAINNODOWNGRADE + one OWNERLESSQUOTACONVERGE + one ADVISORYREVERTRESET + one CLAIMPOLICYRACE + one CLAIMPOLICYLOCKGRANT + one SESSIONCHALLENGESUPERSEDE forward migrations (migrationCount = prior 113 + 5 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 = 150)', () => {
41
+ it('registers the four MBAUTHZ + four MBSENDAUTHZ + four MBSESSION + four MBDELEG + three MBFED + three MBHARDEN + two MBPORTALGATE + two MBPORTAL-PR32 + two ROOMOWN + two ROOMLINK + one ROOMDELEG + one LOCALAUTH + one ROOMUIAPI + one FEDSESSION + one FEDSESSION-AUTOJOIN + one GATENULL + one MEMBSTATE + one AGENTSDIR + one LEAVEIDEM + one ROLERESET + one MBROWID + one MBHYGIENE + one MBCONSENT-A + one MBCONSENT-B(a) + one MBCONSENT-B(i) + one MBCONSENT-B(ii) + one MBCONSENT-B(iii) + one MBHARDEN-#97 + one MBHARDEN-#118 + one INVITEPOLICY + one OBSGATE + one OBSGATE-2 + one OBSGATE-3 + one OBSGATE-4 + one FLEETMACHINERLS + one RESOLVENOOP + one ROOMDELEG-LANEB + one CLAIMKEY + two MINTHARDEN + one FLEETMACHINEHIJACK + one CLAIMGATE-SHADOW + one CLAIMGATE-ENFORCE + one FLEETMACHINEREACTIVATION + one DELEGCAPCEILING + one FLEETMACHINERECLAIM + one DELEGSOLEWRITER + one FLEETDOMAINRECLAIM + five ATOMICFLEETONBOARDING (onboarding + ceiling-lock + revocation-evidence + fedsession-gate + fedsession-lock-order) + one OUTBOXWAKE + one ENROLLLOCKORDER + one FEDPROVIDERABSENCE + one WAKERECOVERY + one CONSENTRECONCILE + one ENROLLRESUMPTION + one SIGNEDAUTHORITY + one SIGNERLIFECYCLE + one LEDGERAUTHORITY + one CEILINGLOCKCOMMENT + one LEDGERBOARDCREATE + one RECEIPTSUBJECTGATE + one TASKTHREADLOCKORDER + one CLAIMSUBJECTGATE + one FEDSESSIONIDENTITYGATE + one FEDIDENTITYSLUG + one INVITENODOWNGRADE + one SIGNEDWRITEFLAG + one SIGNEDWRITEMODEL + one RTCREDCLAIM + one ORIGINPOSTURE + one BOARDCLAIMPOLICY + one HEARTBEATPRESERVE + one LIVENESSDENOM + one OWNERLESSRENEWTTL + one OWNERLESSQUOTACENSUS + one FLEETDOMAINNODOWNGRADE + one OWNERLESSQUOTACONVERGE + one ADVISORYREVERTRESET + one CLAIMPOLICYRACE + one CLAIMPOLICYLOCKGRANT + one SESSIONCHALLENGESUPERSEDE + one ROOMCONTRACT forward migrations (migrationCount = prior 113 + 5 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 = 151)', () => {
42
42
  expect(manifest.migrationCount).toBe(manifest.migrations.length);
43
- expect(manifest.migrationCount).toBe(150);
43
+ expect(manifest.migrationCount).toBe(151);
44
44
  // MBSESSION switched enforcement on (scoped-send re-grant): the client-visible
45
45
  // schema version bumped to 8. MBDELEG tightens delegation and MBFED adds only
46
46
  // enrollment-only additive surface (identity_providers registry, enrollment audit,
@@ -335,6 +335,8 @@ describe('manifest integrity', () => {
335
335
  // is unchanged; mint_fleet_agent_session now names an eviction challenge_superseded
336
336
  // so it is retryable without making nonce_replay retryable with it.
337
337
  '20261002000100_agent_board_session_challenge_supersede.sql',
338
+ // ROOMCONTRACT: owner/admin active roster RPC plus self-membership role.
339
+ '20261003000100_agent_board_room_roster.sql',
338
340
  ]) {
339
341
  expect(names.has(f)).toBe(true);
340
342
  }
@@ -263,7 +263,9 @@ select is(
263
263
  (select string_agg(c.relname, ', ' order by c.relname)
264
264
  from pg_class c join pg_namespace n on n.oid = c.relnamespace
265
265
  where n.nspname = 'agent_board' and c.relkind = 'v'
266
- and pg_get_viewdef(c.oid) ~ 'presence'),
266
+ -- literals KEPT on purpose: v_ops_table_growth names presence as a row-count subject
267
+ -- inside a string literal, and it is in the expected set below
268
+ and agent_board_test.stripped_viewdef(c.oid, true) ~ agent_board_test.qname_re('presence')),
267
269
  'v_agent_dashboard, v_agent_liveness, v_ops_presence_heartbeats, v_ops_table_growth',
268
270
  'AL-11 CLASS: exactly these four agent_board views read presence — a new one fails here and names itself, so its join direction gets stated instead of assumed');
269
271
 
@@ -99,17 +99,14 @@
99
99
  -- assertion description. Where it has none, that is stated too. An honest narrow
100
100
  -- claim beats a broad false one.
101
101
  --
102
- -- WHAT IS *STILL* RAW IN THIS FILE. Only the NINE positional claims were converted.
103
- -- Everything else here still reads raw text and is knowingly left alone, so this
104
- -- file must not be read as "the masking class is closed in
105
- -- agent_board_atomic_fleet_onboarding.test.sql". The remainder, enumerated so the
106
- -- next reader does not have to trust a summary:
107
- -- * the 23 `pg_get_functiondef(...) like '%...%'` PRESENCE assertions (26 calls;
108
- -- two assertions read more than one function). A comment mentioning the
109
- -- pattern satisfies them exactly as it did the positional ones; one was
110
- -- demonstrated silenced that way (#198). They are presence claims, so a
111
- -- comment can only make a MISSING construct look present -- the same
112
- -- fail-open direction, on weaker claims;
102
+ -- WHAT IS *STILL* RAW IN THIS FILE. Only the NINE positional claims were converted
103
+ -- by #196; the `pg_get_functiondef(...) like '%...%'` PRESENCE assertions stayed raw
104
+ -- until Consiliency/message-board#417 PR 3, which moved them onto
105
+ -- `stripped_body(<fn>, true)` -- comments removed, LITERALS KEPT, because several of
106
+ -- them look for a string literal (`fleet_onboarding.v1`, a raise message). A
107
+ -- comment mentioning the pattern used to satisfy them exactly as it did the
108
+ -- positional ones; one was demonstrated silenced that way (#198). What remains raw,
109
+ -- so this file is not read as "the masking class is closed":
113
110
  -- * the ONE remaining deliberate raw read, added by message-board#277: the
114
111
  -- KNOWN-STALE record for set_board_authority_ceiling's inline every-reader
115
112
  -- claim. Its evidence IS a comment, so a stripped body cannot see it by
@@ -181,77 +178,77 @@ select function_privs_are(
181
178
  'fleet redemption helper is service-role-only'
182
179
  );
183
180
  select ok(
184
- pg_get_functiondef('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)
181
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
185
182
  like '%fleet_onboarding.v1%',
186
183
  'atomic onboarding returns the frozen contract version'
187
184
  );
188
185
  select ok(
189
- pg_get_functiondef('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)
186
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
190
187
  like '%for update%',
191
188
  'atomic onboarding catalog-pins row locking'
192
189
  );
193
190
  select ok(
194
- pg_get_functiondef('agent_board.enroll_federated_identity(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text)'::regprocedure)
191
+ agent_board_test.stripped_body('agent_board.enroll_federated_identity(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text)'::regprocedure, true)
195
192
  like '%pg_advisory_xact_lock%',
196
193
  'exact-key create-or-reuse is serialized by fingerprint'
197
194
  );
198
195
  select ok(
199
- pg_get_functiondef('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)
196
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
200
197
  like '%pg_advisory_xact_lock(hashtextextended(p_agent_public_key_fingerprint, 0))%',
201
198
  'composed onboarding serializes identity disposition by fingerprint'
202
199
  );
203
200
  select ok(
204
- pg_get_functiondef('agent_board.enroll_federated_identity(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text)'::regprocedure)
201
+ agent_board_test.stripped_body('agent_board.enroll_federated_identity(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text)'::regprocedure, true)
205
202
  like '%fleet invite requires atomic fleet onboarding%',
206
203
  'legacy enrollment rejects bounded invites before writes'
207
204
  );
208
205
  select ok(
209
- pg_get_functiondef('agent_board._redeem_invite_for_agent(text,uuid)'::regprocedure)
206
+ agent_board_test.stripped_body('agent_board._redeem_invite_for_agent(text,uuid)'::regprocedure, true)
210
207
  like '%reserved for atomic fleet onboarding%',
211
208
  'generic ROOMLINK redemption rejects bounded invites'
212
209
  );
213
210
  select ok(
214
- pg_get_functiondef('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)
211
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
215
212
  like '%phase_worker%worker%reviewer%reviewer%',
216
213
  'only bounded worker and reviewer role pairs are admitted'
217
214
  );
218
215
  select ok(
219
- pg_get_functiondef('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)
216
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
220
217
  like '%signing_key_id is distinct from v_enroll.signing_key_id%',
221
218
  'binding replay pins the exact proven signing key'
222
219
  );
223
220
  select ok(
224
- pg_get_functiondef('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)
221
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
225
222
  like '%v_inv.board_id is distinct from p_board_id%',
226
223
  'requested board is checked against the locked invite before writes'
227
224
  );
228
225
  select ok(
229
- pg_get_functiondef('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)
226
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
230
227
  like '%not v_completed_replay%v_inv.expires_at <= now()%v_inv.uses_count >= v_inv.max_uses%',
231
228
  'only a complete active replay may bypass expiry and exhaustion'
232
229
  );
233
230
  select ok(
234
- pg_get_functiondef('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)
231
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
235
232
  like '%v_inv.revoked_at is not null%a.status = ''active''%_grant_within_ceiling%',
236
233
  'revocation and current creator authority gate every fleet replay'
237
234
  );
238
235
  select ok(
239
- pg_get_functiondef('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)
236
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
240
237
  like '%a.id = any(array[v_inv.created_by_agent_id, v_existing_agent_id])%order by a.id for update%bm.agent_id = v_inv.created_by_agent_id%order by bm.id for update%_grant_within_ceiling%',
241
238
  'creator and enrolled principals lock in UUID order before creator authority'
242
239
  );
243
240
  select ok(
244
- pg_get_functiondef('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)
241
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
245
242
  like '%board_invite_redemptions red%order by red.id for update%bm.agent_id = v_enroll.agent_id%order by bm.id for update%select exists%',
246
243
  'complete-replay proof locks redemption and membership first'
247
244
  );
248
245
  select ok(
249
- pg_get_functiondef('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)
246
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
250
247
  like '%perform 1 from agent_board.fleet_harness_registrations%perform 1 from agent_board.fleet_repo_scopes%perform 1 from agent_board.fleet_machine_agents%',
251
248
  'every matching harness, repo, and binding history row is locked before reuse'
252
249
  );
253
250
  select ok(
254
- pg_get_functiondef('agent_board.revoke_fleet_machine_agent_binding(uuid,uuid,text,jsonb)'::regprocedure)
251
+ agent_board_test.stripped_body('agent_board.revoke_fleet_machine_agent_binding(uuid,uuid,text,jsonb)'::regprocedure, true)
255
252
  like '%lifecycle_state = ''revoked''%',
256
253
  'binding revocation is a permanent tombstone'
257
254
  );
@@ -320,17 +317,17 @@ select ok(
320
317
  'subtree primitive locks the agent before signing keys [SOLE PIN: no isolation spec discriminates this ordering -- measured against all 20]'
321
318
  );
322
319
  select ok(
323
- pg_get_functiondef('agent_board._revoke_agent_principal(uuid,uuid,text)'::regprocedure)
320
+ agent_board_test.stripped_body('agent_board._revoke_agent_principal(uuid,uuid,text)'::regprocedure, true)
324
321
  like '%from agent_board.agent_signing_keys%order by id%for update%',
325
322
  'subtree primitive locks signing keys in UUID order'
326
323
  );
327
324
  select ok(
328
- pg_get_functiondef('agent_board.revoke_agent_subtree(uuid,uuid,text)'::regprocedure)
325
+ agent_board_test.stripped_body('agent_board.revoke_agent_subtree(uuid,uuid,text)'::regprocedure, true)
329
326
  like '%select agent_id from subtree order by agent_id%',
330
327
  'agent subtree enumerates principals in UUID order'
331
328
  );
332
329
  select ok(
333
- pg_get_functiondef('agent_board.revoke_human_subtree(uuid,uuid,text)'::regprocedure)
330
+ agent_board_test.stripped_body('agent_board.revoke_human_subtree(uuid,uuid,text)'::regprocedure, true)
334
331
  like '%where owner_user_id = p_owner_user_id%order by id%',
335
332
  'human subtree enumerates principals in UUID order'
336
333
  );
@@ -366,17 +363,17 @@ select has_pk('agent_board', 'fleet_human_revocations',
366
363
  select has_trigger('agent_board', 'agents', 'agents_protect_federation_contract',
367
364
  'agents protect their reserved federation contract at every write path');
368
365
  select ok(
369
- pg_get_functiondef('agent_board.create_fleet_onboarding_invite(uuid,uuid,text,text,text,text,text,text,text,integer,timestamp with time zone,uuid,jsonb)'::regprocedure)
366
+ agent_board_test.stripped_body('agent_board.create_fleet_onboarding_invite(uuid,uuid,text,text,text,text,text,text,text,integer,timestamp with time zone,uuid,jsonb)'::regprocedure, true)
370
367
  like '%from agent_board.boards b%where b.id = p_board_id%for update%v_board.status <> ''active''%',
371
368
  'bounded invite mint locks and requires an active board'
372
369
  );
373
370
  select ok(
374
- pg_get_functiondef('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)
371
+ agent_board_test.stripped_body('agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text)'::regprocedure, true)
375
372
  like '%from agent_board.boards b%where b.id = v_inv.board_id%for update%v_board.status <> ''active''%',
376
373
  'atomic onboarding locks and requires the invite board to remain active'
377
374
  );
378
375
  select ok(
379
- pg_get_functiondef('agent_board.enroll_federated_identity(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text)'::regprocedure)
376
+ agent_board_test.stripped_body('agent_board.enroll_federated_identity(text,text,text,jsonb,text,text,text,text,boolean,text,text,text[],text[],text)'::regprocedure, true)
380
377
  like '%from auth.users u where u.id = v_owner_user_id for update%fleet_human_revocations%r.issuer = p_issuer and r.subject = p_subject%',
381
378
  'identity enrollment locks the human owner and rejects durable total revocation'
382
379
  );
@@ -434,7 +431,7 @@ select has_function(
434
431
  'board-keyed ceiling advisory lock helper exists'
435
432
  );
436
433
  select ok(
437
- pg_get_functiondef('agent_board._lock_board_authority_ceiling(uuid)'::regprocedure)
434
+ agent_board_test.stripped_body('agent_board._lock_board_authority_ceiling(uuid)'::regprocedure, true)
438
435
  like '%pg_advisory_xact_lock(%hashtextextended(''agent_board.board_authority_ceilings:'' || p_board_id::text, 0)%',
439
436
  'the ceiling lock key is derived once, from the board id, in a transaction advisory lock'
440
437
  );
@@ -530,9 +527,8 @@ create temp table _ceiling_class as
530
527
  select 'agent_board.' || regexp_replace(p.oid::regprocedure::text, '^([^(]*\.)?', '') as ident,
531
528
  p.proname::text as name, -- text, not `name`: array_agg(name) has no is(name[], text[]) overload
532
529
  (strpos(lower(agent_board_test.stripped_body(p.oid)), '_grant_within_ceiling') > 0) as is_reader,
533
- (agent_board_test.stripped_body(p.oid) ~*
534
- '(insert\s+into|update|delete\s+from|merge\s+into)\s+(only\s+)?(agent_board\.)?board_authority_ceilings\M') as is_writer,
535
- (agent_board_test.stripped_body(p.oid) ~* '\magent_board\._lock_board_authority_ceiling\s*\(') as serialized
530
+ (agent_board_test.stripped_body(p.oid) ~ agent_board_test.write_re('board_authority_ceilings')) as is_writer,
531
+ (agent_board_test.stripped_body(p.oid) ~ agent_board_test.call_re('_lock_board_authority_ceiling')) as serialized
536
532
  from pg_proc p
537
533
  join pg_namespace n on n.oid = p.pronamespace
538
534
  where n.nspname = 'agent_board';
@@ -640,6 +636,7 @@ select is(
640
636
  -- assertion fails and names itself -- delete it, and re-derive the COMMENT's blocks
641
637
  -- from the catalog while you are there. Raw prosrc on purpose: the evidence IS a
642
638
  -- comment, so the stripped body cannot see it (presence claim, not positional).
639
+ -- source-scan: raw-subject the evidence IS a comment; a stripped body cannot see it by construction
643
640
  select ok(
644
641
  (select p.prosrc from pg_proc p join pg_namespace n on n.oid = p.pronamespace
645
642
  where n.nspname = 'agent_board' and p.proname = 'set_board_authority_ceiling')
@@ -133,8 +133,9 @@ select ok(
133
133
  join pg_namespace n on n.oid = p.pronamespace
134
134
  where n.nspname = 'agent_board'
135
135
  and p.proname = '_record_signed_write_evidence'
136
- and pg_get_functiondef(p.oid) like '%authbound%'
137
- and pg_get_functiondef(p.oid) like '%metadata-safe%'
136
+ -- literals kept: the posture is recorded as jsonb string values, not as code
137
+ and agent_board_test.stripped_body(p.oid, true) like '%authbound%'
138
+ and agent_board_test.stripped_body(p.oid, true) like '%metadata-safe%'
138
139
  ),
139
140
  'signed-write evidence records AUTHBOUND metadata-safe posture'
140
141
  );
@@ -146,7 +147,7 @@ select ok(
146
147
  join pg_namespace n on n.oid = p.pronamespace
147
148
  where n.nspname = 'agent_board'
148
149
  and p.proname = 'current_agent_id'
149
- and pg_get_functiondef(p.oid) like '%agent_credential_id%'
150
+ and agent_board_test.stripped_body(p.oid, true) like '%agent_credential_id%'
150
151
  ),
151
152
  'current_agent_id records AUTHBOUND credential-claim boundary'
152
153
  );
@@ -431,16 +431,27 @@ end $$;
431
431
  -- itself here rather than silently inheriting a policy change that resets nothing.
432
432
  --
433
433
  -- Two independent bounds, because neither is sufficient alone. BCP-19 is a source scan and a source
434
- -- scan has a floor — it sees `set claim_policy` and not, say, a dynamic EXECUTE. BCP-20 is the
435
- -- privilege bound underneath it: NO role holds UPDATE on agent_board.boards, so the column cannot
436
- -- be written from a session at all, only from a SECURITY DEFINER function (which BCP-19 enumerates)
437
- -- or by the table owner. `boards` also carries no user triggers, so there is no third path.
434
+ -- scan has a floor — it sees a statement assigning the column and not, say, one assembled from
435
+ -- fragments inside a dynamic EXECUTE. BCP-20 is the privilege bound underneath it: NO role holds
436
+ -- UPDATE on agent_board.boards, so the column cannot be written from a session at all, only from a
437
+ -- SECURITY DEFINER function (which BCP-19 enumerates) or by the table owner. `boards` also carries
438
+ -- no user triggers, so there is no third path.
439
+ --
440
+ -- BCP-19's population is `col_write_re('boards','claim_policy')` over EVERY non-system schema —
441
+ -- the same object and the same scope as CPR-6 in agent_board_claimpolicy_race.test.sql, so the
442
+ -- two cannot disagree about who writes the column. It used to be the literal `set\s+claim_policy`
443
+ -- over agent_board only (#416): that spelling missed `SET CLAIM_POLICY`, the multi-column
444
+ -- `set (slug, claim_policy) = (…)`, a MERGE's `then update set`, an INSERT naming the column, and
445
+ -- every no-column-list form that writes all columns — and a SECURITY DEFINER writer living in
446
+ -- another schema was outside its scope entirely. Those forms are pinned by CPR-0's rows.
438
447
  -- ---------------------------------------------------------------------------
439
448
  select is(
440
449
  (select coalesce(string_agg(p.proname, ', ' order by p.proname), '')
441
450
  from pg_proc p join pg_namespace n on n.oid = p.pronamespace
442
- where n.nspname = 'agent_board'
443
- and agent_board_test.stripped_body(p.oid, true) ~ 'set\s+claim_policy'),
451
+ where n.nspname not in ('pg_catalog', 'information_schema', 'agent_board_test')
452
+ and n.nspname !~ '^pg_'
453
+ and p.prolang <> (select oid from pg_language where lanname = 'internal')
454
+ and agent_board_test.stripped_body(p.oid, true) ~ agent_board_test.col_write_re('boards', 'claim_policy')),
444
455
  'set_board_claim_policy',
445
456
  'BCP-19 CLASS: set_board_claim_policy is the ONLY function that writes boards.claim_policy — a second writer would need the same advisory-revert reset and fails here instead of being remembered');
446
457
 
@@ -863,8 +863,7 @@ select is(
863
863
  (select coalesce(string_agg(p.proname, ',' order by p.proname), '(none)')
864
864
  from pg_proc p
865
865
  where p.pronamespace = 'agent_board'::regnamespace
866
- and lower(agent_board_test.stripped_body(p.oid, true))
867
- ~ '(insert\s+into|merge\s+into|update|delete\s+from|truncate)(\s+table)?(\s+only)?\s+(("?agent_board"?\s*\.\s*)?claims\M|("?agent_board"?\s*\.\s*)?"claims")'),
866
+ and agent_board_test.stripped_body(p.oid, true) ~ agent_board_test.write_re('claims')),
868
867
  'acquire_claim,reclaim_fleet_machine,release_claim,renew_claim,set_board_claim_policy',
869
868
  'catalog: these five are the ONLY agent_board functions that write claims (insert/merge/update/delete/TRUNCATE) -- a new writer names itself here');
870
869
 
@@ -872,10 +871,10 @@ select is(
872
871
  (select coalesce(string_agg(p.proname, ',' order by p.proname), '(none)')
873
872
  from pg_proc p
874
873
  where p.pronamespace = 'agent_board'::regnamespace
875
- and lower(agent_board_test.stripped_body(p.oid, true))
876
- ~ '(insert\s+into|merge\s+into)(\s+only)?\s+(("?agent_board"?\s*\.\s*)?claims\M|("?agent_board"?\s*\.\s*)?"claims")'
877
- and lower(agent_board_test.stripped_body(p.oid))
878
- !~ 'not\s+agent_board\._agent_can_access_thread\s*\('),
874
+ and agent_board_test.stripped_body(p.oid, true) ~ agent_board_test.stmt_re('insert into|merge into', 'claims')
875
+ and agent_board_test.stripped_body(p.oid)
876
+ !~ agent_board_test.re('\m' || agent_board_test.ci_re('not') || '\s+'
877
+ || agent_board_test.call_frag('_agent_can_access_thread', false))),
879
878
  '(none)',
880
879
  'catalog: every function that CREATES a claims row contains a REFUSING reference to _agent_can_access_thread (source shape only -- assertion 2 is what proves it refuses)');
881
880
 
@@ -893,8 +892,8 @@ select is(
893
892
  (select coalesce(string_agg(p.proname, ',' order by p.proname), '(none)')
894
893
  from pg_proc p
895
894
  where p.pronamespace = 'agent_board'::regnamespace
896
- and lower(agent_board_test.stripped_body(p.oid, true))
897
- ~ '(delete\s+from|merge\s+into|truncate)(\s+table)?(\s+only)?\s+(("?agent_board"?\s*\.\s*)?claims\M|("?agent_board"?\s*\.\s*)?"claims")'),
895
+ and agent_board_test.stripped_body(p.oid, true)
896
+ ~ agent_board_test.stmt_re('delete from|merge into|truncate|truncate table', 'claims')),
898
897
  '(none)',
899
898
  'catalog: NOTHING REAPS -- no agent_board function deletes or truncates a claims row, which is what makes the ratified permanence true by construction');
900
899
 
@@ -206,7 +206,7 @@ select set_eq(
206
206
  join pg_namespace n on n.oid = p.pronamespace
207
207
  where n.nspname = 'agent_board'
208
208
  and p.prokind = 'f'
209
- and pg_get_functiondef(p.oid) ~* '(insert\s+into|update)\s+(agent_board\.)?claims\M' $$,
209
+ and agent_board_test.stripped_body(p.oid, true) ~ agent_board_test.write_re('claims') $$,
210
210
  -- reclaim_fleet_machine (#150) is a fourth writer: on operator reclaim it RELEASES the squatter's
211
211
  -- active machine claim(s) on the reclaimed fingerprint so the new owner can acquire. It routes the
212
212
  -- key through _canonical_claim_key (assertion 25 stays 0); its positive state claim (status set to
@@ -225,7 +225,7 @@ select is(
225
225
  join pg_namespace n on n.oid = p.pronamespace
226
226
  where n.nspname = 'agent_board'
227
227
  and p.prokind = 'f'
228
- and agent_board_test.stripped_body(p.oid) ~* '(insert\s+into|update)\s+(agent_board\.)?claims\M'
228
+ and agent_board_test.stripped_body(p.oid) ~ agent_board_test.write_re('claims')
229
229
  -- #400: SCOPED TO WRITERS THAT HAVE A KEY TO NORMALIZE. A claims writer whose body never
230
230
  -- mentions resource_key cannot set one -- an UPDATE must name the column in its SET list and
231
231
  -- an INSERT must name it in its column list (resource_key is NOT NULL) -- so requiring it to
@@ -234,19 +234,22 @@ select is(
234
234
  -- is still in the population and still fails closed. STATED HOLE: `insert into claims select *
235
235
  -- from ...` names no column and would slip past, which is the same shape assertion 24's regex
236
236
  -- already cannot see.
237
- and agent_board_test.stripped_body(p.oid) ~* '\mresource_key\M'
237
+ -- The column token QUALIFIED OR BARE (col_ref_frag): `col_frag` alone is the SET-list form
238
+ -- and rejects a preceding dot, so `c.resource_key` would drop a writer out of this set
239
+ -- (measured: _claimgate_promotion_blockers and reclaim_fleet_machine).
240
+ and agent_board_test.stripped_body(p.oid) ~ agent_board_test.re(agent_board_test.col_ref_frag('resource_key'))
238
241
  -- message-board#196 Bucket B: read RAW, one comment naming _canonical_claim_key
239
242
  -- dropped a bypass writer out of this set and the count went to 0 green.
240
243
  -- anchored to the CALL shape: a bare-substring `!~` is satisfied by any
241
244
  -- identifier containing the name, e.g. an argument `p_canonical_claim_key`.
242
- -- SCHEMA-QUALIFIED, because a bare-name anchor proves call SYNTAX, not callee
243
- -- IDENTITY: `\m_canonical_claim_key\s*\(` also matches
245
+ -- SCHEMA-QUALIFIED (`bare_ok => false`), because a bare-name anchor proves call
246
+ -- SYNTAX, not callee IDENTITY: the old `\m_canonical_claim_key\s*\(` also matched
244
247
  -- `zz_other._canonical_claim_key(...)` (measured true). Every live call
245
248
  -- site in agent_board is `agent_board.`-qualified, so requiring the
246
249
  -- qualification costs nothing today and is fail-CLOSED if someone later writes
247
250
  -- an unqualified call (it reads as NOT calling). Overload count is pinned
248
251
  -- separately, so schema + name is the identity.
249
- and agent_board_test.stripped_body(p.oid) !~* '\magent_board\._canonical_claim_key\s*\('),
252
+ and agent_board_test.stripped_body(p.oid) !~ agent_board_test.call_re('_canonical_claim_key', false)),
250
253
  0,
251
254
  'every catalog-enumerated claims writer routes its resource_key through _canonical_claim_key');
252
255