@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,9 @@
|
|
|
1
|
+
-- a routes-core wrapper that writes a delivery itself
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.signed_send_direct_message(jsonb,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ v_evidence_id := agent_board._record_signed_write_evidence($q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.signed_send_direct_message(jsonb,jsonb): %', left($q$ v_evidence_id := agent_board._record_signed_write_evidence($q$, 60); end if;
|
|
4
|
+
n := replace(d, $q$ v_evidence_id := agent_board._record_signed_write_evidence($q$, $q$ if false then perform agent_board._upsert_delivery_recipient('direct', v_actor, 'message', v_row.message_id); end if;
|
|
5
|
+
v_evidence_id := agent_board._record_signed_write_evidence($q$);
|
|
6
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
7
|
+
d := n;
|
|
8
|
+
execute d;
|
|
9
|
+
end $m$;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
-- a class member that builds dynamic SQL (outside the name-match boundary)
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.signed_send_direct_message(jsonb,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ v_evidence_id := agent_board._record_signed_write_evidence($q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.signed_send_direct_message(jsonb,jsonb): %', left($q$ v_evidence_id := agent_board._record_signed_write_evidence($q$, 60); end if;
|
|
4
|
+
n := replace(d, $q$ v_evidence_id := agent_board._record_signed_write_evidence($q$, $q$ if false then execute format('select 1'); end if;
|
|
5
|
+
v_evidence_id := agent_board._record_signed_write_evidence($q$);
|
|
6
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
7
|
+
d := n;
|
|
8
|
+
execute d;
|
|
9
|
+
end $m$;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
do $m$
|
|
2
|
+
declare d text := pg_get_functiondef('agent_board.update_inbox_item_state(uuid,uuid,text,jsonb)'::regprocedure);
|
|
3
|
+
anchor text := ' v_callback_count := agent_board._queue_inbox_callbacks';
|
|
4
|
+
begin
|
|
5
|
+
if position(anchor in d)=0 then raise exception 'missing anchor'; end if;
|
|
6
|
+
execute replace(d, anchor, $inject$
|
|
7
|
+
perform agent_board._upsert_inbox_item(
|
|
8
|
+
v_inbox.delivery_recipient_id, v_inbox.recipient_agent_id, v_inbox.board_id,
|
|
9
|
+
v_inbox.delivery_mode, v_inbox.source_kind, v_inbox.source_message_id,
|
|
10
|
+
v_inbox.source_task_id, v_inbox.source_delegation_id, v_inbox.subject,
|
|
11
|
+
'REVIEW740-TRANSITIVE-CORRUPTION', v_inbox.priority, v_inbox.visible_in_inbox,
|
|
12
|
+
v_inbox.visible_in_board_feed, v_inbox.fetch_descriptor, v_inbox.metadata);
|
|
13
|
+
$inject$ || anchor);
|
|
14
|
+
end $m$;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
-- direct: the idempotency lock is keyed by recipient (split domain)
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ 'send_direct_message:' || p_thread_id::text || ':' || v_actor_agent_id::text,$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb): %', left($q$ 'send_direct_message:' || p_thread_id::text || ':' || v_actor_agent_id::text,$q$, 60); end if;
|
|
4
|
+
n := replace(d, $q$ 'send_direct_message:' || p_thread_id::text || ':' || v_actor_agent_id::text,$q$, $q$ 'send_direct_message:' || p_thread_id::text || ':' || v_actor_agent_id::text || ':' || p_recipient_agent_id::text,$q$);
|
|
5
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
6
|
+
d := n;
|
|
7
|
+
execute d;
|
|
8
|
+
end $m$;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
-- direct: the idempotency lock is removed
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ perform agent_board._lock_idempotency_key(
|
|
4
|
+
'send_direct_message:'$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb): %', left($q$ perform agent_board._lock_idempotency_key(
|
|
5
|
+
'send_direct_message:'$q$, 60); end if;
|
|
6
|
+
n := replace(d, $q$ perform agent_board._lock_idempotency_key(
|
|
7
|
+
'send_direct_message:'$q$, $q$ perform concat(
|
|
8
|
+
'send_direct_message:'$q$);
|
|
9
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
10
|
+
d := n;
|
|
11
|
+
execute d;
|
|
12
|
+
end $m$;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
-- multicast: the idempotency lock is keyed by the recipient set (split domain)
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ 'send_multicast_message:' || p_thread_id::text || ':' || v_actor_agent_id::text,$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb): %', left($q$ 'send_multicast_message:' || p_thread_id::text || ':' || v_actor_agent_id::text,$q$, 60); end if;
|
|
4
|
+
n := replace(d, $q$ 'send_multicast_message:' || p_thread_id::text || ':' || v_actor_agent_id::text,$q$, $q$ 'send_multicast_message:' || p_thread_id::text || ':' || v_actor_agent_id::text || ':' || array_to_string(v_recipient_ids, ','),$q$);
|
|
5
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
6
|
+
d := n;
|
|
7
|
+
execute d;
|
|
8
|
+
end $m$;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
-- multicast: the idempotency lock is removed
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ perform agent_board._lock_idempotency_key(
|
|
4
|
+
'send_multicast_message:'$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb): %', left($q$ perform agent_board._lock_idempotency_key(
|
|
5
|
+
'send_multicast_message:'$q$, 60); end if;
|
|
6
|
+
n := replace(d, $q$ perform agent_board._lock_idempotency_key(
|
|
7
|
+
'send_multicast_message:'$q$, $q$ perform concat(
|
|
8
|
+
'send_multicast_message:'$q$);
|
|
9
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
10
|
+
d := n;
|
|
11
|
+
execute d;
|
|
12
|
+
end $m$;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
-- pre-fix send_direct_message + send_multicast_message (exploit baseline)
|
|
2
|
+
do $m$
|
|
3
|
+
declare d text; s int; e int;
|
|
4
|
+
begin
|
|
5
|
+
foreach d in array array[pg_get_functiondef('agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb)'::regprocedure), pg_get_functiondef('agent_board.send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb)'::regprocedure)] loop
|
|
6
|
+
s := position(' -- #717 F2b:' in d);
|
|
7
|
+
e := position(' raise exception ''message replay cannot widen delivery scope'' using errcode = ''42501'';
|
|
8
|
+
end if;
|
|
9
|
+
|
|
10
|
+
' in d);
|
|
11
|
+
if s = 0 or e = 0 or e < s then raise exception 'M0 anchors not found'; end if;
|
|
12
|
+
execute left(d, s - 1) || substr(d, e + length(' raise exception ''message replay cannot widen delivery scope'' using errcode = ''42501'';
|
|
13
|
+
end if;
|
|
14
|
+
|
|
15
|
+
'));
|
|
16
|
+
end loop;
|
|
17
|
+
end $m$;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- multicast: the replay lookup is widened across authors
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ where thread_id = p_thread_id
|
|
4
|
+
and author_agent_id = v_actor_agent_id
|
|
5
|
+
$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb): %', left($q$ where thread_id = p_thread_id
|
|
6
|
+
and author_agent_id = v_actor_agent_id
|
|
7
|
+
$q$, 60); end if;
|
|
8
|
+
n := replace(d, $q$ where thread_id = p_thread_id
|
|
9
|
+
and author_agent_id = v_actor_agent_id
|
|
10
|
+
$q$, $q$ where thread_id = p_thread_id
|
|
11
|
+
$q$);
|
|
12
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
13
|
+
d := n;
|
|
14
|
+
execute d;
|
|
15
|
+
end $m$;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
-- direct: the core swallows its refusal and returns nothing
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ raise exception 'message replay cannot widen delivery scope' using errcode = '42501';$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb): %', left($q$ raise exception 'message replay cannot widen delivery scope' using errcode = '42501';$q$, 60); end if;
|
|
4
|
+
n := replace(d, $q$ raise exception 'message replay cannot widen delivery scope' using errcode = '42501';$q$, $q$ return;$q$);
|
|
5
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
6
|
+
d := n;
|
|
7
|
+
execute d;
|
|
8
|
+
end $m$;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
-- multicast: the core swallows its refusal and returns nothing
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ raise exception 'message replay cannot widen delivery scope' using errcode = '42501';$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb): %', left($q$ raise exception 'message replay cannot widen delivery scope' using errcode = '42501';$q$, 60); end if;
|
|
4
|
+
n := replace(d, $q$ raise exception 'message replay cannot widen delivery scope' using errcode = '42501';$q$, $q$ return;$q$);
|
|
5
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
6
|
+
d := n;
|
|
7
|
+
execute d;
|
|
8
|
+
end $m$;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
-- signed direct wrapper catches the core's 42501 and records evidence anyway
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.signed_send_direct_message(jsonb,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ select *
|
|
4
|
+
into v_row
|
|
5
|
+
from agent_board.send_direct_message($q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.signed_send_direct_message(jsonb,jsonb): %', left($q$ select *
|
|
6
|
+
into v_row
|
|
7
|
+
from agent_board.send_direct_message($q$, 60); end if;
|
|
8
|
+
n := replace(d, $q$ select *
|
|
9
|
+
into v_row
|
|
10
|
+
from agent_board.send_direct_message($q$, $q$ begin
|
|
11
|
+
select *
|
|
12
|
+
into v_row
|
|
13
|
+
from agent_board.send_direct_message($q$);
|
|
14
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
15
|
+
d := n;
|
|
16
|
+
if position($q$ );
|
|
17
|
+
|
|
18
|
+
v_evidence_id :=$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.signed_send_direct_message(jsonb,jsonb): %', left($q$ );
|
|
19
|
+
|
|
20
|
+
v_evidence_id :=$q$, 60); end if;
|
|
21
|
+
n := replace(d, $q$ );
|
|
22
|
+
|
|
23
|
+
v_evidence_id :=$q$, $q$ );
|
|
24
|
+
exception when insufficient_privilege then
|
|
25
|
+
select null::uuid as message_id, null::uuid as delivery_recipient_id, null::uuid as inbox_item_id, 0 as callback_outbox_count, now() as created_at into v_row;
|
|
26
|
+
end;
|
|
27
|
+
|
|
28
|
+
v_evidence_id :=$q$);
|
|
29
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
30
|
+
d := n;
|
|
31
|
+
execute d;
|
|
32
|
+
end $m$;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-- signed multicast wrapper (both branches) catches the core's 42501
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.signed_send_multicast_message(jsonb,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ if p_payload ? 'p_recipient_agent_ids' then$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.signed_send_multicast_message(jsonb,jsonb): %', left($q$ if p_payload ? 'p_recipient_agent_ids' then$q$, 60); end if;
|
|
4
|
+
n := replace(d, $q$ if p_payload ? 'p_recipient_agent_ids' then$q$, $q$ begin
|
|
5
|
+
if p_payload ? 'p_recipient_agent_ids' then$q$);
|
|
6
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
7
|
+
d := n;
|
|
8
|
+
if position($q$ end if;
|
|
9
|
+
v_evidence_id :=$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.signed_send_multicast_message(jsonb,jsonb): %', left($q$ end if;
|
|
10
|
+
v_evidence_id :=$q$, 60); end if;
|
|
11
|
+
n := replace(d, $q$ end if;
|
|
12
|
+
v_evidence_id :=$q$, $q$ end if;
|
|
13
|
+
exception when insufficient_privilege then
|
|
14
|
+
select null::uuid as message_id, 0 as delivered_recipient_count, 0 as inbox_item_count, 0 as callback_outbox_count, now() as created_at into v_row;
|
|
15
|
+
end;
|
|
16
|
+
v_evidence_id :=$q$);
|
|
17
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
18
|
+
d := n;
|
|
19
|
+
execute d;
|
|
20
|
+
end $m$;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
do $m$
|
|
2
|
+
declare d text := pg_get_functiondef('agent_board.send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb)'::regprocedure);
|
|
3
|
+
anchor text := ' and dr.source_message_id = v_message.id';
|
|
4
|
+
begin
|
|
5
|
+
if position(anchor in d)=0 then raise exception 'missing anchor'; end if;
|
|
6
|
+
execute replace(d,anchor,'');
|
|
7
|
+
end $m$;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
-- direct: the replay ceiling never fires
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ if v_message.id is not null and not exists (
|
|
4
|
+
select 1
|
|
5
|
+
from agent_board.delivery_recipients dr$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb): %', left($q$ if v_message.id is not null and not exists (
|
|
6
|
+
select 1
|
|
7
|
+
from agent_board.delivery_recipients dr$q$, 60); end if;
|
|
8
|
+
n := replace(d, $q$ if v_message.id is not null and not exists (
|
|
9
|
+
select 1
|
|
10
|
+
from agent_board.delivery_recipients dr$q$, $q$ if false and not exists (
|
|
11
|
+
select 1
|
|
12
|
+
from agent_board.delivery_recipients dr$q$);
|
|
13
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
14
|
+
d := n;
|
|
15
|
+
execute d;
|
|
16
|
+
end $m$;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
-- multicast: the replay ceiling never fires
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ if v_message.id is not null and exists (
|
|
4
|
+
select 1
|
|
5
|
+
from unnest(v_recipient_ids) as requested(recipient_id)$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb): %', left($q$ if v_message.id is not null and exists (
|
|
6
|
+
select 1
|
|
7
|
+
from unnest(v_recipient_ids) as requested(recipient_id)$q$, 60); end if;
|
|
8
|
+
n := replace(d, $q$ if v_message.id is not null and exists (
|
|
9
|
+
select 1
|
|
10
|
+
from unnest(v_recipient_ids) as requested(recipient_id)$q$, $q$ if false and exists (
|
|
11
|
+
select 1
|
|
12
|
+
from unnest(v_recipient_ids) as requested(recipient_id)$q$);
|
|
13
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
14
|
+
d := n;
|
|
15
|
+
execute d;
|
|
16
|
+
end $m$;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
-- direct: caller metadata naming the recipient substitutes for a delivery
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ and dr.board_id is null
|
|
4
|
+
) then$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb): %', left($q$ and dr.board_id is null
|
|
5
|
+
) then$q$, 60); end if;
|
|
6
|
+
n := replace(d, $q$ and dr.board_id is null
|
|
7
|
+
) then$q$, $q$ and dr.board_id is null
|
|
8
|
+
) and coalesce(p_metadata ->> 'recipientAgentId', '') <> p_recipient_agent_id::text then$q$);
|
|
9
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
10
|
+
d := n;
|
|
11
|
+
execute d;
|
|
12
|
+
end $m$;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
-- multicast: caller metadata naming the recipient substitutes for a delivery
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ and dr.board_id is null
|
|
4
|
+
)
|
|
5
|
+
) then$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb): %', left($q$ and dr.board_id is null
|
|
6
|
+
)
|
|
7
|
+
) then$q$, 60); end if;
|
|
8
|
+
n := replace(d, $q$ and dr.board_id is null
|
|
9
|
+
)
|
|
10
|
+
) then$q$, $q$ and dr.board_id is null
|
|
11
|
+
) and not (coalesce(p_metadata -> 'recipientAgentIds', '[]'::jsonb) ? requested.recipient_id::text)
|
|
12
|
+
) then$q$);
|
|
13
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
14
|
+
d := n;
|
|
15
|
+
execute d;
|
|
16
|
+
end $m$;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
-- direct: a delivery of the source in ANY mode counts as proof
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ and dr.delivery_mode = 'direct'
|
|
4
|
+
$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb): %', left($q$ and dr.delivery_mode = 'direct'
|
|
5
|
+
$q$, 60); end if;
|
|
6
|
+
n := replace(d, $q$ and dr.delivery_mode = 'direct'
|
|
7
|
+
$q$, $q$$q$);
|
|
8
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
9
|
+
d := n;
|
|
10
|
+
execute d;
|
|
11
|
+
end $m$;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
-- direct: the `board_id is null` conjunct is dropped (EQUIVALENT given delivery_recipients_board_scope; see C6 and assertion 64)
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ and dr.board_id is null
|
|
4
|
+
) then$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb): %', left($q$ and dr.board_id is null
|
|
5
|
+
) then$q$, 60); end if;
|
|
6
|
+
n := replace(d, $q$ and dr.board_id is null
|
|
7
|
+
) then$q$, $q$ ) then$q$);
|
|
8
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
9
|
+
d := n;
|
|
10
|
+
execute d;
|
|
11
|
+
end $m$;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
-- multicast: a delivery of the source in ANY mode counts as proof
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ and dr.delivery_mode = 'multicast'
|
|
4
|
+
$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb): %', left($q$ and dr.delivery_mode = 'multicast'
|
|
5
|
+
$q$, 60); end if;
|
|
6
|
+
n := replace(d, $q$ and dr.delivery_mode = 'multicast'
|
|
7
|
+
$q$, $q$$q$);
|
|
8
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
9
|
+
d := n;
|
|
10
|
+
execute d;
|
|
11
|
+
end $m$;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
-- direct: a recipient on the thread's stamped board counts as proof
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ and dr.board_id is null
|
|
4
|
+
) then$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb): %', left($q$ and dr.board_id is null
|
|
5
|
+
) then$q$, 60); end if;
|
|
6
|
+
n := replace(d, $q$ and dr.board_id is null
|
|
7
|
+
) then$q$, $q$ and dr.board_id is null
|
|
8
|
+
) and not exists (select 1 from agent_board.threads th join agent_board.board_memberships bm on bm.board_id = th.board_id where th.id = p_thread_id and bm.agent_id = p_recipient_agent_id and bm.membership_state = 'joined') then$q$);
|
|
9
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
10
|
+
d := n;
|
|
11
|
+
execute d;
|
|
12
|
+
end $m$;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- direct: the replay lookup is widened across authors
|
|
2
|
+
do $m$ declare d text := pg_get_functiondef('agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb)'::regprocedure); n text; begin
|
|
3
|
+
if position($q$ where thread_id = p_thread_id
|
|
4
|
+
and author_agent_id = v_actor_agent_id
|
|
5
|
+
$q$ in d) = 0 then raise exception 'mutation anchor not found in agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb): %', left($q$ where thread_id = p_thread_id
|
|
6
|
+
and author_agent_id = v_actor_agent_id
|
|
7
|
+
$q$, 60); end if;
|
|
8
|
+
n := replace(d, $q$ where thread_id = p_thread_id
|
|
9
|
+
and author_agent_id = v_actor_agent_id
|
|
10
|
+
$q$, $q$ where thread_id = p_thread_id
|
|
11
|
+
$q$);
|
|
12
|
+
if n = d then raise exception 'mutation is a no-op'; end if;
|
|
13
|
+
d := n;
|
|
14
|
+
execute d;
|
|
15
|
+
end $m$;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# #717 F2b mutation receipts. Connection comes ONLY from libpq environment (PGHOST/PGPORT/PGUSER/
|
|
3
|
+
# PGPASSWORD/PGDATABASE), never argv. The target must be an OWNED database holding this tree's
|
|
4
|
+
# applied bundle.
|
|
5
|
+
#
|
|
6
|
+
# Per mutation:
|
|
7
|
+
# pgTAP the mutation and the test file run in ONE transaction that is rolled back; the file's
|
|
8
|
+
# plan / emitted / ok / not ok counts and the failing numbers are recorded (counted,
|
|
9
|
+
# never inferred from absence).
|
|
10
|
+
# isolation (M*, L* only) the mutation is COMMITTED, the spec is diffed against its golden, and
|
|
11
|
+
# every touched definition is restored from text captured BEFORE mutating.
|
|
12
|
+
# Both legs require the agent_board catalog fingerprint (every routine's signature, ACL, source
|
|
13
|
+
# and comment) to equal the pre-run value afterwards, or the run stops.
|
|
14
|
+
# Usage: run.sh <receipt-path> [mutation.sql ...] (default: every *.sql beside this script)
|
|
15
|
+
set -uo pipefail
|
|
16
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
17
|
+
PKG="$(cd "$HERE/../../.." && pwd)"
|
|
18
|
+
TEST="$PKG/tests/agent_board_717_message_replay_scope.test.sql"
|
|
19
|
+
SPEC="$PKG/tests/isolation/specs/message_replay_scope.spec"
|
|
20
|
+
GOLDEN="$PKG/tests/isolation/expected/message_replay_scope.out"
|
|
21
|
+
OUT="${1:?receipt path}"; shift
|
|
22
|
+
if [ "$#" -gt 0 ]; then MUTS=("$@"); else mapfile -t MUTS < <(find "$HERE" -maxdepth 1 -name '*.sql' | sort -V); fi
|
|
23
|
+
[ -n "${PGDATABASE:-}" ] || { echo "FATAL: PGDATABASE unset (libpq environment required)" >&2; exit 3; }
|
|
24
|
+
ISO="$(ls -1 /usr/lib/postgresql/*/lib/pgxs/src/test/isolation/isolationtester 2>/dev/null | sort -V | tail -1)"
|
|
25
|
+
[ -x "$ISO" ] || { echo "FATAL: isolationtester not found" >&2; exit 3; }
|
|
26
|
+
WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT
|
|
27
|
+
P=(psql -X -qtA)
|
|
28
|
+
FNS=("agent_board.send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb)"
|
|
29
|
+
"agent_board.send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb)"
|
|
30
|
+
"agent_board.signed_send_direct_message(jsonb,jsonb)"
|
|
31
|
+
"agent_board.signed_send_multicast_message(jsonb,jsonb)")
|
|
32
|
+
fp() { "${P[@]}" -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"; }
|
|
33
|
+
: > "$WORK/restore.sql"
|
|
34
|
+
for f in "${FNS[@]}"; do
|
|
35
|
+
"${P[@]}" -c "select pg_get_functiondef('$f'::regprocedure)" >> "$WORK/restore.sql" || exit 3
|
|
36
|
+
printf ';\n' >> "$WORK/restore.sql"
|
|
37
|
+
done
|
|
38
|
+
ORIG="$(fp)"
|
|
39
|
+
|
|
40
|
+
tap() { # $1 label, $2 mutation file or empty
|
|
41
|
+
local out plan ok nok
|
|
42
|
+
if [ -n "$2" ]; then out="$(printf 'begin;\n\\i %s\n\\i %s\nrollback;\n' "$2" "$TEST" | "${P[@]}" 2>&1)"
|
|
43
|
+
else out="$(printf 'begin;\n\\i %s\nrollback;\n' "$TEST" | "${P[@]}" 2>&1)"; fi
|
|
44
|
+
plan="$(sed -nE 's/^1\.\.([0-9]+)$/\1/p' <<<"$out" | head -1)"
|
|
45
|
+
ok="$(grep -cE '^ok [0-9]+' <<<"$out")"; nok="$(grep -cE '^not ok [0-9]+' <<<"$out")"
|
|
46
|
+
printf '%s: pgtap plan=%s emitted=%s ok=%s not_ok=%s\n' "$1" "${plan:-NONE}" "$((ok + nok))" "$ok" "$nok"
|
|
47
|
+
grep -E '^not ok [0-9]+' <<<"$out" | sed 's/^/ /'
|
|
48
|
+
grep -E '(ERROR|FATAL):' <<<"$out" | grep -v 'mutation' | head -3 | sed 's/^/ ERR /'
|
|
49
|
+
grep -E 'ERROR: .*mutation' <<<"$out" | head -1 | sed 's/^/ FATAL-MUTATION /'
|
|
50
|
+
}
|
|
51
|
+
iso() { # $1 label
|
|
52
|
+
"$ISO" "dbname=$PGDATABASE" < "$SPEC" > "$WORK/iso.out" 2>&1
|
|
53
|
+
if diff -q "$GOLDEN" "$WORK/iso.out" >/dev/null; then echo " isolation=identical"
|
|
54
|
+
else
|
|
55
|
+
echo " isolation=DIFFERS ($(diff "$GOLDEN" "$WORK/iso.out" | grep -cE '^[<>]') lines)"
|
|
56
|
+
diff "$GOLDEN" "$WORK/iso.out" | grep -E '^[<>] .*(ERROR|\|)' | head -6 | sed 's/^/ /'
|
|
57
|
+
fi
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
{
|
|
61
|
+
echo "# #717 F2b mutation receipt -- $(date -u +%FT%TZ)"
|
|
62
|
+
echo "# tree $(git -C "$PKG" rev-parse HEAD)$(git -C "$PKG" diff --quiet HEAD -- . || echo ' + uncommitted changes')"
|
|
63
|
+
echo "# catalog fingerprint before: $ORIG"
|
|
64
|
+
tap "BASELINE" ""; iso "BASELINE"
|
|
65
|
+
for m in "${MUTS[@]}"; do
|
|
66
|
+
name="$(basename "$m" .sql)"
|
|
67
|
+
echo "-- $name: $(head -1 "$m" | sed 's/^-- //')"
|
|
68
|
+
tap "$name" "$m"
|
|
69
|
+
[ "$(fp)" = "$ORIG" ] || { echo "FATAL: fingerprint changed after the rolled-back pgTAP leg of $name"; exit 3; }
|
|
70
|
+
case "$name" in
|
|
71
|
+
M*|L*)
|
|
72
|
+
"${P[@]}" -v ON_ERROR_STOP=1 -1 -f "$m" >/dev/null 2>&1 || { echo "FATAL: $name did not apply for the isolation leg"; exit 3; }
|
|
73
|
+
[ "$(fp)" != "$ORIG" ] || { echo "FATAL: $name applied but the fingerprint is unchanged (no-op)"; exit 3; }
|
|
74
|
+
iso "$name"
|
|
75
|
+
"${P[@]}" -v ON_ERROR_STOP=1 -1 -f "$WORK/restore.sql" >/dev/null || { echo "FATAL: restore failed after $name"; exit 3; }
|
|
76
|
+
[ "$(fp)" = "$ORIG" ] || { echo "FATAL: restore fingerprint differs after $name"; exit 3; }
|
|
77
|
+
echo " restored: fingerprint identical" ;;
|
|
78
|
+
esac
|
|
79
|
+
done
|
|
80
|
+
tap "AFTER-RESTORE" ""; iso "AFTER-RESTORE"
|
|
81
|
+
echo "# catalog fingerprint after: $(fp)"
|
|
82
|
+
} | tee "$OUT"
|