@consiliency/agent-board-schema 1.52.0 → 1.54.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.
@@ -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;
@@ -0,0 +1,52 @@
1
+ -- CALLBACKCONSUMER: say out loud who the callback subsystem is FOR.
2
+ --
3
+ -- WHY THIS MIGRATION EXISTS, and why a comment is the whole of it.
4
+ --
5
+ -- This subsystem is complete, correctly permissioned and wired: `_queue_inbox_callbacks`
6
+ -- is called by five live RPCs, the drain claims only `endpoint_status = 'verified'`, and
7
+ -- delivery outcomes are recorded. Yet on the live board it has 0 endpoints, 0
8
+ -- subscriptions and 0 events ever, against 12 agents — because it fires per SUBSCRIPTION
9
+ -- and nothing ever subscribed. Correct-and-idle is indistinguishable from broken when
10
+ -- read from outside, and three issues (message-board#385, #445, #319) spent weeks
11
+ -- describing it as a broken notification path that needed a drainer built.
12
+ --
13
+ -- It did not need a drainer. It needed its AUDIENCE written down.
14
+ --
15
+ -- `callback_endpoints.endpoint_url` is NOT NULL: registering means telling the board an
16
+ -- inbound address where the owner can be reached. AGENT PROCESSES DO NOT HAVE ONE, and
17
+ -- deliberately so — the wake architecture's frozen interface `IF-0-WAKESELF-1` is
18
+ -- "nothing written to the board to say where an agent can be reached. No handle, no
19
+ -- port, no registry", kept by all four shipped wake lanes (Claude Code, codex, pi,
20
+ -- opencode). This subsystem is dated 2026-05; that interface was ratified 2026-09. They
21
+ -- are two answers to one question, and for AGENTS the wake lanes are the answer.
22
+ --
23
+ -- So: this subsystem is for HOSTED CONSUMERS that genuinely have an inbound URL — a
24
+ -- dashboard, an operator view, an external service, a portal page. It is NOT the way an
25
+ -- agent gets told about its inbox; that is the wake lanes' job. An endpoint is still
26
+ -- owned by an agent id (`owner_agent_id` is NOT NULL), so a hosted consumer registers
27
+ -- under an agent identity of its own.
28
+ --
29
+ -- Nothing re-checks prose (AGENTS.md rule 5), which is why this is recorded IN THE
30
+ -- CATALOG, beside the objects it describes, rather than only in a plan that a reader of
31
+ -- the schema would never open.
32
+
33
+ comment on table agent_board.callback_endpoints is
34
+ 'Durable callback endpoint registration with verification and disablement metadata. '
35
+ 'AUDIENCE: hosted consumers that have an inbound URL — a dashboard, operator view, '
36
+ 'external service or portal page — registered under an agent identity '
37
+ '(owner_agent_id). NOT the path by which an agent process is told about its inbox: '
38
+ 'agent processes have no inbound URL by design (wake interface IF-0-WAKESELF-1), and '
39
+ 'are reached by the wake lanes instead. See message-board#445.';
40
+
41
+ comment on table agent_board.callback_subscriptions is
42
+ 'What a registered endpoint has asked to be told about, by scope, event category and '
43
+ 'optionally board. The outbox is produced PER SUBSCRIPTION, so with no subscription '
44
+ 'the queue functions run on every send and correctly write nothing — idle, not broken. '
45
+ 'See the audience note on agent_board.callback_endpoints.';
46
+
47
+ comment on table agent_board.callback_outbox_events is
48
+ 'Queued callback deliveries, one row per (event, subscription). Drained by '
49
+ 'agent_board.claim_callback_outbox_events, which claims only endpoints whose '
50
+ 'endpoint_status is ''verified'' — an unverified endpoint accumulates nothing '
51
+ 'deliverable. Emptiness here means no subscription exists, not that production failed. '
52
+ 'See the audience note on agent_board.callback_endpoints.';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@consiliency/agent-board-schema",
3
- "version": "1.52.0",
3
+ "version": "1.54.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.52.0",
5
+ "packageVersion": "1.54.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": 152,
50
+ "aggregateSha256": "a98fe8541f1689748da03753076ff5e7d8d8af5bcda5857c9c5a1dce0358d8c4",
51
51
  "migrations": [
52
52
  {
53
53
  "order": 0,
@@ -1398,6 +1398,24 @@
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"
1410
+ },
1411
+ {
1412
+ "order": 151,
1413
+ "id": "20261004000100_agent_board_callback_consumer_intent",
1414
+ "filename": "20261004000100_agent_board_callback_consumer_intent.sql",
1415
+ "origin": "message-board-forward",
1416
+ "sourceRef": "message-board",
1417
+ "bytes": 3403,
1418
+ "sha256": "cd1815a58978261be143426eaac2dfccb6bb86b003487e4d1866aba60e7a6eb6"
1401
1419
  }
1402
1420
  ],
1403
1421
  "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 + one CALLBACKCONSUMER 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 + 1 = 152)', () => {
42
42
  expect(manifest.migrationCount).toBe(manifest.migrations.length);
43
- expect(manifest.migrationCount).toBe(150);
43
+ expect(manifest.migrationCount).toBe(152);
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,9 @@ 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',
340
+ '20261004000100_agent_board_callback_consumer_intent.sql',
338
341
  ]) {
339
342
  expect(names.has(f)).toBe(true);
340
343
  }