@consiliency/agent-board-schema 1.59.0 → 1.63.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 (73) hide show
  1. package/README.md +80 -0
  2. package/fixtures/already-applied.json +16 -0
  3. package/migrations/20261010000100_agent_board_own_post_not_delivered.sql +637 -0
  4. package/migrations/20261011000100_agent_board_session_identities.sql +1964 -0
  5. package/migrations/20261012000100_agent_board_sidowner.sql +209 -0
  6. package/migrations/20261014000100_agent_board_grant_target_role.sql +218 -0
  7. package/package.json +1 -1
  8. package/schema-manifest.json +39 -3
  9. package/src/tooling.test.mjs +9 -2
  10. package/tests/agent_board_addressing.test.sql +3 -1
  11. package/tests/agent_board_atomic_fleet_onboarding.test.sql +13 -4
  12. package/tests/agent_board_consent_emission.test.sql +30 -11
  13. package/tests/agent_board_consent_gating.test.sql +16 -2
  14. package/tests/agent_board_delegate_role_class.test.sql +1 -1
  15. package/tests/agent_board_delegation_cap_ceiling.test.sql +26 -15
  16. package/tests/agent_board_delegation_enforcement.test.sql +1 -1
  17. package/tests/agent_board_enroll_resumption.test.sql +22 -4
  18. package/tests/agent_board_enroll_token_single_use.test.sql +18 -3
  19. package/tests/agent_board_federated_lock_order_class.test.sql +2 -2
  20. package/tests/agent_board_fk_lock_edges.test.sql +74 -14
  21. package/tests/agent_board_grant_target_role.test.sql +1118 -0
  22. package/tests/agent_board_inbox_rpcs.test.sql +5 -2
  23. package/tests/agent_board_invite_no_downgrade.test.sql +10 -8
  24. package/tests/agent_board_ledger_authority.test.sql +70 -12
  25. package/tests/agent_board_membership_current_row_class.test.sql +9 -1
  26. package/tests/agent_board_mint_fleet_agent_session.test.sql +2 -2
  27. package/tests/agent_board_mint_harden.test.sql +38 -27
  28. package/tests/agent_board_own_post_not_delivered.test.sql +490 -0
  29. package/tests/agent_board_rolereset.test.sql +8 -2
  30. package/tests/agent_board_roomdeleg_lane_b.test.sql +6 -6
  31. package/tests/agent_board_rowid.test.sql +3 -1
  32. package/tests/agent_board_session_identity_approval_revocation.test.sql +334 -0
  33. package/tests/agent_board_session_identity_backfill.test.sql +82 -0
  34. package/tests/agent_board_session_identity_backfill_refusal.test.sql +14 -0
  35. package/tests/agent_board_session_identity_behavior_classes.test.sql +176 -0
  36. package/tests/agent_board_session_identity_class.test.sql +118 -0
  37. package/tests/agent_board_session_identity_fingerprint.test.sql +271 -0
  38. package/tests/agent_board_session_identity_labels_limits.test.sql +205 -0
  39. package/tests/agent_board_session_identity_lease.test.sql +235 -0
  40. package/tests/agent_board_session_identity_lifetime.test.sql +230 -0
  41. package/tests/agent_board_session_identity_limits.test.sql +208 -0
  42. package/tests/agent_board_session_identity_lineage.test.sql +64 -0
  43. package/tests/agent_board_session_identity_link.test.sql +201 -0
  44. package/tests/agent_board_session_identity_mint.test.sql +233 -0
  45. package/tests/agent_board_session_identity_panel_r1.test.sql +194 -0
  46. package/tests/agent_board_session_identity_panel_r2.test.sql +174 -0
  47. package/tests/agent_board_session_identity_protected_actions.test.sql +156 -0
  48. package/tests/agent_board_session_identity_revoke_closure.test.sql +131 -0
  49. package/tests/agent_board_sidowner_leases.test.sql +107 -0
  50. package/tests/agent_board_sidowner_owner_required.test.sql +97 -0
  51. package/tests/agent_board_sidowner_read_class.test.sql +171 -0
  52. package/tests/agent_board_sidowner_reads.test.sql +136 -0
  53. package/tests/agent_board_signer_lifecycle.test.sql +26 -4
  54. package/tests/agent_board_signing_challenge_pending_cap.test.sql +1 -1
  55. package/tests/agent_board_v2_invariants.test.sql +4 -4
  56. package/tests/agent_board_write_enforcement_class.test.sql +74 -15
  57. package/tests/catalog/function_execute_privileges.json +218 -2
  58. package/tests/catalog/p_metadata_parameters.json +98 -2
  59. package/tests/isolation/expected/grant_target_role_race.out +348 -0
  60. package/tests/isolation/expected/session_identity_limits.out +165 -0
  61. package/tests/isolation/expected/session_identity_links.out +235 -0
  62. package/tests/isolation/expected/session_identity_reclaim.out +253 -0
  63. package/tests/isolation/expected/session_identity_renew_revoke.out +115 -0
  64. package/tests/isolation/expected/session_identity_renew_revoke_key_gate.out +138 -0
  65. package/tests/isolation/expected/session_identity_tombstone_onboarding.out +247 -0
  66. package/tests/isolation/specs/grant_target_role_race.spec +207 -0
  67. package/tests/isolation/specs/session_identity_limits.spec +81 -0
  68. package/tests/isolation/specs/session_identity_links.spec +80 -0
  69. package/tests/isolation/specs/session_identity_reclaim.spec +125 -0
  70. package/tests/isolation/specs/session_identity_renew_revoke.spec +84 -0
  71. package/tests/isolation/specs/session_identity_renew_revoke_key_gate.spec +94 -0
  72. package/tests/isolation/specs/session_identity_tombstone_onboarding.spec +108 -0
  73. package/tests/seeds/20261011000100_agent_board_session_identities.sql.pre.sql +47 -0
@@ -332,7 +332,7 @@ set search_path = extensions, public, agent_board;
332
332
 
333
333
  begin;
334
334
 
335
- select plan(25);
335
+ select plan(30);
336
336
 
337
337
  -- ===========================================================================================
338
338
  -- THE CLASS, derived from pg_catalog. Nothing below is a list of names.
@@ -430,10 +430,12 @@ select distinct p.oid,
430
430
  -- 60 -> 62 with HOSTED's delegate_instances and delegate_allowed_operations (20261008000100).
431
431
  -- Both are written only by service_role-only definers or the migration itself, so neither joins
432
432
  -- the `authenticated` writer population below.
433
+ -- SIDSCHEMA adds identities and aliases. Human-owner readmission writes them and
434
+ -- delegation_grants; these remain in the derived class, not an exemption list.
433
435
  select is(
434
436
  (select count(*)::text from _wec_target where relkind = 'r'),
435
- '62',
436
- '#325 canary: agent_board has 62 base tables — the universe the write scan runs over, pinned so an empty violating set below cannot mean an empty universe');
437
+ '64',
438
+ '#325 canary: agent_board has 64 base tables — the universe the write scan runs over, pinned so an empty violating set below cannot mean an empty universe');
437
439
 
438
440
  -- WEC-2. The relations the class writes, as an EXACT SET. A new one names itself.
439
441
  select is(
@@ -459,6 +461,7 @@ select is(
459
461
  'callback_subscriptions',
460
462
  'claims',
461
463
  'conflicts',
464
+ 'delegation_grants',
462
465
  'delegation_policies',
463
466
  'delegations',
464
467
  'delivery_recipients',
@@ -476,11 +479,13 @@ select is(
476
479
  'presence',
477
480
  'read_receipts',
478
481
  'send_authz_telemetry',
482
+ 'session_id_aliases',
483
+ 'session_identities',
479
484
  'task_events',
480
485
  'tasks',
481
486
  'threads'
482
487
  ]::text[],
483
- '#325: these 37 agent_board relations are written by an authenticated-executable function, directly or through a callee — five of them (board_invite_redemptions, board_membership_events, callback_outbox_events, delivery_recipients, send_authz_telemetry) were invisible while membership was own-body');
488
+ '#325: these 40 agent_board relations are written by an authenticated-executable function, directly or through a callee — SIDSCHEMA readmission adds grants, identities and aliases without excluding their writers');
484
489
 
485
490
  -- WEC-3. The class itself, as an EXACT SET of SIGNATURES, and it is signatures rather than
486
491
  -- `count(*) = 59` for two reasons. The routine response to a red count is to bump the count,
@@ -495,6 +500,8 @@ select is( -- pgtap-allow-raise-checked
495
500
  -- HOSTED (20261008000100): the owner places its hosted delegate through the sink.
496
501
  'agent_board.add_delegate_to_board(uuid,uuid)',
497
502
  'agent_board.append_task_event(uuid,uuid,text,jsonb,text,uuid,timestamp with time zone)',
503
+ -- Approval's measured fingerprint is service-only until SIDSERVER binds it.
504
+ -- Its ACL and real human/ancestor actors are pinned in the fingerprint suite.
498
505
  'agent_board.block_board_inviter(uuid,uuid,uuid,jsonb)',
499
506
  'agent_board.create_board(uuid,text,text,text,jsonb,text)',
500
507
  'agent_board.create_fleet_onboarding_invite(uuid,uuid,text,text,text,text,text,text,text,integer,timestamp with time zone,uuid,jsonb)',
@@ -509,6 +516,7 @@ select is( -- pgtap-allow-raise-checked
509
516
  'agent_board.disable_callback_endpoint(uuid,uuid,text,jsonb)',
510
517
  'agent_board.grant_board_role(uuid,uuid,text,uuid)',
511
518
  'agent_board.heartbeat(uuid,uuid,text,text,interval,jsonb)',
519
+ 'agent_board.heartbeat_session_identity(uuid)',
512
520
  'agent_board.invite_board_member(uuid,uuid,uuid,jsonb)',
513
521
  'agent_board.issue_signing_key_challenge(uuid,text,interval)',
514
522
  'agent_board.join_board(uuid,uuid,text,uuid,jsonb)',
@@ -520,13 +528,17 @@ select is( -- pgtap-allow-raise-checked
520
528
  'agent_board.provision_agent(text,text,jsonb,uuid)',
521
529
  'agent_board.raise_conflict(uuid,uuid,text,text,text,uuid,jsonb)',
522
530
  'agent_board.raise_escalation(uuid,uuid,text,text,text,uuid,uuid,jsonb)',
531
+ 'agent_board.readmit_session_identity(uuid,text,text,jsonb,jsonb,interval)',
523
532
  'agent_board.record_signing_key_proof_result(uuid,uuid,text,boolean,text,jsonb)',
524
533
  'agent_board.redeem_invite_link(text,jsonb)',
525
534
  'agent_board.register_agent(text,text,text,text,jsonb)',
526
535
  'agent_board.register_callback_endpoint(uuid,text,jsonb)',
527
536
  'agent_board.register_fleet_machine(uuid,uuid,text,text,text,jsonb)',
528
537
  'agent_board.release_claim(uuid)',
538
+ 'agent_board.rename_agent(uuid,text)',
529
539
  'agent_board.renew_claim(uuid,timestamp with time zone)',
540
+ -- Fresh renewal attestation is service-role-only; lifetime's trust assertion
541
+ -- positively pins the ACL, rather than excluding a reachable writer here.
530
542
  'agent_board.request_join_board(uuid,uuid,jsonb)',
531
543
  'agent_board.resolve_board_request(uuid,uuid,text,uuid,jsonb)',
532
544
  'agent_board.resolve_conflict(uuid,uuid,jsonb)',
@@ -535,6 +547,7 @@ select is( -- pgtap-allow-raise-checked
535
547
  'agent_board.revoke_fleet_machine_agent_binding(uuid,uuid,text,jsonb)',
536
548
  'agent_board.revoke_fleet_subject(text,uuid,uuid,text,jsonb)',
537
549
  'agent_board.revoke_invite_link(text,jsonb)',
550
+ 'agent_board.revoke_session_identity(uuid)',
538
551
  'agent_board.rotate_callback_signing_key(uuid,uuid,jsonb)',
539
552
  'agent_board.self_enroll_human(text)',
540
553
  'agent_board.send_direct_message(uuid,uuid,jsonb,text,text,text,text,text,text,jsonb)',
@@ -543,6 +556,7 @@ select is( -- pgtap-allow-raise-checked
543
556
  'agent_board.send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb)',
544
557
  'agent_board.set_board_authority_ceiling(uuid,text,uuid)',
545
558
  'agent_board.set_board_claim_policy(uuid,text)',
559
+ 'agent_board.set_board_member_label(uuid,uuid,text)',
546
560
  'agent_board.set_board_role_gate(uuid,boolean)',
547
561
  'agent_board.set_fleet_receive_policy(uuid,text,text,boolean,uuid,uuid,jsonb)',
548
562
  'agent_board.subscribe_callback_endpoint(uuid,uuid,text,text,uuid,jsonb)',
@@ -553,7 +567,7 @@ select is( -- pgtap-allow-raise-checked
553
567
  'agent_board.upsert_delegation_policy(uuid,text,jsonb,integer,interval,boolean)',
554
568
  'agent_board.verify_callback_endpoint(uuid,uuid,jsonb)'
555
569
  ]::text[],
556
- '#325: the effect-derived class is exactly these 61 authenticated-executable writer OVERLOADS — per-OID, and TRANSITIVE, so a delegating wrapper whose own body holds no DML is a member rather than a blind spot');
570
+ '#325: the effect-derived class is exactly these 66 authenticated-executable writer OVERLOADS — per-OID, and TRANSITIVE, so a delegating wrapper whose own body holds no DML is a member rather than a blind spot');
557
571
 
558
572
  -- WEC-4. THE DELEGATION-CHAIN LIVENESS PIN, and the mitigation for the fail-open inversion the
559
573
  -- header describes. Everything above depends on `fn_call_closure()` actually resolving edges; a
@@ -614,8 +628,8 @@ select is(
614
628
  (select coalesce(array_agg(p.sig order by p.sig), array[]::text[])
615
629
  from _wec_pop p
616
630
  where agent_board_test.stripped_body(p.oid, true) ~ agent_board_test.call_re('auth.uid', false)),
617
- array['agent_board.self_enroll_human(text)']::text[],
618
- '#325: exactly ONE writer derives from raw auth.uid() — self_enroll_human CREATES the identity a session would resolve to, so it cannot use the resolver; scored on the own body because the closure form matches all 59 and would make Half A vacuous');
631
+ array['agent_board.rename_agent(uuid,text)', 'agent_board.self_enroll_human(text)']::text[],
632
+ '#325: exactly these writers derive from raw auth.uid() — self enrollment creates the human identity; rename requires its active human owner, positively exercised by WEC-26..29');
619
633
 
620
634
  -- ===========================================================================================
621
635
  -- HALF B — every writer REFUSES SOMEBODY. Labelled honestly: it pins that a refusal exists on the
@@ -673,7 +687,6 @@ select is( -- pgtap-allow-raise-checked
673
687
  'agent_board.issue_signing_key_challenge(uuid,text,interval) -> _resolved_actor_agent_id',
674
688
  'agent_board.provision_agent(text,text,jsonb,uuid) -> _require_platform_admin_user_id',
675
689
  'agent_board.register_callback_endpoint(uuid,text,jsonb) -> _resolved_actor_agent_id',
676
- 'agent_board.revoke_agent(uuid,text) -> _require_platform_admin_user_id',
677
690
  'agent_board.rotate_callback_signing_key(uuid,uuid,jsonb) -> _resolved_actor_agent_id',
678
691
  'agent_board.subscribe_callback_endpoint(uuid,uuid,text,text,uuid,jsonb) -> _resolved_actor_agent_id',
679
692
  'agent_board.unblock_board_inviter(uuid,uuid,uuid) -> _resolved_actor_agent_id',
@@ -682,7 +695,7 @@ select is( -- pgtap-allow-raise-checked
682
695
  'agent_board.upsert_delegation_policy(uuid,text,jsonb,integer,interval,boolean) -> _resolved_actor_agent_id',
683
696
  'agent_board.verify_callback_endpoint(uuid,uuid,jsonb) -> _resolved_actor_agent_id'
684
697
  ]::text[],
685
- '#325 HALF B, part 1: exactly these 16 writers reach NO shared authz predicate and raise no 42501 of their own, and each NAMES the raising resolver it relies on — 14 the session resolver, 2 the platform-admin one; a new ungated writer joins this set, and a member that loses or switches its resolver reds here');
698
+ '#325 HALF B, part 1: exactly these 15 writers reach NO shared authz predicate and raise no 42501 of their own, and each NAMES the raising resolver it relies on — 14 the session resolver, 1 the platform-admin one; revoke_agent now refuses an unresolved actor in its own body; a new ungated writer joins this set, and a member that loses or switches its resolver reds here');
686
699
 
687
700
  -- WEC-9. The union, and the only assertion here that is a claim about the WHOLE class rather than
688
701
  -- about a category. Not spelled over the CLOSURE for 42501: that form is satisfied by 59 of 59
@@ -875,7 +888,7 @@ select is(
875
888
  cross join unnest(array['INSERT', 'UPDATE', 'DELETE', 'TRUNCATE']) as pv
876
889
  where has_table_privilege(r, t.reloid, pv)),
877
890
  array[]::text[],
878
- '#325: NEITHER authenticated NOR anon holds a direct write privilege on ANY of the 37 class relations — so for those two roles every write goes through some agent_board function; WEC-3 is what says which');
891
+ '#325: NEITHER authenticated NOR anon holds a direct write privilege on ANY of the 40 class relations — so for those two roles every write goes through some agent_board function; WEC-3 is what says which');
879
892
 
880
893
  -- WEC-18. The arm that DOES NOT hold, pinned as an exact set rather than omitted. service_role
881
894
  -- writes thirteen of these relations directly, with no function and no body — the structural
@@ -900,6 +913,7 @@ select is(
900
913
  'INSERT:board_invite_redemptions',
901
914
  'INSERT:board_invite_tokens',
902
915
  'INSERT:board_membership_events',
916
+ 'INSERT:delegation_grants',
903
917
  'INSERT:fleet_harness_registrations',
904
918
  'INSERT:fleet_machine_agents',
905
919
  'INSERT:fleet_machines',
@@ -911,6 +925,7 @@ select is(
911
925
  'UPDATE:board_authority_ceilings',
912
926
  'UPDATE:board_invite_redemptions',
913
927
  'UPDATE:board_invite_tokens',
928
+ 'UPDATE:delegation_grants',
914
929
  'UPDATE:fleet_harness_registrations',
915
930
  'UPDATE:fleet_machine_agents',
916
931
  'UPDATE:fleet_machines',
@@ -919,7 +934,7 @@ select is(
919
934
  'UPDATE:fleet_revocation_events',
920
935
  'UPDATE:fleet_trust_domains'
921
936
  ]::text[],
922
- '#325: service_role holds direct DML on THIRTEEN of the 37 class relations — the plan asserts this bound holds and it DOES NOT; pinned as an exact set so the hole is bounded and a widening names itself');
937
+ '#325: service_role holds direct DML on FOURTEEN of the 40 class relations — grants newly enter this class through readmission, not through a new grant; the exact privilege set bounds the hole');
923
938
 
924
939
  -- WEC-19. The rule-1-correct arm. WEC-17 and WEC-18 are hand lists of three role names between
925
940
  -- them, so an INSERT granted to a brand-new role self-names in NEITHER. This reads the ACL itself.
@@ -952,7 +967,7 @@ select ok(
952
967
  where c.relowner <> 'postgres'::regrole) = 0
953
968
  and has_table_privilege('postgres', 'agent_board.threads', 'INSERT')
954
969
  and has_table_privilege('postgres', 'agent_board.threads', 'DELETE'),
955
- '#325: postgres OWNS every one of the 37 class relations (pg_class.relowner, not inferred from a privilege) and holds writes on them — so the WEC-19 exclusion is a fact about ownership, not a carve-out');
970
+ '#325: postgres OWNS every one of the 40 class relations (pg_class.relowner, not inferred from a privilege) and holds writes on them — so the WEC-19 exclusion is a fact about ownership, not a carve-out');
956
971
 
957
972
  -- WEC-21. The over-revoke guard. A file that only ever asserts absence would call a schema with
958
973
  -- every privilege revoked a success.
@@ -1006,12 +1021,13 @@ select is(
1006
1021
  'agent_board.fleet_trust_domains',
1007
1022
  'agent_board.inbox_items',
1008
1023
  'agent_board.messages',
1024
+ 'agent_board.session_identities',
1009
1025
  'agent_board.task_events',
1010
1026
  'agent_board.tasks',
1011
1027
  'agent_board.threads',
1012
1028
  'auth.users'
1013
1029
  ]::text[],
1014
- '#325: deleting a row in one of these 19 parents CASCADES or SET NULLs into a class relation — a write path with no privilege check and no function body, so no assertion in this file gates it; one parent is auth.users, i.e. outside agent_board entirely');
1030
+ '#325: deleting a row in one of these 20 parents CASCADES or SET NULLs into a class relation — a write path with no privilege check and no function body; one parent is auth.users, i.e. outside agent_board entirely');
1015
1031
 
1016
1032
  -- WEC-23. The edge count. This IS a bare count and the hazard is stated rather than left for a
1017
1033
  -- reviewer: the routine response to a red count is to bump it, which absorbs a new cascade edge
@@ -1023,8 +1039,8 @@ select is(
1023
1039
  from _wec_class_tables t
1024
1040
  join pg_constraint con on con.conrelid = t.reloid
1025
1041
  where con.contype = 'f' and con.confdeltype in ('c', 'n', 'd')),
1026
- '88',
1027
- '#325: 88 writing referential actions (74 CASCADE + 14 SET NULL) reach the 37 class relations — bump this number only after confirming the new edge is intended; see WEC-22, which names parents independently');
1042
+ '92',
1043
+ '#325: 92 writing referential actions (78 CASCADE + 14 SET NULL) reach the 40 class relations — the four SIDSCHEMA class additions are positively pinned in WEC-30');
1028
1044
 
1029
1045
  -- ===========================================================================================
1030
1046
  -- WEC-24/25 — THE CROSS-CHECK (Consiliency/message-board#416 / #417 / #424, plan change 9).
@@ -1138,6 +1154,49 @@ select is(
1138
1154
  'mark_retention_metadata',
1139
1155
  'WEC-25 (change 9): the plpgsql bodies WEC-24 skips for dynamic SQL are exactly these — mark_retention_metadata writes board_memberships through format(), which neither write_re nor the parser can see; a second dynamic writer fails here by name');
1140
1156
 
1157
+ -- WEC-26..29: raw auth.uid() for rename is a human-ownership check, not agent
1158
+ -- self-authentication or a service-role assertion. Exercise the real API role.
1159
+ create temp table _wec_owner(user_id uuid, original_handle text) on commit drop;
1160
+ do $$ declare u uuid; begin
1161
+ u := public.ensure_github_user('wec-owner-' || gen_random_uuid(), 'wec-owner@example.test',
1162
+ 'wec-owner-' || gen_random_uuid(), 'WEC owner');
1163
+ insert into agent_board.agents(agent_type, handle, display_name, status, auth_user_id, owner_user_id)
1164
+ values ('human', 'wec-human-' || gen_random_uuid(), 'WEC owner', 'active', u, u);
1165
+ update agent_board.agents set owner_user_id = u
1166
+ where id = (select agent_id from _wec_agent where label = 'a');
1167
+ insert into _wec_owner select u, handle from agent_board.agents
1168
+ where id = (select agent_id from _wec_agent where label = 'a');
1169
+ end $$;
1170
+ grant select on _wec_owner, _wec_agent to authenticated;
1171
+ grant usage on schema extensions to authenticated;
1172
+ set local role authenticated;
1173
+ select set_config('request.jwt.claim.role', 'authenticated', true);
1174
+ select set_config('request.jwt.claim.sub', (select user_id from _wec_owner)::text, true);
1175
+ select lives_ok($$select agent_board.rename_agent((select agent_id from _wec_agent where label = 'a'), 'WEC renamed')$$,
1176
+ 'WEC-26: active authenticated human owner can rename its agent');
1177
+ reset role;
1178
+ select is((select display_name || '/' || (handle = original_handle)::text
1179
+ from agent_board.agents cross join _wec_owner where id = (select agent_id from _wec_agent where label = 'a')),
1180
+ 'WEC renamed/true', 'WEC-27: raw-human rename writes only the intended display name and preserves handle');
1181
+ -- The nearest invalid owner: ownership matches the agent's auth user, but that
1182
+ -- principal is not a human. A stranger would hide loss of the human predicate.
1183
+ update agent_board.agents set owner_user_id = (select user_id from _wec_agent where label = 'a')
1184
+ where id = (select agent_id from _wec_agent where label = 'a');
1185
+ set local role authenticated;
1186
+ select set_config('request.jwt.claim.sub', (select user_id from _wec_agent where label = 'a')::text, true);
1187
+ select throws_ok($$select agent_board.rename_agent((select agent_id from _wec_agent where label = 'a'), 'WEC agent renamed')$$,
1188
+ '42501', 'agent rename requires the human owner', 'WEC-28: the agent credential is not its human owner');
1189
+ reset role;
1190
+ select is((select display_name from agent_board.agents where id = (select agent_id from _wec_agent where label = 'a')),
1191
+ 'WEC renamed', 'WEC-29: refused agent rename leaves the human-owned state unchanged');
1192
+ select is((select array_agg(c.conname::text || ':' || c.confdeltype::text order by c.conname::text)
1193
+ from _wec_class_tables t join pg_constraint c on c.conrelid = t.reloid
1194
+ where t.relname in ('delegation_grants', 'session_identities', 'session_id_aliases')
1195
+ and c.contype = 'f' and c.confdeltype in ('c', 'n', 'd')),
1196
+ array['delegation_grants_child_agent_id_fkey:c', 'delegation_grants_root_agent_id_fkey:c',
1197
+ 'session_id_aliases_identity_agent_id_fkey:c', 'session_identities_agent_id_fkey:c']::text[],
1198
+ 'WEC-30: all four newly class-visible referential writes are the intended principal/grant/identity/alias cascades');
1199
+
1141
1200
  select * from finish();
1142
1201
 
1143
1202
  rollback;
@@ -559,6 +559,15 @@
559
559
  "authenticated": false
560
560
  }
561
561
  },
562
+ {
563
+ "name": "_mint_fleet_agent_session_legacy",
564
+ "signature": "_mint_fleet_agent_session_legacy(p_challenge_id uuid, p_nonce text, p_signing_key_id uuid, p_agent_id uuid, p_max_ttl_seconds integer)",
565
+ "executable_by": {
566
+ "anon": false,
567
+ "service_role": false,
568
+ "authenticated": false
569
+ }
570
+ },
562
571
  {
563
572
  "name": "_next_callback_attempt_number",
564
573
  "signature": "_next_callback_attempt_number(p_outbox_event_id uuid)",
@@ -775,6 +784,15 @@
775
784
  "authenticated": false
776
785
  }
777
786
  },
787
+ {
788
+ "name": "_revoke_session_identity_family",
789
+ "signature": "_revoke_session_identity_family(p_agent_id uuid, p_actor uuid, p_reason text)",
790
+ "executable_by": {
791
+ "anon": false,
792
+ "service_role": true,
793
+ "authenticated": false
794
+ }
795
+ },
778
796
  {
779
797
  "name": "_role_rank",
780
798
  "signature": "_role_rank(p_role text)",
@@ -793,6 +811,105 @@
793
811
  "authenticated": false
794
812
  }
795
813
  },
814
+ {
815
+ "name": "_session_identity_agent_lineage",
816
+ "signature": "_session_identity_agent_lineage()",
817
+ "executable_by": {
818
+ "anon": false,
819
+ "service_role": true,
820
+ "authenticated": false
821
+ }
822
+ },
823
+ {
824
+ "name": "_session_identity_ancestor_actor",
825
+ "signature": "_session_identity_ancestor_actor(p_identity_agent_id uuid)",
826
+ "executable_by": {
827
+ "anon": false,
828
+ "service_role": true,
829
+ "authenticated": false
830
+ }
831
+ },
832
+ {
833
+ "name": "_session_identity_ancestor_ceiling",
834
+ "signature": "_session_identity_ancestor_ceiling(p_agent_id uuid)",
835
+ "executable_by": {
836
+ "anon": false,
837
+ "service_role": false,
838
+ "authenticated": false
839
+ }
840
+ },
841
+ {
842
+ "name": "_session_identity_check_limits",
843
+ "signature": "_session_identity_check_limits(p_root uuid, p_parent uuid, p_reactivation uuid)",
844
+ "executable_by": {
845
+ "anon": false,
846
+ "service_role": false,
847
+ "authenticated": false
848
+ }
849
+ },
850
+ {
851
+ "name": "_session_identity_has_human_owner",
852
+ "signature": "_session_identity_has_human_owner(p_root_agent_id uuid)",
853
+ "executable_by": {
854
+ "anon": false,
855
+ "service_role": true,
856
+ "authenticated": false
857
+ }
858
+ },
859
+ {
860
+ "name": "_session_identity_kind_immutable",
861
+ "signature": "_session_identity_kind_immutable()",
862
+ "executable_by": {
863
+ "anon": false,
864
+ "service_role": true,
865
+ "authenticated": false
866
+ }
867
+ },
868
+ {
869
+ "name": "_session_identity_lease_live",
870
+ "signature": "_session_identity_lease_live(p_identity uuid)",
871
+ "executable_by": {
872
+ "anon": false,
873
+ "service_role": true,
874
+ "authenticated": false
875
+ }
876
+ },
877
+ {
878
+ "name": "_session_identity_lineage",
879
+ "signature": "_session_identity_lineage()",
880
+ "executable_by": {
881
+ "anon": false,
882
+ "service_role": true,
883
+ "authenticated": false
884
+ }
885
+ },
886
+ {
887
+ "name": "_session_identity_owner",
888
+ "signature": "_session_identity_owner(p_identity_agent_id uuid)",
889
+ "executable_by": {
890
+ "anon": false,
891
+ "service_role": true,
892
+ "authenticated": false
893
+ }
894
+ },
895
+ {
896
+ "name": "_session_identity_read_allowed",
897
+ "signature": "_session_identity_read_allowed(p_identity_agent_id uuid)",
898
+ "executable_by": {
899
+ "anon": false,
900
+ "service_role": true,
901
+ "authenticated": false
902
+ }
903
+ },
904
+ {
905
+ "name": "_session_identity_revocation_actor",
906
+ "signature": "_session_identity_revocation_actor(p_identity_agent_id uuid)",
907
+ "executable_by": {
908
+ "anon": false,
909
+ "service_role": false,
910
+ "authenticated": false
911
+ }
912
+ },
796
913
  {
797
914
  "name": "_signed_write_actor_id",
798
915
  "signature": "_signed_write_actor_id(p_payload jsonb, p_gateway_decision jsonb, p_actor_field text)",
@@ -883,6 +1000,15 @@
883
1000
  "authenticated": true
884
1001
  }
885
1002
  },
1003
+ {
1004
+ "name": "approve_identity_state",
1005
+ "signature": "approve_identity_state(p_identity_agent_id uuid, p_risky_digest text, p_recorded_fingerprint jsonb)",
1006
+ "executable_by": {
1007
+ "anon": false,
1008
+ "service_role": true,
1009
+ "authenticated": false
1010
+ }
1011
+ },
886
1012
  {
887
1013
  "name": "block_board_inviter",
888
1014
  "signature": "block_board_inviter(p_board_id uuid, p_inviter_agent_id uuid, p_target_agent_id uuid, p_metadata jsonb)",
@@ -1108,6 +1234,15 @@
1108
1234
  "authenticated": true
1109
1235
  }
1110
1236
  },
1237
+ {
1238
+ "name": "get_session_identity_state",
1239
+ "signature": "get_session_identity_state(p_identity_agent_id uuid)",
1240
+ "executable_by": {
1241
+ "anon": false,
1242
+ "service_role": true,
1243
+ "authenticated": true
1244
+ }
1245
+ },
1111
1246
  {
1112
1247
  "name": "grant_board_role",
1113
1248
  "signature": "grant_board_role(p_board_id uuid, p_agent_id uuid, p_grantee_role text, p_grantor_agent_id uuid)",
@@ -1126,6 +1261,15 @@
1126
1261
  "authenticated": true
1127
1262
  }
1128
1263
  },
1264
+ {
1265
+ "name": "heartbeat_session_identity",
1266
+ "signature": "heartbeat_session_identity(p_identity_agent_id uuid)",
1267
+ "executable_by": {
1268
+ "anon": false,
1269
+ "service_role": false,
1270
+ "authenticated": true
1271
+ }
1272
+ },
1129
1273
  {
1130
1274
  "name": "invite_board_for_display",
1131
1275
  "signature": "invite_board_for_display(p_code_sha256 text)",
@@ -1171,6 +1315,15 @@
1171
1315
  "authenticated": true
1172
1316
  }
1173
1317
  },
1318
+ {
1319
+ "name": "link_session_alias",
1320
+ "signature": "link_session_alias(p_identity_agent_id uuid, p_identity_lease_epoch text, p_session_id text)",
1321
+ "executable_by": {
1322
+ "anon": false,
1323
+ "service_role": true,
1324
+ "authenticated": false
1325
+ }
1326
+ },
1174
1327
  {
1175
1328
  "name": "list_board_members",
1176
1329
  "signature": "list_board_members(p_board_id uuid)",
@@ -1198,6 +1351,15 @@
1198
1351
  "authenticated": true
1199
1352
  }
1200
1353
  },
1354
+ {
1355
+ "name": "list_session_children",
1356
+ "signature": "list_session_children(p_parent_agent_id uuid)",
1357
+ "executable_by": {
1358
+ "anon": false,
1359
+ "service_role": true,
1360
+ "authenticated": true
1361
+ }
1362
+ },
1201
1363
  {
1202
1364
  "name": "mark_read",
1203
1365
  "signature": "mark_read(p_thread_id uuid, p_agent_id uuid, p_last_read_message_sequence bigint)",
@@ -1218,7 +1380,7 @@
1218
1380
  },
1219
1381
  {
1220
1382
  "name": "mint_delegated_child",
1221
- "signature": "mint_delegated_child(p_root_agent_id uuid, p_child_slug text, p_requested_scopes jsonb, p_child_public_key text, p_ttl interval, p_self_service boolean)",
1383
+ "signature": "mint_delegated_child(p_root_agent_id uuid, p_child_slug text, p_requested_scopes jsonb, p_child_public_key text, p_ttl interval, p_self_service boolean, p_session_harness text, p_session_id text, p_minter_lease_epoch text, p_requested_rooms jsonb, p_fingerprint jsonb, p_forked_from uuid)",
1222
1384
  "executable_by": {
1223
1385
  "anon": false,
1224
1386
  "service_role": true,
@@ -1227,7 +1389,7 @@
1227
1389
  },
1228
1390
  {
1229
1391
  "name": "mint_fleet_agent_session",
1230
- "signature": "mint_fleet_agent_session(p_challenge_id uuid, p_nonce text, p_signing_key_id uuid, p_agent_id uuid, p_max_ttl_seconds integer)",
1392
+ "signature": "mint_fleet_agent_session(p_challenge_id uuid, p_nonce text, p_signing_key_id uuid, p_agent_id uuid, p_max_ttl_seconds integer, p_holder_secret text, p_session_id text)",
1231
1393
  "executable_by": {
1232
1394
  "anon": false,
1233
1395
  "service_role": true,
@@ -1315,6 +1477,15 @@
1315
1477
  "authenticated": false
1316
1478
  }
1317
1479
  },
1480
+ {
1481
+ "name": "readmit_session_identity",
1482
+ "signature": "readmit_session_identity(p_identity_agent_id uuid, p_child_slug text, p_child_public_key text, p_requested_scopes jsonb, p_requested_rooms jsonb, p_ttl interval)",
1483
+ "executable_by": {
1484
+ "anon": false,
1485
+ "service_role": false,
1486
+ "authenticated": true
1487
+ }
1488
+ },
1318
1489
  {
1319
1490
  "name": "reap_expired_enroll_rate_events",
1320
1491
  "signature": "reap_expired_enroll_rate_events(p_older_than interval)",
@@ -1396,6 +1567,15 @@
1396
1567
  "authenticated": false
1397
1568
  }
1398
1569
  },
1570
+ {
1571
+ "name": "record_session_identity_state",
1572
+ "signature": "record_session_identity_state(p_identity_agent_id uuid, p_fingerprint jsonb)",
1573
+ "executable_by": {
1574
+ "anon": false,
1575
+ "service_role": true,
1576
+ "authenticated": false
1577
+ }
1578
+ },
1399
1579
  {
1400
1580
  "name": "record_signing_key_proof_result",
1401
1581
  "signature": "record_signing_key_proof_result(p_challenge_id uuid, p_agent_id uuid, p_key_id text, p_verified boolean, p_failure_class text, p_metadata jsonb)",
@@ -1450,6 +1630,15 @@
1450
1630
  "authenticated": true
1451
1631
  }
1452
1632
  },
1633
+ {
1634
+ "name": "rename_agent",
1635
+ "signature": "rename_agent(p_agent_id uuid, p_display_name text)",
1636
+ "executable_by": {
1637
+ "anon": false,
1638
+ "service_role": false,
1639
+ "authenticated": true
1640
+ }
1641
+ },
1453
1642
  {
1454
1643
  "name": "renew_claim",
1455
1644
  "signature": "renew_claim(p_lease_token uuid, p_lease_expires_at timestamp with time zone)",
@@ -1468,6 +1657,15 @@
1468
1657
  "authenticated": false
1469
1658
  }
1470
1659
  },
1660
+ {
1661
+ "name": "renew_session_identity",
1662
+ "signature": "renew_session_identity(p_identity_agent_id uuid, p_risky_digest text)",
1663
+ "executable_by": {
1664
+ "anon": false,
1665
+ "service_role": true,
1666
+ "authenticated": false
1667
+ }
1668
+ },
1471
1669
  {
1472
1670
  "name": "request_join_board",
1473
1671
  "signature": "request_join_board(p_board_id uuid, p_agent_id uuid, p_metadata jsonb)",
@@ -1585,6 +1783,15 @@
1585
1783
  "authenticated": true
1586
1784
  }
1587
1785
  },
1786
+ {
1787
+ "name": "revoke_session_identity",
1788
+ "signature": "revoke_session_identity(p_identity_agent_id uuid)",
1789
+ "executable_by": {
1790
+ "anon": false,
1791
+ "service_role": false,
1792
+ "authenticated": true
1793
+ }
1794
+ },
1588
1795
  {
1589
1796
  "name": "rotate_agent_token",
1590
1797
  "signature": "rotate_agent_token(p_agent_id uuid)",
@@ -1666,6 +1873,15 @@
1666
1873
  "authenticated": true
1667
1874
  }
1668
1875
  },
1876
+ {
1877
+ "name": "set_board_member_label",
1878
+ "signature": "set_board_member_label(p_board_id uuid, p_agent_id uuid, p_label text)",
1879
+ "executable_by": {
1880
+ "anon": false,
1881
+ "service_role": false,
1882
+ "authenticated": true
1883
+ }
1884
+ },
1669
1885
  {
1670
1886
  "name": "set_board_role_gate",
1671
1887
  "signature": "set_board_role_gate(p_board_id uuid, p_enforced boolean)",