@consiliency/agent-board-schema 1.28.0 → 1.38.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 (43) hide show
  1. package/fixtures/already-applied.json +40 -0
  2. package/migrations/20260910000100_agent_board_ceiling_lock_comment_truth.sql +89 -0
  3. package/migrations/20260911000100_agent_board_ledger_revoked_board_creation.sql +330 -0
  4. package/migrations/20260912000100_agent_board_receipt_subject_gate.sql +348 -0
  5. package/migrations/20260913000100_agent_board_task_thread_lock_order.sql +292 -0
  6. package/migrations/20260914000100_agent_board_claim_subject_gate.sql +500 -0
  7. package/migrations/20260915000100_agent_board_fedsession_stored_identity_gate.sql +353 -0
  8. package/migrations/20260916000100_agent_board_federated_identity_injective_slug.sql +1646 -0
  9. package/migrations/20260917000100_agent_board_invite_no_downgrade.sql +398 -0
  10. package/migrations/20260918000100_agent_board_signed_write_enforced_repoint.sql +139 -0
  11. package/migrations/20260919000100_agent_board_signed_write_model_key.sql +107 -0
  12. package/package.json +1 -1
  13. package/schema-manifest.json +93 -3
  14. package/src/deployment-drift-script.test.mjs +424 -0
  15. package/src/deployment-drift.test.mjs +301 -0
  16. package/src/tooling.test.mjs +83 -2
  17. package/tests/agent_board_atomic_fleet_onboarding.test.sql +144 -29
  18. package/tests/agent_board_bundle_endpoint_compat.test.sql +73 -18
  19. package/tests/agent_board_claim_subject_gate.test.sql +1025 -0
  20. package/tests/agent_board_consent_substrate.test.sql +8 -1
  21. package/tests/agent_board_enroll_resumption.test.sql +7 -1
  22. package/tests/agent_board_federated_identity_injective_slug.test.sql +388 -0
  23. package/tests/agent_board_fedsession.test.sql +17 -2
  24. package/tests/agent_board_fedsession_stored_identity_gate.test.sql +606 -0
  25. package/tests/agent_board_fk_lock_edges.test.sql +422 -0
  26. package/tests/agent_board_invite_no_downgrade.test.sql +689 -0
  27. package/tests/agent_board_ledger_authority.test.sql +142 -71
  28. package/tests/agent_board_receipt_subject_gate.test.sql +624 -0
  29. package/tests/agent_board_signed_write_enforced.test.sql +366 -0
  30. package/tests/agent_board_signer_lifecycle.test.sql +23 -6
  31. package/tests/agent_board_source_strip_helper.test.sql +256 -1
  32. package/tests/agent_board_task_thread_lock_order.test.sql +337 -0
  33. package/tests/agent_board_write_enforcement_class.test.sql +1016 -0
  34. package/tests/catalog/p_metadata_parameters.json +12 -0
  35. package/tests/fixtures/isolation-substrate.sql +1 -1
  36. package/tests/fixtures/test-prereqs.sql +442 -11
  37. package/tests/isolation/expected/ledger_authority_revoke.out +219 -1
  38. package/tests/isolation/expected/task_thread_lock_order.out +203 -0
  39. package/tests/isolation/specs/enroll_identity_board_owner_lock_order.spec +5 -4
  40. package/tests/isolation/specs/fedsession_provider_absent_owner_lock.spec +4 -4
  41. package/tests/isolation/specs/ledger_authority_revoke.spec +115 -0
  42. package/tests/isolation/specs/task_thread_lock_order.spec +197 -0
  43. package/tests/seeds/20260916000100_agent_board_federated_identity_injective_slug.sql.pre.sql +196 -0
@@ -28,15 +28,28 @@
28
28
  -- is the failure mode throws_like alone cannot see.
29
29
  -- * The class assertions in section 9 are catalog-derived (pg_proc + stripped_body), not a
30
30
  -- list written here, so a NEW authority write that forgets the gate fails and NAMES ITSELF
31
- -- (CLAUDE.md rule 1). The one exemption is asserted POSITIVELY — by three properties it
31
+ -- (CLAUDE.md rule 1). Each remaining exemption is asserted POSITIVELY — by properties it
32
32
  -- actually has — never by the absence of a match.
33
+ --
34
+ -- #280 (20260911000100) AMENDS THIS FILE'S CLAIM. `create_board` was #276's ONE
35
+ -- authenticated-callable exemption, and its residual was asserted here as BEHAVIOUR — a
36
+ -- ledger-revoked agent COULD create a board and be seeded owner — precisely so that closing #280
37
+ -- would be a deliberate test change rather than a silent behaviour change. The owner ratified
38
+ -- "revoked means revoked", so those assertions are FLIPPED in section 11 and the EXEMPTION B
39
+ -- block that licensed them is DELETED rather than left standing: its three properties are still
40
+ -- true, but they exist to license an exemption that no longer exists, and a live assertion that
41
+ -- licenses nothing is this repo's recorded failure shape. The exemption list is now THREE and
42
+ -- the ledger-consult set is now SIX; both are derived from pg_catalog on every run, so those two
43
+ -- edits must agree with the shipped schema or the difference NAMES create_board.
33
44
 
34
45
  create extension if not exists pgtap with schema extensions;
35
46
  set search_path = extensions, public, agent_board;
36
47
 
37
48
  begin;
38
49
 
39
- select plan(88);
50
+ -- 88 at #276; #280 deletes 7 (EXEMPTION B1/B2/B3, the three RESIDUAL assertions, and a fourth
51
+ -- residual companion in section 10 that named only the slug) and adds 10 in section 11.
52
+ select plan(91);
40
53
 
41
54
  create function pg_temp.claims(p_sub text) returns void language plpgsql as $$
42
55
  begin
@@ -544,13 +557,17 @@ select is(
544
557
  where n.nspname = 'agent_board'
545
558
  and p.proname not in ('_assert_actor_not_fleet_revoked', '_actor_fleet_revoked')
546
559
  and agent_board_test.stripped_body(p.oid) ~ '\m(_assert_actor_not_fleet_revoked|_actor_fleet_revoked)\s*\('),
547
- '_redeem_unbounded_invite_for_agent,create_invite_link,grant_board_role,'
560
+ '_redeem_unbounded_invite_for_agent,create_board,create_invite_link,grant_board_role,'
548
561
  || 'set_board_authority_ceiling,set_board_role_gate',
549
- 'the functions that consult the fleet revocation ledger are exactly these five');
562
+ 'the functions that consult the fleet revocation ledger are exactly these six (#280 added '
563
+ 'create_board)');
550
564
 
551
- -- The gate proper: the two sets must differ by EXACTLY the four asserted exemptions, and any
565
+ -- The gate proper: the two sets must differ by EXACTLY the three asserted exemptions, and any
552
566
  -- other member of the difference is NAMED in the failure. Each excluded name is licensed by a
553
- -- positive disposition below, never by the absence of a match.
567
+ -- positive disposition below, never by the absence of a match. create_board was the fourth
568
+ -- until #280 gated it; removing it from this list is what makes the migration's claim checkable
569
+ -- rather than asserted — if the gate were dropped from the function, create_board reappears in
570
+ -- the difference and this assertion prints its name.
554
571
  select is(
555
572
  (select string_agg(x.proname || ':' || x.why, '; ' order by x.proname)
556
573
  from (
@@ -562,8 +579,8 @@ select is(
562
579
  from pg_proc p join pg_namespace n on n.oid = p.pronamespace
563
580
  where n.nspname = 'agent_board'
564
581
  and p.proname not in (
565
- -- the four asserted exemptions...
566
- '_upsert_board_membership_state', 'create_board',
582
+ -- the three asserted exemptions (create_board was the fourth until #280)...
583
+ '_upsert_board_membership_state',
567
584
  '_fedsession_autojoin_default_board', 'bootstrap_board_owner_memberships',
568
585
  -- ...and the two helpers, which ARE the gate rather than users of it
569
586
  '_assert_actor_not_fleet_revoked', '_actor_fleet_revoked')
@@ -665,63 +682,12 @@ select is(
665
682
  'EXEMPTION A4: EVERY caller of _upsert_board_membership_state takes EXACTLY these parameters '
666
683
  '— not one declares a role, so "no caller can hand it one" is checked rather than argued');
667
684
 
668
- -- ---------------------------------------------------------------------------
669
- -- EXEMPTION B — create_board, the ONE authenticated-callable member of the class.
670
- -- ---------------------------------------------------------------------------
671
- select is(
672
- (select string_agg(pg_get_function_identity_arguments(p.oid), ' | ' order by p.oid)
673
- from pg_proc p join pg_namespace n on n.oid = p.pronamespace
674
- where n.nspname = 'agent_board' and p.proname = 'create_board'),
675
- 'p_created_by_agent_id uuid, p_slug text, p_name text, p_description text, p_metadata jsonb, '
676
- || 'p_visibility text',
677
- 'EXEMPTION B1: create_board takes EXACTLY these parameters — no role among them, and no '
678
- 'overload carrying one, so the owner seat it writes is a literal and not a caller choice');
679
-
680
- select ok(
681
- agent_board_test.stripped_body('agent_board.create_board(uuid,text,text,text,jsonb,text)'::regprocedure, true)
682
- like '%values (v_board.id, v_board.created_by_agent_id, ''joined'', ''owner'', v_board.created_by_agent_id)%',
683
- 'EXEMPTION B2: the seat it writes is the literal owner for the board OWN created_by_agent_id '
684
- '— the resolved actor, seeding itself on a board it just inserted');
685
-
686
- select ok(
687
- agent_board_test.stripped_body('agent_board.create_board(uuid,text,text,text,jsonb,text)'::regprocedure, true)
688
- like '%board slug already in use%'
689
- and strpos(agent_board_test.stripped_body('agent_board.create_board(uuid,text,text,text,jsonb,text)'::regprocedure, true),
690
- 'update agent_board.boards as b')
691
- < strpos(agent_board_test.stripped_body('agent_board.create_board(uuid,text,text,text,jsonb,text)'::regprocedure, true),
692
- 'insert into agent_board.board_memberships'),
693
- 'EXEMPTION B3: the slug-COLLISION branch refuses a non-owner and updates the boards row '
694
- 'BEFORE the else-branch membership insert — the seat is on the fresh-board path only, so it '
695
- 'cannot elevate a membership on a pre-existing board');
696
-
697
- -- ...and the residual, asserted as BEHAVIOUR so #280 closing flips a test rather than a comment.
698
- select pg_temp.claims('27600000-0000-0000-0000-000000000001');
699
-
700
- select lives_ok(
701
- $$create temp table _cb as select * from agent_board.create_board(
702
- '27610000-0000-4000-8000-000000000001','k276-residual-board','K276 Residual Board')$$,
703
- 'RESIDUAL (#280): a ledger-revoked agent CAN still create a board and be seeded its owner');
704
-
705
- select is(
706
- (select bm.role from agent_board.board_memberships bm
707
- join agent_board.boards b on b.id = bm.board_id
708
- where b.slug = 'k276-residual-board'
709
- and bm.agent_id = '27610000-0000-4000-8000-000000000001'),
710
- 'owner',
711
- 'RESIDUAL (#280): and the owner seat really landed — this is the manufacture-new-authority '
712
- 'gap #276 does NOT close, measured rather than described');
713
-
714
- select throws_like(
715
- $$select * from agent_board.grant_board_role(
716
- (select id from agent_board.boards where slug = 'k276-residual-board'),
717
- '27610000-0000-4000-8000-000000000002','admin')$$,
718
- '%fleet_revoked:%',
719
- 'but the gate still holds on the board it just created: it cannot GRANT there either, even '
720
- 'as that board owner with no ceiling row');
721
-
722
685
  -- ---------------------------------------------------------------------------
723
686
  -- EXEMPTIONS C and D — the two remaining service_role-only seeders. (What used to be
724
- -- exemption C, _redeem_unbounded_invite_for_agent, is now GATED — see section 10.)
687
+ -- exemption C, _redeem_unbounded_invite_for_agent, is now GATED — see section 10; what used to
688
+ -- be exemption B, create_board, is now GATED too — see section 11, which is where its old
689
+ -- RESIDUAL assertions were flipped. Its EXEMPTION B1/B2/B3 properties were deleted rather than
690
+ -- kept: they are still true, but they existed to license an exemption that no longer exists.)
725
691
  -- ---------------------------------------------------------------------------
726
692
  select is(
727
693
  (select string_agg(p.proname || '=' ||
@@ -867,14 +833,14 @@ select is(
867
833
  'joined',
868
834
  'and that redemption really placed the revoked redeemer');
869
835
 
870
- -- The residual companion: the board a revoked agent CAN still create is not a way back in.
871
- select throws_like(
872
- $$select * from agent_board.create_invite_link(
873
- (select id from agent_board.boards where slug = 'k276-residual-board'),
874
- 'admin','joined',5, now() + interval '2 days')$$,
875
- '%fleet_revoked:%',
876
- 'RESIDUAL (#280) is contained: on the board it just created, a revoked agent still cannot '
877
- 'mint a link either — so the residual confers nothing on anyone else');
836
+ -- #280 DELETED a fourth residual assertion that stood here: "on the board it just created, a
837
+ -- revoked agent still cannot mint a link either". It was found by the SUITE, not by reading —
838
+ -- it names no function, only the slug `k276-residual-board`, so a grep for `create_board` missed
839
+ -- it and it went red as a null board id. Recorded because it is CLAUDE.md rule 1 in miniature:
840
+ -- the class of things to change came from a list written by hand, and the one member phrased
841
+ -- differently was the one that escaped. It is deleted rather than re-pointed: with the board it
842
+ -- referenced gone, the claim it made is exactly the G3 MINT assertion above (a ledger-revoked
843
+ -- owner cannot mint on a board it owns), driven there against board 1.
878
844
 
879
845
 
880
846
  -- The RESIDUAL BOUND rests on a lock that is already in this function: the token FOR UPDATE is
@@ -1006,5 +972,110 @@ select ok(
1006
972
  !~ '\m(effective_at|reactivated_at)\M',
1007
973
  'the gate has no supersession clause, matching the send path''s agent-subject predicate');
1008
974
 
975
+ -- ===========================================================================
976
+ -- 11. #280 — create_board is GATED. These assertions are the FLIP of the RESIDUAL pair that
977
+ -- lived in what was EXEMPTION B: this file previously asserted, as behaviour, that a
978
+ -- ledger-revoked agent COULD create a board and be seeded its owner. #276's lane wrote it
979
+ -- that way deliberately so that closing #280 would be a visible test change. This is that
980
+ -- change.
981
+ --
982
+ -- THE ALLOW SIDE COMES FIRST AND MATTERS MORE THAN THE REFUSAL. create_board is the only
983
+ -- gated site every ordinary user reaches, on the CLI's default post-enroll intent among
984
+ -- other paths, so the expensive failure here is not "a revoked agent slipped through" but
985
+ -- "nobody can create a board any more". JUDGEMENT 2 of the #276 header — a principal that
986
+ -- was never in a fleet has no ledger row and passes by construction — is therefore asserted
987
+ -- POSITIVELY, on BOTH routes (authenticated and the service_role create-on-behalf-of the
988
+ -- gateway uses), by driving a real creation and reading the owner seat back. Never by the
989
+ -- absence of a refusal.
990
+ --
991
+ -- Every refusal probe is followed by a STATE check, because a gate that raises AND writes is
992
+ -- the failure throws_like alone cannot see. Here the state check is the operation's own
993
+ -- outcome: no boards row for the slug (which is also why no membership can exist).
994
+ -- ===========================================================================
995
+
996
+ -- ALLOW, ROUTE 1: the NON-FLEET agent (agent 3 has no fleet_machine_agents row at all).
997
+ select pg_temp.claims('27600000-0000-0000-0000-000000000003');
998
+
999
+ select lives_ok(
1000
+ $$create temp table _cb280a as select * from agent_board.create_board(
1001
+ '27610000-0000-4000-8000-000000000003','k280-allow-board','K280 Allow Board')$$,
1002
+ 'ALLOW (#280): a NON-FLEET agent still creates a board — the capability this gate must not '
1003
+ 'regress, driven rather than inferred from the absence of a refusal');
1004
+
1005
+ select is(
1006
+ (select bm.role from agent_board.board_memberships bm
1007
+ join agent_board.boards b on b.id = bm.board_id
1008
+ where b.slug = 'k280-allow-board'
1009
+ and bm.agent_id = '27610000-0000-4000-8000-000000000003'),
1010
+ 'owner',
1011
+ 'and its owner seat really landed, so the allow side is the whole operation and not just the '
1012
+ 'absence of a raise');
1013
+
1014
+ -- ALLOW, ROUTE 2: service_role acting on behalf of a non-revoked principal — the gateway path.
1015
+ select pg_temp.service();
1016
+
1017
+ select lives_ok(
1018
+ $$create temp table _cb280b as select * from agent_board.create_board(
1019
+ '27610000-0000-4000-8000-000000000003','k280-allow-board-svc','K280 Allow Board Svc')$$,
1020
+ 'ALLOW (#280): and service_role create-on-behalf-of a non-revoked agent still works');
1021
+
1022
+ select is(
1023
+ (select bm.role from agent_board.board_memberships bm
1024
+ join agent_board.boards b on b.id = bm.board_id
1025
+ where b.slug = 'k280-allow-board-svc'
1026
+ and bm.agent_id = '27610000-0000-4000-8000-000000000003'),
1027
+ 'owner',
1028
+ 'and that owner seat landed too — the gate binds the PRINCIPAL, so a clean principal is '
1029
+ 'unaffected on either route');
1030
+
1031
+ -- REFUSED, ROUTE 1: the ledger-revoked agent 1 (revoked in section 4; agents.status still
1032
+ -- 'active', which is this migration's precondition and what makes the refusal the LEDGER's).
1033
+ select pg_temp.claims('27600000-0000-0000-0000-000000000001');
1034
+
1035
+ select throws_like(
1036
+ $$select * from agent_board.create_board(
1037
+ '27610000-0000-4000-8000-000000000001','k280-refused-board','K280 Refused Board')$$,
1038
+ '%fleet_revoked:%create_board%',
1039
+ '#280: a ledger-revoked agent can no longer create a board (this is the flipped RESIDUAL)');
1040
+
1041
+ select is(
1042
+ (select count(*)::int from agent_board.boards where slug = 'k280-refused-board'),
1043
+ 0,
1044
+ 'and the refused creation left NO board behind — the raise rolled back the boards insert, so '
1045
+ 'the owner seat and the owner_seed event cannot exist either');
1046
+
1047
+ -- REFUSED, the SLUG-COLLISION branch. Agent 1 holds an ACTIVE owner membership on board 1, so
1048
+ -- it passes the owner check and would previously have renamed/reopened it. This is the scope
1049
+ -- consequence the migration header records explicitly: the gate covers BOTH branches.
1050
+ select throws_like(
1051
+ $$select * from agent_board.create_board(
1052
+ '27610000-0000-4000-8000-000000000001','k276-board-1','K276 Board 1 RENAMED')$$,
1053
+ '%fleet_revoked:%create_board%',
1054
+ '#280: and the slug-COLLISION branch is refused too — a revoked owner cannot reopen or '
1055
+ 'rename an existing board it still owns');
1056
+
1057
+ select is(
1058
+ (select b.name from agent_board.boards b where b.slug = 'k276-board-1'),
1059
+ 'K276 Board 1',
1060
+ 'and that board is UNCHANGED: the raise rolled the collision-branch UPDATE back, which is a '
1061
+ 'state check the message alone could not make');
1062
+
1063
+ -- REFUSED, ROUTE 2: service_role supplying a REVOKED p_created_by_agent_id. The check binds the
1064
+ -- principal the write is attributed to, not the connection role — a gateway acting for a
1065
+ -- revoked agent is the case #276 was filed about, restated here for the one function #276 left.
1066
+ select pg_temp.service();
1067
+
1068
+ select throws_like(
1069
+ $$select * from agent_board.create_board(
1070
+ '27610000-0000-4000-8000-000000000001','k280-refused-svc','K280 Refused Svc')$$,
1071
+ '%fleet_revoked:%create_board%',
1072
+ '#280: service_role create-on-behalf-of a REVOKED agent is refused — the gate binds the '
1073
+ 'principal, never the connection role');
1074
+
1075
+ select is(
1076
+ (select count(*)::int from agent_board.boards where slug = 'k280-refused-svc'),
1077
+ 0,
1078
+ 'and that one left no board behind either');
1079
+
1009
1080
  select finish();
1010
1081
  rollback;