@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.
- package/fixtures/already-applied.json +611 -603
- package/migrations/20261003000100_agent_board_room_roster.sql +115 -0
- package/migrations/20261004000100_agent_board_callback_consumer_intent.sql +52 -0
- package/package.json +1 -1
- package/schema-manifest.json +21 -3
- package/src/tooling.test.mjs +5 -2
- package/tests/agent_board_room_roster.test.sql +466 -0
- package/tests/agent_board_rowid.test.sql +5 -4
- package/tests/catalog/function_execute_privileges.json +9 -0
- package/tests/catalog/p_metadata_parameters.json +4 -0
|
@@ -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.
|
|
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",
|
package/schema-manifest.json
CHANGED
|
@@ -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.
|
|
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":
|
|
50
|
-
"aggregateSha256": "
|
|
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": {
|
package/src/tooling.test.mjs
CHANGED
|
@@ -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 =
|
|
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(
|
|
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
|
}
|