@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,41 @@
|
|
|
1
|
+
# #667: thread-only artifact, thread-only claim and taskless escalation each
|
|
2
|
+
# serialize with first association. Each writer and the association win once.
|
|
3
|
+
setup
|
|
4
|
+
{
|
|
5
|
+
insert into agent_board.agents(id,agent_type,handle,display_name,status)
|
|
6
|
+
values ('66720000-0000-4000-8000-000000000001','agent','xb-art-claim-esc','XB art claim escalation','active');
|
|
7
|
+
insert into agent_board.boards(id,slug,name,created_by_agent_id,visibility,status)
|
|
8
|
+
values ('66720000-0000-4000-8000-000000000020','xb-art-claim-esc-board','XB art claim escalation board',
|
|
9
|
+
'66720000-0000-4000-8000-000000000001','private','active');
|
|
10
|
+
insert into agent_board.threads(id,thread_type,created_by_agent_id,subject)
|
|
11
|
+
values ('66720000-0000-4000-8000-000000000010','coordination',
|
|
12
|
+
'66720000-0000-4000-8000-000000000001','artifact claim escalation race');
|
|
13
|
+
}
|
|
14
|
+
teardown
|
|
15
|
+
{
|
|
16
|
+
delete from agent_board.artifacts where thread_id='66720000-0000-4000-8000-000000000010';
|
|
17
|
+
delete from agent_board.claims where thread_id='66720000-0000-4000-8000-000000000010';
|
|
18
|
+
delete from agent_board.escalations where thread_id='66720000-0000-4000-8000-000000000010';
|
|
19
|
+
delete from agent_board.messages where thread_id='66720000-0000-4000-8000-000000000010';
|
|
20
|
+
delete from agent_board.threads where id='66720000-0000-4000-8000-000000000010';
|
|
21
|
+
delete from agent_board.boards where id='66720000-0000-4000-8000-000000000020';
|
|
22
|
+
delete from agent_board.agents where id='66720000-0000-4000-8000-000000000001';
|
|
23
|
+
}
|
|
24
|
+
session "w"
|
|
25
|
+
step "w_begin" { begin; select set_config('request.jwt.claim.role','service_role',true); }
|
|
26
|
+
step "w_artifact" { select count(*) from agent_board.add_artifact('66720000-0000-4000-8000-000000000001','https://example.test/xb',p_thread_id=>'66720000-0000-4000-8000-000000000010'); }
|
|
27
|
+
step "w_claim" { select count(*) from agent_board.acquire_claim('66720000-0000-4000-8000-000000000001','custom','xb-race-key',now()+interval '1 hour',p_thread_id=>'66720000-0000-4000-8000-000000000010'); }
|
|
28
|
+
step "w_escalation" { select count(*) from agent_board.raise_escalation('66720000-0000-4000-8000-000000000010','66720000-0000-4000-8000-000000000001','private escalation'); }
|
|
29
|
+
step "w_commit" { commit; }
|
|
30
|
+
session "a"
|
|
31
|
+
step "a_begin" { begin; select set_config('request.jwt.claim.role','service_role',true); }
|
|
32
|
+
step "a_post" { select count(*) from agent_board.post_board_message('66720000-0000-4000-8000-000000000010','66720000-0000-4000-8000-000000000001','66720000-0000-4000-8000-000000000020','s','b'); }
|
|
33
|
+
step "a_end" { end; }
|
|
34
|
+
session "check"
|
|
35
|
+
step "check" { select (t.board_id is not null) as stamped, (select count(*) from agent_board.artifacts where thread_id=t.id) as artifacts, (select count(*) from agent_board.claims where thread_id=t.id) as claims, (select count(*) from agent_board.escalations where thread_id=t.id) as escalations, (select count(*) from agent_board.messages where thread_id=t.id) as messages from agent_board.threads t where t.id='66720000-0000-4000-8000-000000000010'; }
|
|
36
|
+
permutation "w_begin" "w_artifact" "a_begin" "a_post" "w_commit" "a_end" "check"
|
|
37
|
+
permutation "a_begin" "a_post" "w_begin" "w_artifact" "a_end" "w_commit" "check"
|
|
38
|
+
permutation "w_begin" "w_claim" "a_begin" "a_post" "w_commit" "a_end" "check"
|
|
39
|
+
permutation "a_begin" "a_post" "w_begin" "w_claim" "a_end" "w_commit" "check"
|
|
40
|
+
permutation "w_begin" "w_escalation" "a_begin" "a_post" "w_commit" "a_end" "check"
|
|
41
|
+
permutation "a_begin" "a_post" "w_begin" "w_escalation" "a_end" "w_commit" "check"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# #667: a heartbeat retaining its old thread and a first board association
|
|
2
|
+
# serialize on the thread. The second permutation covers an empty association
|
|
3
|
+
# winning before a heartbeat explicitly supplies that thread.
|
|
4
|
+
setup
|
|
5
|
+
{
|
|
6
|
+
insert into agent_board.agents(id, agent_type, handle, display_name, status)
|
|
7
|
+
values ('66700000-0000-4000-8000-000000000001', 'agent', 'xb-heartbeat', 'XB heartbeat', 'active');
|
|
8
|
+
select set_config('request.jwt.claim.role','service_role',false);
|
|
9
|
+
insert into agent_board.boards(id,slug,name,created_by_agent_id,visibility,status)
|
|
10
|
+
values ('66700000-0000-4000-8000-000000000020','xb-heartbeat-board','XB heartbeat board',
|
|
11
|
+
'66700000-0000-4000-8000-000000000001','private','active');
|
|
12
|
+
insert into agent_board.threads(id,thread_type,created_by_agent_id,subject)
|
|
13
|
+
values ('66700000-0000-4000-8000-000000000010','coordination',
|
|
14
|
+
'66700000-0000-4000-8000-000000000001','heartbeat race');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
teardown
|
|
18
|
+
{
|
|
19
|
+
delete from agent_board.callback_outbox_events where source_message_id in (select id from agent_board.messages where thread_id='66700000-0000-4000-8000-000000000010');
|
|
20
|
+
delete from agent_board.inbox_items where source_message_id in (select id from agent_board.messages where thread_id='66700000-0000-4000-8000-000000000010');
|
|
21
|
+
delete from agent_board.delivery_recipients where source_message_id in (select id from agent_board.messages where thread_id='66700000-0000-4000-8000-000000000010');
|
|
22
|
+
delete from agent_board.messages where thread_id='66700000-0000-4000-8000-000000000010';
|
|
23
|
+
delete from agent_board.presence where agent_id='66700000-0000-4000-8000-000000000001';
|
|
24
|
+
delete from agent_board.threads where id='66700000-0000-4000-8000-000000000010';
|
|
25
|
+
delete from agent_board.board_membership_events where board_id in (select id from agent_board.boards where slug='xb-heartbeat-board');
|
|
26
|
+
delete from agent_board.board_memberships where board_id in (select id from agent_board.boards where slug='xb-heartbeat-board');
|
|
27
|
+
delete from agent_board.boards where slug='xb-heartbeat-board';
|
|
28
|
+
delete from agent_board.agents where id='66700000-0000-4000-8000-000000000001';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
session "h"
|
|
32
|
+
step "h_prime" { select set_config('request.jwt.claim.role','service_role',false); select count(*) from agent_board.heartbeat('66700000-0000-4000-8000-000000000001','66700000-0000-4000-8000-000000000010',p_activity_summary => 'initial'); }
|
|
33
|
+
step "h_begin" { begin; select set_config('request.jwt.claim.role','service_role',true); }
|
|
34
|
+
step "h_retain" { select count(*) from agent_board.heartbeat('66700000-0000-4000-8000-000000000001',null,p_activity_summary => 'retained',p_metadata => jsonb_build_object('note','private')); }
|
|
35
|
+
step "h_attach" { select count(*) from agent_board.heartbeat('66700000-0000-4000-8000-000000000001','66700000-0000-4000-8000-000000000010',p_activity_summary => 'after association'); }
|
|
36
|
+
step "h_commit" { commit; }
|
|
37
|
+
|
|
38
|
+
session "a"
|
|
39
|
+
step "a_begin" { begin; select set_config('request.jwt.claim.role','service_role',true); }
|
|
40
|
+
step "a_post" { select count(*) from agent_board.post_board_message('66700000-0000-4000-8000-000000000010','66700000-0000-4000-8000-000000000001',(select id from agent_board.boards where slug='xb-heartbeat-board'),'s','b'); }
|
|
41
|
+
step "a_end" { end; }
|
|
42
|
+
|
|
43
|
+
session "check"
|
|
44
|
+
step "check" { select (t.board_id is not null) as stamped, p.thread_id is not null as presence_has_thread, p.activity_summary from agent_board.threads t left join agent_board.presence p on p.agent_id='66700000-0000-4000-8000-000000000001' where t.id='66700000-0000-4000-8000-000000000010'; }
|
|
45
|
+
|
|
46
|
+
permutation "h_prime" "h_begin" "h_retain" "a_begin" "a_post" "h_commit" "a_end" "check"
|
|
47
|
+
permutation "a_begin" "a_post" "h_begin" "h_attach" "a_end" "h_commit" "check"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# #667: an artifact may name an empty explicit thread and a task on another
|
|
2
|
+
# thread. The explicit thread must serialize with first association even when a
|
|
3
|
+
# different canonical anchor is also present.
|
|
4
|
+
setup
|
|
5
|
+
{
|
|
6
|
+
insert into agent_board.agents(id,agent_type,handle,display_name,status)
|
|
7
|
+
values ('66730000-0000-4000-8000-000000000001','agent','xb-multi-anchor','XB multi anchor','active');
|
|
8
|
+
insert into agent_board.boards(id,slug,name,created_by_agent_id,visibility,status)
|
|
9
|
+
values ('66730000-0000-4000-8000-000000000020','xb-multi-anchor-board','XB multi anchor board',
|
|
10
|
+
'66730000-0000-4000-8000-000000000001','private','active');
|
|
11
|
+
insert into agent_board.threads(id,thread_type,created_by_agent_id,subject)
|
|
12
|
+
values ('66730000-0000-4000-8000-000000000010','coordination','66730000-0000-4000-8000-000000000001','explicit empty'),
|
|
13
|
+
('66730000-0000-4000-8000-000000000011','coordination','66730000-0000-4000-8000-000000000001','task parent');
|
|
14
|
+
insert into agent_board.tasks(id,thread_id,created_by_agent_id,title)
|
|
15
|
+
values ('66730000-0000-4000-8000-000000000030','66730000-0000-4000-8000-000000000011','66730000-0000-4000-8000-000000000001','parent task');
|
|
16
|
+
}
|
|
17
|
+
teardown
|
|
18
|
+
{
|
|
19
|
+
delete from agent_board.artifacts where thread_id='66730000-0000-4000-8000-000000000010';
|
|
20
|
+
delete from agent_board.task_events where task_id='66730000-0000-4000-8000-000000000030';
|
|
21
|
+
delete from agent_board.tasks where id='66730000-0000-4000-8000-000000000030';
|
|
22
|
+
delete from agent_board.messages where thread_id='66730000-0000-4000-8000-000000000010';
|
|
23
|
+
delete from agent_board.threads where id in ('66730000-0000-4000-8000-000000000010','66730000-0000-4000-8000-000000000011');
|
|
24
|
+
delete from agent_board.boards where id='66730000-0000-4000-8000-000000000020';
|
|
25
|
+
delete from agent_board.agents where id='66730000-0000-4000-8000-000000000001';
|
|
26
|
+
}
|
|
27
|
+
session "w"
|
|
28
|
+
step "w_begin" { begin; select set_config('request.jwt.claim.role','service_role',true); }
|
|
29
|
+
step "w_artifact" { select count(*) from agent_board.add_artifact('66730000-0000-4000-8000-000000000001','https://example.test/mixed',p_thread_id=>'66730000-0000-4000-8000-000000000010',p_task_id=>'66730000-0000-4000-8000-000000000030'); }
|
|
30
|
+
step "w_commit" { commit; }
|
|
31
|
+
session "a"
|
|
32
|
+
step "a_begin" { begin; select set_config('request.jwt.claim.role','service_role',true); }
|
|
33
|
+
step "a_post" { select count(*) from agent_board.post_board_message('66730000-0000-4000-8000-000000000010','66730000-0000-4000-8000-000000000001','66730000-0000-4000-8000-000000000020','s','b'); }
|
|
34
|
+
step "a_end" { end; }
|
|
35
|
+
session "check"
|
|
36
|
+
step "check" { select (t.board_id is not null) as stamped, (select count(*) from agent_board.artifacts where thread_id=t.id and task_id='66730000-0000-4000-8000-000000000030') as mixed_artifacts, (select count(*) from agent_board.messages where thread_id=t.id) as messages from agent_board.threads t where t.id='66730000-0000-4000-8000-000000000010'; }
|
|
37
|
+
permutation "w_begin" "w_artifact" "a_begin" "a_post" "w_commit" "a_end" "check"
|
|
38
|
+
permutation "a_begin" "a_post" "w_begin" "w_artifact" "a_end" "w_commit" "check"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# #667: a private append and first board association serialize on the thread.
|
|
2
|
+
# The private writer wins with committed history and the association refuses;
|
|
3
|
+
# if association wins, the later service writer uses the stamped thread.
|
|
4
|
+
setup
|
|
5
|
+
{
|
|
6
|
+
insert into agent_board.agents(id,agent_type,handle,display_name,status)
|
|
7
|
+
values ('66810000-0000-4000-8000-000000000001','agent','xb-private-race','XB private race','active');
|
|
8
|
+
insert into agent_board.boards(id,slug,name,created_by_agent_id,visibility,status)
|
|
9
|
+
values ('66810000-0000-4000-8000-000000000020','xb-private-race-board','XB private race board',
|
|
10
|
+
'66810000-0000-4000-8000-000000000001','private','active');
|
|
11
|
+
insert into agent_board.threads(id,thread_type,created_by_agent_id,subject)
|
|
12
|
+
values ('66810000-0000-4000-8000-000000000010','coordination',
|
|
13
|
+
'66810000-0000-4000-8000-000000000001','private race');
|
|
14
|
+
}
|
|
15
|
+
teardown
|
|
16
|
+
{
|
|
17
|
+
delete from agent_board.messages where thread_id='66810000-0000-4000-8000-000000000010';
|
|
18
|
+
delete from agent_board.threads where id='66810000-0000-4000-8000-000000000010';
|
|
19
|
+
delete from agent_board.boards where id='66810000-0000-4000-8000-000000000020';
|
|
20
|
+
delete from agent_board.agents where id='66810000-0000-4000-8000-000000000001';
|
|
21
|
+
}
|
|
22
|
+
session "w"
|
|
23
|
+
step "w_begin" { begin; select set_config('request.jwt.claim.role','service_role',true); }
|
|
24
|
+
step "w_post" { select count(*) from agent_board.post_message('66810000-0000-4000-8000-000000000010','66810000-0000-4000-8000-000000000001','private race message'); }
|
|
25
|
+
step "w_commit" { commit; }
|
|
26
|
+
session "a"
|
|
27
|
+
step "a_begin" { begin; select set_config('request.jwt.claim.role','service_role',true); }
|
|
28
|
+
step "a_post" { select count(*) from agent_board.post_board_message('66810000-0000-4000-8000-000000000010','66810000-0000-4000-8000-000000000001','66810000-0000-4000-8000-000000000020','board','board race message'); }
|
|
29
|
+
step "a_end" { end; }
|
|
30
|
+
session "check"
|
|
31
|
+
step "check" { select (t.board_id is not null) as stamped, (select count(*) from agent_board.messages m where m.thread_id=t.id and m.body='private race message') as private_messages, (select count(*) from agent_board.messages m where m.thread_id=t.id and m.body='board race message') as board_messages from agent_board.threads t where t.id='66810000-0000-4000-8000-000000000010'; }
|
|
32
|
+
permutation "w_begin" "w_post" "a_begin" "a_post" "w_commit" "a_end" "check"
|
|
33
|
+
permutation "a_begin" "a_post" "w_begin" "w_post" "a_end" "w_commit" "check"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# #667: private task creation and taskless conflict serialize with first board
|
|
2
|
+
# association. Each operation wins once; the checker observes the resulting rows.
|
|
3
|
+
setup
|
|
4
|
+
{
|
|
5
|
+
insert into agent_board.agents(id,agent_type,handle,display_name,status)
|
|
6
|
+
values ('66710000-0000-4000-8000-000000000001','agent','xb-task-conflict','XB task conflict','active');
|
|
7
|
+
insert into agent_board.boards(id,slug,name,created_by_agent_id,visibility,status)
|
|
8
|
+
values ('66710000-0000-4000-8000-000000000020','xb-task-conflict-board','XB task conflict board',
|
|
9
|
+
'66710000-0000-4000-8000-000000000001','private','active');
|
|
10
|
+
insert into agent_board.threads(id,thread_type,created_by_agent_id,subject)
|
|
11
|
+
values ('66710000-0000-4000-8000-000000000010','coordination',
|
|
12
|
+
'66710000-0000-4000-8000-000000000001','task conflict race');
|
|
13
|
+
}
|
|
14
|
+
teardown
|
|
15
|
+
{
|
|
16
|
+
delete from agent_board.conflicts where thread_id='66710000-0000-4000-8000-000000000010';
|
|
17
|
+
delete from agent_board.task_events where thread_id='66710000-0000-4000-8000-000000000010';
|
|
18
|
+
delete from agent_board.tasks where thread_id='66710000-0000-4000-8000-000000000010';
|
|
19
|
+
delete from agent_board.messages where thread_id='66710000-0000-4000-8000-000000000010';
|
|
20
|
+
delete from agent_board.threads where id='66710000-0000-4000-8000-000000000010';
|
|
21
|
+
delete from agent_board.boards where id='66710000-0000-4000-8000-000000000020';
|
|
22
|
+
delete from agent_board.agents where id='66710000-0000-4000-8000-000000000001';
|
|
23
|
+
}
|
|
24
|
+
session "w"
|
|
25
|
+
step "w_begin" { begin; select set_config('request.jwt.claim.role','service_role',true); }
|
|
26
|
+
step "w_task" { select count(*) from agent_board.create_task('66710000-0000-4000-8000-000000000010','66710000-0000-4000-8000-000000000001','private task'); }
|
|
27
|
+
step "w_conflict" { select count(*) from agent_board.raise_conflict('66710000-0000-4000-8000-000000000010','66710000-0000-4000-8000-000000000001','private conflict'); }
|
|
28
|
+
step "w_commit" { commit; }
|
|
29
|
+
session "a"
|
|
30
|
+
step "a_begin" { begin; select set_config('request.jwt.claim.role','service_role',true); }
|
|
31
|
+
step "a_post" { select count(*) from agent_board.post_board_message('66710000-0000-4000-8000-000000000010','66710000-0000-4000-8000-000000000001','66710000-0000-4000-8000-000000000020','s','b'); }
|
|
32
|
+
step "a_end" { end; }
|
|
33
|
+
session "check"
|
|
34
|
+
step "check" { select (t.board_id is not null) as stamped, (select count(*) from agent_board.tasks where thread_id=t.id) as tasks, (select count(*) from agent_board.conflicts where thread_id=t.id) as conflicts, (select count(*) from agent_board.messages where thread_id=t.id) as messages from agent_board.threads t where t.id='66710000-0000-4000-8000-000000000010'; }
|
|
35
|
+
permutation "w_begin" "w_task" "a_begin" "a_post" "w_commit" "a_end" "check"
|
|
36
|
+
permutation "a_begin" "a_post" "w_begin" "w_task" "a_end" "w_commit" "check"
|
|
37
|
+
permutation "w_begin" "w_conflict" "a_begin" "a_post" "w_commit" "a_end" "check"
|
|
38
|
+
permutation "a_begin" "a_post" "w_begin" "w_conflict" "a_end" "w_commit" "check"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# #667: a task argument does not prove that a conflict or escalation's own
|
|
2
|
+
# thread equals its task thread. Both taskful writers serialize on their supplied
|
|
3
|
+
# thread before appending an event to the separately anchored task.
|
|
4
|
+
setup
|
|
5
|
+
{
|
|
6
|
+
insert into agent_board.agents(id,agent_type,handle,display_name,status)
|
|
7
|
+
values ('66760000-0000-4000-8000-000000000001','agent','xb-cross-task','XB cross task','active');
|
|
8
|
+
insert into agent_board.boards(id,slug,name,created_by_agent_id,visibility,status)
|
|
9
|
+
values ('66760000-0000-4000-8000-000000000020','xb-cross-task-board','XB cross task board',
|
|
10
|
+
'66760000-0000-4000-8000-000000000001','private','active');
|
|
11
|
+
insert into agent_board.threads(id,thread_type,created_by_agent_id,subject)
|
|
12
|
+
values ('66760000-0000-4000-8000-000000000010','coordination','66760000-0000-4000-8000-000000000001','empty target'),
|
|
13
|
+
('66760000-0000-4000-8000-000000000011','coordination','66760000-0000-4000-8000-000000000001','task parent');
|
|
14
|
+
insert into agent_board.tasks(id,thread_id,created_by_agent_id,title)
|
|
15
|
+
values ('66760000-0000-4000-8000-000000000030','66760000-0000-4000-8000-000000000011','66760000-0000-4000-8000-000000000001','parent task');
|
|
16
|
+
}
|
|
17
|
+
teardown
|
|
18
|
+
{
|
|
19
|
+
delete from agent_board.conflicts where thread_id='66760000-0000-4000-8000-000000000010';
|
|
20
|
+
delete from agent_board.escalations where thread_id='66760000-0000-4000-8000-000000000010';
|
|
21
|
+
delete from agent_board.task_events where task_id='66760000-0000-4000-8000-000000000030';
|
|
22
|
+
delete from agent_board.tasks where id='66760000-0000-4000-8000-000000000030';
|
|
23
|
+
delete from agent_board.messages where thread_id='66760000-0000-4000-8000-000000000010';
|
|
24
|
+
delete from agent_board.threads where id in ('66760000-0000-4000-8000-000000000010','66760000-0000-4000-8000-000000000011');
|
|
25
|
+
delete from agent_board.boards where id='66760000-0000-4000-8000-000000000020';
|
|
26
|
+
delete from agent_board.agents where id='66760000-0000-4000-8000-000000000001';
|
|
27
|
+
}
|
|
28
|
+
session "w"
|
|
29
|
+
step "w_begin" { begin; select set_config('request.jwt.claim.role','service_role',true); }
|
|
30
|
+
step "w_conflict" { select count(*) from agent_board.raise_conflict('66760000-0000-4000-8000-000000000010','66760000-0000-4000-8000-000000000001','private conflict',p_task_id=>'66760000-0000-4000-8000-000000000030'); }
|
|
31
|
+
step "w_escalation" { select count(*) from agent_board.raise_escalation('66760000-0000-4000-8000-000000000010','66760000-0000-4000-8000-000000000001','private escalation',p_task_id=>'66760000-0000-4000-8000-000000000030'); }
|
|
32
|
+
step "w_commit" { commit; }
|
|
33
|
+
session "a"
|
|
34
|
+
step "a_begin" { begin; select set_config('request.jwt.claim.role','service_role',true); }
|
|
35
|
+
step "a_post" { select count(*) from agent_board.post_board_message('66760000-0000-4000-8000-000000000010','66760000-0000-4000-8000-000000000001','66760000-0000-4000-8000-000000000020','s','b'); }
|
|
36
|
+
step "a_end" { end; }
|
|
37
|
+
session "check"
|
|
38
|
+
step "check" { select (t.board_id is not null) as stamped, (select count(*) from agent_board.conflicts where thread_id=t.id and task_id='66760000-0000-4000-8000-000000000030') as conflicts, (select count(*) from agent_board.escalations where thread_id=t.id and task_id='66760000-0000-4000-8000-000000000030') as escalations, (select count(*) from agent_board.messages where thread_id=t.id) as messages from agent_board.threads t where t.id='66760000-0000-4000-8000-000000000010'; }
|
|
39
|
+
permutation "w_begin" "w_conflict" "a_begin" "a_post" "w_commit" "a_end" "check"
|
|
40
|
+
permutation "a_begin" "a_post" "w_begin" "w_conflict" "a_end" "w_commit" "check"
|
|
41
|
+
permutation "w_begin" "w_escalation" "a_begin" "a_post" "w_commit" "a_end" "check"
|
|
42
|
+
permutation "a_begin" "a_post" "w_begin" "w_escalation" "a_end" "w_commit" "check"
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# #717 F2b: concurrent same-key send_direct_message / send_multicast_message calls serialize on the
|
|
2
|
+
# unchanged (rpc, thread, resolved author, key) lock, and the second one is judged against what the
|
|
3
|
+
# first COMMITTED. Two sends naming DIFFERENT recipients on one key: whichever commits first is the
|
|
4
|
+
# message, and the other is refused (42501) rather than widening it. Two identical retries: one
|
|
5
|
+
# message, one delivery per recipient, one inbox item each, one callback each.
|
|
6
|
+
# A lock keyed by recipient (split domain) or removed lets the second send miss the uncommitted
|
|
7
|
+
# first message and mint a second one; the check step then counts two messages.
|
|
8
|
+
setup
|
|
9
|
+
{
|
|
10
|
+
insert into auth.users(id) values ('717b4000-0000-4000-8000-0000000000a1'),('717b4000-0000-4000-8000-0000000000c1'),('717b4000-0000-4000-8000-0000000000c2'),('717b4000-0000-4000-8000-0000000000e1');
|
|
11
|
+
insert into agent_board.agents(id,agent_type,handle,display_name,status,auth_user_id)
|
|
12
|
+
values ('717b4000-0000-4000-8000-000000000001','agent','f2b717-iso-a','F2b iso A','active','717b4000-0000-4000-8000-0000000000a1'),
|
|
13
|
+
('717b4000-0000-4000-8000-000000000003','agent','f2b717-iso-c','F2b iso C','active','717b4000-0000-4000-8000-0000000000c1'),
|
|
14
|
+
('717b4000-0000-4000-8000-000000000004','agent','f2b717-iso-c2','F2b iso C2','active','717b4000-0000-4000-8000-0000000000c2'),
|
|
15
|
+
('717b4000-0000-4000-8000-000000000005','agent','f2b717-iso-x','F2b iso X','active','717b4000-0000-4000-8000-0000000000e1');
|
|
16
|
+
insert into agent_board.threads(id,thread_type,created_by_agent_id,subject)
|
|
17
|
+
values ('717b4000-0000-4000-8000-000000000010','coordination','717b4000-0000-4000-8000-000000000001','f2b iso thread');
|
|
18
|
+
insert into agent_board.callback_endpoints(id,owner_agent_id,endpoint_url,endpoint_status)
|
|
19
|
+
values ('717b4000-0000-4000-8000-000000000030','717b4000-0000-4000-8000-000000000003','https://example.test/f2b717-iso-c','verified'),
|
|
20
|
+
('717b4000-0000-4000-8000-000000000031','717b4000-0000-4000-8000-000000000004','https://example.test/f2b717-iso-c2','verified'),
|
|
21
|
+
('717b4000-0000-4000-8000-000000000032','717b4000-0000-4000-8000-000000000005','https://example.test/f2b717-iso-x','verified');
|
|
22
|
+
insert into agent_board.callback_subscriptions(endpoint_id,agent_id,subscription_scope,event_category)
|
|
23
|
+
values ('717b4000-0000-4000-8000-000000000030','717b4000-0000-4000-8000-000000000003','own_inbox','inbox.item.delivered'),
|
|
24
|
+
('717b4000-0000-4000-8000-000000000031','717b4000-0000-4000-8000-000000000004','own_inbox','inbox.item.delivered'),
|
|
25
|
+
('717b4000-0000-4000-8000-000000000032','717b4000-0000-4000-8000-000000000005','own_inbox','inbox.item.delivered');
|
|
26
|
+
}
|
|
27
|
+
teardown
|
|
28
|
+
{
|
|
29
|
+
delete from agent_board.callback_outbox_events where recipient_agent_id in ('717b4000-0000-4000-8000-000000000003','717b4000-0000-4000-8000-000000000004','717b4000-0000-4000-8000-000000000005');
|
|
30
|
+
delete from agent_board.callback_subscriptions where endpoint_id in ('717b4000-0000-4000-8000-000000000030','717b4000-0000-4000-8000-000000000031','717b4000-0000-4000-8000-000000000032');
|
|
31
|
+
delete from agent_board.callback_endpoints where id in ('717b4000-0000-4000-8000-000000000030','717b4000-0000-4000-8000-000000000031','717b4000-0000-4000-8000-000000000032');
|
|
32
|
+
delete from agent_board.inbox_items where recipient_agent_id in ('717b4000-0000-4000-8000-000000000003','717b4000-0000-4000-8000-000000000004','717b4000-0000-4000-8000-000000000005');
|
|
33
|
+
delete from agent_board.delivery_recipients where recipient_agent_id in ('717b4000-0000-4000-8000-000000000003','717b4000-0000-4000-8000-000000000004','717b4000-0000-4000-8000-000000000005');
|
|
34
|
+
delete from agent_board.messages where thread_id='717b4000-0000-4000-8000-000000000010';
|
|
35
|
+
delete from agent_board.threads where id='717b4000-0000-4000-8000-000000000010';
|
|
36
|
+
delete from agent_board.agents where id in ('717b4000-0000-4000-8000-000000000001','717b4000-0000-4000-8000-000000000003','717b4000-0000-4000-8000-000000000004','717b4000-0000-4000-8000-000000000005');
|
|
37
|
+
delete from auth.users where id in ('717b4000-0000-4000-8000-0000000000a1','717b4000-0000-4000-8000-0000000000c1','717b4000-0000-4000-8000-0000000000c2','717b4000-0000-4000-8000-0000000000e1');
|
|
38
|
+
}
|
|
39
|
+
# Each sending session is the author A: authenticated role AND matching JSON claims (built with
|
|
40
|
+
# json_build_object: a literal brace would end the step), with the flattened claim GUCs cleared so
|
|
41
|
+
# they cannot stand in for another identity.
|
|
42
|
+
session "p"
|
|
43
|
+
step "p_begin" { begin; set local role authenticated; select set_config('request.jwt.claim.role','',true), set_config('request.jwt.claim.sub','',true), set_config('request.jwt.claims',json_build_object('role','authenticated','sub','717b4000-0000-4000-8000-0000000000a1')::text,true); }
|
|
44
|
+
step "p_dm_c" { select callback_outbox_count from agent_board.send_direct_message('717b4000-0000-4000-8000-000000000010','717b4000-0000-4000-8000-000000000001','717b4000-0000-4000-8000-000000000003','iso','iso dm','comment','medium','f2b717-kd',jsonb_build_object()); }
|
|
45
|
+
step "p_mc_cc2" { select delivered_recipient_count, inbox_item_count, callback_outbox_count from agent_board.send_multicast_message('717b4000-0000-4000-8000-000000000010','717b4000-0000-4000-8000-000000000001',array['717b4000-0000-4000-8000-000000000003','717b4000-0000-4000-8000-000000000004']::uuid[],'iso','iso mc','comment','medium','f2b717-km',jsonb_build_object()); }
|
|
46
|
+
step "p_end" { end; }
|
|
47
|
+
session "w"
|
|
48
|
+
step "w_begin" { begin; set local role authenticated; select set_config('request.jwt.claim.role','',true), set_config('request.jwt.claim.sub','',true), set_config('request.jwt.claims',json_build_object('role','authenticated','sub','717b4000-0000-4000-8000-0000000000a1')::text,true); }
|
|
49
|
+
step "w_dm_x" { select callback_outbox_count from agent_board.send_direct_message('717b4000-0000-4000-8000-000000000010','717b4000-0000-4000-8000-000000000001','717b4000-0000-4000-8000-000000000005','iso','iso dm','comment','medium','f2b717-kd',jsonb_build_object()); }
|
|
50
|
+
step "w_mc_cx" { select delivered_recipient_count, inbox_item_count, callback_outbox_count from agent_board.send_multicast_message('717b4000-0000-4000-8000-000000000010','717b4000-0000-4000-8000-000000000001',array['717b4000-0000-4000-8000-000000000003','717b4000-0000-4000-8000-000000000005']::uuid[],'iso','iso mc','comment','medium','f2b717-km',jsonb_build_object()); }
|
|
51
|
+
step "w_end" { end; }
|
|
52
|
+
session "r"
|
|
53
|
+
step "r_begin" { begin; set local role authenticated; select set_config('request.jwt.claim.role','',true), set_config('request.jwt.claim.sub','',true), set_config('request.jwt.claims',json_build_object('role','authenticated','sub','717b4000-0000-4000-8000-0000000000a1')::text,true); }
|
|
54
|
+
step "r_dm_c" { select callback_outbox_count from agent_board.send_direct_message('717b4000-0000-4000-8000-000000000010','717b4000-0000-4000-8000-000000000001','717b4000-0000-4000-8000-000000000003','iso','iso dm','comment','medium','f2b717-kd',jsonb_build_object()); }
|
|
55
|
+
step "r_mc_c2c" { select delivered_recipient_count, inbox_item_count, callback_outbox_count from agent_board.send_multicast_message('717b4000-0000-4000-8000-000000000010','717b4000-0000-4000-8000-000000000001',array['717b4000-0000-4000-8000-000000000004','717b4000-0000-4000-8000-000000000003','717b4000-0000-4000-8000-000000000004']::uuid[],'iso','iso mc','comment','medium','f2b717-km',jsonb_build_object()); }
|
|
56
|
+
step "r_end" { end; }
|
|
57
|
+
session "check"
|
|
58
|
+
step "check" {
|
|
59
|
+
select count(*) as messages,
|
|
60
|
+
(select string_agg(a.handle || ':' || dr.delivery_mode, ',' order by a.handle, dr.delivery_mode)
|
|
61
|
+
from agent_board.delivery_recipients dr join agent_board.messages m on m.id = dr.source_message_id
|
|
62
|
+
join agent_board.agents a on a.id = dr.recipient_agent_id
|
|
63
|
+
where m.thread_id = '717b4000-0000-4000-8000-000000000010') as deliveries,
|
|
64
|
+
(select count(*) from agent_board.inbox_items ii join agent_board.messages m on m.id = ii.source_message_id where m.thread_id = '717b4000-0000-4000-8000-000000000010') as inbox,
|
|
65
|
+
(select count(*) from agent_board.callback_outbox_events co join agent_board.messages m on m.id = co.source_message_id where m.thread_id = '717b4000-0000-4000-8000-000000000010') as callbacks
|
|
66
|
+
from agent_board.messages where thread_id = '717b4000-0000-4000-8000-000000000010';
|
|
67
|
+
}
|
|
68
|
+
# direct, C wins: the send to X waits on the key lock, then is refused against the committed message
|
|
69
|
+
permutation "p_begin" "p_dm_c" "w_begin" "w_dm_x" "p_end" "w_end" "check"
|
|
70
|
+
# direct, X wins: the send to C waits, then is refused (C holds no direct delivery of X's message)
|
|
71
|
+
permutation "w_begin" "w_dm_x" "p_begin" "p_dm_c" "w_end" "p_end" "check"
|
|
72
|
+
# direct, identical retries: the second waits and reuses -- one message, delivery, inbox item, callback
|
|
73
|
+
permutation "p_begin" "p_dm_c" "r_begin" "r_dm_c" "p_end" "r_end" "check"
|
|
74
|
+
# multicast, {C,C2} wins: {C,X} waits, then is refused (X is new)
|
|
75
|
+
permutation "p_begin" "p_mc_cc2" "w_begin" "w_mc_cx" "p_end" "w_end" "check"
|
|
76
|
+
# multicast, identical set reordered and duplicated: one message, two deliveries, two inbox items, two callbacks
|
|
77
|
+
permutation "p_begin" "p_mc_cc2" "r_begin" "r_mc_c2c" "p_end" "r_end" "check"
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# #717 F2: concurrent same-key create_tasking_delivery sends serialize on the unchanged
|
|
2
|
+
# (thread, resolved creator, key) lock, and the second one is judged against what the first
|
|
3
|
+
# COMMITTED. A private send and a board send racing on one key: whichever commits first is the
|
|
4
|
+
# task, and the other is refused (42501) rather than widening it. Two identical retries racing:
|
|
5
|
+
# one task, one created event, one delivery, one inbox item, one callback.
|
|
6
|
+
# The thread is already stamped to the board, so first association never serializes them: only
|
|
7
|
+
# the idempotency lock does.
|
|
8
|
+
setup
|
|
9
|
+
{
|
|
10
|
+
insert into auth.users(id) values ('71740000-0000-4000-8000-0000000000a1'),('71740000-0000-4000-8000-0000000000c1');
|
|
11
|
+
insert into agent_board.agents(id,agent_type,handle,display_name,status,auth_user_id)
|
|
12
|
+
values ('71740000-0000-4000-8000-000000000001','agent','f2717-iso-a','F2 iso A','active','71740000-0000-4000-8000-0000000000a1'),
|
|
13
|
+
('71740000-0000-4000-8000-000000000003','agent','f2717-iso-c','F2 iso C','active','71740000-0000-4000-8000-0000000000c1');
|
|
14
|
+
insert into agent_board.boards(id,slug,name,created_by_agent_id,visibility,status)
|
|
15
|
+
values ('71740000-0000-4000-8000-000000000020','f2717-iso-b','F2 iso board','71740000-0000-4000-8000-000000000001','private','active');
|
|
16
|
+
select set_config('request.jwt.claim.role','service_role',false);
|
|
17
|
+
insert into agent_board.board_memberships(board_id,agent_id,membership_state,created_by_agent_id,role)
|
|
18
|
+
values ('71740000-0000-4000-8000-000000000020','71740000-0000-4000-8000-000000000001','joined','71740000-0000-4000-8000-000000000001','member');
|
|
19
|
+
insert into agent_board.threads(id,thread_type,created_by_agent_id,subject,board_id)
|
|
20
|
+
values ('71740000-0000-4000-8000-000000000010','coordination','71740000-0000-4000-8000-000000000001','f2 iso thread','71740000-0000-4000-8000-000000000020');
|
|
21
|
+
insert into agent_board.callback_endpoints(id,owner_agent_id,endpoint_url,endpoint_status)
|
|
22
|
+
values ('71740000-0000-4000-8000-000000000030','71740000-0000-4000-8000-000000000003','https://example.test/f2717-iso','verified');
|
|
23
|
+
insert into agent_board.callback_subscriptions(endpoint_id,agent_id,subscription_scope,event_category)
|
|
24
|
+
values ('71740000-0000-4000-8000-000000000030','71740000-0000-4000-8000-000000000003','own_inbox','inbox.item.delivered');
|
|
25
|
+
}
|
|
26
|
+
teardown
|
|
27
|
+
{
|
|
28
|
+
delete from agent_board.callback_outbox_events where recipient_agent_id in ('71740000-0000-4000-8000-000000000001','71740000-0000-4000-8000-000000000003');
|
|
29
|
+
delete from agent_board.callback_subscriptions where endpoint_id='71740000-0000-4000-8000-000000000030';
|
|
30
|
+
delete from agent_board.callback_endpoints where id='71740000-0000-4000-8000-000000000030';
|
|
31
|
+
delete from agent_board.inbox_items where recipient_agent_id in ('71740000-0000-4000-8000-000000000001','71740000-0000-4000-8000-000000000003');
|
|
32
|
+
delete from agent_board.delivery_recipients where recipient_agent_id in ('71740000-0000-4000-8000-000000000001','71740000-0000-4000-8000-000000000003');
|
|
33
|
+
delete from agent_board.task_events where thread_id='71740000-0000-4000-8000-000000000010';
|
|
34
|
+
delete from agent_board.tasks where thread_id='71740000-0000-4000-8000-000000000010';
|
|
35
|
+
delete from agent_board.threads where id='71740000-0000-4000-8000-000000000010';
|
|
36
|
+
delete from agent_board.board_membership_events where board_id='71740000-0000-4000-8000-000000000020';
|
|
37
|
+
delete from agent_board.boards where id='71740000-0000-4000-8000-000000000020';
|
|
38
|
+
delete from agent_board.agents where id in ('71740000-0000-4000-8000-000000000001','71740000-0000-4000-8000-000000000003');
|
|
39
|
+
delete from auth.users where id in ('71740000-0000-4000-8000-0000000000a1','71740000-0000-4000-8000-0000000000c1');
|
|
40
|
+
}
|
|
41
|
+
# Each sending session is the creator A: authenticated role AND matching JSON claims (built with
|
|
42
|
+
# json_build_object: a literal brace would end the step), with the
|
|
43
|
+
# flattened claim GUCs cleared so they cannot stand in for another identity.
|
|
44
|
+
session "p"
|
|
45
|
+
step "p_begin" { begin; set local role authenticated; select set_config('request.jwt.claim.role','',true), set_config('request.jwt.claim.sub','',true), set_config('request.jwt.claims',json_build_object('role','authenticated','sub','71740000-0000-4000-8000-0000000000a1')::text,true); }
|
|
46
|
+
step "p_private" { select delivered_recipient_count, inbox_item_count, callback_outbox_count from agent_board.create_tasking_delivery('71740000-0000-4000-8000-000000000010','71740000-0000-4000-8000-000000000001','iso private',null,array['71740000-0000-4000-8000-000000000003']::uuid[],null,'medium','distributed','f2717-kc',jsonb_build_object()); }
|
|
47
|
+
step "p_end" { end; }
|
|
48
|
+
session "b"
|
|
49
|
+
step "b_begin" { begin; set local role authenticated; select set_config('request.jwt.claim.role','',true), set_config('request.jwt.claim.sub','',true), set_config('request.jwt.claims',json_build_object('role','authenticated','sub','71740000-0000-4000-8000-0000000000a1')::text,true); }
|
|
50
|
+
step "b_board" { select delivered_recipient_count, inbox_item_count, callback_outbox_count from agent_board.create_tasking_delivery('71740000-0000-4000-8000-000000000010','71740000-0000-4000-8000-000000000001','iso board',null,null,'71740000-0000-4000-8000-000000000020','medium','distributed','f2717-kc',jsonb_build_object()); }
|
|
51
|
+
step "b_end" { end; }
|
|
52
|
+
session "r"
|
|
53
|
+
step "r_begin" { begin; set local role authenticated; select set_config('request.jwt.claim.role','',true), set_config('request.jwt.claim.sub','',true), set_config('request.jwt.claims',json_build_object('role','authenticated','sub','71740000-0000-4000-8000-0000000000a1')::text,true); }
|
|
54
|
+
step "r_private" { select delivered_recipient_count, inbox_item_count, callback_outbox_count from agent_board.create_tasking_delivery('71740000-0000-4000-8000-000000000010','71740000-0000-4000-8000-000000000001','iso private',null,array['71740000-0000-4000-8000-000000000003']::uuid[],null,'medium','distributed','f2717-kc',jsonb_build_object()); }
|
|
55
|
+
step "r_end" { end; }
|
|
56
|
+
session "check"
|
|
57
|
+
step "check" {
|
|
58
|
+
select count(*) as tasks,
|
|
59
|
+
(select count(*) from agent_board.task_events te join agent_board.tasks t on t.id = te.task_id where t.thread_id = '71740000-0000-4000-8000-000000000010') as events,
|
|
60
|
+
(select count(*) from agent_board.delivery_recipients dr join agent_board.tasks t on t.id = dr.source_task_id where t.thread_id = '71740000-0000-4000-8000-000000000010' and dr.board_id is null) as private_deliveries,
|
|
61
|
+
(select count(*) from agent_board.delivery_recipients dr join agent_board.tasks t on t.id = dr.source_task_id where t.thread_id = '71740000-0000-4000-8000-000000000010' and dr.board_id is not null) as board_deliveries,
|
|
62
|
+
(select count(*) from agent_board.inbox_items ii join agent_board.tasks t on t.id = ii.source_task_id where t.thread_id = '71740000-0000-4000-8000-000000000010') as inbox,
|
|
63
|
+
(select count(*) from agent_board.callback_outbox_events co join agent_board.tasks t on t.id = co.source_task_id where t.thread_id = '71740000-0000-4000-8000-000000000010') as callbacks,
|
|
64
|
+
string_agg(title, ',' order by title) as titles
|
|
65
|
+
from agent_board.tasks where thread_id = '71740000-0000-4000-8000-000000000010';
|
|
66
|
+
}
|
|
67
|
+
# private wins: the board send waits on the key lock, then is refused against the committed private task
|
|
68
|
+
permutation "p_begin" "p_private" "b_begin" "b_board" "p_end" "b_end" "check"
|
|
69
|
+
# board wins: the private send waits, then is refused (C holds no private delivery of the board task)
|
|
70
|
+
permutation "b_begin" "b_board" "p_begin" "p_private" "b_end" "p_end" "check"
|
|
71
|
+
# identical retries: the second waits and reuses -- one task, event, delivery, inbox item, callback
|
|
72
|
+
permutation "p_begin" "p_private" "r_begin" "r_private" "p_end" "r_end" "check"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
-- Reverse of 20261104180000: the exact origin/main 21b7b158 ACL (service_role EXECUTE) and comments.
|
|
2
|
+
grant execute on function agent_board._upsert_delivery_recipient(text,uuid,text,uuid,uuid,uuid,uuid,uuid,jsonb) to service_role;
|
|
3
|
+
grant execute on function agent_board._upsert_inbox_item(uuid,uuid,uuid,text,text,uuid,uuid,uuid,text,text,text,boolean,boolean,jsonb,jsonb) to service_role;
|
|
4
|
+
grant execute on function agent_board._queue_inbox_callbacks(uuid,text) to service_role;
|
|
5
|
+
grant execute on function agent_board._queue_board_post_callbacks(uuid,uuid,text,text,text,jsonb) to service_role;
|
|
6
|
+
grant execute on function agent_board._next_thread_message_sequence(uuid) to service_role;
|
|
7
|
+
comment on function agent_board._upsert_delivery_recipient(text,uuid,text,uuid,uuid,uuid,uuid,uuid,jsonb) is 'Enforces thread/board identity and the locked empty-history boundary for message, task and delegation delivery sources. Supplying a source ID does not exempt its private history. The sending RPC validates caller authority before its own writes.';
|
|
8
|
+
comment on function agent_board._upsert_inbox_item(uuid,uuid,uuid,text,text,uuid,uuid,uuid,text,text,text,boolean,boolean,jsonb,jsonb) is null;
|
|
9
|
+
comment on function agent_board._queue_inbox_callbacks(uuid,text) is null;
|
|
10
|
+
comment on function agent_board._queue_board_post_callbacks(uuid,uuid,text,text,text,jsonb) is null;
|
|
11
|
+
comment on function agent_board._next_thread_message_sequence(uuid) is null;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.post_board_message(uuid,uuid,text,text,text,text,text,text,text,jsonb)'::regprocedure);
|
|
2
|
+
hdr text; body text;
|
|
3
|
+
begin
|
|
4
|
+
hdr := split_part(d, '$function$', 1); body := split_part(d, '$function$', 2);
|
|
5
|
+
if body !~ 'agent_board\.post_board_message\(' then raise exception 'mutation anchor missing'; end if;
|
|
6
|
+
execute hdr || '$function$' || regexp_replace(body, 'agent_board\.post_board_message\(', 'agent_board.post_board_message_gone(', 'g') || '$function$';
|
|
7
|
+
end $m$;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
create function agent_board.review_f1_widen(p_inbox uuid) returns void language sql security definer set search_path=agent_board,public as $f$
|
|
2
|
+
update agent_board.inbox_items set preview='forged preview through state update' where id=p_inbox;
|
|
3
|
+
$f$;
|
|
4
|
+
revoke all on function agent_board.review_f1_widen(uuid) from public,anon,authenticated,service_role,agent_board_delegate;
|
|
5
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.update_inbox_item_state(uuid,uuid,text,jsonb)'::regprocedure); begin
|
|
6
|
+
if position('v_callback_count := agent_board._queue_inbox_callbacks' in d)=0 then raise exception 'missing anchor'; end if;
|
|
7
|
+
execute replace(d, 'v_callback_count := agent_board._queue_inbox_callbacks', 'perform agent_board.review_f1_widen(v_inbox.id); v_callback_count := agent_board._queue_inbox_callbacks');
|
|
8
|
+
end $m$;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
create function agent_board.review_f1_widen(p_inbox uuid) returns void language sql security definer set search_path=agent_board,public as $f$
|
|
2
|
+
update agent_board.inbox_items set visible_in_board_feed=true where id=p_inbox;
|
|
3
|
+
$f$;
|
|
4
|
+
revoke all on function agent_board.review_f1_widen(uuid) from public,anon,authenticated,service_role,agent_board_delegate;
|
|
5
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.update_inbox_item_state(uuid,uuid,text,jsonb)'::regprocedure); begin
|
|
6
|
+
if position('v_callback_count := agent_board._queue_inbox_callbacks' in d)=0 then raise exception 'missing anchor'; end if;
|
|
7
|
+
execute replace(d, 'v_callback_count := agent_board._queue_inbox_callbacks', 'perform agent_board.review_f1_widen(v_inbox.id); v_callback_count := agent_board._queue_inbox_callbacks');
|
|
8
|
+
end $m$;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- Two private hops must not hide a worker's content write from DWX-4c.
|
|
2
|
+
create function agent_board.dwx_worker_sink() returns void language sql security definer
|
|
3
|
+
set search_path=agent_board,public as $f$
|
|
4
|
+
update agent_board.inbox_items set preview='forged worker preview' where false;
|
|
5
|
+
$f$;
|
|
6
|
+
create function agent_board.dwx_worker_bridge() returns void language sql security definer
|
|
7
|
+
set search_path=agent_board,public as $f$ select agent_board.dwx_worker_sink(); $f$;
|
|
8
|
+
revoke all on function agent_board.dwx_worker_sink(), agent_board.dwx_worker_bridge()
|
|
9
|
+
from public,anon,authenticated,service_role,agent_board_delegate;
|
|
10
|
+
do $m$
|
|
11
|
+
declare d text := pg_get_functiondef('agent_board.claim_callback_outbox_events(integer)'::regprocedure);
|
|
12
|
+
begin
|
|
13
|
+
if position(E'begin\n' in d)=0 then raise exception 'missing worker begin anchor'; end if;
|
|
14
|
+
execute replace(d, E'begin\n', E'begin\n perform agent_board.dwx_worker_bridge();\n');
|
|
15
|
+
end $m$;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
grant execute on function agent_board._upsert_delivery_recipient(text,uuid,text,uuid,uuid,uuid,uuid,uuid,jsonb) to service_role;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
grant execute on function agent_board._upsert_inbox_item(uuid,uuid,uuid,text,text,uuid,uuid,uuid,text,text,text,boolean,boolean,jsonb,jsonb) to service_role;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
grant execute on function agent_board._queue_inbox_callbacks(uuid,text) to service_role;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
grant execute on function agent_board._queue_board_post_callbacks(uuid,uuid,text,text,text,jsonb) to service_role;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
grant execute on function agent_board._next_thread_message_sequence(uuid) to service_role;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
grant execute on all functions in schema agent_board to service_role;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
create function agent_board._upsert_delivery_recipient(p_delivery_mode text, p_recipient_agent_id uuid, p_source_kind text, p_source_message_id uuid, p_extra text)
|
|
2
|
+
returns uuid language plpgsql security definer set search_path = agent_board, public as $f$
|
|
3
|
+
declare v uuid;
|
|
4
|
+
begin
|
|
5
|
+
insert into agent_board.delivery_recipients(delivery_mode, recipient_agent_id, source_kind, source_message_id)
|
|
6
|
+
values (p_delivery_mode, p_recipient_agent_id, p_source_kind, p_source_message_id) returning id into v;
|
|
7
|
+
return v;
|
|
8
|
+
end $f$;
|
|
9
|
+
revoke all on function agent_board._upsert_delivery_recipient(text,uuid,text,uuid,text) from public;
|
|
10
|
+
grant execute on function agent_board._upsert_delivery_recipient(text,uuid,text,uuid,text) to service_role;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
create function agent_board.relay_inbox_item(p_dr uuid, p_recipient uuid)
|
|
2
|
+
returns uuid language plpgsql security definer set search_path = agent_board, public as $f$
|
|
3
|
+
begin
|
|
4
|
+
return agent_board._upsert_inbox_item(p_dr, p_recipient, null, 'direct', 'message', null, null, null, 's', 'p', 'medium', true, false, '{}'::jsonb, '{}'::jsonb);
|
|
5
|
+
end $f$;
|
|
6
|
+
revoke all on function agent_board.relay_inbox_item(uuid,uuid) from public;
|
|
7
|
+
grant execute on function agent_board.relay_inbox_item(uuid,uuid) to authenticated;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
do $m$ begin
|
|
2
|
+
execute replace(pg_get_functiondef('agent_board._resolved_actor_agent_id(uuid,text)'::regprocedure),
|
|
3
|
+
'if p_supplied_agent_id is not null and p_supplied_agent_id <> v_current_agent_id then',
|
|
4
|
+
'if p_supplied_agent_id is not null then return p_supplied_agent_id; end if; if false then');
|
|
5
|
+
end $m$;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
grant execute on function agent_board.signed_post_board_message(jsonb,jsonb) to authenticated;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
grant execute on function agent_board.claim_callback_outbox_events(integer) to authenticated;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
revoke execute on function agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb) from service_role;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
grant execute on function agent_board._queue_inbox_callbacks(uuid,text) to service_role;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
revoke execute on function agent_board.claim_callback_outbox_events(integer) from service_role;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
create function agent_board.wo_invoker_probe(p uuid) returns integer language sql security invoker as $f$ select agent_board._queue_inbox_callbacks(p, 'inbox.item.delivered') $f$;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
grant execute on function agent_board._queue_board_post_callbacks(uuid,uuid,text,text,text,jsonb) to public;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Usage: PG* env set; run.sh <test.sql> <mutation.sql>... Each mutation runs in ONE transaction with
|
|
3
|
+
# the test file and is rolled back; the agent_board catalog fingerprint must match before and after.
|
|
4
|
+
set -uo pipefail
|
|
5
|
+
TEST="$1"; shift
|
|
6
|
+
fp() { psql -qtAX -c "select md5(string_agg(p.oid::regprocedure::text||coalesce(p.proacl::text,'')||p.prosrc||coalesce(obj_description(p.oid,'pg_proc'),''), '|' order by p.oid::regprocedure::text)) from pg_proc p where p.pronamespace='agent_board'::regnamespace"; }
|
|
7
|
+
for m in "$@"; do
|
|
8
|
+
before="$(fp)"
|
|
9
|
+
out="$(printf 'begin;\n\\i %s\n\\i %s\nrollback;\n' "$m" "$TEST" | psql -qtA 2>&1)"
|
|
10
|
+
after="$(fp)"
|
|
11
|
+
plan="$(sed -nE 's/^1\.\.([0-9]+)$/\1/p' <<<"$out" | head -1)"
|
|
12
|
+
nok="$(grep -cE '^ok [0-9]+' <<<"$out")"; nnot="$(grep -cE '^not ok [0-9]+' <<<"$out")"
|
|
13
|
+
errs="$(grep -E '(ERROR|FATAL):' <<<"$out" | head -3 | tr '\n' ' ')"
|
|
14
|
+
labels="$(grep -E '^not ok [0-9]+' <<<"$out" | sed -E 's/^not ok ([0-9]+) - ([A-Z]+-[A-Za-z0-9-]+( CLASS)?).*/#\1:\2/' | tr '\n' ' ')"
|
|
15
|
+
[ "$before" = "$after" ] && fpok=restored || fpok="FINGERPRINT-CHANGED"
|
|
16
|
+
echo "$(basename "$m" .sql) | plan=${plan:-none} ok=$nok not_ok=$nnot emitted=$((nok+nnot)) | $fpok | red: ${labels:-<none>} ${errs:+| errors: $errs}"
|
|
17
|
+
done
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
-- a new API route that calls the delivery writer
|
|
2
|
+
create function agent_board.zz_f2b_new_route(p uuid) returns void language plpgsql security definer set search_path = agent_board, public as $f$
|
|
3
|
+
begin perform agent_board._upsert_delivery_recipient('direct', p, 'message', p); end $f$;
|
|
4
|
+
grant execute on function agent_board.zz_f2b_new_route(uuid) to authenticated;
|