@consiliency/agent-board-schema 1.68.0 → 1.74.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 +28 -4
- package/migrations/20261026000100_agent_board_thread_association.sql +1287 -0
- package/migrations/20261101000100_agent_board_session_measured_mint.sql +145 -0
- package/migrations/20261104065658_agent_board_reply_delivery.sql +659 -0
- package/migrations/20261104170000_agent_board_tasking_replay_scope.sql +320 -0
- package/migrations/20261104180000_agent_board_delivery_writer_execute.sql +51 -0
- package/migrations/20261104190000_agent_board_message_replay_scope.sql +373 -0
- package/package.json +1 -1
- package/schema-manifest.json +57 -3
- package/src/tooling.test.mjs +8 -2
- package/tests/agent_board_661_reply_delivery.test.sql +185 -0
- package/tests/agent_board_667_boundary.test.sql +162 -0
- package/tests/agent_board_667_conversion_branches.test.sql +91 -0
- package/tests/agent_board_667_conversion_profile.test.sql +112 -0
- package/tests/agent_board_667_history_class.test.sql +785 -0
- package/tests/agent_board_667_native_cast_effect.test.sql +88 -0
- package/tests/agent_board_667_realtime_apply_behavior.test.sql +117 -0
- package/tests/agent_board_667_realtime_apply_controls.test.sql +143 -0
- package/tests/agent_board_667_realtime_apply_profile.test.sql +61 -0
- package/tests/agent_board_667_retention.test.sql +201 -0
- package/tests/agent_board_667_routing.test.sql +294 -0
- package/tests/agent_board_667_sender_effects.test.sql +100 -0
- package/tests/agent_board_667_sender_profile_controls.test.sql +29 -0
- package/tests/agent_board_667_storage_delimiter_behavior.test.sql +84 -0
- package/tests/agent_board_667_storage_delimiter_controls.test.sql +23 -0
- package/tests/agent_board_667_storage_delimiter_profile.test.sql +61 -0
- package/tests/agent_board_667_storage_search_behavior.test.sql +93 -0
- package/tests/agent_board_667_storage_search_controls.test.sql +22 -0
- package/tests/agent_board_667_storage_search_profile.test.sql +60 -0
- package/tests/agent_board_667_storage_timestamp_behavior.test.sql +58 -0
- package/tests/agent_board_667_storage_timestamp_controls.test.sql +22 -0
- package/tests/agent_board_667_storage_timestamp_profile.test.sql +54 -0
- package/tests/agent_board_667_writers.test.sql +281 -0
- package/tests/agent_board_717_message_replay_scope.test.sql +828 -0
- package/tests/agent_board_717_tasking_replay_scope.test.sql +661 -0
- package/tests/agent_board_addressing.test.sql +8 -5
- package/tests/agent_board_claim_subject_gate.test.sql +7 -3
- package/tests/agent_board_claimpolicy_race.test.sql +39 -11
- package/tests/agent_board_delegate_role_class.test.sql +33 -15
- package/tests/agent_board_delivery_writer_execute.test.sql +371 -0
- package/tests/agent_board_fk_lock_edges.test.sql +3 -0
- package/tests/agent_board_hosted_delegates.test.sql +5 -2
- package/tests/agent_board_inbox_rpcs.test.sql +5 -3
- package/tests/agent_board_obsgate.test.sql +8 -4
- package/tests/agent_board_obsgate4.test.sql +2 -2
- package/tests/agent_board_outbox_wake_ordering.test.sql +25 -2
- package/tests/agent_board_own_post_not_delivered.test.sql +11 -9
- package/tests/agent_board_session_identity_behavior_classes.test.sql +2 -2
- package/tests/agent_board_session_identity_limits.test.sql +1 -1
- package/tests/agent_board_session_identity_measured_mint.test.sql +342 -0
- package/tests/agent_board_session_identity_predicate_isolation.test.sql +146 -0
- package/tests/agent_board_sidserver_authority.test.sql +69 -0
- package/tests/agent_board_sidserver_catalog.test.sql +47 -0
- package/tests/agent_board_signed_write_gateway.test.sql +4 -2
- package/tests/agent_board_task_thread_lock_order.test.sql +67 -42
- package/tests/agent_board_v2_invariants.test.sql +5 -2
- package/tests/agent_board_write_enforcement_class.test.sql +3 -3
- package/tests/catalog/function_execute_privileges.json +41 -5
- package/tests/catalog/p_metadata_parameters.json +16 -0
- package/tests/fixtures/agent_board_667_apply_rls_guard.sql +195 -0
- package/tests/fixtures/agent_board_667_apply_rls_provenance.md +125 -0
- package/tests/fixtures/agent_board_667_apply_rls_reference.sql +1903 -0
- package/tests/fixtures/agent_board_667_conversion_guard.sql +162 -0
- package/tests/fixtures/agent_board_667_conversion_reference.md +23 -0
- package/tests/fixtures/agent_board_667_conversion_reference.sql +5325 -0
- package/tests/fixtures/agent_board_667_delimiter_guard.sql +92 -0
- package/tests/fixtures/agent_board_667_delimiter_provenance.md +116 -0
- package/tests/fixtures/agent_board_667_delimiter_reference.sql +10150 -0
- package/tests/fixtures/agent_board_667_search_guard.sql +92 -0
- package/tests/fixtures/agent_board_667_search_provenance.md +141 -0
- package/tests/fixtures/agent_board_667_search_reference.sql +4779 -0
- package/tests/fixtures/agent_board_667_sender_guard.sql +102 -0
- package/tests/fixtures/agent_board_667_sender_reference.md +20 -0
- package/tests/fixtures/agent_board_667_sender_reference.sql +875 -0
- package/tests/fixtures/agent_board_667_timestamp_graph.sql +84 -0
- package/tests/fixtures/agent_board_667_timestamp_guard.sql +94 -0
- package/tests/fixtures/agent_board_667_timestamp_provenance.md +99 -0
- package/tests/fixtures/agent_board_667_timestamp_reference.sql +2938 -0
- package/tests/isolation/expected/association_artifact_anchor_order.out +108 -0
- package/tests/isolation/expected/association_board_vs_board.out +71 -0
- package/tests/isolation/expected/association_post_wait_authority.out +77 -0
- package/tests/isolation/expected/association_strong_history_writers.out +203 -0
- package/tests/isolation/expected/association_strong_isolation.out +185 -0
- package/tests/isolation/expected/association_vs_artifact_claim_escalation.out +199 -0
- package/tests/isolation/expected/association_vs_heartbeat.out +78 -0
- package/tests/isolation/expected/association_vs_multi_anchor_artifact.out +67 -0
- package/tests/isolation/expected/association_vs_private_message.out +67 -0
- package/tests/isolation/expected/association_vs_task_and_conflict.out +133 -0
- package/tests/isolation/expected/association_vs_taskful_cross_thread.out +133 -0
- package/tests/isolation/expected/message_replay_scope.out +209 -0
- package/tests/isolation/expected/tasking_replay_scope.out +125 -0
- package/tests/isolation/specs/association_artifact_anchor_order.spec +46 -0
- package/tests/isolation/specs/association_board_vs_board.spec +37 -0
- package/tests/isolation/specs/association_post_wait_authority.spec +48 -0
- package/tests/isolation/specs/association_strong_history_writers.spec +61 -0
- package/tests/isolation/specs/association_strong_isolation.spec +50 -0
- package/tests/isolation/specs/association_vs_artifact_claim_escalation.spec +41 -0
- package/tests/isolation/specs/association_vs_heartbeat.spec +47 -0
- package/tests/isolation/specs/association_vs_multi_anchor_artifact.spec +38 -0
- package/tests/isolation/specs/association_vs_private_message.spec +33 -0
- package/tests/isolation/specs/association_vs_task_and_conflict.spec +38 -0
- package/tests/isolation/specs/association_vs_taskful_cross_thread.spec +42 -0
- package/tests/isolation/specs/message_replay_scope.spec +77 -0
- package/tests/isolation/specs/tasking_replay_scope.spec +72 -0
- package/tests/mutations/717-f1/M0_prefix_main_acl_and_comments.sql +11 -0
- package/tests/mutations/717-f1/M10_wrapper_stops_delegating.sql +7 -0
- package/tests/mutations/717-f1/M11_transitive_recipient_content.sql +8 -0
- package/tests/mutations/717-f1/M12_transitive_recipient_visibility.sql +8 -0
- package/tests/mutations/717-f1/M13_transitive_worker_content.sql +15 -0
- package/tests/mutations/717-f1/M1a_regrant_upsert_delivery_recipient.sql +1 -0
- package/tests/mutations/717-f1/M1b_regrant_upsert_inbox_item.sql +1 -0
- package/tests/mutations/717-f1/M1c_regrant_queue_inbox_callbacks.sql +1 -0
- package/tests/mutations/717-f1/M1d_regrant_queue_board_post_callbacks.sql +1 -0
- package/tests/mutations/717-f1/M1e_regrant_next_thread_message_sequence.sql +1 -0
- package/tests/mutations/717-f1/M2_blanket_schema_grant.sql +1 -0
- package/tests/mutations/717-f1/M3_new_overload_of_sink.sql +10 -0
- package/tests/mutations/717-f1/M4_new_route_calling_sink.sql +7 -0
- package/tests/mutations/717-f1/M5_actor_trusts_supplied_id.sql +5 -0
- package/tests/mutations/717-f1/M6_signed_route_granted_to_authenticated.sql +1 -0
- package/tests/mutations/717-f1/M7_worker_granted_to_authenticated.sql +1 -0
- package/tests/mutations/717-f1/M8_recipient_state_rewrites_visibility.sql +5 -0
- package/tests/mutations/717-f1/M9_public_route_revoked_from_service_role.sql +1 -0
- package/tests/mutations/717-f1/outbox_wake_ordering/W1_regrant_queue_inbox_callbacks.sql +1 -0
- package/tests/mutations/717-f1/outbox_wake_ordering/W2_revoke_worker_from_service_role.sql +1 -0
- package/tests/mutations/717-f1/outbox_wake_ordering/W3_invoker_caller_of_sink.sql +1 -0
- package/tests/mutations/717-f1/outbox_wake_ordering/W4_public_regrant_board_post_sink.sql +1 -0
- package/tests/mutations/717-f1/run.sh +17 -0
- package/tests/mutations/717-f2b/C1_new_delivery_route.sql +4 -0
- package/tests/mutations/717-f2b/C2_new_replay_lookup.sql +3 -0
- package/tests/mutations/717-f2b/C3_internal_regranted.sql +2 -0
- package/tests/mutations/717-f2b/C4_wrapper_writes_delivery.sql +9 -0
- package/tests/mutations/717-f2b/C5_class_member_dynamic_sql.sql +9 -0
- package/tests/mutations/717-f2b/C6_drop_board_scope_constraint.sql +2 -0
- package/tests/mutations/717-f2b/C7_state_private_helper_content.sql +14 -0
- package/tests/mutations/717-f2b/L1_dm_lock_split_by_recipient.sql +8 -0
- package/tests/mutations/717-f2b/L2_dm_lock_removed.sql +12 -0
- package/tests/mutations/717-f2b/L3_mc_lock_split_by_recipients.sql +8 -0
- package/tests/mutations/717-f2b/L4_mc_lock_removed.sql +12 -0
- package/tests/mutations/717-f2b/M0_prefix_definitions.sql +17 -0
- package/tests/mutations/717-f2b/M10_mc_lookup_any_author.sql +15 -0
- package/tests/mutations/717-f2b/M11_dm_swallow_refusal.sql +8 -0
- package/tests/mutations/717-f2b/M12_mc_swallow_refusal.sql +8 -0
- package/tests/mutations/717-f2b/M13_signed_dm_swallows_refusal.sql +32 -0
- package/tests/mutations/717-f2b/M14_signed_mc_swallows_refusal.sql +20 -0
- package/tests/mutations/717-f2b/M15_mc_any_source.sql +7 -0
- package/tests/mutations/717-f2b/M1_dm_drop_recipient_proof.sql +16 -0
- package/tests/mutations/717-f2b/M2_mc_drop_recipient_proof.sql +16 -0
- package/tests/mutations/717-f2b/M3_dm_metadata_as_proof.sql +12 -0
- package/tests/mutations/717-f2b/M4_mc_metadata_as_proof.sql +16 -0
- package/tests/mutations/717-f2b/M5_dm_any_mode.sql +11 -0
- package/tests/mutations/717-f2b/M6_dm_board_delivery_counts.sql +11 -0
- package/tests/mutations/717-f2b/M7_mc_any_mode.sql +11 -0
- package/tests/mutations/717-f2b/M8_dm_thread_stamp_as_proof.sql +12 -0
- package/tests/mutations/717-f2b/M9_dm_lookup_any_author.sql +15 -0
- package/tests/mutations/717-f2b/run.sh +82 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
-- Resolution checks remain exercised even when native Storage is absent.
|
|
2
|
+
create extension if not exists pgtap with schema extensions;
|
|
3
|
+
set search_path=extensions,public,agent_board;
|
|
4
|
+
begin;
|
|
5
|
+
\ir fixtures/agent_board_667_conversion_guard.sql
|
|
6
|
+
\ir fixtures/agent_board_667_sender_guard.sql
|
|
7
|
+
\ir fixtures/agent_board_667_timestamp_guard.sql
|
|
8
|
+
select plan(4);
|
|
9
|
+
create temp table xb_st_prior_problems on commit drop as select * from pg_temp.xb_st_prerequisite_problems();
|
|
10
|
+
create function pg_temp.replace(text,text,text) returns text language sql immutable as $body$select $1$body$;
|
|
11
|
+
select ok(exists(select 1 from pg_proc where oid='pg_temp.replace(text,text,text)'::regprocedure),
|
|
12
|
+
'ST own overload control is installed in the live catalog');
|
|
13
|
+
select ok(exists(select 1 from pg_temp.xb_st_prerequisite_problems()
|
|
14
|
+
where identity=pg_temp.xb_cf_fn('pg_temp.replace(text,text,text)'::regprocedure) and problem='unreviewed overload or shadow'),
|
|
15
|
+
'ST resolution census names the injected overload identity');
|
|
16
|
+
select is((select count(*) from pg_temp.xb_st_admitted()),0::bigint,'ST injected overload closes admission');
|
|
17
|
+
drop function pg_temp.replace(text,text,text);
|
|
18
|
+
select is((select jsonb_agg(jsonb_build_array(identity,problem) order by identity,problem) from pg_temp.xb_st_prerequisite_problems()),
|
|
19
|
+
(select jsonb_agg(jsonb_build_array(identity,problem) order by identity,problem) from xb_st_prior_problems),
|
|
20
|
+
'ST removing own overload restores the exact prior prerequisite findings');
|
|
21
|
+
select * from finish();
|
|
22
|
+
rollback;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
-- Admission requires a complete native reader, not a similarly named stand-in.
|
|
2
|
+
create extension if not exists pgtap with schema extensions;
|
|
3
|
+
create extension if not exists plpgsql_check with schema extensions;
|
|
4
|
+
set search_path=extensions,public,agent_board;
|
|
5
|
+
begin;
|
|
6
|
+
\ir fixtures/agent_board_667_conversion_guard.sql
|
|
7
|
+
\ir fixtures/agent_board_667_sender_guard.sql
|
|
8
|
+
\ir fixtures/agent_board_667_timestamp_guard.sql
|
|
9
|
+
select to_regprocedure('storage.search_by_timestamp(text,text,integer,integer,text,text,text,text,text,text,text)') is not null as native_present \gset
|
|
10
|
+
\if :native_present
|
|
11
|
+
select plan(15);
|
|
12
|
+
select is((select string_agg(identity||': '||problem,', ' order by identity,problem) from pg_temp.xb_st_problems()),
|
|
13
|
+
null::text,'ST finite query, target, resolution and callback profile matches');
|
|
14
|
+
select is((select array_agg(fn order by fn) from pg_temp.xb_st_admitted()),
|
|
15
|
+
array['storage.search_by_timestamp(text,text,integer,integer,text,text,text,text,text,text,text)'::regprocedure::oid],
|
|
16
|
+
'ST exactly the native timestamp reader OID is admitted');
|
|
17
|
+
select is((select count(*) from pg_temp.xb_st_views),9::bigint,'ST all eight SELECT shapes and helper are parsed');
|
|
18
|
+
select ok(exists(select 1 from pg_temp.xb_st_roots() where fn='pg_catalog.min(timestamp with time zone)'::regprocedure),
|
|
19
|
+
'ST pinned built-in aggregate is discovered despite pg_depend omission');
|
|
20
|
+
select ok(exists(select 1 from pg_temp.xb_st_reach() where fn='pg_catalog.timestamptz_smaller(timestamp with time zone,timestamp with time zone)'::regprocedure),
|
|
21
|
+
'ST aggregate transition and combine callbacks are reached');
|
|
22
|
+
select ok(exists(select 1 from pg_temp.xb_st_roots() where fn='storage.get_common_prefix(text,text,text)'::regprocedure),
|
|
23
|
+
'ST parsed query reaches the reviewed SQL folder helper');
|
|
24
|
+
select ok(exists(select 1 from pg_temp.xb_st_roots() where fn='pg_catalog.replace(text,text,text)'::regprocedure),
|
|
25
|
+
'ST static normalization callable class is discovered');
|
|
26
|
+
select ok(exists(select 1 from pg_temp.xb_st_roots() where fn='pg_catalog.string_to_array(text,text)'::regprocedure),
|
|
27
|
+
'ST stored generated-column expression is included');
|
|
28
|
+
select is((select count(*) from pg_temp.xb_st_static where problem is not null),0::bigint,
|
|
29
|
+
'ST PLpgSQL static dependency parser completed');
|
|
30
|
+
select is((select count(*) from pg_temp.xb_st_reference where kind='aggregate'),1::bigint,
|
|
31
|
+
'ST only the reached timestamp MIN aggregate receives executable aggregate credit');
|
|
32
|
+
-- A SELECT cannot fire a DML trigger. Prove the exemption positively with one
|
|
33
|
+
-- live callback and its DML control, rather than excluding trigger names.
|
|
34
|
+
create temp table xb_st_dml_effect(note text) on commit drop;
|
|
35
|
+
create function pg_temp.xb_st_dml_callback() returns trigger language plpgsql as $callback$
|
|
36
|
+
begin insert into pg_temp.xb_st_dml_effect values('called'); return new; end $callback$;
|
|
37
|
+
create trigger xb_st_dml_control before insert on storage.objects for each row execute function pg_temp.xb_st_dml_callback();
|
|
38
|
+
insert into storage.buckets(id,name) values('667-timestamp-dml-control','667-timestamp-dml-control');
|
|
39
|
+
select lives_ok($call$select * from storage.search_by_timestamp('','667-timestamp-dml-control',10,1,'','asc','updated_at','')$call$,
|
|
40
|
+
'ST SELECT lives with a live DML callback installed');
|
|
41
|
+
select is((select count(*) from pg_temp.xb_st_dml_effect),0::bigint,'ST SELECT does not invoke the DML callback');
|
|
42
|
+
select lives_ok($call$insert into storage.objects(bucket_id,name) values('667-timestamp-dml-control','own-trigger-control')$call$,
|
|
43
|
+
'ST own DML trigger control lives');
|
|
44
|
+
select is((select count(*) from pg_temp.xb_st_dml_effect),1::bigint,'ST DML control positively invokes the same callback');
|
|
45
|
+
select is((select array_agg(fn order by fn) from pg_temp.xb_st_admitted()),
|
|
46
|
+
array['storage.search_by_timestamp(text,text,integer,integer,text,text,text,text,text,text,text)'::regprocedure::oid],
|
|
47
|
+
'ST DML-only callbacks do not change this SELECT effect admission');
|
|
48
|
+
\else
|
|
49
|
+
select plan(1);
|
|
50
|
+
select ok(to_regprocedure('storage.search_by_timestamp(text,text,integer,integer,text,text,text,text,text,text,text)') is null
|
|
51
|
+
and not exists(select 1 from pg_temp.xb_st_admitted()),'ST absent native reader receives no admission or behavior credit');
|
|
52
|
+
\endif
|
|
53
|
+
select * from finish();
|
|
54
|
+
rollback;
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
-- #667 writer and own-operation behavior witnesses; class and race proof still required.
|
|
2
|
+
create extension if not exists pgtap with schema extensions;
|
|
3
|
+
set search_path = extensions, public, agent_board;
|
|
4
|
+
begin;
|
|
5
|
+
select plan(81);
|
|
6
|
+
create temp table xb_fixture (board1 uuid, board2 uuid, actor uuid, actor_user uuid, thread uuid, private_thread uuid) on commit drop;
|
|
7
|
+
do $$
|
|
8
|
+
declare
|
|
9
|
+
suffix text := replace(substr(gen_random_uuid()::text, 1, 8), '-', '');
|
|
10
|
+
actor_user uuid; actor uuid := gen_random_uuid(); creator uuid := gen_random_uuid();
|
|
11
|
+
board1 uuid; board2 uuid; th uuid := gen_random_uuid(); private_th uuid := gen_random_uuid();
|
|
12
|
+
begin
|
|
13
|
+
actor_user := public.ensure_github_user('xb-actor-' || suffix, 'xb-actor-' || suffix || '@example.test', 'xb-actor-' || suffix, 'XB Actor');
|
|
14
|
+
insert into agent_board.agents(id,agent_type,handle,display_name,status,auth_user_id,owner_user_id)
|
|
15
|
+
values (creator,'agent','xb-creator-' || suffix,'XB Creator','active',null,null),
|
|
16
|
+
(actor,'agent','xb-actor-' || suffix,'XB Actor','active',actor_user,actor_user);
|
|
17
|
+
select board_id into board1 from agent_board.create_board(creator,'xb-one-' || suffix,'XB One','fixture','{}'::jsonb,'private');
|
|
18
|
+
select board_id into board2 from agent_board.create_board(creator,'xb-two-' || suffix,'XB Two','fixture','{}'::jsonb,'private');
|
|
19
|
+
insert into agent_board.board_memberships(board_id,agent_id,membership_state,created_by_agent_id,role)
|
|
20
|
+
values (board1,actor,'joined',creator,'member'), (board2,actor,'joined',creator,'member');
|
|
21
|
+
insert into agent_board.threads(id,thread_type,created_by_agent_id,subject)
|
|
22
|
+
values (th,'coordination',actor,'XB associated'),(private_th,'coordination',actor,'XB private');
|
|
23
|
+
perform set_config('request.jwt.claim.role','authenticated',true);
|
|
24
|
+
perform set_config('request.jwt.claim.sub',actor_user::text,true);
|
|
25
|
+
insert into xb_fixture values(board1,board2,actor,actor_user,th,private_th);
|
|
26
|
+
end $$;
|
|
27
|
+
create temp table xb_writer_threads(kind text primary key, id uuid not null) on commit drop;
|
|
28
|
+
insert into xb_writer_threads select kind, gen_random_uuid() from unnest(array['artifact','claim','heartbeat','task','conflict','anchor','receipt','explicit','rollback','rollback_post','signed_post','signed_task','released_claim','resolved_conflict','escalation','membership']) kind;
|
|
29
|
+
insert into agent_board.threads(id, thread_type, created_by_agent_id, subject)
|
|
30
|
+
select id, 'coordination', (select actor from xb_fixture), kind from xb_writer_threads;
|
|
31
|
+
select lives_ok($$select * from agent_board.add_artifact((select actor from xb_fixture),'https://example.test/private', p_thread_id => (select id from xb_writer_threads where kind='artifact'))$$, 'XB artifact writes private history');
|
|
32
|
+
select throws_ok($$select * from agent_board.post_board_message((select id from xb_writer_threads where kind='artifact'),(select actor from xb_fixture),(select board1 from xb_fixture),'s','b')$$,'42501','thread has private history','XB artifact history refuses association');
|
|
33
|
+
select is((select board_id from agent_board.threads where id=(select id from xb_writer_threads where kind='artifact')),null::uuid,'XB artifact thread remains unstamped');
|
|
34
|
+
select lives_ok($$select * from agent_board.acquire_claim((select actor from xb_fixture),'custom','xb-writer-claim',now()+interval '1 hour',p_thread_id => (select id from xb_writer_threads where kind='claim'))$$,'XB thread-only claim writes history');
|
|
35
|
+
select throws_ok($$select * from agent_board.post_board_message((select id from xb_writer_threads where kind='claim'),(select actor from xb_fixture),(select board1 from xb_fixture),'s','b')$$,'42501','thread has private history','XB thread-only claim refuses association');
|
|
36
|
+
select is((select board_id from agent_board.threads where id=(select id from xb_writer_threads where kind='claim')),null::uuid,'XB claim thread remains unstamped');
|
|
37
|
+
select lives_ok($$select * from agent_board.heartbeat((select actor from xb_fixture),(select id from xb_writer_threads where kind='heartbeat'),p_activity_summary => 'first')$$,'XB heartbeat establishes thread');
|
|
38
|
+
select lives_ok($$select * from agent_board.heartbeat((select actor from xb_fixture),null,p_activity_summary => 'retained',p_metadata => '{"note":"private"}'::jsonb)$$,'XB heartbeat retains thread with private metadata');
|
|
39
|
+
select is((select thread_id from agent_board.presence where agent_id=(select actor from xb_fixture)),(select id from xb_writer_threads where kind='heartbeat'),'XB heartbeat retains stored thread');
|
|
40
|
+
select ok(exists (select 1 from pg_locks l where l.pid=pg_backend_pid() and l.locktype='advisory' and l.granted
|
|
41
|
+
and l.classid::bigint=(hashtext('agent_board.heartbeat_thread')::bigint & 4294967295)
|
|
42
|
+
and l.objid::bigint=(hashtext((select actor::text from xb_fixture))::bigint & 4294967295) and l.objsubid=2),
|
|
43
|
+
'XB heartbeat actually holds its actor-scoped advisory namespace');
|
|
44
|
+
select is((select jsonb_build_object('summary',activity_summary,'note',metadata->>'note') from agent_board.presence where agent_id=(select actor from xb_fixture)),
|
|
45
|
+
'{"summary":"retained","note":"private"}'::jsonb, 'XB presence stores caller summary and metadata before history refusal');
|
|
46
|
+
select throws_ok($$select * from agent_board.post_board_message((select id from xb_writer_threads where kind='heartbeat'),(select actor from xb_fixture),(select board1 from xb_fixture),'s','b')$$,'42501','thread has private history','XB retained presence refuses association');
|
|
47
|
+
select is((select board_id from agent_board.threads where id=(select id from xb_writer_threads where kind='heartbeat')),null::uuid,'XB presence thread remains unstamped');
|
|
48
|
+
select lives_ok($$select * from agent_board.create_tasking_delivery((select id from xb_writer_threads where kind='task'),(select actor from xb_fixture),'own task',p_board_id => (select board1 from xb_fixture))$$,'XB empty first board task succeeds');
|
|
49
|
+
select is((select board_id from agent_board.threads where id=(select id from xb_writer_threads where kind='task')),(select board1 from xb_fixture),'XB task stamps before own history');
|
|
50
|
+
select is((select count(*) from agent_board.tasks where thread_id=(select id from xb_writer_threads where kind='task')),1::bigint,'XB own task is recorded');
|
|
51
|
+
select lives_ok($$select * from agent_board.create_task((select id from xb_writer_threads where kind='anchor'),(select actor from xb_fixture),'anchor')$$,'XB foreign task anchor created');
|
|
52
|
+
select lives_ok($$select * from agent_board.raise_conflict((select id from xb_writer_threads where kind='conflict'),(select actor from xb_fixture),'foreign task',p_task_id => (select id from agent_board.tasks where thread_id=(select id from xb_writer_threads where kind='anchor')))$$,'XB taskful conflict stores a different thread');
|
|
53
|
+
select throws_ok($$select * from agent_board.post_board_message((select id from xb_writer_threads where kind='conflict'),(select actor from xb_fixture),(select board1 from xb_fixture),'s','b')$$,'42501','thread has private history','XB foreign-task conflict refuses association');
|
|
54
|
+
select lives_ok($$select * from agent_board.mark_read((select id from xb_writer_threads where kind='receipt'),(select actor from xb_fixture),0)$$,'XB creator can write empty-thread receipt');
|
|
55
|
+
select is((select last_read_message_sequence from agent_board.read_receipts where thread_id=(select id from xb_writer_threads where kind='receipt') and agent_id=(select actor from xb_fixture)),0::bigint,'XB receipt is bounded to sequence zero');
|
|
56
|
+
select lives_ok($$select * from agent_board.post_board_message((select id from xb_writer_threads where kind='receipt'),(select actor from xb_fixture),(select board1 from xb_fixture),'s','b')$$,'XB receipt alone permits first association');
|
|
57
|
+
select is((select board_id from agent_board.threads where id=(select id from xb_writer_threads where kind='receipt')),(select board1 from xb_fixture),'XB receipt control actually stamps the empty thread');
|
|
58
|
+
select lives_ok($$select * from agent_board.acquire_claim((select actor from xb_fixture),'custom','xb-task-claim',now()+interval '1 hour',p_task_id => (select id from agent_board.tasks where thread_id=(select id from xb_writer_threads where kind='anchor')))$$,'XB taskful claim is created');
|
|
59
|
+
select is((select c.thread_id from agent_board.claims c join agent_board.tasks t on t.id=c.task_id where c.resource_key='xb-task-claim'),(select id from xb_writer_threads where kind='anchor'),'XB taskful claim stores its task-derived thread');
|
|
60
|
+
select ok(exists(select 1 from agent_board.task_events e join agent_board.tasks t on t.id=e.task_id
|
|
61
|
+
where t.thread_id=(select id from xb_writer_threads where kind='anchor')), 'XB task-event equality checks an actual event');
|
|
62
|
+
select is((select count(*) from agent_board.task_events e join agent_board.tasks t on t.id=e.task_id where t.thread_id=(select id from xb_writer_threads where kind='anchor') and e.thread_id is distinct from t.thread_id),0::bigint,'XB task events store the task thread');
|
|
63
|
+
select throws_ok($$select set_config('request.jwt.claim.role','service_role',true); select * from agent_board.mark_read((select id from xb_writer_threads where kind='anchor'),(select created_by_agent_id from agent_board.boards where id=(select board1 from xb_fixture)),0)$$,'42501','read receipt subject has no access to thread','XB receipt subject gate blocks a service caller from minting access');
|
|
64
|
+
select is((select count(*) from agent_board.read_receipts where thread_id=(select id from xb_writer_threads where kind='anchor') and agent_id=(select created_by_agent_id from agent_board.boards where id=(select board1 from xb_fixture))),0::bigint,'XB denied subject has no receipt');
|
|
65
|
+
-- Multi-anchor controls: parent histories exist before the artifact. A mixed
|
|
66
|
+
-- artifact stores references to a task and a message on different threads.
|
|
67
|
+
select lives_ok($$select * from agent_board.post_message((select private_thread from xb_fixture),(select actor from xb_fixture),'private message anchor')$$,
|
|
68
|
+
'XB private message anchor is created');
|
|
69
|
+
select lives_ok($$select * from agent_board.add_artifact((select actor from xb_fixture),'https://example.test/mixed',
|
|
70
|
+
p_task_id => (select id from agent_board.tasks where thread_id=(select id from xb_writer_threads where kind='anchor')),
|
|
71
|
+
p_message_id => (select id from agent_board.messages where thread_id=(select private_thread from xb_fixture)))$$,
|
|
72
|
+
'XB mixed task and message artifact is created');
|
|
73
|
+
select is((select count(*) from agent_board.artifacts a join agent_board.tasks t on t.id=a.task_id join agent_board.messages m on m.id=a.message_id
|
|
74
|
+
where a.uri='https://example.test/mixed' and t.thread_id=(select id from xb_writer_threads where kind='anchor')
|
|
75
|
+
and m.thread_id=(select private_thread from xb_fixture)), 1::bigint, 'XB artifact stores both distinct anchors');
|
|
76
|
+
select lives_ok($$select * from agent_board.add_artifact((select actor from xb_fixture),'https://example.test/event',
|
|
77
|
+
p_task_event_id => (select id from agent_board.task_events where thread_id=(select id from xb_writer_threads where kind='anchor') order by event_sequence limit 1))$$,
|
|
78
|
+
'XB task-event anchored artifact is created');
|
|
79
|
+
select is((select count(*) from agent_board.artifacts where uri='https://example.test/event' and task_event_id is not null),1::bigint,
|
|
80
|
+
'XB event-anchored artifact retains its parent');
|
|
81
|
+
select throws_ok($$select * from agent_board.post_board_message((select private_thread from xb_fixture),(select actor from xb_fixture),(select board1 from xb_fixture),'s','b')$$,
|
|
82
|
+
'42501','thread has private history','XB message-anchor thread cannot be promoted');
|
|
83
|
+
select throws_ok($$select * from agent_board.post_board_message((select id from xb_writer_threads where kind='anchor'),(select actor from xb_fixture),(select board1 from xb_fixture),'s','b')$$,
|
|
84
|
+
'42501','thread has private history','XB task and event anchor thread cannot be promoted');
|
|
85
|
+
-- The service-only retention routine dynamically updates existing presence rows.
|
|
86
|
+
-- Exercise its actual state effect: it must preserve the thread and caller data.
|
|
87
|
+
update agent_board.presence set expires_at=now()-interval '1 hour' where agent_id=(select actor from xb_fixture);
|
|
88
|
+
select lives_ok($$select agent_board.mark_retention_metadata('presence',now(),gen_random_uuid(),'xb test')$$,
|
|
89
|
+
'XB retention processes existing presence');
|
|
90
|
+
select is((select thread_id from agent_board.presence where agent_id=(select actor from xb_fixture)),
|
|
91
|
+
(select id from xb_writer_threads where kind='heartbeat'), 'XB retention preserves presence thread');
|
|
92
|
+
select is((select metadata->>'note' from agent_board.presence where agent_id=(select actor from xb_fixture)),
|
|
93
|
+
'private', 'XB retention preserves caller metadata');
|
|
94
|
+
select ok((select metadata ? 'ops_retained_at' from agent_board.presence where agent_id=(select actor from xb_fixture)),
|
|
95
|
+
'XB retention actually updates the existing row');
|
|
96
|
+
-- A board task can also explicitly address an agent. Its private explicit
|
|
97
|
+
-- recipient is NULL-board even while the board copy uses the task's board.
|
|
98
|
+
select lives_ok($$select * from agent_board.create_tasking_delivery((select id from xb_writer_threads where kind='explicit'),
|
|
99
|
+
(select actor from xb_fixture),'XB explicit task',p_recipient_agent_ids=>array[(select actor from xb_fixture)],p_board_id=>(select board1 from xb_fixture))$$,
|
|
100
|
+
'XB empty board task with explicit recipient succeeds');
|
|
101
|
+
select is((select board_id from agent_board.threads where id=(select id from xb_writer_threads where kind='explicit')),
|
|
102
|
+
(select board1 from xb_fixture),'XB explicit task stamps its thread');
|
|
103
|
+
select ok(exists(select 1 from agent_board.delivery_recipients d join agent_board.tasks t on t.id=d.source_task_id
|
|
104
|
+
where t.thread_id=(select id from xb_writer_threads where kind='explicit') and d.recipient_agent_id=(select actor from xb_fixture)
|
|
105
|
+
and d.delivery_mode='tasking' and d.board_id is null), 'XB explicit task recipient remains private');
|
|
106
|
+
-- Delegation is anchored to an existing parent, rather than an empty-thread
|
|
107
|
+
-- first-association route. Verify its actual child/task linkage on a board.
|
|
108
|
+
select lives_ok($$select * from agent_board.delegate_task((select id from agent_board.tasks where thread_id=(select id from xb_writer_threads where kind='task') and title='own task'),
|
|
109
|
+
(select actor from xb_fixture),'XB child',p_delegated_to_agent_id=>(select actor from xb_fixture))$$,
|
|
110
|
+
'XB delegation on an associated parent task succeeds');
|
|
111
|
+
select ok(exists(select 1 from agent_board.delegations d join agent_board.tasks parent on parent.id=d.parent_task_id
|
|
112
|
+
join agent_board.tasks child on child.id=d.child_task_id join agent_board.threads th on th.id=child.thread_id
|
|
113
|
+
where parent.thread_id=(select id from xb_writer_threads where kind='task') and child.thread_id=parent.thread_id
|
|
114
|
+
and child.title='XB child' and th.board_id=(select board1 from xb_fixture)),
|
|
115
|
+
'XB delegation child preserves its parent thread and board');
|
|
116
|
+
-- Inject a late failure after create_task has inserted its task but before its
|
|
117
|
+
-- event can complete. The caller's first stamp and all its writes must roll back.
|
|
118
|
+
create function pg_temp.xb_fail_task_event() returns trigger language plpgsql as $body$
|
|
119
|
+
begin
|
|
120
|
+
if exists(select 1 from agent_board.tasks where id=new.task_id and title='XB rollback task') then
|
|
121
|
+
if not exists(select 1 from agent_board.tasks t join agent_board.threads th on th.id=t.thread_id
|
|
122
|
+
where t.id=new.task_id and th.board_id=(select board1 from xb_fixture)) then
|
|
123
|
+
raise exception 'XB event reached before first stamp' using errcode='P0001';
|
|
124
|
+
end if;
|
|
125
|
+
raise exception 'XB injected task-event failure' using errcode='P0001';
|
|
126
|
+
end if;
|
|
127
|
+
return new;
|
|
128
|
+
end $body$;
|
|
129
|
+
create trigger xb_fail_task_event before insert on agent_board.task_events for each row execute function pg_temp.xb_fail_task_event();
|
|
130
|
+
create temp table xb_rollback_before on commit drop as
|
|
131
|
+
select (select count(*) from agent_board.delivery_recipients where board_id=(select board1 from xb_fixture)) as deliveries,
|
|
132
|
+
(select count(*) from agent_board.inbox_items where board_id=(select board1 from xb_fixture)) as inbox;
|
|
133
|
+
select throws_ok($$select * from agent_board.create_tasking_delivery((select id from xb_writer_threads where kind='rollback'),
|
|
134
|
+
(select actor from xb_fixture),'XB rollback task',p_board_id=>(select board1 from xb_fixture))$$,
|
|
135
|
+
'P0001','XB injected task-event failure','XB tasking late failure is caught after the first stamp');
|
|
136
|
+
select is((select board_id from agent_board.threads where id=(select id from xb_writer_threads where kind='rollback')),
|
|
137
|
+
null::uuid,'XB late tasking failure rolls back the first stamp');
|
|
138
|
+
select is((select count(*) from agent_board.tasks where thread_id=(select id from xb_writer_threads where kind='rollback'))
|
|
139
|
+
+(select count(*) from agent_board.task_events where thread_id=(select id from xb_writer_threads where kind='rollback'))
|
|
140
|
+
+(select count(*) from agent_board.messages where thread_id=(select id from xb_writer_threads where kind='rollback')),
|
|
141
|
+
0::bigint,'XB late tasking failure leaves no task, event or message');
|
|
142
|
+
select ok((select deliveries=(select count(*) from agent_board.delivery_recipients where board_id=(select board1 from xb_fixture))
|
|
143
|
+
and inbox=(select count(*) from agent_board.inbox_items where board_id=(select board1 from xb_fixture)) from xb_rollback_before),
|
|
144
|
+
'XB late tasking failure leaves board deliveries and inbox unchanged');
|
|
145
|
+
-- A post failure at the inbox insertion boundary occurs after its message and
|
|
146
|
+
-- first delivery row exist. Observe that custody before raising, then verify
|
|
147
|
+
-- the complete operation including its initial stamp rolled back.
|
|
148
|
+
create function pg_temp.xb_fail_post_inbox() returns trigger language plpgsql as $body$
|
|
149
|
+
begin
|
|
150
|
+
if new.subject='XB rollback post' then
|
|
151
|
+
if not exists(select 1 from agent_board.messages m join agent_board.threads th on th.id=m.thread_id
|
|
152
|
+
join agent_board.delivery_recipients d on d.source_message_id=m.id
|
|
153
|
+
where m.id=new.source_message_id and th.board_id=(select board1 from xb_fixture) and d.id=new.delivery_recipient_id) then
|
|
154
|
+
raise exception 'XB inbox reached without stamped message and delivery' using errcode='P0001';
|
|
155
|
+
end if;
|
|
156
|
+
raise exception 'XB injected inbox failure' using errcode='P0001';
|
|
157
|
+
end if;
|
|
158
|
+
return new;
|
|
159
|
+
end $body$;
|
|
160
|
+
create trigger xb_fail_post_inbox before insert on agent_board.inbox_items for each row execute function pg_temp.xb_fail_post_inbox();
|
|
161
|
+
create temp table xb_post_before on commit drop as
|
|
162
|
+
select (select count(*) from agent_board.delivery_recipients where board_id=(select board1 from xb_fixture)) as deliveries,
|
|
163
|
+
(select count(*) from agent_board.inbox_items where board_id=(select board1 from xb_fixture)) as inbox;
|
|
164
|
+
select throws_ok($$select * from agent_board.post_board_message((select id from xb_writer_threads where kind='rollback_post'),
|
|
165
|
+
(select actor from xb_fixture),(select board1 from xb_fixture),'XB rollback post','body')$$,
|
|
166
|
+
'P0001','XB injected inbox failure','XB post fails after stamp, message and delivery exist');
|
|
167
|
+
select is((select board_id from agent_board.threads where id=(select id from xb_writer_threads where kind='rollback_post')),
|
|
168
|
+
null::uuid,'XB late post failure rolls back its stamp');
|
|
169
|
+
select is((select count(*) from agent_board.messages where thread_id=(select id from xb_writer_threads where kind='rollback_post')),
|
|
170
|
+
0::bigint,'XB late post failure rolls back its message');
|
|
171
|
+
select ok((select deliveries=(select count(*) from agent_board.delivery_recipients where board_id=(select board1 from xb_fixture))
|
|
172
|
+
and inbox=(select count(*) from agent_board.inbox_items where board_id=(select board1 from xb_fixture)) from xb_post_before),
|
|
173
|
+
'XB late post failure rolls back delivery and inbox rows');
|
|
174
|
+
-- Signed wrappers must use the same empty-thread boundary and retain the
|
|
175
|
+
-- actor/source evidence linking the operation to the resulting history.
|
|
176
|
+
select lives_ok($$select * from agent_board.signed_post_board_message(
|
|
177
|
+
jsonb_build_object('p_thread_id',(select id from xb_writer_threads where kind='signed_post'),
|
|
178
|
+
'p_author_agent_id',(select actor from xb_fixture),'p_board_id',(select board1 from xb_fixture),
|
|
179
|
+
'p_subject','XB signed post','p_body','signed body'),
|
|
180
|
+
jsonb_build_object('agent_id',(select actor from xb_fixture),'verification_status','warn',
|
|
181
|
+
'payload_digest','sha256:'||repeat('a',64),'enforcement_mode','warn'))$$,
|
|
182
|
+
'XB signed board post first-associates an empty thread');
|
|
183
|
+
select ok(exists(select 1 from agent_board.messages m join agent_board.threads th on th.id=m.thread_id
|
|
184
|
+
join agent_board.signature_evidence e on e.source_message_id=m.id
|
|
185
|
+
where th.id=(select id from xb_writer_threads where kind='signed_post') and th.board_id=(select board1 from xb_fixture)
|
|
186
|
+
and e.agent_id=(select actor from xb_fixture) and e.operation='post_board_message' and e.verification_status='warn'),
|
|
187
|
+
'XB signed board post retains board, actor and message evidence');
|
|
188
|
+
select lives_ok($$select * from agent_board.signed_create_tasking_delivery(
|
|
189
|
+
jsonb_build_object('p_thread_id',(select id from xb_writer_threads where kind='signed_task'),
|
|
190
|
+
'p_created_by_agent_id',(select actor from xb_fixture),'p_board_id',(select board1 from xb_fixture),'p_title','XB signed task'),
|
|
191
|
+
jsonb_build_object('agent_id',(select actor from xb_fixture),'verification_status','warn',
|
|
192
|
+
'payload_digest','sha256:'||repeat('b',64),'enforcement_mode','warn'))$$,
|
|
193
|
+
'XB signed board task first-associates an empty thread');
|
|
194
|
+
select ok(exists(select 1 from agent_board.tasks t join agent_board.threads th on th.id=t.thread_id
|
|
195
|
+
join agent_board.task_events ev on ev.task_id=t.id join agent_board.signature_evidence e on e.source_task_event_id=ev.id
|
|
196
|
+
where th.id=(select id from xb_writer_threads where kind='signed_task') and th.board_id=(select board1 from xb_fixture)
|
|
197
|
+
and e.agent_id=(select actor from xb_fixture) and e.operation='create_tasking_delivery' and e.verification_status='warn'),
|
|
198
|
+
'XB signed board task retains board, actor and task-event evidence');
|
|
199
|
+
select lives_ok($$select * from agent_board.delegate_task((select id from agent_board.tasks where thread_id=(select id from xb_writer_threads where kind='anchor') and title='anchor'),
|
|
200
|
+
(select actor from xb_fixture),'XB private child',p_delegated_to_agent_id=>(select actor from xb_fixture))$$,
|
|
201
|
+
'XB delegation on a private parent succeeds without association');
|
|
202
|
+
select ok(exists(select 1 from agent_board.delegations d join agent_board.tasks parent on parent.id=d.parent_task_id
|
|
203
|
+
join agent_board.tasks child on child.id=d.child_task_id where parent.thread_id=(select id from xb_writer_threads where kind='anchor')
|
|
204
|
+
and child.thread_id=parent.thread_id and child.title='XB private child'),
|
|
205
|
+
'XB private delegation keeps the actual parent and child on the same thread');
|
|
206
|
+
select throws_ok($$select * from agent_board.post_board_message((select id from xb_writer_threads where kind='anchor'),
|
|
207
|
+
(select actor from xb_fixture),(select board1 from xb_fixture),'after delegation','body')$$,
|
|
208
|
+
'42501','thread has private history','XB delegated private history cannot be associated');
|
|
209
|
+
select ok((select board_id is null from agent_board.threads where id=(select id from xb_writer_threads where kind='anchor'))
|
|
210
|
+
and exists(select 1 from agent_board.tasks where title='XB private child' and thread_id=(select id from xb_writer_threads where kind='anchor')),
|
|
211
|
+
'XB refusal preserves the private delegated child and null stamp');
|
|
212
|
+
-- Existing-row updates do not erase private history. Exercise a released
|
|
213
|
+
-- claim and a resolved conflict, retaining each real thread anchor.
|
|
214
|
+
create temp table xb_retained_claim(lease_token uuid) on commit drop;
|
|
215
|
+
select lives_ok($$insert into xb_retained_claim select lease_token from agent_board.acquire_claim((select actor from xb_fixture),
|
|
216
|
+
'custom','xb-released-history',now()+interval '1 hour',p_thread_id=>(select id from xb_writer_threads where kind='released_claim'))$$,
|
|
217
|
+
'XB released-history claim is created');
|
|
218
|
+
select lives_ok($$select * from agent_board.release_claim((select lease_token from xb_retained_claim))$$,
|
|
219
|
+
'XB claim release succeeds');
|
|
220
|
+
select ok(exists(select 1 from agent_board.claims where lease_token=(select lease_token from xb_retained_claim)
|
|
221
|
+
and status='released' and thread_id=(select id from xb_writer_threads where kind='released_claim')),
|
|
222
|
+
'XB released claim retains its thread anchor');
|
|
223
|
+
select throws_ok($$select * from agent_board.post_board_message((select id from xb_writer_threads where kind='released_claim'),
|
|
224
|
+
(select actor from xb_fixture),(select board1 from xb_fixture),'released','body')$$,
|
|
225
|
+
'42501','thread has private history','XB released claim still prevents association');
|
|
226
|
+
select is((select board_id from agent_board.threads where id=(select id from xb_writer_threads where kind='released_claim')),
|
|
227
|
+
null::uuid,'XB released-claim refusal preserves null stamp');
|
|
228
|
+
create temp table xb_resolved_conflict(conflict_id uuid) on commit drop;
|
|
229
|
+
select lives_ok($$insert into xb_resolved_conflict select conflict_id from agent_board.raise_conflict(
|
|
230
|
+
(select id from xb_writer_threads where kind='resolved_conflict'),(select actor from xb_fixture),'XB resolved history')$$,
|
|
231
|
+
'XB resolved-history conflict is created');
|
|
232
|
+
select lives_ok($$select * from agent_board.resolve_conflict((select conflict_id from xb_resolved_conflict),(select actor from xb_fixture))$$,
|
|
233
|
+
'XB conflict resolution succeeds');
|
|
234
|
+
select ok(exists(select 1 from agent_board.conflicts where id=(select conflict_id from xb_resolved_conflict)
|
|
235
|
+
and status='resolved' and thread_id=(select id from xb_writer_threads where kind='resolved_conflict')),
|
|
236
|
+
'XB resolved conflict retains its thread anchor');
|
|
237
|
+
select throws_ok($$select * from agent_board.post_board_message((select id from xb_writer_threads where kind='resolved_conflict'),
|
|
238
|
+
(select actor from xb_fixture),(select board1 from xb_fixture),'resolved','body')$$,
|
|
239
|
+
'42501','thread has private history','XB resolved conflict still prevents association');
|
|
240
|
+
select is((select board_id from agent_board.threads where id=(select id from xb_writer_threads where kind='resolved_conflict')),
|
|
241
|
+
null::uuid,'XB resolved-conflict refusal preserves null stamp');
|
|
242
|
+
-- Escalations and thread memberships are history too (#707 B1). Each thread's
|
|
243
|
+
-- only history is that one row; association must refuse and leave it unstamped
|
|
244
|
+
-- and messageless. The membership is a direct insert under the service_role
|
|
245
|
+
-- grant, which is the only writer of that relation.
|
|
246
|
+
select lives_ok($$select * from agent_board.raise_escalation((select id from xb_writer_threads where kind='escalation'),
|
|
247
|
+
(select actor from xb_fixture),'XB private escalation')$$,'XB thread-level escalation writes history');
|
|
248
|
+
select ok((select count(*) from agent_board.escalations where thread_id=t.id)=1
|
|
249
|
+
and not exists(select 1 from agent_board.messages where thread_id=t.id) and not exists(select 1 from agent_board.tasks where thread_id=t.id)
|
|
250
|
+
and not exists(select 1 from agent_board.task_events where thread_id=t.id) and not exists(select 1 from agent_board.artifacts where thread_id=t.id)
|
|
251
|
+
and not exists(select 1 from agent_board.claims where thread_id=t.id) and not exists(select 1 from agent_board.conflicts where thread_id=t.id)
|
|
252
|
+
and not exists(select 1 from agent_board.presence where thread_id=t.id) and not exists(select 1 from agent_board.thread_memberships where thread_id=t.id),
|
|
253
|
+
'XB escalation is the thread''s only history') from xb_writer_threads t where t.kind='escalation';
|
|
254
|
+
select throws_ok($$select * from agent_board.post_board_message((select id from xb_writer_threads where kind='escalation'),
|
|
255
|
+
(select actor from xb_fixture),(select board1 from xb_fixture),'escalated','body')$$,
|
|
256
|
+
'42501','thread has private history','XB escalation-only history refuses association');
|
|
257
|
+
select is((select board_id from agent_board.threads where id=(select id from xb_writer_threads where kind='escalation')),
|
|
258
|
+
null::uuid,'XB escalation-only refusal preserves null stamp');
|
|
259
|
+
select is((select count(*) from agent_board.messages where thread_id=(select id from xb_writer_threads where kind='escalation')),
|
|
260
|
+
0::bigint,'XB escalation-only refusal leaves the message count at zero');
|
|
261
|
+
-- Ids are interpolated first: service_role cannot read this file's temp tables.
|
|
262
|
+
select lives_ok(format($$set local role service_role;
|
|
263
|
+
insert into agent_board.thread_memberships(thread_id,agent_id,granted_by_agent_id) values (%L::uuid,%L::uuid,%L::uuid);
|
|
264
|
+
reset role$$,(select id from xb_writer_threads where kind='membership'),
|
|
265
|
+
(select created_by_agent_id from agent_board.boards where id=(select board1 from xb_fixture)),(select actor from xb_fixture)),
|
|
266
|
+
'XB service_role direct membership insert writes history');
|
|
267
|
+
select ok((select count(*) from agent_board.thread_memberships where thread_id=t.id)=1
|
|
268
|
+
and not exists(select 1 from agent_board.messages where thread_id=t.id) and not exists(select 1 from agent_board.tasks where thread_id=t.id)
|
|
269
|
+
and not exists(select 1 from agent_board.task_events where thread_id=t.id) and not exists(select 1 from agent_board.artifacts where thread_id=t.id)
|
|
270
|
+
and not exists(select 1 from agent_board.claims where thread_id=t.id) and not exists(select 1 from agent_board.conflicts where thread_id=t.id)
|
|
271
|
+
and not exists(select 1 from agent_board.presence where thread_id=t.id) and not exists(select 1 from agent_board.escalations where thread_id=t.id),
|
|
272
|
+
'XB membership is the thread''s only history') from xb_writer_threads t where t.kind='membership';
|
|
273
|
+
select throws_ok($$select * from agent_board.post_board_message((select id from xb_writer_threads where kind='membership'),
|
|
274
|
+
(select actor from xb_fixture),(select board1 from xb_fixture),'membership','body')$$,
|
|
275
|
+
'42501','thread has private history','XB membership-only history refuses association');
|
|
276
|
+
select is((select board_id from agent_board.threads where id=(select id from xb_writer_threads where kind='membership')),
|
|
277
|
+
null::uuid,'XB membership-only refusal preserves null stamp');
|
|
278
|
+
select is((select count(*) from agent_board.messages where thread_id=(select id from xb_writer_threads where kind='membership')),
|
|
279
|
+
0::bigint,'XB membership-only refusal leaves the message count at zero');
|
|
280
|
+
select * from finish();
|
|
281
|
+
rollback;
|