@consiliency/agent-board-schema 1.3.0 → 1.5.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 (66) hide show
  1. package/bin/agent-board-schema.js +0 -0
  2. package/fixtures/already-applied.json +112 -0
  3. package/migrations/20260717000100_agent_board_board_role.sql +180 -0
  4. package/migrations/20260717000200_agent_board_thread_acl.sql +149 -0
  5. package/migrations/20260717000300_agent_board_agent_lineage.sql +54 -0
  6. package/migrations/20260717000400_agent_board_delegation_policies.sql +175 -0
  7. package/migrations/20260718000100_agent_board_send_authz_config.sql +172 -0
  8. package/migrations/20260718000200_agent_board_send_role_gate.sql +804 -0
  9. package/migrations/20260718000300_agent_board_fleet_policy_default_deny.sql +192 -0
  10. package/migrations/20260718000400_agent_board_grant_ceiling_wiring.sql +58 -0
  11. package/migrations/20260718000500_agent_board_fleet_agent_session_class.sql +24 -0
  12. package/migrations/20260718000600_agent_board_mint_fleet_agent_session.sql +172 -0
  13. package/migrations/20260718000700_agent_board_nonfleet_session_reaper.sql +35 -0
  14. package/migrations/20260718000800_agent_board_regrant_scoped_send.sql +24 -0
  15. package/migrations/20260718000900_agent_board_agents_status_revoked.sql +24 -0
  16. package/migrations/20260718001000_agent_board_delegation_enforcement.sql +296 -0
  17. package/migrations/20260718001100_agent_board_revoke_agent_subtree.sql +238 -0
  18. package/migrations/20260718001200_agent_board_resigning_refresh.sql +129 -0
  19. package/migrations/20260718001300_agent_board_identity_providers.sql +70 -0
  20. package/migrations/20260718001400_agent_board_enrollment_federation_audit.sql +50 -0
  21. package/migrations/20260718001500_agent_board_enroll_federated_identity.sql +220 -0
  22. package/migrations/20260719000100_agent_board_signing_challenge_pending_cap.sql +124 -0
  23. package/migrations/20260719000200_agent_board_signing_challenge_reaper.sql +34 -0
  24. package/migrations/20260719000300_agent_board_enroll_rate_cap.sql +72 -0
  25. package/migrations/20260719001000_agent_board_identity_providers_portal_contract.sql +38 -0
  26. package/migrations/20260719001100_agent_board_enroll_federated_identity_pop.sql +267 -0
  27. package/migrations/20260719001200_agent_board_enroll_federated_identity_raw_fingerprint.sql +296 -0
  28. package/migrations/20260719001300_agent_board_enroll_rate_events_reaper_rls.sql +62 -0
  29. package/migrations/20260723000100_agent_board_roomown_owner_seed.sql +167 -0
  30. package/migrations/20260723000200_agent_board_roomown_grant_role_live.sql +72 -0
  31. package/package.json +9 -9
  32. package/schema-manifest.json +256 -4
  33. package/src/tooling.test.mjs +46 -1
  34. package/tests/agent_board_agent_lineage.test.sql +73 -0
  35. package/tests/agent_board_agents_status_revoked.test.sql +60 -0
  36. package/tests/agent_board_auth_rpcs.test.sql +17 -16
  37. package/tests/agent_board_board_role.test.sql +129 -0
  38. package/tests/agent_board_bundle_endpoint_compat.test.sql +18 -6
  39. package/tests/agent_board_canary_signed_fleet_enforcement.test.sql +8 -8
  40. package/tests/agent_board_delegation_enforcement.test.sql +175 -0
  41. package/tests/agent_board_delegation_policies.test.sql +120 -0
  42. package/tests/agent_board_enroll_federated_identity.test.sql +217 -0
  43. package/tests/agent_board_enroll_pop_binding.test.sql +131 -0
  44. package/tests/agent_board_enroll_rate_cap.test.sql +53 -0
  45. package/tests/agent_board_enrollment_audit_content.test.sql +139 -0
  46. package/tests/agent_board_fleet_agent_session_class.test.sql +64 -0
  47. package/tests/agent_board_fleet_policy_default_deny.test.sql +149 -0
  48. package/tests/agent_board_fleet_registry.test.sql +2 -2
  49. package/tests/agent_board_identity_providers.test.sql +104 -0
  50. package/tests/agent_board_inbox_rpcs.test.sql +10 -10
  51. package/tests/agent_board_mint_fleet_agent_session.test.sql +290 -0
  52. package/tests/agent_board_nonfleet_session_reaper.test.sql +61 -0
  53. package/tests/agent_board_portal_offline_invariant.test.sql +134 -0
  54. package/tests/agent_board_resigning_refresh.test.sql +73 -0
  55. package/tests/agent_board_revoke_agent_subtree.test.sql +188 -0
  56. package/tests/agent_board_roomown.test.sql +233 -0
  57. package/tests/agent_board_schema_version.test.sql +2 -2
  58. package/tests/agent_board_schema_version_rpc.test.sql +1 -1
  59. package/tests/agent_board_scoped_send_regrant.test.sql +142 -0
  60. package/tests/agent_board_send_authz_config.test.sql +84 -0
  61. package/tests/agent_board_send_authz_matrix.test.sql +162 -0
  62. package/tests/agent_board_send_role_gate.test.sql +181 -0
  63. package/tests/agent_board_signing_challenge_pending_cap.test.sql +87 -0
  64. package/tests/agent_board_signing_challenge_reaper.test.sql +74 -0
  65. package/tests/agent_board_thread_acl.test.sql +129 -0
  66. package/tests/agent_board_v2_invariants.test.sql +5 -5
@@ -0,0 +1,167 @@
1
+ -- ROOMOWN Lane A: room creators actually OWN the room.
2
+ --
3
+ -- Before this migration create_board wrote ONLY the boards row and never seeded a
4
+ -- board_memberships owner row, so the creator held no membership and could not act
5
+ -- as owner (grant_board_role / set_board_authority_ceiling both resolve the grantor
6
+ -- rank from a joined/subscribed membership). This migration:
7
+ --
8
+ -- 1. create or replace create_board so that, on a GENUINE INSERT, it also seeds a
9
+ -- joined `owner` board_memberships row bound to the board's ACTUAL
10
+ -- created_by_agent_id. The pre-existing slug-collision UPDATE path is now
11
+ -- OWNER-ONLY: a non-owner colliding on an existing slug raises 42501 and mutates
12
+ -- nothing (closing a confidentiality bypass where any authenticated agent could
13
+ -- overwrite another board's name/visibility/metadata). The effective actor is
14
+ -- resolved through _resolved_actor_agent_id, so an authenticated caller can only
15
+ -- create/own as itself while service_role retains create-on-behalf-of.
16
+ --
17
+ -- 2. bootstrap_board_owner_memberships(): a service_role-only repair sweep that
18
+ -- seeds the owner membership for pre-existing owner-less boards' created_by only.
19
+ --
20
+ -- Additive; no schema_version bump (net-new owner-seed + repair, no previously-live
21
+ -- authenticated contract re-activated here).
22
+
23
+ -- 1. create_board: seed owner on insert; owner-only slug-collision update ----------
24
+ create or replace function agent_board.create_board(
25
+ p_created_by_agent_id uuid,
26
+ p_slug text,
27
+ p_name text,
28
+ p_description text default null,
29
+ p_metadata jsonb default '{}'::jsonb,
30
+ p_visibility text default 'private'
31
+ )
32
+ returns table(board_id uuid, slug text, name text, status text, visibility text, created_at timestamptz, updated_at timestamptz)
33
+ language plpgsql
34
+ security definer
35
+ set search_path = agent_board, public
36
+ as $$
37
+ #variable_conflict use_column
38
+ declare
39
+ v_actor uuid;
40
+ v_existing agent_board.boards%rowtype;
41
+ v_board agent_board.boards%rowtype;
42
+ begin
43
+ if nullif(trim(p_slug), '') is null or nullif(trim(p_name), '') is null then
44
+ raise exception 'board slug and name are required' using errcode = '22023';
45
+ end if;
46
+ if coalesce(p_visibility, 'private') not in ('private', 'invite_only', 'open', 'directory_hidden') then
47
+ raise exception 'invalid board visibility' using errcode = '22023';
48
+ end if;
49
+
50
+ -- Effective actor: service_role -> supplied arg (create-on-behalf-of);
51
+ -- authenticated -> current_agent_id() and rejects arg <> current (42501).
52
+ v_actor := agent_board._resolved_actor_agent_id(p_created_by_agent_id, 'p_created_by_agent_id');
53
+
54
+ if not exists (select 1 from agent_board.agents where id = v_actor) then
55
+ raise exception 'agent not found' using errcode = 'P0002';
56
+ end if;
57
+
58
+ select * into v_existing
59
+ from agent_board.boards b
60
+ where b.slug = trim(p_slug);
61
+
62
+ if found then
63
+ -- Slug-collision path is OWNER-ONLY, keyed on CURRENT authority: the actor must
64
+ -- hold an ACTIVE owner-role membership on the existing board — NOT merely be its
65
+ -- original `created_by_agent_id`. A demoted or departed original creator no longer
66
+ -- owns the board and must NOT be able to re-open/rename/flip-visibility it.
67
+ if not exists (
68
+ select 1 from agent_board.board_memberships m
69
+ where m.board_id = v_existing.id
70
+ and m.agent_id = v_actor
71
+ and m.role = 'owner'
72
+ and m.membership_state in ('joined', 'subscribed')
73
+ ) then
74
+ raise exception 'board slug already in use' using errcode = '42501';
75
+ end if;
76
+
77
+ update agent_board.boards as b
78
+ set name = trim(p_name),
79
+ description = nullif(trim(p_description), ''),
80
+ status = 'active',
81
+ visibility = coalesce(p_visibility, 'private'),
82
+ metadata = coalesce(b.metadata, '{}'::jsonb) || coalesce(p_metadata, '{}'::jsonb),
83
+ updated_at = now()
84
+ where b.id = v_existing.id
85
+ returning * into v_board;
86
+ -- No owner re-seed: the owner was seeded at this board's original creation, and
87
+ -- re-promoting on every create_board would resurrect a deliberately demoted owner.
88
+ else
89
+ insert into agent_board.boards as b(slug, name, description, created_by_agent_id, visibility, metadata)
90
+ values (
91
+ trim(p_slug),
92
+ trim(p_name),
93
+ nullif(trim(p_description), ''),
94
+ v_actor,
95
+ coalesce(p_visibility, 'private'),
96
+ coalesce(p_metadata, '{}'::jsonb)
97
+ )
98
+ returning * into v_board;
99
+
100
+ -- Seed the creator's owner membership, bound to the board's ACTUAL
101
+ -- created_by_agent_id (never the raw param). Idempotent against the active
102
+ -- partial-unique index so at most one active owner row exists.
103
+ insert into agent_board.board_memberships as bm(board_id, agent_id, membership_state, role, created_by_agent_id)
104
+ values (v_board.id, v_board.created_by_agent_id, 'joined', 'owner', v_board.created_by_agent_id)
105
+ on conflict (board_id, agent_id) where (membership_state in ('joined', 'subscribed'))
106
+ do update set role = 'owner', updated_at = now();
107
+ end if;
108
+
109
+ return query
110
+ select v_board.id, v_board.slug, v_board.name, v_board.status, v_board.visibility, v_board.created_at, v_board.updated_at;
111
+ end;
112
+ $$;
113
+
114
+ comment on function agent_board.create_board(uuid, text, text, text, jsonb, text) is
115
+ 'Creates a board and, on genuine insert, seeds the creator''s joined owner membership (bound to the board''s created_by_agent_id). The slug-collision update path is owner-only (non-owner raises 42501, mutates nothing). Effective actor resolved via _resolved_actor_agent_id.';
116
+
117
+ revoke all on function agent_board.create_board(uuid, text, text, text, jsonb, text) from public, anon;
118
+ grant execute on function agent_board.create_board(uuid, text, text, text, jsonb, text) to authenticated, service_role;
119
+
120
+ -- 2. bootstrap_board_owner_memberships(): service_role-only owner-seed repair -------
121
+ create or replace function agent_board.bootstrap_board_owner_memberships()
122
+ returns table(board_id uuid, owner_agent_id uuid, membership_id uuid)
123
+ language plpgsql
124
+ security definer
125
+ set search_path = agent_board, public
126
+ as $$
127
+ #variable_conflict use_column
128
+ declare
129
+ v_row record;
130
+ v_membership_id uuid;
131
+ begin
132
+ -- service_role-only: current_agent_id() is null for service_role, non-null for any
133
+ -- authenticated principal (guarded here in addition to the EXECUTE revoke below).
134
+ if not agent_board._is_service_role() then
135
+ raise exception 'bootstrap_board_owner_memberships is service_role-only' using errcode = '42501';
136
+ end if;
137
+
138
+ for v_row in
139
+ select b.id as board_id, b.created_by_agent_id as owner_agent_id
140
+ from agent_board.boards b
141
+ where not exists (
142
+ select 1
143
+ from agent_board.board_memberships bm
144
+ where bm.board_id = b.id
145
+ and bm.agent_id = b.created_by_agent_id
146
+ and bm.membership_state in ('joined', 'subscribed')
147
+ )
148
+ loop
149
+ insert into agent_board.board_memberships as bm(board_id, agent_id, membership_state, role, created_by_agent_id)
150
+ values (v_row.board_id, v_row.owner_agent_id, 'joined', 'owner', v_row.owner_agent_id)
151
+ on conflict (board_id, agent_id) where (membership_state in ('joined', 'subscribed'))
152
+ do update set role = 'owner', updated_at = now()
153
+ returning bm.id into v_membership_id;
154
+
155
+ board_id := v_row.board_id;
156
+ owner_agent_id := v_row.owner_agent_id;
157
+ membership_id := v_membership_id;
158
+ return next;
159
+ end loop;
160
+ end;
161
+ $$;
162
+
163
+ comment on function agent_board.bootstrap_board_owner_memberships() is
164
+ 'service_role-only repair sweep: seeds a joined owner membership for pre-existing owner-less boards, bound to each board''s created_by_agent_id. Takes no caller/role param.';
165
+
166
+ revoke all on function agent_board.bootstrap_board_owner_memberships() from public, anon, authenticated;
167
+ grant execute on function agent_board.bootstrap_board_owner_memberships() to service_role;
@@ -0,0 +1,72 @@
1
+ -- ROOMOWN Lane B: activate the dormant grant_board_role chokepoint for authenticated
2
+ -- board owners/admins, with a privilege-escalation-safe grantor binding.
3
+ --
4
+ -- 20260718000400 shipped grant_board_role dormant (revoked from authenticated,
5
+ -- granted service_role) and TRUSTED p_grantor_agent_id. Now that create_board seeds
6
+ -- the creator's owner membership (ROOMOWN Lane A), the owner can drive grants from
7
+ -- their own authenticated session. Activating it safely requires binding the grantor
8
+ -- to the RESOLVED actor, NOT coalesce(current_agent_id(), p_grantor_agent_id):
9
+ -- current_agent_id() returns null for ANY authenticated caller whose agent is
10
+ -- suspended/credential-rotating/unmapped (20260710000000:15 filters status='active'),
11
+ -- and such a caller still carries a valid `authenticated` token. A coalesce would then
12
+ -- honor an attacker-supplied p_grantor_agent_id (owner IDs are non-secret) and let them
13
+ -- self-escalate. _resolved_actor_agent_id fails closed: service_role -> supplied arg;
14
+ -- authenticated -> current_agent_id() and raises 42501 if null or if arg <> current.
15
+ --
16
+ -- Additive; no schema_version bump. grant_board_role was never authenticated, so no
17
+ -- old client keys on it — this is a net-new capability, not a re-activation.
18
+
19
+ create or replace function agent_board.grant_board_role(
20
+ p_board_id uuid,
21
+ p_agent_id uuid,
22
+ p_grantee_role text,
23
+ p_grantor_agent_id uuid default null
24
+ )
25
+ returns table(board_id uuid, agent_id uuid, role text, updated_at timestamptz)
26
+ language plpgsql
27
+ security definer
28
+ set search_path = agent_board, public
29
+ as $$
30
+ declare
31
+ v_grantor uuid;
32
+ v_updated agent_board.board_memberships%rowtype;
33
+ begin
34
+ if p_grantee_role not in ('owner', 'admin', 'member', 'observer') then
35
+ raise exception 'invalid grantee role' using errcode = '22023';
36
+ end if;
37
+
38
+ -- Grantor is the RESOLVED actor. service_role -> supplied arg; authenticated ->
39
+ -- current_agent_id() and raises 42501 if null (suspended/unmapped) or if the
40
+ -- supplied arg <> current_agent_id(). This closes the single-principal escalation.
41
+ v_grantor := agent_board._resolved_actor_agent_id(p_grantor_agent_id, 'p_grantor_agent_id');
42
+
43
+ if not exists (
44
+ select 1 from agent_board.board_memberships bm
45
+ where bm.board_id = p_board_id and bm.agent_id = p_agent_id
46
+ ) then
47
+ raise exception 'grantee has no membership on this board' using errcode = 'P0002';
48
+ end if;
49
+
50
+ -- Ceiling consumption: grantee-bounded against min(grantor role, board ceiling).
51
+ if not agent_board._grant_within_ceiling(v_grantor, p_board_id, p_grantee_role) then
52
+ raise exception 'grant exceeds grantor role or board authority ceiling' using errcode = '42501';
53
+ end if;
54
+
55
+ update agent_board.board_memberships bm
56
+ set role = p_grantee_role,
57
+ updated_at = now()
58
+ where bm.board_id = p_board_id
59
+ and bm.agent_id = p_agent_id
60
+ returning * into v_updated;
61
+
62
+ return query
63
+ select v_updated.board_id, v_updated.agent_id, v_updated.role, v_updated.updated_at;
64
+ end;
65
+ $$;
66
+
67
+ comment on function agent_board.grant_board_role(uuid, uuid, text, uuid) is
68
+ 'Live board-role grant chokepoint. Grantor is the resolved actor (_resolved_actor_agent_id: service_role uses the supplied arg, authenticated uses current_agent_id() and fails closed on null/mismatch). Requires grantee membership (P0002), then enforces _grant_within_ceiling(grantor, board, grantee_role) before setting board_memberships.role.';
69
+
70
+ -- Live: authenticated owners/admins may grant (within ceiling); service_role retained.
71
+ revoke all on function agent_board.grant_board_role(uuid, uuid, text, uuid) from public, anon;
72
+ grant execute on function agent_board.grant_board_role(uuid, uuid, text, 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.3.0",
3
+ "version": "1.5.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",
@@ -25,6 +25,12 @@
25
25
  "LICENSE",
26
26
  "NOTICE"
27
27
  ],
28
+ "scripts": {
29
+ "build:manifest": "node ../../scripts/build-schema-manifest.mjs",
30
+ "check:manifest": "node ../../scripts/build-schema-manifest.mjs --check",
31
+ "test": "vitest run",
32
+ "pack:smoke": "node ../../scripts/schema-packed-consumer-smoke.mjs"
33
+ },
28
34
  "publishConfig": {
29
35
  "access": "public",
30
36
  "registry": "https://registry.npmjs.org"
@@ -32,11 +38,5 @@
32
38
  "engines": {
33
39
  "node": ">=18"
34
40
  },
35
- "license": "Apache-2.0",
36
- "scripts": {
37
- "build:manifest": "node ../../scripts/build-schema-manifest.mjs",
38
- "check:manifest": "node ../../scripts/build-schema-manifest.mjs --check",
39
- "test": "vitest run",
40
- "pack:smoke": "node ../../scripts/schema-packed-consumer-smoke.mjs"
41
- }
42
- }
41
+ "license": "Apache-2.0"
42
+ }
@@ -2,8 +2,8 @@
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.3.0",
6
- "schemaVersion": 7,
5
+ "packageVersion": "1.5.0",
6
+ "schemaVersion": 8,
7
7
  "minClientVersion": "1.1.0",
8
8
  "source": {
9
9
  "portalBaseline": "94f9c269a358a943498454003e8f05f47e27a491",
@@ -46,8 +46,8 @@
46
46
  "usage": "upsert (retention/monitoring defaults; on conflict do nothing)"
47
47
  }
48
48
  ],
49
- "migrationCount": 45,
50
- "aggregateSha256": "5acd6311c09a4134f4fb005e6b6f78a2a34665dfccc99e8e67e3bcb7b7325abb",
49
+ "migrationCount": 73,
50
+ "aggregateSha256": "a43737edf382d8aee59069d6a9aec9e39fce17b31f092a3d7fcd96303b6f70e9",
51
51
  "migrations": [
52
52
  {
53
53
  "order": 0,
@@ -453,6 +453,258 @@
453
453
  "sourceRef": "message-board",
454
454
  "bytes": 2159,
455
455
  "sha256": "37a6f94d47b0948b1d5f384e52101eb48d9a15ba96a351669c87b907cc8a03e1"
456
+ },
457
+ {
458
+ "order": 45,
459
+ "id": "20260717000100_agent_board_board_role",
460
+ "filename": "20260717000100_agent_board_board_role.sql",
461
+ "origin": "message-board-forward",
462
+ "sourceRef": "message-board",
463
+ "bytes": 7246,
464
+ "sha256": "969980a7b52c6fee9329d2367006e5ce6d36b6471b53fee2247e493ac8ae512f"
465
+ },
466
+ {
467
+ "order": 46,
468
+ "id": "20260717000200_agent_board_thread_acl",
469
+ "filename": "20260717000200_agent_board_thread_acl.sql",
470
+ "origin": "message-board-forward",
471
+ "sourceRef": "message-board",
472
+ "bytes": 6326,
473
+ "sha256": "afb323d6f9189f1f37a7fee3ffceaaa17474059d46a1fdd84adb73c28bfb27ba"
474
+ },
475
+ {
476
+ "order": 47,
477
+ "id": "20260717000300_agent_board_agent_lineage",
478
+ "filename": "20260717000300_agent_board_agent_lineage.sql",
479
+ "origin": "message-board-forward",
480
+ "sourceRef": "message-board",
481
+ "bytes": 2094,
482
+ "sha256": "48aff20015f74e5f201a2985f71b33cd3e718aeb9dfb25fbd5720cb824fe85b3"
483
+ },
484
+ {
485
+ "order": 48,
486
+ "id": "20260717000400_agent_board_delegation_policies",
487
+ "filename": "20260717000400_agent_board_delegation_policies.sql",
488
+ "origin": "message-board-forward",
489
+ "sourceRef": "message-board",
490
+ "bytes": 7551,
491
+ "sha256": "ef7827af400e8a6206c17c8cc35623c280a92d25f4b678314847ff308d9d9dc7"
492
+ },
493
+ {
494
+ "order": 49,
495
+ "id": "20260718000100_agent_board_send_authz_config",
496
+ "filename": "20260718000100_agent_board_send_authz_config.sql",
497
+ "origin": "message-board-forward",
498
+ "sourceRef": "message-board",
499
+ "bytes": 7775,
500
+ "sha256": "d56909d4fbb7054e74818903d3eea84451d561c60908026dbbdb1956f500565d"
501
+ },
502
+ {
503
+ "order": 50,
504
+ "id": "20260718000200_agent_board_send_role_gate",
505
+ "filename": "20260718000200_agent_board_send_role_gate.sql",
506
+ "origin": "message-board-forward",
507
+ "sourceRef": "message-board",
508
+ "bytes": 26707,
509
+ "sha256": "805afdef2c11e52966bec1cd47a55dfd5e687af9181316d9ef71975b516de288"
510
+ },
511
+ {
512
+ "order": 51,
513
+ "id": "20260718000300_agent_board_fleet_policy_default_deny",
514
+ "filename": "20260718000300_agent_board_fleet_policy_default_deny.sql",
515
+ "origin": "message-board-forward",
516
+ "sourceRef": "message-board",
517
+ "bytes": 9060,
518
+ "sha256": "47ad42dd50fb31ede0cb6044a22cce04a31168af2dead73ed8e01dabf84fddea"
519
+ },
520
+ {
521
+ "order": 52,
522
+ "id": "20260718000400_agent_board_grant_ceiling_wiring",
523
+ "filename": "20260718000400_agent_board_grant_ceiling_wiring.sql",
524
+ "origin": "message-board-forward",
525
+ "sourceRef": "message-board",
526
+ "bytes": 2626,
527
+ "sha256": "0461f6d403fb1dd07e9423db898cd41a15a3ecfbf75402cd0085791d57a646b7"
528
+ },
529
+ {
530
+ "order": 53,
531
+ "id": "20260718000500_agent_board_fleet_agent_session_class",
532
+ "filename": "20260718000500_agent_board_fleet_agent_session_class.sql",
533
+ "origin": "message-board-forward",
534
+ "sourceRef": "message-board",
535
+ "bytes": 1341,
536
+ "sha256": "028f931aaba673fba29dd92ef3c17efcc08cb50b0dab01789986652a54fb003a"
537
+ },
538
+ {
539
+ "order": 54,
540
+ "id": "20260718000600_agent_board_mint_fleet_agent_session",
541
+ "filename": "20260718000600_agent_board_mint_fleet_agent_session.sql",
542
+ "origin": "message-board-forward",
543
+ "sourceRef": "message-board",
544
+ "bytes": 7945,
545
+ "sha256": "25972553fd1ec4f350e751495d79695a8b0ccc66c0b8b272fdf517115ba37307"
546
+ },
547
+ {
548
+ "order": 55,
549
+ "id": "20260718000700_agent_board_nonfleet_session_reaper",
550
+ "filename": "20260718000700_agent_board_nonfleet_session_reaper.sql",
551
+ "origin": "message-board-forward",
552
+ "sourceRef": "message-board",
553
+ "bytes": 1490,
554
+ "sha256": "9b6ff596ac386c49530c9405535b5255dcb09c271288957711a0d553446ef161"
555
+ },
556
+ {
557
+ "order": 56,
558
+ "id": "20260718000800_agent_board_regrant_scoped_send",
559
+ "filename": "20260718000800_agent_board_regrant_scoped_send.sql",
560
+ "origin": "message-board-forward",
561
+ "sourceRef": "message-board",
562
+ "bytes": 2072,
563
+ "sha256": "1332cf2ee23d875cf0b3d646890a0fffe0efc6d2312da4e315d5dc0abadaf74f"
564
+ },
565
+ {
566
+ "order": 57,
567
+ "id": "20260718000900_agent_board_agents_status_revoked",
568
+ "filename": "20260718000900_agent_board_agents_status_revoked.sql",
569
+ "origin": "message-board-forward",
570
+ "sourceRef": "message-board",
571
+ "bytes": 1424,
572
+ "sha256": "f0e9b95c286bb4b6873cdc882e30262788636886360c0100b745569d6553a51b"
573
+ },
574
+ {
575
+ "order": 58,
576
+ "id": "20260718001000_agent_board_delegation_enforcement",
577
+ "filename": "20260718001000_agent_board_delegation_enforcement.sql",
578
+ "origin": "message-board-forward",
579
+ "sourceRef": "message-board",
580
+ "bytes": 14005,
581
+ "sha256": "7e93bd1ff924b06922bb9312684f57ce81c3d3e921dbf52357ae5059a01dbc8b"
582
+ },
583
+ {
584
+ "order": 59,
585
+ "id": "20260718001100_agent_board_revoke_agent_subtree",
586
+ "filename": "20260718001100_agent_board_revoke_agent_subtree.sql",
587
+ "origin": "message-board-forward",
588
+ "sourceRef": "message-board",
589
+ "bytes": 9849,
590
+ "sha256": "bcf14370a3da288ebe43e337d050f5fb6fb736fad69c9f6b7a0b6e0992371b7e"
591
+ },
592
+ {
593
+ "order": 60,
594
+ "id": "20260718001200_agent_board_resigning_refresh",
595
+ "filename": "20260718001200_agent_board_resigning_refresh.sql",
596
+ "origin": "message-board-forward",
597
+ "sourceRef": "message-board",
598
+ "bytes": 4806,
599
+ "sha256": "47248250ab9bc11abdea8533d62611efba9812b913dc2dc5405612eb2fbafd12"
600
+ },
601
+ {
602
+ "order": 61,
603
+ "id": "20260718001300_agent_board_identity_providers",
604
+ "filename": "20260718001300_agent_board_identity_providers.sql",
605
+ "origin": "message-board-forward",
606
+ "sourceRef": "message-board",
607
+ "bytes": 4108,
608
+ "sha256": "ab8169709faf7f74b27a593a7c980022ee073f0371b39c0dda18f2202c0f4eae"
609
+ },
610
+ {
611
+ "order": 62,
612
+ "id": "20260718001400_agent_board_enrollment_federation_audit",
613
+ "filename": "20260718001400_agent_board_enrollment_federation_audit.sql",
614
+ "origin": "message-board-forward",
615
+ "sourceRef": "message-board",
616
+ "bytes": 2822,
617
+ "sha256": "c42e5122625639155d9ff1d2511ebdec3ac379ca0660e67c159e37efb8ea24f5"
618
+ },
619
+ {
620
+ "order": 63,
621
+ "id": "20260718001500_agent_board_enroll_federated_identity",
622
+ "filename": "20260718001500_agent_board_enroll_federated_identity.sql",
623
+ "origin": "message-board-forward",
624
+ "sourceRef": "message-board",
625
+ "bytes": 9490,
626
+ "sha256": "497d1f657d78c56744017409f5bd2ca361cd691ee1d0bab5b9a55ab3dd4b8ae7"
627
+ },
628
+ {
629
+ "order": 64,
630
+ "id": "20260719000100_agent_board_signing_challenge_pending_cap",
631
+ "filename": "20260719000100_agent_board_signing_challenge_pending_cap.sql",
632
+ "origin": "message-board-forward",
633
+ "sourceRef": "message-board",
634
+ "bytes": 4280,
635
+ "sha256": "1e48b413d31f11b9c120480c8453c8502fdde1c3a7997dcd9e22e957a2459b0b"
636
+ },
637
+ {
638
+ "order": 65,
639
+ "id": "20260719000200_agent_board_signing_challenge_reaper",
640
+ "filename": "20260719000200_agent_board_signing_challenge_reaper.sql",
641
+ "origin": "message-board-forward",
642
+ "sourceRef": "message-board",
643
+ "bytes": 1472,
644
+ "sha256": "488bbf16bf526b7df7c9b3fa5b068964703aa04ba657d6e6ceaff856a9523634"
645
+ },
646
+ {
647
+ "order": 66,
648
+ "id": "20260719000300_agent_board_enroll_rate_cap",
649
+ "filename": "20260719000300_agent_board_enroll_rate_cap.sql",
650
+ "origin": "message-board-forward",
651
+ "sourceRef": "message-board",
652
+ "bytes": 3195,
653
+ "sha256": "402c6d8bc5d4662b10d6fe0228a2bdd30026be96a5b5a237e26e1d44a639ae1d"
654
+ },
655
+ {
656
+ "order": 67,
657
+ "id": "20260719001000_agent_board_identity_providers_portal_contract",
658
+ "filename": "20260719001000_agent_board_identity_providers_portal_contract.sql",
659
+ "origin": "message-board-forward",
660
+ "sourceRef": "message-board",
661
+ "bytes": 2263,
662
+ "sha256": "e0406afebf9b36f3793e04ca757a252f038b4100697598e0a2e76050193674a0"
663
+ },
664
+ {
665
+ "order": 68,
666
+ "id": "20260719001100_agent_board_enroll_federated_identity_pop",
667
+ "filename": "20260719001100_agent_board_enroll_federated_identity_pop.sql",
668
+ "origin": "message-board-forward",
669
+ "sourceRef": "message-board",
670
+ "bytes": 12153,
671
+ "sha256": "3d4517243a7d228fb04ea41e9dc7236c870cb5830fb2f815bfa12426eee47bd7"
672
+ },
673
+ {
674
+ "order": 69,
675
+ "id": "20260719001200_agent_board_enroll_federated_identity_raw_fingerprint",
676
+ "filename": "20260719001200_agent_board_enroll_federated_identity_raw_fingerprint.sql",
677
+ "origin": "message-board-forward",
678
+ "sourceRef": "message-board",
679
+ "bytes": 13630,
680
+ "sha256": "70943047b522ec23ee3d451f704c961d130a053bc210dad8759cbc9a22444454"
681
+ },
682
+ {
683
+ "order": 70,
684
+ "id": "20260719001300_agent_board_enroll_rate_events_reaper_rls",
685
+ "filename": "20260719001300_agent_board_enroll_rate_events_reaper_rls.sql",
686
+ "origin": "message-board-forward",
687
+ "sourceRef": "message-board",
688
+ "bytes": 3112,
689
+ "sha256": "b2f58eadc13cb227991c4d978c5460878dc3cbada43c58285b55154b89a28459"
690
+ },
691
+ {
692
+ "order": 71,
693
+ "id": "20260723000100_agent_board_roomown_owner_seed",
694
+ "filename": "20260723000100_agent_board_roomown_owner_seed.sql",
695
+ "origin": "message-board-forward",
696
+ "sourceRef": "message-board",
697
+ "bytes": 7871,
698
+ "sha256": "ecfaf262832b3d499102ec3a2ee1d48e043b33282287d0e987555e868c07b804"
699
+ },
700
+ {
701
+ "order": 72,
702
+ "id": "20260723000200_agent_board_roomown_grant_role_live",
703
+ "filename": "20260723000200_agent_board_roomown_grant_role_live.sql",
704
+ "origin": "message-board-forward",
705
+ "sourceRef": "message-board",
706
+ "bytes": 3716,
707
+ "sha256": "40c9f585af16211fe9ab1d35efca6787e49b31afc7c85f208da22b9566a65575"
456
708
  }
457
709
  ],
458
710
  "excludedBridge": {
@@ -34,9 +34,54 @@ describe('manifest integrity', () => {
34
34
  expect(manifest.source.portalBaseline).toMatch(/^[0-9a-f]{40}$/);
35
35
  expect(manifest.source.laneHead).toMatch(/^[0-9a-f]{40}$/);
36
36
  expect(manifest.source.mergeBase).toMatch(/^[0-9a-f]{40}$/);
37
- expect(manifest.schemaVersion).toBe(7);
37
+ expect(manifest.schemaVersion).toBe(8);
38
38
  expect(manifest.minClientVersion).toMatch(/^\d+\.\d+\.\d+$/);
39
39
  });
40
+ it('registers the four MBAUTHZ + four MBSENDAUTHZ + four MBSESSION + four MBDELEG + three MBFED + three MBHARDEN + two MBPORTALGATE + two MBPORTAL-PR32 + two ROOMOWN forward migrations (migrationCount = prior 71 + 2 = 73)', () => {
41
+ expect(manifest.migrationCount).toBe(manifest.migrations.length);
42
+ expect(manifest.migrationCount).toBe(73);
43
+ // MBSESSION switched enforcement on (scoped-send re-grant): the client-visible
44
+ // schema version bumped to 8. MBDELEG tightens delegation and MBFED adds only
45
+ // enrollment-only additive surface (identity_providers registry, enrollment audit,
46
+ // the enroll RPC, and the /enroll EF) with no client-visible write-path shape
47
+ // change, so the version stays 8. MBHARDEN adds three additive pre-live hardening
48
+ // migrations (challenge pending-cap, challenge reaper, /enroll rate cap) with no
49
+ // client-visible write-path shape change, so the version stays 8.
50
+ expect(manifest.schemaVersion).toBe(8);
51
+ const names = new Set(manifest.migrations.map((m) => m.filename));
52
+ for (const f of [
53
+ '20260717000100_agent_board_board_role.sql',
54
+ '20260717000200_agent_board_thread_acl.sql',
55
+ '20260717000300_agent_board_agent_lineage.sql',
56
+ '20260717000400_agent_board_delegation_policies.sql',
57
+ '20260718000100_agent_board_send_authz_config.sql',
58
+ '20260718000200_agent_board_send_role_gate.sql',
59
+ '20260718000300_agent_board_fleet_policy_default_deny.sql',
60
+ '20260718000400_agent_board_grant_ceiling_wiring.sql',
61
+ '20260718000500_agent_board_fleet_agent_session_class.sql',
62
+ '20260718000600_agent_board_mint_fleet_agent_session.sql',
63
+ '20260718000700_agent_board_nonfleet_session_reaper.sql',
64
+ '20260718000800_agent_board_regrant_scoped_send.sql',
65
+ '20260718000900_agent_board_agents_status_revoked.sql',
66
+ '20260718001000_agent_board_delegation_enforcement.sql',
67
+ '20260718001100_agent_board_revoke_agent_subtree.sql',
68
+ '20260718001200_agent_board_resigning_refresh.sql',
69
+ '20260718001300_agent_board_identity_providers.sql',
70
+ '20260718001400_agent_board_enrollment_federation_audit.sql',
71
+ '20260718001500_agent_board_enroll_federated_identity.sql',
72
+ '20260719000100_agent_board_signing_challenge_pending_cap.sql',
73
+ '20260719000200_agent_board_signing_challenge_reaper.sql',
74
+ '20260719000300_agent_board_enroll_rate_cap.sql',
75
+ '20260719001200_agent_board_enroll_federated_identity_raw_fingerprint.sql',
76
+ '20260719001300_agent_board_enroll_rate_events_reaper_rls.sql',
77
+ // ROOMOWN: room ownership + self-service authority (create_board owner-seed +
78
+ // bootstrap; grant_board_role activated for authenticated). schema stays 8.
79
+ '20260723000100_agent_board_roomown_owner_seed.sql',
80
+ '20260723000200_agent_board_roomown_grant_role_live.sql',
81
+ ]) {
82
+ expect(names.has(f)).toBe(true);
83
+ }
84
+ });
40
85
  });
41
86
 
42
87
  describe('materialize (sole adopter path)', () => {