@consiliency/agent-board-schema 1.12.0 → 1.22.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 +124 -0
- package/migrations/20260731000100_agent_board_agentsdir_column_scope.sql +88 -0
- package/migrations/20260801000100_agent_board_leaveidem_terminal_noop.sql +317 -0
- package/migrations/20260802000100_agent_board_rolereset_reentry_clamp.sql +237 -0
- package/migrations/20260803000100_agent_board_rowid_total_order.sql +1201 -0
- package/migrations/20260804000100_agent_board_hygiene_actor_binding_rls.sql +229 -0
- package/migrations/20260806000100_agent_board_consent_substrate.sql +339 -0
- package/migrations/20260807000100_agent_board_consent_emission.sql +619 -0
- package/migrations/20260808000100_agent_board_rejectsem.sql +145 -0
- package/migrations/20260809000100_agent_board_consent_gating.sql +492 -0
- package/migrations/20260810000100_agent_board_consent_locking.sql +485 -0
- package/migrations/20260811000100_agent_board_retention_metadata_revoke.sql +23 -0
- package/migrations/20260812000100_agent_board_pubexec_class_revoke.sql +54 -0
- package/migrations/20260813000100_agent_board_invitepolicy_block.sql +420 -0
- package/migrations/20260814000100_agent_board_obsgate_thread_post_policy.sql +239 -0
- package/migrations/20260815000100_agent_board_obsgate2_authoring_gates.sql +287 -0
- package/migrations/20260816000100_agent_board_obsgate3_task_lifecycle.sql +118 -0
- package/migrations/20260817000100_agent_board_obsgate4_claims_lifecycle.sql +250 -0
- package/migrations/20260818000100_agent_board_fleet_machines_rls_correlation.sql +43 -0
- package/migrations/20260819000100_agent_board_resolve_noop_retire.sql +166 -0
- package/migrations/20260820000100_agent_board_roomdeleg_lane_b.sql +206 -0
- package/migrations/20260821000100_agent_board_claimgate_claimkey.sql +372 -0
- package/migrations/20260822000100_agent_board_mint_harden.sql +710 -0
- package/migrations/20260822000200_agent_board_mint_harden_fingerprint_backfill.sql +207 -0
- package/migrations/20260823000100_agent_board_fleet_machine_hijack.sql +106 -0
- package/migrations/20260824000100_agent_board_claimgate_shadow.sql +443 -0
- package/migrations/20260825000100_agent_board_claimgate_enforce.sql +660 -0
- package/migrations/20260826000100_agent_board_fleet_machine_reactivation.sql +514 -0
- package/migrations/20260828000100_agent_board_delegation_cap_ceiling.sql +474 -0
- package/migrations/20260829000100_agent_board_fleet_machine_reclaim.sql +432 -0
- package/migrations/20260830000100_agent_board_delegation_sole_writer.sql +41 -0
- package/migrations/20260831000100_agent_board_fleet_domain_reclaim.sql +389 -0
- package/package.json +1 -1
- package/schema-manifest.json +282 -3
- package/src/tooling.test.mjs +113 -2
- package/tests/agent_board_actorspoof.test.sql +349 -0
- package/tests/agent_board_agentsdir.test.sql +154 -0
- package/tests/agent_board_authbound_boundary.test.sql +9 -1
- package/tests/agent_board_claimgate_enforce.test.sql +464 -0
- package/tests/agent_board_claimgate_predicate.test.sql +360 -0
- package/tests/agent_board_claimkey.test.sql +229 -0
- package/tests/agent_board_consent_backfill.test.sql +48 -0
- package/tests/agent_board_consent_emission.test.sql +601 -0
- package/tests/agent_board_consent_gating.test.sql +489 -0
- package/tests/agent_board_consent_substrate.test.sql +720 -0
- package/tests/agent_board_delegation_cap_ceiling.test.sql +303 -0
- package/tests/agent_board_delegation_enforcement.test.sql +55 -6
- package/tests/agent_board_delegation_policies.test.sql +107 -1
- package/tests/agent_board_fedsession.test.sql +6 -4
- package/tests/agent_board_fleet_domain_reclaim.test.sql +419 -0
- package/tests/agent_board_fleet_machine_hijack.test.sql +190 -0
- package/tests/agent_board_fleet_machine_reactivation.test.sql +426 -0
- package/tests/agent_board_fleet_machine_reclaim.test.sql +509 -0
- package/tests/agent_board_fleet_machines_rls.test.sql +79 -0
- package/tests/agent_board_fleet_policy.test.sql +2 -2
- package/tests/agent_board_grants_hygiene.test.sql +408 -0
- package/tests/agent_board_identity_providers.test.sql +9 -0
- package/tests/agent_board_inbox_reads.test.sql +29 -12
- package/tests/agent_board_inbox_rpcs.test.sql +44 -34
- package/tests/agent_board_invitepolicy.test.sql +205 -0
- package/tests/agent_board_leaveidem.test.sql +194 -0
- package/tests/agent_board_mint_fleet_agent_session.test.sql +10 -10
- package/tests/agent_board_mint_harden.test.sql +303 -0
- package/tests/agent_board_mint_harden_backfill.test.sql +95 -0
- package/tests/agent_board_obsgate.test.sql +178 -0
- package/tests/agent_board_obsgate2.test.sql +153 -0
- package/tests/agent_board_obsgate3.test.sql +170 -0
- package/tests/agent_board_obsgate4.test.sql +196 -0
- package/tests/agent_board_portal_offline_invariant.test.sql +13 -2
- package/tests/agent_board_pubexec_class.test.sql +53 -0
- package/tests/agent_board_rejectsem.test.sql +271 -0
- package/tests/agent_board_resigning_refresh.test.sql +1 -1
- package/tests/agent_board_resolve_noop_retire.test.sql +71 -0
- package/tests/agent_board_retention_metadata_acl.test.sql +53 -0
- package/tests/agent_board_revoke_agent_subtree.test.sql +3 -3
- package/tests/agent_board_rolereset.test.sql +351 -0
- package/tests/agent_board_roomdeleg.test.sql +5 -3
- package/tests/agent_board_roomdeleg_lane_b.test.sql +144 -0
- package/tests/agent_board_roomlink.test.sql +25 -9
- package/tests/agent_board_rowid.test.sql +1274 -0
- package/tests/agent_board_signature_rls_reads.test.sql +3 -3
- package/tests/agent_board_signed_write_gateway.test.sql +3 -3
- package/tests/agent_board_signing_challenge_pending_cap.test.sql +2 -2
- package/tests/agent_board_signing_challenge_reaper.test.sql +2 -2
- package/tests/agent_board_signing_proof_rpcs.test.sql +4 -4
- package/tests/agent_board_signing_store.test.sql +6 -6
- package/tests/fixtures/isolation-substrate.sql +93 -0
- package/tests/fixtures/test-prereqs.sql +33 -0
- package/tests/isolation/expected/claimgate_ownerless_quota.out +36 -0
- package/tests/isolation/expected/claimkey_renew_race.out +38 -0
- package/tests/isolation/expected/consent_approval_lock.out +43 -0
- package/tests/isolation/expected/consent_emit_actual_state.out +37 -0
- package/tests/isolation/expected/domain_reclaim_revoke_race.out +40 -0
- package/tests/isolation/expected/reactivate_revoke_race.out +80 -0
- package/tests/isolation/expected/reclaim_revoke_race.out +40 -0
- package/tests/isolation/specs/claimgate_ownerless_quota.spec +77 -0
- package/tests/isolation/specs/claimkey_renew_race.spec +67 -0
- package/tests/isolation/specs/consent_approval_lock.spec +98 -0
- package/tests/isolation/specs/consent_emit_actual_state.spec +79 -0
- package/tests/isolation/specs/domain_reclaim_revoke_race.spec +88 -0
- package/tests/isolation/specs/reactivate_revoke_race.spec +115 -0
- package/tests/isolation/specs/reclaim_revoke_race.spec +94 -0
- package/tests/seeds/20260806000100_agent_board_consent_substrate.sql.pre.sql +19 -0
- package/tests/seeds/20260822000200_agent_board_mint_harden_fingerprint_backfill.sql.pre.sql +39 -0
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
-- MBHYGIENE pgTAP: schema-version coverage and callback-policy liveness.
|
|
2
|
+
--
|
|
3
|
+
-- SECURITY DEFINER RPC controls are driven by JWT claims only. Direct-table RLS
|
|
4
|
+
-- probes additionally SET LOCAL ROLE authenticated because PostgreSQL RLS binds
|
|
5
|
+
-- to the database role, not to JWT claims.
|
|
6
|
+
create extension if not exists pgtap with schema extensions;
|
|
7
|
+
set search_path = extensions, public, agent_board;
|
|
8
|
+
|
|
9
|
+
begin;
|
|
10
|
+
|
|
11
|
+
select plan(20);
|
|
12
|
+
|
|
13
|
+
create temp table _grants_hygiene_fixture (
|
|
14
|
+
owner_user uuid,
|
|
15
|
+
owner_agent uuid,
|
|
16
|
+
subscriber_user uuid,
|
|
17
|
+
subscriber_agent uuid,
|
|
18
|
+
unrelated_user uuid,
|
|
19
|
+
unrelated_agent uuid,
|
|
20
|
+
endpoint_id uuid,
|
|
21
|
+
subscription_id uuid,
|
|
22
|
+
outbox_id uuid,
|
|
23
|
+
attempt_id uuid
|
|
24
|
+
) on commit drop;
|
|
25
|
+
grant select on table _grants_hygiene_fixture to authenticated, service_role;
|
|
26
|
+
|
|
27
|
+
do $$
|
|
28
|
+
declare
|
|
29
|
+
v_suffix text := replace(substr(gen_random_uuid()::text, 1, 8), '-', '');
|
|
30
|
+
v_owner_user uuid;
|
|
31
|
+
v_owner_agent uuid := gen_random_uuid();
|
|
32
|
+
v_subscriber_user uuid;
|
|
33
|
+
v_subscriber_agent uuid := gen_random_uuid();
|
|
34
|
+
v_unrelated_user uuid;
|
|
35
|
+
v_unrelated_agent uuid := gen_random_uuid();
|
|
36
|
+
v_thread_id uuid := gen_random_uuid();
|
|
37
|
+
v_message_id uuid := gen_random_uuid();
|
|
38
|
+
v_endpoint_id uuid := gen_random_uuid();
|
|
39
|
+
v_subscription_id uuid := gen_random_uuid();
|
|
40
|
+
v_outbox_id uuid := gen_random_uuid();
|
|
41
|
+
v_attempt_id uuid := gen_random_uuid();
|
|
42
|
+
begin
|
|
43
|
+
v_owner_user := public.ensure_github_user(
|
|
44
|
+
'hygiene-owner-' || v_suffix,
|
|
45
|
+
'hygiene-owner-' || v_suffix || '@example.test',
|
|
46
|
+
'hygiene-owner-' || v_suffix,
|
|
47
|
+
'HYGIENE Owner'
|
|
48
|
+
);
|
|
49
|
+
v_subscriber_user := public.ensure_github_user(
|
|
50
|
+
'hygiene-subscriber-' || v_suffix,
|
|
51
|
+
'hygiene-subscriber-' || v_suffix || '@example.test',
|
|
52
|
+
'hygiene-subscriber-' || v_suffix,
|
|
53
|
+
'HYGIENE Subscriber'
|
|
54
|
+
);
|
|
55
|
+
v_unrelated_user := public.ensure_github_user(
|
|
56
|
+
'hygiene-unrelated-' || v_suffix,
|
|
57
|
+
'hygiene-unrelated-' || v_suffix || '@example.test',
|
|
58
|
+
'hygiene-unrelated-' || v_suffix,
|
|
59
|
+
'HYGIENE Unrelated'
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
insert into agent_board.agents(
|
|
63
|
+
id, agent_type, kind, handle, display_name, status, auth_user_id, owner_user_id
|
|
64
|
+
)
|
|
65
|
+
values
|
|
66
|
+
(
|
|
67
|
+
v_owner_agent,
|
|
68
|
+
'agent',
|
|
69
|
+
'agent',
|
|
70
|
+
'hygiene-owner-' || v_suffix,
|
|
71
|
+
'HYGIENE Owner',
|
|
72
|
+
'active',
|
|
73
|
+
v_owner_user,
|
|
74
|
+
v_owner_user
|
|
75
|
+
),
|
|
76
|
+
(
|
|
77
|
+
v_subscriber_agent,
|
|
78
|
+
'agent',
|
|
79
|
+
'agent',
|
|
80
|
+
'hygiene-subscriber-' || v_suffix,
|
|
81
|
+
'HYGIENE Subscriber',
|
|
82
|
+
'active',
|
|
83
|
+
v_subscriber_user,
|
|
84
|
+
v_subscriber_user
|
|
85
|
+
),
|
|
86
|
+
(
|
|
87
|
+
v_unrelated_agent,
|
|
88
|
+
'agent',
|
|
89
|
+
'agent',
|
|
90
|
+
'hygiene-unrelated-' || v_suffix,
|
|
91
|
+
'HYGIENE Unrelated',
|
|
92
|
+
'active',
|
|
93
|
+
v_unrelated_user,
|
|
94
|
+
v_unrelated_user
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
insert into agent_board.threads(
|
|
98
|
+
id, thread_type, created_by_agent_id, subject
|
|
99
|
+
)
|
|
100
|
+
values (
|
|
101
|
+
v_thread_id, 'coordination', v_owner_agent, 'HYGIENE callback source'
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
insert into agent_board.messages(
|
|
105
|
+
id, thread_id, author_agent_id, message_sequence, message_type, body
|
|
106
|
+
)
|
|
107
|
+
values (
|
|
108
|
+
v_message_id, v_thread_id, v_owner_agent, 1, 'comment', 'HYGIENE callback source'
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
-- Load-bearing dead-arm arrangement: the subscriber is NOT the endpoint owner,
|
|
112
|
+
-- and the outbox recipient is also NOT the subscriber. The subscriber can reach
|
|
113
|
+
-- these rows only through callback_subscriptions.agent_id.
|
|
114
|
+
insert into agent_board.callback_endpoints(
|
|
115
|
+
id,
|
|
116
|
+
owner_agent_id,
|
|
117
|
+
endpoint_url,
|
|
118
|
+
endpoint_status,
|
|
119
|
+
verification_requested_at,
|
|
120
|
+
verified_at
|
|
121
|
+
)
|
|
122
|
+
values (
|
|
123
|
+
v_endpoint_id,
|
|
124
|
+
v_owner_agent,
|
|
125
|
+
'https://example.test/hygiene-' || v_suffix,
|
|
126
|
+
'verified',
|
|
127
|
+
timestamptz '2020-01-01',
|
|
128
|
+
timestamptz '2020-01-01'
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
insert into agent_board.callback_subscriptions(
|
|
132
|
+
id,
|
|
133
|
+
endpoint_id,
|
|
134
|
+
agent_id,
|
|
135
|
+
subscription_scope,
|
|
136
|
+
event_category,
|
|
137
|
+
subscription_status
|
|
138
|
+
)
|
|
139
|
+
values (
|
|
140
|
+
v_subscription_id,
|
|
141
|
+
v_endpoint_id,
|
|
142
|
+
v_subscriber_agent,
|
|
143
|
+
'own_inbox',
|
|
144
|
+
'inbox.item.delivered',
|
|
145
|
+
'active'
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
insert into agent_board.callback_outbox_events(
|
|
149
|
+
id,
|
|
150
|
+
endpoint_id,
|
|
151
|
+
subscription_id,
|
|
152
|
+
recipient_agent_id,
|
|
153
|
+
event_category,
|
|
154
|
+
delivery_mode,
|
|
155
|
+
source_kind,
|
|
156
|
+
source_message_id,
|
|
157
|
+
idempotency_key,
|
|
158
|
+
fetch_descriptor
|
|
159
|
+
)
|
|
160
|
+
values (
|
|
161
|
+
v_outbox_id,
|
|
162
|
+
v_endpoint_id,
|
|
163
|
+
v_subscription_id,
|
|
164
|
+
v_owner_agent,
|
|
165
|
+
'inbox.item.delivered',
|
|
166
|
+
'direct',
|
|
167
|
+
'message',
|
|
168
|
+
v_message_id,
|
|
169
|
+
'hygiene-outbox-' || v_suffix,
|
|
170
|
+
jsonb_build_object(
|
|
171
|
+
'surface', 'thread',
|
|
172
|
+
'targetId', v_thread_id::text,
|
|
173
|
+
'view', 'agent_board.v_thread_feed',
|
|
174
|
+
'filters', jsonb_build_object(),
|
|
175
|
+
'reason', 'MBHYGIENE callback policy liveness'
|
|
176
|
+
)
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
insert into agent_board.callback_delivery_attempts(
|
|
180
|
+
id,
|
|
181
|
+
outbox_event_id,
|
|
182
|
+
endpoint_id,
|
|
183
|
+
attempt_number,
|
|
184
|
+
attempt_outcome,
|
|
185
|
+
started_at,
|
|
186
|
+
finished_at
|
|
187
|
+
)
|
|
188
|
+
values (
|
|
189
|
+
v_attempt_id,
|
|
190
|
+
v_outbox_id,
|
|
191
|
+
v_endpoint_id,
|
|
192
|
+
1,
|
|
193
|
+
'delivered',
|
|
194
|
+
timestamptz '2020-01-01',
|
|
195
|
+
timestamptz '2020-01-01'
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
insert into _grants_hygiene_fixture(
|
|
199
|
+
owner_user,
|
|
200
|
+
owner_agent,
|
|
201
|
+
subscriber_user,
|
|
202
|
+
subscriber_agent,
|
|
203
|
+
unrelated_user,
|
|
204
|
+
unrelated_agent,
|
|
205
|
+
endpoint_id,
|
|
206
|
+
subscription_id,
|
|
207
|
+
outbox_id,
|
|
208
|
+
attempt_id
|
|
209
|
+
)
|
|
210
|
+
values (
|
|
211
|
+
v_owner_user,
|
|
212
|
+
v_owner_agent,
|
|
213
|
+
v_subscriber_user,
|
|
214
|
+
v_subscriber_agent,
|
|
215
|
+
v_unrelated_user,
|
|
216
|
+
v_unrelated_agent,
|
|
217
|
+
v_endpoint_id,
|
|
218
|
+
v_subscription_id,
|
|
219
|
+
v_outbox_id,
|
|
220
|
+
v_attempt_id
|
|
221
|
+
);
|
|
222
|
+
end
|
|
223
|
+
$$;
|
|
224
|
+
|
|
225
|
+
-- 1. Initial service/operator control.
|
|
226
|
+
select set_config('request.jwt.claim.role', '', true);
|
|
227
|
+
select set_config('request.jwt.claim.sub', '', true);
|
|
228
|
+
select is(
|
|
229
|
+
agent_board._is_service_role(),
|
|
230
|
+
true,
|
|
231
|
+
'control: grants-hygiene fixture block really runs as service_role');
|
|
232
|
+
|
|
233
|
+
-- 2-7. schema_version is a service-role island with defense-in-depth RLS.
|
|
234
|
+
select is(
|
|
235
|
+
(
|
|
236
|
+
select c.relrowsecurity
|
|
237
|
+
from pg_class c
|
|
238
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
239
|
+
where n.nspname = 'agent_board'
|
|
240
|
+
and c.relname = 'schema_version'
|
|
241
|
+
and c.relkind = 'r'
|
|
242
|
+
),
|
|
243
|
+
true,
|
|
244
|
+
'schema_version has row level security enabled');
|
|
245
|
+
|
|
246
|
+
select ok(
|
|
247
|
+
exists (
|
|
248
|
+
select 1
|
|
249
|
+
from pg_policy p
|
|
250
|
+
where p.polrelid = 'agent_board.schema_version'::regclass
|
|
251
|
+
and p.polpermissive
|
|
252
|
+
and p.polcmd = 'r'
|
|
253
|
+
and pg_get_expr(p.polqual, p.polrelid) = 'true'
|
|
254
|
+
),
|
|
255
|
+
'schema_version has an explicit permissive read policy');
|
|
256
|
+
|
|
257
|
+
select ok(
|
|
258
|
+
not has_table_privilege(
|
|
259
|
+
'authenticated',
|
|
260
|
+
'agent_board.schema_version',
|
|
261
|
+
'SELECT'
|
|
262
|
+
),
|
|
263
|
+
'schema_version has no authenticated SELECT grant');
|
|
264
|
+
|
|
265
|
+
select ok(
|
|
266
|
+
not has_table_privilege('anon', 'agent_board.schema_version', 'SELECT'),
|
|
267
|
+
'schema_version has no anon SELECT grant');
|
|
268
|
+
|
|
269
|
+
select is(
|
|
270
|
+
(select version from agent_board.get_schema_version()),
|
|
271
|
+
8,
|
|
272
|
+
'service/operator get_schema_version RPC still returns version 8');
|
|
273
|
+
|
|
274
|
+
set local role service_role;
|
|
275
|
+
select is(
|
|
276
|
+
(select version from agent_board.schema_version),
|
|
277
|
+
8,
|
|
278
|
+
'service_role direct schema_version read remains allowed under RLS');
|
|
279
|
+
reset role;
|
|
280
|
+
|
|
281
|
+
-- Subscriber JWT controls run before SET LOCAL ROLE so the internal helpers remain
|
|
282
|
+
-- callable. The following RPC probe intentionally stays JWT-only.
|
|
283
|
+
select set_config('request.jwt.claim.role', 'authenticated', true);
|
|
284
|
+
select set_config(
|
|
285
|
+
'request.jwt.claim.sub',
|
|
286
|
+
(select subscriber_user::text from _grants_hygiene_fixture),
|
|
287
|
+
true
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
-- 8-10. Required authenticated controls plus the RPC-only ALLOW contract.
|
|
291
|
+
select is(
|
|
292
|
+
agent_board._is_service_role(),
|
|
293
|
+
false,
|
|
294
|
+
'control: subscriber callback block is not service_role');
|
|
295
|
+
select is(
|
|
296
|
+
agent_board.current_agent_id(),
|
|
297
|
+
(select subscriber_agent from _grants_hygiene_fixture),
|
|
298
|
+
'control: subscriber callback block resolves the expected actor');
|
|
299
|
+
select is(
|
|
300
|
+
(select version from agent_board.get_schema_version()),
|
|
301
|
+
8,
|
|
302
|
+
'authenticated get_schema_version RPC remains available without direct table access');
|
|
303
|
+
|
|
304
|
+
-- Direct table and RLS probes require the database role switch.
|
|
305
|
+
set local role authenticated;
|
|
306
|
+
|
|
307
|
+
-- 11. Direct schema_version remains denied by the deployed RPC-only grant posture.
|
|
308
|
+
select throws_ok(
|
|
309
|
+
$$select version from agent_board.schema_version$$,
|
|
310
|
+
'42501',
|
|
311
|
+
null,
|
|
312
|
+
'authenticated direct schema_version read remains denied');
|
|
313
|
+
|
|
314
|
+
-- 12-13. Arrangement controls: subscriber is not endpoint owner, while the
|
|
315
|
+
-- callback_subscriptions policy's separate EXISTS keeps its subscriber arm live.
|
|
316
|
+
select is(
|
|
317
|
+
(
|
|
318
|
+
select count(*)::int
|
|
319
|
+
from agent_board.callback_endpoints ce
|
|
320
|
+
where ce.id = (select endpoint_id from _grants_hygiene_fixture)
|
|
321
|
+
),
|
|
322
|
+
0,
|
|
323
|
+
'subscriber cannot read the foreign-owned callback endpoint');
|
|
324
|
+
|
|
325
|
+
select is(
|
|
326
|
+
(
|
|
327
|
+
select count(*)::int
|
|
328
|
+
from agent_board.callback_subscriptions cs
|
|
329
|
+
where cs.id = (select subscription_id from _grants_hygiene_fixture)
|
|
330
|
+
),
|
|
331
|
+
1,
|
|
332
|
+
'subscriber can read its callback subscription despite not owning the endpoint');
|
|
333
|
+
|
|
334
|
+
-- 14-15. ALLOW: both formerly-dead subscriber arms must return their own rows.
|
|
335
|
+
select is(
|
|
336
|
+
(
|
|
337
|
+
select count(*)::int
|
|
338
|
+
from agent_board.callback_outbox_events coe
|
|
339
|
+
where coe.id = (select outbox_id from _grants_hygiene_fixture)
|
|
340
|
+
),
|
|
341
|
+
1,
|
|
342
|
+
'non-owner subscriber can read its callback outbox event');
|
|
343
|
+
|
|
344
|
+
select is(
|
|
345
|
+
(
|
|
346
|
+
select count(*)::int
|
|
347
|
+
from agent_board.callback_delivery_attempts cda
|
|
348
|
+
where cda.id = (select attempt_id from _grants_hygiene_fixture)
|
|
349
|
+
),
|
|
350
|
+
1,
|
|
351
|
+
'non-owner subscriber can read its callback delivery attempt');
|
|
352
|
+
|
|
353
|
+
reset role;
|
|
354
|
+
|
|
355
|
+
-- Unrelated-agent DENY twin, again with JWT controls before the DB role switch.
|
|
356
|
+
select set_config(
|
|
357
|
+
'request.jwt.claim.sub',
|
|
358
|
+
(select unrelated_user::text from _grants_hygiene_fixture),
|
|
359
|
+
true
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
-- 16-17. Required non-vacuity controls.
|
|
363
|
+
select is(
|
|
364
|
+
agent_board._is_service_role(),
|
|
365
|
+
false,
|
|
366
|
+
'control: unrelated callback block is not service_role');
|
|
367
|
+
select is(
|
|
368
|
+
agent_board.current_agent_id(),
|
|
369
|
+
(select unrelated_agent from _grants_hygiene_fixture),
|
|
370
|
+
'control: unrelated callback block resolves the expected actor');
|
|
371
|
+
|
|
372
|
+
set local role authenticated;
|
|
373
|
+
|
|
374
|
+
-- 18-20. DENY: an unrelated agent sees none of the callback chain.
|
|
375
|
+
select is(
|
|
376
|
+
(
|
|
377
|
+
select count(*)::int
|
|
378
|
+
from agent_board.callback_subscriptions cs
|
|
379
|
+
where cs.id = (select subscription_id from _grants_hygiene_fixture)
|
|
380
|
+
),
|
|
381
|
+
0,
|
|
382
|
+
'unrelated agent cannot read the callback subscription');
|
|
383
|
+
|
|
384
|
+
select is(
|
|
385
|
+
(
|
|
386
|
+
select count(*)::int
|
|
387
|
+
from agent_board.callback_outbox_events coe
|
|
388
|
+
where coe.id = (select outbox_id from _grants_hygiene_fixture)
|
|
389
|
+
),
|
|
390
|
+
0,
|
|
391
|
+
'unrelated agent cannot read the callback outbox event');
|
|
392
|
+
|
|
393
|
+
select is(
|
|
394
|
+
(
|
|
395
|
+
select count(*)::int
|
|
396
|
+
from agent_board.callback_delivery_attempts cda
|
|
397
|
+
where cda.id = (select attempt_id from _grants_hygiene_fixture)
|
|
398
|
+
),
|
|
399
|
+
0,
|
|
400
|
+
'unrelated agent cannot read the callback delivery attempt');
|
|
401
|
+
|
|
402
|
+
reset role;
|
|
403
|
+
select set_config('request.jwt.claim.role', '', true);
|
|
404
|
+
select set_config('request.jwt.claim.sub', '', true);
|
|
405
|
+
|
|
406
|
+
select finish();
|
|
407
|
+
|
|
408
|
+
rollback;
|
|
@@ -77,6 +77,15 @@ select is(
|
|
|
77
77
|
where issuer = 'https://portal.consiliency.io'),
|
|
78
78
|
'https://message-board.consiliency.io',
|
|
79
79
|
'real Portal audience is byte-identical to the frozen resource constant (RFC 8707)');
|
|
80
|
+
-- Freeze the delegated-actor claim too (message-board#87): MBPORTALGATE froze both
|
|
81
|
+
-- the resource-audience AND actor_claim; the audience is broken out above, this is
|
|
82
|
+
-- the matching actor_claim freeze the plan budgeted but never wrote.
|
|
83
|
+
select is(
|
|
84
|
+
(select actor_claim from agent_board.identity_providers
|
|
85
|
+
where issuer = 'https://portal.consiliency.io'
|
|
86
|
+
and audience = 'https://message-board.consiliency.io'),
|
|
87
|
+
'act.sub',
|
|
88
|
+
'real Portal actor_claim is byte-identical to the frozen act.sub constant (audit-only delegated actor)');
|
|
80
89
|
select is(
|
|
81
90
|
(select policy_approved from agent_board.identity_providers
|
|
82
91
|
where issuer = 'https://portal.consiliency.io'
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
begin;
|
|
2
2
|
|
|
3
|
-
select plan(
|
|
3
|
+
select plan(25);
|
|
4
|
+
|
|
5
|
+
-- The 11 read-model assertions below run inside a `do` block. pgTAP assertions
|
|
6
|
+
-- must NOT be called via `perform` there: `perform is(...)` RUNS the assertion
|
|
7
|
+
-- (incrementing the plan counter) but DISCARDS the returned TAP line, so a
|
|
8
|
+
-- failure prints no `not ok` (message-board#87). Buffer each line into this temp
|
|
9
|
+
-- table and emit them after the block instead.
|
|
10
|
+
create temp table _tap (seq serial primary key, line text) on commit drop;
|
|
4
11
|
|
|
5
12
|
select has_view('agent_board', 'v_agent_inbox', 'v_agent_inbox view exists');
|
|
6
13
|
select has_view('agent_board', 'v_agent_inbox_counts', 'v_agent_inbox_counts view exists');
|
|
@@ -391,43 +398,50 @@ begin
|
|
|
391
398
|
now() + interval '5 minutes'
|
|
392
399
|
);
|
|
393
400
|
|
|
394
|
-
|
|
401
|
+
insert into _tap(line) select is(
|
|
395
402
|
(select count(*)::bigint from agent_board.v_agent_inbox where recipient_agent_id = v_recipient),
|
|
396
403
|
2::bigint,
|
|
397
404
|
'v_agent_inbox returns visible inbox rows only'
|
|
398
405
|
);
|
|
399
406
|
|
|
400
|
-
|
|
407
|
+
insert into _tap(line) select is(
|
|
401
408
|
(select unread_count from agent_board.v_agent_inbox_counts where recipient_agent_id = v_recipient),
|
|
402
409
|
1::bigint,
|
|
403
410
|
'v_agent_inbox_counts computes unread_count from new and restored rows'
|
|
404
411
|
);
|
|
405
412
|
|
|
406
|
-
|
|
413
|
+
insert into _tap(line) select is(
|
|
407
414
|
(select pending_action_count from agent_board.v_agent_inbox_counts where recipient_agent_id = v_recipient),
|
|
408
415
|
2::bigint,
|
|
409
416
|
'v_agent_inbox_counts computes pending_action_count from actionable rows'
|
|
410
417
|
);
|
|
411
418
|
|
|
412
|
-
|
|
419
|
+
-- Pre-existing failure surfaced by making the discarded assertions visible
|
|
420
|
+
-- (message-board#87): BOARDOPT (20260515000000:411) narrowed v_board_directory's
|
|
421
|
+
-- row scope, so this fixture board is absent from the view and the scalar
|
|
422
|
+
-- subquery returns NULL (have: NULL / want: 2). Whether the VIEW's row scope or
|
|
423
|
+
-- this assertion is wrong is a product question — marked TODO so it stays
|
|
424
|
+
-- visible and non-blocking rather than deleted or silently "corrected".
|
|
425
|
+
perform todo('v_board_directory row scope narrowed by BOARDOPT; view-vs-assertion is a product question — see message-board#87', 1);
|
|
426
|
+
insert into _tap(line) select is(
|
|
413
427
|
(select active_member_count from agent_board.v_board_directory where board_id = v_board),
|
|
414
428
|
2::bigint,
|
|
415
429
|
'v_board_directory counts joined and subscribed members only'
|
|
416
430
|
);
|
|
417
431
|
|
|
418
|
-
|
|
432
|
+
insert into _tap(line) select is(
|
|
419
433
|
(select count(*)::bigint from agent_board.v_board_feed where board_id = v_board and message_id = v_message),
|
|
420
434
|
1::bigint,
|
|
421
435
|
'v_board_feed deduplicates a canonical board post across recipient fan-out'
|
|
422
436
|
);
|
|
423
437
|
|
|
424
|
-
|
|
438
|
+
insert into _tap(line) select is(
|
|
425
439
|
(select active_subscription_count from agent_board.v_callback_endpoint_status where endpoint_id = v_endpoint),
|
|
426
440
|
1::bigint,
|
|
427
441
|
'v_callback_endpoint_status counts active subscriptions'
|
|
428
442
|
);
|
|
429
443
|
|
|
430
|
-
|
|
444
|
+
insert into _tap(line) select ok(
|
|
431
445
|
exists (
|
|
432
446
|
select 1
|
|
433
447
|
from agent_board.v_callback_subscriptions
|
|
@@ -437,13 +451,13 @@ begin
|
|
|
437
451
|
'v_callback_subscriptions exposes board-scoped subscription metadata'
|
|
438
452
|
);
|
|
439
453
|
|
|
440
|
-
|
|
454
|
+
insert into _tap(line) select is(
|
|
441
455
|
(select latest_attempt_outcome from agent_board.v_callback_delivery_health where subscription_id = v_subscription),
|
|
442
456
|
'retryable_failure',
|
|
443
457
|
'v_callback_delivery_health anchors on the latest delivery attempt'
|
|
444
458
|
);
|
|
445
459
|
|
|
446
|
-
|
|
460
|
+
insert into _tap(line) select ok(
|
|
447
461
|
(
|
|
448
462
|
select latest_next_retry_at is not null
|
|
449
463
|
from agent_board.v_callback_delivery_health
|
|
@@ -452,13 +466,13 @@ begin
|
|
|
452
466
|
'v_callback_delivery_health exposes retry scheduling metadata'
|
|
453
467
|
);
|
|
454
468
|
|
|
455
|
-
|
|
469
|
+
insert into _tap(line) select is(
|
|
456
470
|
(select inbox_unread_count from agent_board.v_agent_dashboard where agent_id = v_recipient),
|
|
457
471
|
1::bigint,
|
|
458
472
|
'v_agent_dashboard exposes compact unread inbox counts'
|
|
459
473
|
);
|
|
460
474
|
|
|
461
|
-
|
|
475
|
+
insert into _tap(line) select is(
|
|
462
476
|
(select inbox_pending_action_count from agent_board.v_agent_dashboard where agent_id = v_recipient),
|
|
463
477
|
2::bigint,
|
|
464
478
|
'v_agent_dashboard exposes compact pending-action inbox counts'
|
|
@@ -466,6 +480,9 @@ begin
|
|
|
466
480
|
end;
|
|
467
481
|
$$;
|
|
468
482
|
|
|
483
|
+
-- Emit the buffered TAP lines (tests 15-25) that the `do` block produced.
|
|
484
|
+
select line from _tap order by seq;
|
|
485
|
+
|
|
469
486
|
select * from finish();
|
|
470
487
|
|
|
471
488
|
rollback;
|