@consiliency/agent-board-schema 1.64.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.
Files changed (171) hide show
  1. package/README.md +19 -0
  2. package/fixtures/already-applied.json +44 -4
  3. package/migrations/20261015000100_agent_board_session_provenance.sql +367 -0
  4. package/migrations/20261015000200_agent_board_request_preserve_removed.sql +87 -0
  5. package/migrations/20261015000300_agent_board_rls_correlation.sql +132 -0
  6. package/migrations/20261015000400_agent_board_resolution_authority.sql +146 -0
  7. package/migrations/20261026000100_agent_board_thread_association.sql +1287 -0
  8. package/migrations/20261101000100_agent_board_session_measured_mint.sql +145 -0
  9. package/migrations/20261104065658_agent_board_reply_delivery.sql +659 -0
  10. package/migrations/20261104170000_agent_board_tasking_replay_scope.sql +320 -0
  11. package/migrations/20261104180000_agent_board_delivery_writer_execute.sql +51 -0
  12. package/migrations/20261104190000_agent_board_message_replay_scope.sql +373 -0
  13. package/package.json +1 -1
  14. package/schema-manifest.json +93 -3
  15. package/src/deployment-drift-main.test.mjs +156 -0
  16. package/src/deployment-drift-script.test.mjs +14 -1
  17. package/src/policy-comment.test.mjs +17 -0
  18. package/src/tooling.test.mjs +12 -2
  19. package/tests/agent_board_661_reply_delivery.test.sql +185 -0
  20. package/tests/agent_board_667_boundary.test.sql +162 -0
  21. package/tests/agent_board_667_conversion_branches.test.sql +91 -0
  22. package/tests/agent_board_667_conversion_profile.test.sql +112 -0
  23. package/tests/agent_board_667_history_class.test.sql +785 -0
  24. package/tests/agent_board_667_native_cast_effect.test.sql +88 -0
  25. package/tests/agent_board_667_realtime_apply_behavior.test.sql +117 -0
  26. package/tests/agent_board_667_realtime_apply_controls.test.sql +143 -0
  27. package/tests/agent_board_667_realtime_apply_profile.test.sql +61 -0
  28. package/tests/agent_board_667_retention.test.sql +201 -0
  29. package/tests/agent_board_667_routing.test.sql +294 -0
  30. package/tests/agent_board_667_sender_effects.test.sql +100 -0
  31. package/tests/agent_board_667_sender_profile_controls.test.sql +29 -0
  32. package/tests/agent_board_667_storage_delimiter_behavior.test.sql +84 -0
  33. package/tests/agent_board_667_storage_delimiter_controls.test.sql +23 -0
  34. package/tests/agent_board_667_storage_delimiter_profile.test.sql +61 -0
  35. package/tests/agent_board_667_storage_search_behavior.test.sql +93 -0
  36. package/tests/agent_board_667_storage_search_controls.test.sql +22 -0
  37. package/tests/agent_board_667_storage_search_profile.test.sql +60 -0
  38. package/tests/agent_board_667_storage_timestamp_behavior.test.sql +58 -0
  39. package/tests/agent_board_667_storage_timestamp_controls.test.sql +22 -0
  40. package/tests/agent_board_667_storage_timestamp_profile.test.sql +54 -0
  41. package/tests/agent_board_667_writers.test.sql +281 -0
  42. package/tests/agent_board_717_message_replay_scope.test.sql +828 -0
  43. package/tests/agent_board_717_tasking_replay_scope.test.sql +661 -0
  44. package/tests/agent_board_addressing.test.sql +8 -5
  45. package/tests/agent_board_claim_subject_gate.test.sql +7 -3
  46. package/tests/agent_board_claimpolicy_race.test.sql +39 -11
  47. package/tests/agent_board_consent_gating.test.sql +4 -4
  48. package/tests/agent_board_delegate_role_class.test.sql +33 -15
  49. package/tests/agent_board_delivery_writer_execute.test.sql +371 -0
  50. package/tests/agent_board_fk_lock_edges.test.sql +3 -0
  51. package/tests/agent_board_gatenull_membstate.test.sql +7 -4
  52. package/tests/agent_board_hosted_delegates.test.sql +5 -2
  53. package/tests/agent_board_inbox_rpcs.test.sql +5 -3
  54. package/tests/agent_board_obsgate.test.sql +8 -4
  55. package/tests/agent_board_obsgate4.test.sql +2 -2
  56. package/tests/agent_board_outbox_wake_ordering.test.sql +25 -2
  57. package/tests/agent_board_own_post_not_delivered.test.sql +11 -9
  58. package/tests/agent_board_policy_correlation.test.sql +189 -0
  59. package/tests/agent_board_request_removed.test.sql +96 -0
  60. package/tests/agent_board_resolution_authority.test.sql +190 -0
  61. package/tests/agent_board_session_identity_behavior_classes.test.sql +2 -2
  62. package/tests/agent_board_session_identity_limits.test.sql +1 -1
  63. package/tests/agent_board_session_identity_measured_mint.test.sql +342 -0
  64. package/tests/agent_board_session_identity_predicate_isolation.test.sql +146 -0
  65. package/tests/agent_board_session_provenance.test.sql +106 -0
  66. package/tests/agent_board_sidserver_authority.test.sql +69 -0
  67. package/tests/agent_board_sidserver_catalog.test.sql +47 -0
  68. package/tests/agent_board_signed_write_gateway.test.sql +4 -2
  69. package/tests/agent_board_task_thread_lock_order.test.sql +67 -42
  70. package/tests/agent_board_v2_invariants.test.sql +5 -2
  71. package/tests/agent_board_write_enforcement_class.test.sql +3 -3
  72. package/tests/catalog/function_execute_privileges.json +43 -7
  73. package/tests/catalog/p_metadata_parameters.json +16 -0
  74. package/tests/fixtures/agent_board_667_apply_rls_guard.sql +195 -0
  75. package/tests/fixtures/agent_board_667_apply_rls_provenance.md +125 -0
  76. package/tests/fixtures/agent_board_667_apply_rls_reference.sql +1903 -0
  77. package/tests/fixtures/agent_board_667_conversion_guard.sql +162 -0
  78. package/tests/fixtures/agent_board_667_conversion_reference.md +23 -0
  79. package/tests/fixtures/agent_board_667_conversion_reference.sql +5325 -0
  80. package/tests/fixtures/agent_board_667_delimiter_guard.sql +92 -0
  81. package/tests/fixtures/agent_board_667_delimiter_provenance.md +116 -0
  82. package/tests/fixtures/agent_board_667_delimiter_reference.sql +10150 -0
  83. package/tests/fixtures/agent_board_667_search_guard.sql +92 -0
  84. package/tests/fixtures/agent_board_667_search_provenance.md +141 -0
  85. package/tests/fixtures/agent_board_667_search_reference.sql +4779 -0
  86. package/tests/fixtures/agent_board_667_sender_guard.sql +102 -0
  87. package/tests/fixtures/agent_board_667_sender_reference.md +20 -0
  88. package/tests/fixtures/agent_board_667_sender_reference.sql +875 -0
  89. package/tests/fixtures/agent_board_667_timestamp_graph.sql +84 -0
  90. package/tests/fixtures/agent_board_667_timestamp_guard.sql +94 -0
  91. package/tests/fixtures/agent_board_667_timestamp_provenance.md +99 -0
  92. package/tests/fixtures/agent_board_667_timestamp_reference.sql +2938 -0
  93. package/tests/isolation/expected/association_artifact_anchor_order.out +108 -0
  94. package/tests/isolation/expected/association_board_vs_board.out +71 -0
  95. package/tests/isolation/expected/association_post_wait_authority.out +77 -0
  96. package/tests/isolation/expected/association_strong_history_writers.out +203 -0
  97. package/tests/isolation/expected/association_strong_isolation.out +185 -0
  98. package/tests/isolation/expected/association_vs_artifact_claim_escalation.out +199 -0
  99. package/tests/isolation/expected/association_vs_heartbeat.out +78 -0
  100. package/tests/isolation/expected/association_vs_multi_anchor_artifact.out +67 -0
  101. package/tests/isolation/expected/association_vs_private_message.out +67 -0
  102. package/tests/isolation/expected/association_vs_task_and_conflict.out +133 -0
  103. package/tests/isolation/expected/association_vs_taskful_cross_thread.out +133 -0
  104. package/tests/isolation/expected/consent_approval_lock.out +1 -1
  105. package/tests/isolation/expected/message_replay_scope.out +209 -0
  106. package/tests/isolation/expected/tasking_replay_scope.out +125 -0
  107. package/tests/isolation/specs/association_artifact_anchor_order.spec +46 -0
  108. package/tests/isolation/specs/association_board_vs_board.spec +37 -0
  109. package/tests/isolation/specs/association_post_wait_authority.spec +48 -0
  110. package/tests/isolation/specs/association_strong_history_writers.spec +61 -0
  111. package/tests/isolation/specs/association_strong_isolation.spec +50 -0
  112. package/tests/isolation/specs/association_vs_artifact_claim_escalation.spec +41 -0
  113. package/tests/isolation/specs/association_vs_heartbeat.spec +47 -0
  114. package/tests/isolation/specs/association_vs_multi_anchor_artifact.spec +38 -0
  115. package/tests/isolation/specs/association_vs_private_message.spec +33 -0
  116. package/tests/isolation/specs/association_vs_task_and_conflict.spec +38 -0
  117. package/tests/isolation/specs/association_vs_taskful_cross_thread.spec +42 -0
  118. package/tests/isolation/specs/consent_approval_lock.spec +13 -7
  119. package/tests/isolation/specs/message_replay_scope.spec +77 -0
  120. package/tests/isolation/specs/tasking_replay_scope.spec +72 -0
  121. package/tests/mutations/717-f1/M0_prefix_main_acl_and_comments.sql +11 -0
  122. package/tests/mutations/717-f1/M10_wrapper_stops_delegating.sql +7 -0
  123. package/tests/mutations/717-f1/M11_transitive_recipient_content.sql +8 -0
  124. package/tests/mutations/717-f1/M12_transitive_recipient_visibility.sql +8 -0
  125. package/tests/mutations/717-f1/M13_transitive_worker_content.sql +15 -0
  126. package/tests/mutations/717-f1/M1a_regrant_upsert_delivery_recipient.sql +1 -0
  127. package/tests/mutations/717-f1/M1b_regrant_upsert_inbox_item.sql +1 -0
  128. package/tests/mutations/717-f1/M1c_regrant_queue_inbox_callbacks.sql +1 -0
  129. package/tests/mutations/717-f1/M1d_regrant_queue_board_post_callbacks.sql +1 -0
  130. package/tests/mutations/717-f1/M1e_regrant_next_thread_message_sequence.sql +1 -0
  131. package/tests/mutations/717-f1/M2_blanket_schema_grant.sql +1 -0
  132. package/tests/mutations/717-f1/M3_new_overload_of_sink.sql +10 -0
  133. package/tests/mutations/717-f1/M4_new_route_calling_sink.sql +7 -0
  134. package/tests/mutations/717-f1/M5_actor_trusts_supplied_id.sql +5 -0
  135. package/tests/mutations/717-f1/M6_signed_route_granted_to_authenticated.sql +1 -0
  136. package/tests/mutations/717-f1/M7_worker_granted_to_authenticated.sql +1 -0
  137. package/tests/mutations/717-f1/M8_recipient_state_rewrites_visibility.sql +5 -0
  138. package/tests/mutations/717-f1/M9_public_route_revoked_from_service_role.sql +1 -0
  139. package/tests/mutations/717-f1/outbox_wake_ordering/W1_regrant_queue_inbox_callbacks.sql +1 -0
  140. package/tests/mutations/717-f1/outbox_wake_ordering/W2_revoke_worker_from_service_role.sql +1 -0
  141. package/tests/mutations/717-f1/outbox_wake_ordering/W3_invoker_caller_of_sink.sql +1 -0
  142. package/tests/mutations/717-f1/outbox_wake_ordering/W4_public_regrant_board_post_sink.sql +1 -0
  143. package/tests/mutations/717-f1/run.sh +17 -0
  144. package/tests/mutations/717-f2b/C1_new_delivery_route.sql +4 -0
  145. package/tests/mutations/717-f2b/C2_new_replay_lookup.sql +3 -0
  146. package/tests/mutations/717-f2b/C3_internal_regranted.sql +2 -0
  147. package/tests/mutations/717-f2b/C4_wrapper_writes_delivery.sql +9 -0
  148. package/tests/mutations/717-f2b/C5_class_member_dynamic_sql.sql +9 -0
  149. package/tests/mutations/717-f2b/C6_drop_board_scope_constraint.sql +2 -0
  150. package/tests/mutations/717-f2b/C7_state_private_helper_content.sql +14 -0
  151. package/tests/mutations/717-f2b/L1_dm_lock_split_by_recipient.sql +8 -0
  152. package/tests/mutations/717-f2b/L2_dm_lock_removed.sql +12 -0
  153. package/tests/mutations/717-f2b/L3_mc_lock_split_by_recipients.sql +8 -0
  154. package/tests/mutations/717-f2b/L4_mc_lock_removed.sql +12 -0
  155. package/tests/mutations/717-f2b/M0_prefix_definitions.sql +17 -0
  156. package/tests/mutations/717-f2b/M10_mc_lookup_any_author.sql +15 -0
  157. package/tests/mutations/717-f2b/M11_dm_swallow_refusal.sql +8 -0
  158. package/tests/mutations/717-f2b/M12_mc_swallow_refusal.sql +8 -0
  159. package/tests/mutations/717-f2b/M13_signed_dm_swallows_refusal.sql +32 -0
  160. package/tests/mutations/717-f2b/M14_signed_mc_swallows_refusal.sql +20 -0
  161. package/tests/mutations/717-f2b/M15_mc_any_source.sql +7 -0
  162. package/tests/mutations/717-f2b/M1_dm_drop_recipient_proof.sql +16 -0
  163. package/tests/mutations/717-f2b/M2_mc_drop_recipient_proof.sql +16 -0
  164. package/tests/mutations/717-f2b/M3_dm_metadata_as_proof.sql +12 -0
  165. package/tests/mutations/717-f2b/M4_mc_metadata_as_proof.sql +16 -0
  166. package/tests/mutations/717-f2b/M5_dm_any_mode.sql +11 -0
  167. package/tests/mutations/717-f2b/M6_dm_board_delivery_counts.sql +11 -0
  168. package/tests/mutations/717-f2b/M7_mc_any_mode.sql +11 -0
  169. package/tests/mutations/717-f2b/M8_dm_thread_stamp_as_proof.sql +12 -0
  170. package/tests/mutations/717-f2b/M9_dm_lookup_any_author.sql +15 -0
  171. package/tests/mutations/717-f2b/run.sh +82 -0
@@ -67,6 +67,8 @@ let PRELOAD;
67
67
  beforeAll(() => {
68
68
  SHIMS = mkdtempSync(join(tmpdir(), "drift-shims-"));
69
69
  ARGV_LOG = join(SHIMS, "argv.log");
70
+ writeFileSync(join(SHIMS, "package.json"), readFileSync(PKG_JSON));
71
+ writeFileSync(join(SHIMS, "manifest.json"), readFileSync(MANIFEST));
70
72
 
71
73
  // One arg per line, `--` between invocations, so an assertion can read exact argv rather
72
74
  // than a space-joined approximation.
@@ -78,7 +80,7 @@ beforeAll(() => {
78
80
  );
79
81
  writeFileSync(
80
82
  join(SHIMS, "git"),
81
- `#!/bin/sh\n${logArgv}\ncase " $* " in\n *" rev-parse "*) printf "%s\\n" "\${FAKE_SHALLOW:-false}" ;;\n *" log "*) [ -n "\${FAKE_BUMP_ISO:-}" ] || exit 1; printf "%s\\n" "$FAKE_BUMP_ISO" ;;\n *) exit 1 ;;\nesac\n`,
83
+ `#!/bin/sh\n${logArgv}\ncase " $* " in\n *" --is-shallow-repository "*) printf "%s\n" "\${FAKE_SHALLOW:-false}" ;;\n *" cat-file "*) exit 0 ;;\n *" show "*"/package.json "*) cat "$DRIFT_SHIMS/package.json" ;;\n *" show "*"/schema-manifest.json "*) cat "$DRIFT_SHIMS/manifest.json" ;;\n *" log "*) [ -n "\${FAKE_BUMP_ISO:-}" ] || exit 1; printf "%s\n" "$FAKE_BUMP_ISO" ;;\n *) exit 1 ;;\nesac\n`,
82
84
  );
83
85
  chmodSync(join(SHIMS, "npm"), 0o755);
84
86
  chmodSync(join(SHIMS, "git"), 0o755);
@@ -139,6 +141,11 @@ function runScript(args = [], env = {}) {
139
141
  PATH: `${SHIMS}:${process.env.PATH}`,
140
142
  NODE_OPTIONS: `--import ${pathToFileURL(PRELOAD).href}`,
141
143
  DRIFT_ARGV_LOG: ARGV_LOG,
144
+ DRIFT_SHIMS: SHIMS,
145
+ GITHUB_ACTIONS: "true",
146
+ GITHUB_EVENT_NAME: "push",
147
+ GITHUB_REF: "refs/heads/main",
148
+ GITHUB_SHA: "a".repeat(40),
142
149
  FAKE_BUMP_ISO: FRESH_BUMP,
143
150
  FAKE_SHALLOW: "false",
144
151
  FAKE_FETCH_MODE: "",
@@ -186,6 +193,12 @@ describe("the shipped script's exit mapping", () => {
186
193
  expect(r.status).toBe(2);
187
194
  });
188
195
 
196
+ it("missing bump history is UNKNOWN even when merged and published agree", () => {
197
+ const r = runScript([], { FAKE_NPM_VERSION: MERGED, FAKE_BUMP_ISO: "" });
198
+ expect(r.status).toBe(2);
199
+ expect(r.out).toMatch(/authoritative main\/history unavailable/);
200
+ });
201
+
189
202
  it("refuses to date the bump on a shallow clone, so an unpublished gap is UNKNOWN", () => {
190
203
  // FAKE_SHALLOW was never true in any test, so the shallow-clone refusal the previous
191
204
  // round added was driven by nothing. Making isShallowClone() return false
@@ -0,0 +1,17 @@
1
+ import { expect, it } from 'vitest';
2
+ import { readFileSync } from 'node:fs';
3
+
4
+ const comment = (file) => {
5
+ const sql = readFileSync(new URL('../migrations/' + file, import.meta.url), 'utf8');
6
+ const statement = sql.match(/^comment on function agent_board\._current_agent_can_access_thread\(uuid\) is\s*((?:'(?:''|[^'])*'\s*)+);/im);
7
+ if (!statement) throw new Error('thread-access comment missing in ' + file);
8
+ return [...statement[1].matchAll(/'((?:''|[^'])*)'/g)].map((part) => part[1].replaceAll("''", "'")).join('');
9
+ };
10
+
11
+ it('RLS-comment-ruling: authenticated grants preserve the complete ratified thread-access comment', () => {
12
+ const ruling = comment('20260914000100_agent_board_claim_subject_gate.sql');
13
+ expect(ruling).toContain('message-board#291');
14
+ expect(ruling).toContain('REVERSING A RULING');
15
+ expect(comment('20261015000300_agent_board_rls_correlation.sql')).toBe(
16
+ ruling + ' Callable by authenticated for its RLS policies and by service_role.');
17
+ });
@@ -41,9 +41,9 @@ describe('manifest integrity', () => {
41
41
  expect(manifest.schemaVersion).toBe(8);
42
42
  expect(manifest.minClientVersion).toMatch(/^\d+\.\d+\.\d+$/);
43
43
  });
44
- it('registers the four MBAUTHZ + four MBSENDAUTHZ + four MBSESSION + four MBDELEG + three MBFED + three MBHARDEN + two MBPORTALGATE + two MBPORTAL-PR32 + two ROOMOWN + two ROOMLINK + one ROOMDELEG + one LOCALAUTH + one ROOMUIAPI + one FEDSESSION + one FEDSESSION-AUTOJOIN + one GATENULL + one MEMBSTATE + one AGENTSDIR + one LEAVEIDEM + one ROLERESET + one MBROWID + one MBHYGIENE + one MBCONSENT-A + one MBCONSENT-B(a) + one MBCONSENT-B(i) + one MBCONSENT-B(ii) + one MBCONSENT-B(iii) + one MBHARDEN-#97 + one MBHARDEN-#118 + one INVITEPOLICY + one OBSGATE + one OBSGATE-2 + one OBSGATE-3 + one OBSGATE-4 + one FLEETMACHINERLS + one RESOLVENOOP + one ROOMDELEG-LANEB + one CLAIMKEY + two MINTHARDEN + one FLEETMACHINEHIJACK + one CLAIMGATE-SHADOW + one CLAIMGATE-ENFORCE + one FLEETMACHINEREACTIVATION + one DELEGCAPCEILING + one FLEETMACHINERECLAIM + one DELEGSOLEWRITER + one FLEETDOMAINRECLAIM + five ATOMICFLEETONBOARDING (onboarding + ceiling-lock + revocation-evidence + fedsession-gate + fedsession-lock-order) + one OUTBOXWAKE + one ENROLLLOCKORDER + one FEDPROVIDERABSENCE + one WAKERECOVERY + one CONSENTRECONCILE + one ENROLLRESUMPTION + one SIGNEDAUTHORITY + one SIGNERLIFECYCLE + one LEDGERAUTHORITY + one CEILINGLOCKCOMMENT + one LEDGERBOARDCREATE + one RECEIPTSUBJECTGATE + one TASKTHREADLOCKORDER + one CLAIMSUBJECTGATE + one FEDSESSIONIDENTITYGATE + one FEDIDENTITYSLUG + one INVITENODOWNGRADE + one SIGNEDWRITEFLAG + one SIGNEDWRITEMODEL + one RTCREDCLAIM + one ORIGINPOSTURE + one BOARDCLAIMPOLICY + one HEARTBEATPRESERVE + one LIVENESSDENOM + one OWNERLESSRENEWTTL + one OWNERLESSQUOTACENSUS + one FLEETDOMAINNODOWNGRADE + one OWNERLESSQUOTACONVERGE + one ADVISORYREVERTRESET + one CLAIMPOLICYRACE + one CLAIMPOLICYLOCKGRANT + one SESSIONCHALLENGESUPERSEDE + one ROOMCONTRACT + one CALLBACKCONSUMER + one ENROLLTOKENSINGLEUSE + one ROOMCONTRACT-ACTIVEFIRST + one ONBOARD + one HOSTED + one HOSTED-FOLLOWUPS + one OWNPOST + one SIDSCHEMA + one SIDOWNER + one GRANTTARGETROLE (#674) + one LASTOWNER (Consiliency/message-board#675) forward migrations (migrationCount = prior 113 + 5 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 = 162)', () => {
44
+ it('registers the four MBAUTHZ + four MBSENDAUTHZ + four MBSESSION + four MBDELEG + three MBFED + three MBHARDEN + two MBPORTALGATE + two MBPORTAL-PR32 + two ROOMOWN + two ROOMLINK + one ROOMDELEG + one LOCALAUTH + one ROOMUIAPI + one FEDSESSION + one FEDSESSION-AUTOJOIN + one GATENULL + one MEMBSTATE + one AGENTSDIR + one LEAVEIDEM + one ROLERESET + one MBROWID + one MBHYGIENE + one MBCONSENT-A + one MBCONSENT-B(a) + one MBCONSENT-B(i) + one MBCONSENT-B(ii) + one MBCONSENT-B(iii) + one MBHARDEN-#97 + one MBHARDEN-#118 + one INVITEPOLICY + one OBSGATE + one OBSGATE-2 + one OBSGATE-3 + one OBSGATE-4 + one FLEETMACHINERLS + one RESOLVENOOP + one ROOMDELEG-LANEB + one CLAIMKEY + two MINTHARDEN + one FLEETMACHINEHIJACK + one CLAIMGATE-SHADOW + one CLAIMGATE-ENFORCE + one FLEETMACHINEREACTIVATION + one DELEGCAPCEILING + one FLEETMACHINERECLAIM + one DELEGSOLEWRITER + one FLEETDOMAINRECLAIM + five ATOMICFLEETONBOARDING (onboarding + ceiling-lock + revocation-evidence + fedsession-gate + fedsession-lock-order) + one OUTBOXWAKE + one ENROLLLOCKORDER + one FEDPROVIDERABSENCE + one WAKERECOVERY + one CONSENTRECONCILE + one ENROLLRESUMPTION + one SIGNEDAUTHORITY + one SIGNERLIFECYCLE + one LEDGERAUTHORITY + one CEILINGLOCKCOMMENT + one LEDGERBOARDCREATE + one RECEIPTSUBJECTGATE + one TASKTHREADLOCKORDER + one CLAIMSUBJECTGATE + one FEDSESSIONIDENTITYGATE + one FEDIDENTITYSLUG + one INVITENODOWNGRADE + one SIGNEDWRITEFLAG + one SIGNEDWRITEMODEL + one RTCREDCLAIM + one ORIGINPOSTURE + one BOARDCLAIMPOLICY + one HEARTBEATPRESERVE + one LIVENESSDENOM + one OWNERLESSRENEWTTL + one OWNERLESSQUOTACENSUS + one FLEETDOMAINNODOWNGRADE + one OWNERLESSQUOTACONVERGE + one ADVISORYREVERTRESET + one CLAIMPOLICYRACE + one CLAIMPOLICYLOCKGRANT + one SESSIONCHALLENGESUPERSEDE + one ROOMCONTRACT + one CALLBACKCONSUMER + one ENROLLTOKENSINGLEUSE + one ROOMCONTRACT-ACTIVEFIRST + one ONBOARD + one HOSTED + one HOSTED-FOLLOWUPS + one OWNPOST + one SIDSCHEMA + one SIDOWNER + one GRANTTARGETROLE (#674) + one LASTOWNER (Consiliency/message-board#675) + one SESSIONPROVENANCE (Consiliency/message-board#665) + one REQUESTREMOVED (Consiliency/message-board#680) + one RLSCORRELATION (Consiliency/message-board#589) + one RESOLUTIONAUTHORITY (Consiliency/message-board#668) + one THREADASSOCIATION checkpoint (#667) + one SIDSERVER-MEASUREDMINT (Consiliency/message-board#653) + one REPLYDELIVERY (#661) + one DELIVERYWRITEREXEC (#717 F1) + one REPLAYSCOPE (#717 F2) + one MESSAGEREPLAYSCOPE (#717 F2b) forward migrations (migrationCount = prior 113 + 5 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 = 172)', () => {
45
45
  expect(manifest.migrationCount).toBe(manifest.migrations.length);
46
- expect(manifest.migrationCount).toBe(162);
46
+ expect(manifest.migrationCount).toBe(172);
47
47
  // MBSESSION switched enforcement on (scoped-send re-grant): the client-visible
48
48
  // schema version bumped to 8. MBDELEG tightens delegation and MBFED adds only
49
49
  // enrollment-only additive surface (identity_providers registry, enrollment audit,
@@ -364,6 +364,16 @@ describe('manifest integrity', () => {
364
364
  // #674: grant_board_role checks the target's current role (C-A + C-B).
365
365
  '20261014000100_agent_board_grant_target_role.sql',
366
366
  '20261014000200_agent_board_last_owner.sql',
367
+ '20261015000100_agent_board_session_provenance.sql',
368
+ '20261015000200_agent_board_request_preserve_removed.sql',
369
+ '20261015000300_agent_board_rls_correlation.sql',
370
+ '20261015000400_agent_board_resolution_authority.sql',
371
+ '20261026000100_agent_board_thread_association.sql',
372
+ '20261101000100_agent_board_session_measured_mint.sql',
373
+ '20261104065658_agent_board_reply_delivery.sql',
374
+ '20261104170000_agent_board_tasking_replay_scope.sql',
375
+ '20261104180000_agent_board_delivery_writer_execute.sql',
376
+ '20261104190000_agent_board_message_replay_scope.sql',
367
377
  ]) {
368
378
  expect(names.has(f)).toBe(true);
369
379
  }
@@ -0,0 +1,185 @@
1
+ create extension if not exists pgtap with schema extensions;
2
+ set search_path=extensions,public,agent_board;
3
+ begin;
4
+ select no_plan();
5
+ create temp table f(a uuid,b uuid,ua uuid,ub uuid,board uuid,thread uuid, outsider uuid, uo uuid, private_thread uuid);
6
+ grant select on f to authenticated;
7
+ do $$
8
+ declare a uuid:=gen_random_uuid(); b uuid:=gen_random_uuid(); ua uuid; ub uuid; board uuid:=gen_random_uuid(); t uuid:=gen_random_uuid(); o uuid:=gen_random_uuid(); uo uuid; pt uuid:=gen_random_uuid();
9
+ begin
10
+ ua:=public.ensure_github_user('661a-'||a,'661a-'||a||'@example.test','661a-'||a,'A');
11
+ ub:=public.ensure_github_user('661b-'||b,'661b-'||b||'@example.test','661b-'||b,'B');
12
+ insert into agent_board.agents(id,agent_type,handle,display_name,status,auth_user_id,owner_user_id) values(a,'agent','661a-'||a,'A','active',ua,ua),(b,'agent','661b-'||b,'B','active',ub,ub);
13
+ insert into agent_board.boards(id,slug,name,created_by_agent_id,visibility) values(board,'661-'||board,'661',a,'private');
14
+ insert into agent_board.board_memberships(board_id,agent_id,membership_state,role) values(board,a,'joined','owner'),(board,b,'joined','member');
15
+ insert into agent_board.threads(id,thread_type,created_by_agent_id,subject,board_id) values(t,'coordination',a,'661 reply',board);
16
+ uo:=public.ensure_github_user('661o-'||o,'661o-'||o||'@example.test','661o-'||o,'Outsider');
17
+ insert into agent_board.agents(id,agent_type,handle,display_name,status,auth_user_id,owner_user_id) values(o,'agent','661o-'||o,'Outsider','active',uo,uo);
18
+ insert into agent_board.threads(id,thread_type,created_by_agent_id,subject) values(pt,'coordination',a,'Private');
19
+ insert into agent_board.callback_endpoints(id,owner_agent_id,endpoint_url,endpoint_status) values(o,b,'https://example.test/661','verified');
20
+ insert into agent_board.callback_subscriptions(endpoint_id,agent_id,subscription_scope,event_category) values(o,b,'own_inbox','inbox.item.delivered');
21
+ insert into agent_board.callback_endpoints(id,owner_agent_id,endpoint_url,endpoint_status) values(a,a,'https://example.test/661-author','verified');
22
+ insert into agent_board.callback_subscriptions(endpoint_id,agent_id,subscription_scope,event_category) values(a,a,'own_inbox','inbox.item.delivered');
23
+ insert into agent_board.callback_subscriptions(endpoint_id,agent_id,subscription_scope,event_category,board_id,board_membership_id)
24
+ select o,b,'board','board.post.delivered',board,bm.id from agent_board.board_memberships bm where bm.board_id=board and bm.agent_id=b;
25
+ insert into f values(a,b,ua,ub,board,t,o,uo,pt);
26
+ end $$;
27
+ select set_config('request.jwt.claim.role','authenticated',true);
28
+ select set_config('request.jwt.claim.sub',(select ua::text from f),true);
29
+ set local role authenticated;
30
+ select lives_ok($$select * from agent_board.post_message((select thread from f),(select a from f),'661 reply')$$,'661 author posts reply');
31
+ select is((select count(*) from agent_board.messages where thread_id=(select thread from f)),1::bigint,'661 author reads persisted reply');
32
+ reset role;
33
+ select is((select count(*) from agent_board.delivery_recipients where board_id=(select board from f)),2::bigint,'661 reply has member and feed-only author deliveries');
34
+ select is((select count(*) from agent_board.inbox_items where board_id=(select board from f) and visible_in_inbox),1::bigint,'661 reply creates recipient inbox');
35
+ select set_config('request.jwt.claim.sub',(select ub::text from f),true);
36
+ set local role authenticated;
37
+ select is((select count(*) from agent_board.threads where id=(select thread from f)),1::bigint,'661 second principal reads thread');
38
+ select is((select count(*) from agent_board.messages where thread_id=(select thread from f)),1::bigint,'661 second principal reads reply');
39
+ reset role;
40
+
41
+ select is((select count(*) from agent_board.inbox_items where board_id=(select board from f) and recipient_agent_id=(select a from f) and not visible_in_inbox),1::bigint,'661 author has feed-only self delivery');
42
+ select is((select count(*) from agent_board.callback_outbox_events where source_message_id in (select id from agent_board.messages where thread_id=(select thread from f)) and event_category='inbox.item.delivered'),1::bigint,'661 recipient inbox callback queued once');
43
+ select is((select count(*) from agent_board.callback_outbox_events where source_message_id in (select id from agent_board.messages where thread_id=(select thread from f)) and event_category='board.post.delivered'),1::bigint,'661 board callback queued once');
44
+ select set_config('request.jwt.claim.sub',(select ub::text from f),true);
45
+ set local role authenticated;
46
+ select lives_ok($$select * from agent_board.post_message((select thread from f),null,'661 response')$$,'661 second principal replies with session-derived author');
47
+ reset role;
48
+ select is((select author_agent_id from agent_board.messages where thread_id=(select thread from f) and body='661 response'),(select b from f),'661 null caller author resolves from second session');
49
+ select set_config('request.jwt.claim.sub',(select ua::text from f),true);
50
+ set local role authenticated;
51
+ select is((select count(*) from agent_board.messages where thread_id=(select thread from f) and body='661 response'),1::bigint,'661 first principal reads second principal response');
52
+ select throws_ok($$select * from agent_board.post_message((select thread from f),(select b from f),'spoof')$$,'42501',null,'661 spoofed author denied');
53
+ select lives_ok($$select * from agent_board.post_message((select private_thread from f),null,'661 private')$$,'661 non-board posting succeeds');
54
+ select lives_ok($$select * from agent_board.send_direct_message((select thread from f),null,(select b from f),'private direct','661 direct')$$,'661 private direct send on board thread succeeds');
55
+ select lives_ok($$select * from agent_board.send_multicast_message((select thread from f),null,array[(select b from f)],'private multi','661 multi')$$,'661 private multicast send on board thread succeeds');
56
+ reset role;
57
+ select is((select count(*) from agent_board.delivery_recipients d join agent_board.messages m on m.id=d.source_message_id where m.thread_id=(select private_thread from f)),0::bigint,'661 non-board message retains no implicit deliveries');
58
+ select is((select count(*) from agent_board.delivery_recipients d join agent_board.messages m on m.id=d.source_message_id where m.body in ('661 direct','661 multi') and m.thread_id=(select thread from f) and d.board_id is null and d.recipient_agent_id=(select b from f)),2::bigint,'661 private sends deliver only explicit recipient with null board');
59
+ select is((select count(*) from agent_board.delivery_recipients d join agent_board.messages m on m.id=d.source_message_id where m.body in ('661 direct','661 multi') and m.thread_id=(select thread from f) and d.board_id is not null),0::bigint,'661 private sends never fan out to board');
60
+ select set_config('request.jwt.claim.sub',(select uo::text from f),true);
61
+ set local role authenticated;
62
+ select is((select count(*) from agent_board.messages where thread_id=(select thread from f)),0::bigint,'661 nonmember cannot read board messages');
63
+ select throws_ok($$select * from agent_board.post_message((select thread from f),null,'outsider')$$,'42501',null,'661 nonmember cannot post');
64
+ reset role;
65
+ update agent_board.board_memberships set membership_state='removed',left_at=now() where board_id=(select board from f) and agent_id=(select b from f);
66
+ select set_config('request.jwt.claim.sub',(select ub::text from f),true);
67
+ set local role authenticated;
68
+ select throws_ok($$select * from agent_board.post_message((select thread from f),null,'removed')$$,'42501',null,'661 removed member cannot post');
69
+ reset role;
70
+ select set_config('request.jwt.claim.sub',(select ua::text from f),true);
71
+ set local role authenticated;
72
+ select lives_ok($$select * from agent_board.post_message((select thread from f),null,'661 after removal')$$,'661 remaining member can reply after removal');
73
+ reset role;
74
+ select is((select count(*) from agent_board.delivery_recipients d join agent_board.messages m on m.id=d.source_message_id where m.body='661 after removal' and m.thread_id=(select thread from f) and d.recipient_agent_id=(select b from f)),0::bigint,'661 removed member receives no new delivery');
75
+ select is((select count(*) from agent_board.delivery_recipients d join agent_board.messages m on m.id=d.source_message_id where m.body='661 after removal' and m.thread_id=(select thread from f)),1::bigint,'661 author-only board preserves feed anchor');
76
+ update agent_board.board_memberships set membership_state='subscribed',role='observer',left_at=null where board_id=(select board from f) and agent_id=(select b from f);
77
+ select set_config('request.jwt.claim.sub',(select ub::text from f),true);
78
+ set local role authenticated;
79
+ select throws_ok($$select * from agent_board.post_message((select thread from f),null,'observer')$$,'42501',null,'661 observer cannot post');
80
+ reset role;
81
+ select set_config('request.jwt.claim.sub',(select ua::text from f),true);
82
+ set local role authenticated;
83
+ select lives_ok($$select * from agent_board.post_message((select thread from f),null,'661 subscribed')$$,'661 reply with subscribed recipient succeeds');
84
+ reset role;
85
+ select is((select count(*) from agent_board.delivery_recipients d join agent_board.messages m on m.id=d.source_message_id where m.body='661 subscribed' and m.thread_id=(select thread from f) and d.recipient_agent_id=(select b from f)),1::bigint,'661 subscribed member receives reply');
86
+ select is((select board_id from agent_board.threads where id=(select thread from f)),(select board from f),'661 replies preserve board association');
87
+ select is((select visibility from agent_board.boards where id=(select board from f)),'private','661 replies preserve private board');
88
+ -- Catalog census: new direct writers or overloads name themselves. Internal
89
+ -- helpers have no API grants; their callers must remain the reviewed routes.
90
+ select is((select string_agg(p.oid::regprocedure::text,', ' order by p.oid::regprocedure::text) from pg_proc p where p.pronamespace='agent_board'::regnamespace and agent_board_test.stripped_body(p.oid) ~ agent_board_test.stmt_re('insert into','messages')),
91
+ '_insert_thread_message(uuid,uuid,text,text,uuid,jsonb)','661 catalog message insertion has one authorized primitive');
92
+ select is((select string_agg(p.oid::regprocedure::text,', ' order by p.oid::regprocedure::text) from pg_proc p where p.pronamespace='agent_board'::regnamespace and agent_board_test.stripped_body(p.oid) ~ agent_board_test.call_re('_insert_thread_message')),
93
+ 'post_board_message(uuid,uuid,uuid,text,text,text,text,text,jsonb), post_message(uuid,uuid,text,text,uuid,jsonb), send_direct_message(uuid,uuid,uuid,text,text,text,text,text,jsonb), send_multicast_message(uuid,uuid,uuid[],text,text,text,text,text,jsonb)',
94
+ '661 catalog insertion callers name every route and overload');
95
+ select is((select string_agg(p.oid::regprocedure::text,', ' order by p.oid::regprocedure::text) from pg_proc p where p.pronamespace='agent_board'::regnamespace and agent_board_test.stripped_body(p.oid) ~ agent_board_test.call_re('_deliver_board_message')),
96
+ 'post_board_message(uuid,uuid,uuid,text,text,text,text,text,jsonb), post_message(uuid,uuid,text,text,uuid,jsonb)',
97
+ '661 catalog fan-out callers are canonical board post and reply');
98
+ select is((select string_agg(p.oid::regprocedure::text||':'||r,', ' order by p.oid,r) from pg_proc p cross join unnest(array['anon','authenticated','service_role','agent_board_delegate']) r where p.pronamespace='agent_board'::regnamespace and p.proname in ('_insert_thread_message','_deliver_board_message') and has_function_privilege(r,p.oid,'execute')),null::text,'661 internal insertion and delivery helpers are not API callable');
99
+
100
+
101
+ -- Existing metadata keys are data, never an internal delivery bypass switch.
102
+ select set_config('request.jwt.claim.sub',(select ua::text from f),true);
103
+ set local role authenticated;
104
+ select lives_ok($$select * from agent_board.post_message((select thread from f),null,'661 forged metadata',p_metadata=>'{"rpcName":"send_direct_message","boardId":"forged","delegate":{"delegate":true}}')$$,'661 caller metadata cannot select private routing');
105
+ reset role;
106
+ select is((select count(*) from agent_board.delivery_recipients d join agent_board.messages m on m.id=d.source_message_id where m.thread_id=(select thread from f) and m.body='661 forged metadata' and d.board_id=(select board from f)),2::bigint,'661 forged rpcName still receives canonical board delivery');
107
+ select ok((select not (metadata ? 'delegate') from agent_board.messages where thread_id=(select thread from f) and body='661 forged metadata'),'661 nondelegate cannot forge hosted attribution');
108
+ insert into agent_board.board_memberships(board_id,agent_id,membership_state,role) select board,outsider,'joined','member' from f;
109
+ select set_config('request.jwt.claim.sub',(select uo::text from f),true);
110
+ set local role authenticated;
111
+ select is((select count(*) from agent_board.messages where thread_id=(select thread from f) and body in ('661 direct','661 multi')),0::bigint,'661 unrelated board member cannot read private sends');
112
+ select is((select count(*) from agent_board.messages where thread_id=(select thread from f) and body='661 forged metadata'),1::bigint,'661 current board member can read delivered board reply');
113
+ reset role;
114
+ create temp table result661(id uuid);
115
+ grant insert on result661 to authenticated;
116
+ select set_config('request.jwt.claim.sub',(select ua::text from f),true);
117
+ set local role authenticated;
118
+ select lives_ok($$insert into result661 select message_id from agent_board.post_board_message((select thread from f),null,(select board from f),'Canonical','661 canonical',p_idempotency_key=>'661-key')$$,'661 canonical board post succeeds');
119
+ select lives_ok($$insert into result661 select message_id from agent_board.post_board_message((select thread from f),null,(select board from f),'Canonical','661 replay preview',p_idempotency_key=>'661-key')$$,'661 canonical board replay succeeds');
120
+ reset role;
121
+ select is((select count(distinct id) from result661),1::bigint,'661 canonical replay reuses message');
122
+ select is((select count(*) from agent_board.delivery_recipients where source_message_id in(select id from result661)),3::bigint,'661 canonical post uses one fan-out without duplicate delivery');
123
+ select is((select count(*) from agent_board.inbox_items where source_message_id in(select id from result661) and visible_in_inbox),2::bigint,'661 canonical post retains recipient count excluding sender');
124
+ select is((select array_agg(distinct p.proname||'('||oidvectortypes(p.proargtypes)||')' order by p.proname||'('||oidvectortypes(p.proargtypes)||')')
125
+ from agent_board_test.fn_call_closure() c join pg_proc p on p.oid=c.root_oid
126
+ where c.reached_name='_insert_thread_message' and (has_function_privilege('authenticated',p.oid,'execute') or has_function_privilege('service_role',p.oid,'execute') or has_function_privilege('agent_board_delegate',p.oid,'execute'))),
127
+ array[
128
+ 'post_board_message(uuid, uuid, text, text, text, text, text, text, text, jsonb)',
129
+ 'post_board_message(uuid, uuid, uuid, text, text, text, text, text, jsonb)',
130
+ 'post_message(uuid, uuid, text, text, uuid, jsonb)',
131
+ 'send_direct_message(uuid, uuid, jsonb, text, text, text, text, text, text, jsonb)',
132
+ 'send_direct_message(uuid, uuid, uuid, text, text, text, text, text, jsonb)',
133
+ 'send_multicast_message(uuid, uuid, jsonb, text, text, text, text, text, text, jsonb)',
134
+ 'send_multicast_message(uuid, uuid, uuid[], text, text, text, text, text, jsonb)',
135
+ 'signed_post_board_message(jsonb, jsonb)',
136
+ 'signed_send_direct_message(jsonb, jsonb)',
137
+ 'signed_send_multicast_message(jsonb, jsonb)']::text[],
138
+ '661 catalog transitive message entrypoints name every overload and signed route');
139
+
140
+
141
+ create temp table task661(id uuid);
142
+ insert into task661 values(gen_random_uuid());
143
+ insert into agent_board.tasks(id,thread_id,created_by_agent_id,title) select t.id,f.thread,f.a,'661 task' from task661 t cross join f;
144
+ grant select on task661 to authenticated;
145
+ create temp table reply661(message_id uuid,thread_id uuid,task_id uuid,message_sequence bigint,created_at timestamptz);
146
+ grant insert on reply661 to authenticated;
147
+ set local role authenticated;
148
+ select lives_ok($$insert into reply661 select * from agent_board.post_message((select thread from f),null,'661 task reply',p_task_id=>(select id from task661),p_metadata=>'{"custom":"kept"}')$$,'661 task reply retains public return shape');
149
+ reset role;
150
+ select ok((select r.task_id=t.id and r.thread_id=f.thread and r.message_sequence=m.message_sequence and r.created_at=m.created_at from reply661 r cross join task661 t cross join f join agent_board.messages m on m.thread_id=f.thread and m.body='661 task reply'),'661 reply result preserves task thread sequence and timestamp');
151
+ select is((select metadata->>'custom' from agent_board.messages where id=(select message_id from reply661)),'kept','661 reply preserves caller application metadata');
152
+ select is((select count(*) from agent_board.inbox_items where source_message_id=(select message_id from reply661) and subject='661 reply' and priority='medium'),3::bigint,'661 reply inbox uses thread subject and medium priority');
153
+ create function pg_temp.fail661() returns trigger language plpgsql as $$
154
+ begin
155
+ if new.preview='661 atomic' then raise exception '661 injected inbox failure'; end if;
156
+ return new;
157
+ end $$;
158
+ create trigger fail661 before insert on agent_board.inbox_items for each row execute function pg_temp.fail661();
159
+ set local role authenticated;
160
+ select throws_ok($$select * from agent_board.post_message((select thread from f),null,'661 atomic')$$,'P0001','661 injected inbox failure','661 downstream delivery failure reaches caller');
161
+ reset role;
162
+ select is((select count(*) from agent_board.messages where thread_id=(select thread from f) and body='661 atomic'),0::bigint,'661 delivery failure rolls back persisted reply');
163
+
164
+
165
+ -- Dormant restricted state is seeded by the operator, never made public by a reply.
166
+ create temp table restricted661(id uuid);
167
+ insert into restricted661 values(gen_random_uuid());
168
+ grant select on restricted661 to authenticated;
169
+ insert into agent_board.threads(id,thread_type,created_by_agent_id,subject,board_id,access_mode)
170
+ select r.id,'coordination',f.a,'Restricted',f.board,'restricted' from restricted661 r cross join f;
171
+ insert into agent_board.thread_memberships(thread_id,agent_id,role,granted_by_agent_id)
172
+ select r.id,f.a,'member',f.a from restricted661 r cross join f;
173
+ set local role authenticated;
174
+ select lives_ok($$select * from agent_board.post_message((select id from restricted661),null,'661 restricted')$$,'661 authorized restricted-thread reply succeeds');
175
+ reset role;
176
+ select is((select count(*) from agent_board.messages where thread_id=(select id from restricted661)),1::bigint,'661 restricted reply persists');
177
+ select is((select count(*) from agent_board.delivery_recipients d join agent_board.messages m on m.id=d.source_message_id where m.thread_id=(select id from restricted661)),0::bigint,'661 restricted reply does not implicitly broadcast to board');
178
+ select set_config('request.jwt.claim.sub',(select ub::text from f),true);
179
+ set local role authenticated;
180
+ select is((select count(*) from agent_board.messages where thread_id=(select id from restricted661)),0::bigint,'661 board membership does not disclose restricted reply');
181
+ reset role;
182
+ select is((select count(*) from agent_board.inbox_items where source_message_id in(select id from result661) and preview='661 replay preview'),3::bigint,'661 canonical retry retains caller-body preview semantics');
183
+ select ok((select access_mode='restricted' from agent_board.threads where id=(select id from restricted661)) and (select access_mode='inherit' from agent_board.threads where id=(select thread from f)),'661 replies preserve both access modes');
184
+ select * from finish();
185
+ rollback;
@@ -0,0 +1,162 @@
1
+ -- #667 association, private-history and delivery-source boundary witnesses.
2
+ create extension if not exists pgtap with schema extensions;
3
+ set search_path = extensions, public, agent_board;
4
+ begin;
5
+ select plan(36);
6
+ create temp table xb_fixture (board1 uuid, board2 uuid, empty_board uuid, actor uuid, actor_user uuid, thread uuid, private_thread uuid) on commit drop;
7
+ do $$
8
+ declare
9
+ suffix text := replace(substr(gen_random_uuid()::text, 1, 8), '-', '');
10
+ actor_user uuid; actor uuid := gen_random_uuid(); creator uuid := gen_random_uuid();
11
+ board1 uuid; board2 uuid; empty_board uuid := gen_random_uuid(); th uuid := gen_random_uuid(); private_th uuid := gen_random_uuid();
12
+ begin
13
+ actor_user := public.ensure_github_user('xb-actor-' || suffix, 'xb-actor-' || suffix || '@example.test', 'xb-actor-' || suffix, 'XB Actor');
14
+ insert into agent_board.agents(id,agent_type,handle,display_name,status,auth_user_id,owner_user_id)
15
+ values (creator,'agent','xb-creator-' || suffix,'XB Creator','active',null,null),
16
+ (actor,'agent','xb-actor-' || suffix,'XB Actor','active',actor_user,actor_user);
17
+ select board_id into board1 from agent_board.create_board(creator,'xb-one-' || suffix,'XB One','fixture','{}'::jsonb,'private');
18
+ select board_id into board2 from agent_board.create_board(creator,'xb-two-' || suffix,'XB Two','fixture','{}'::jsonb,'private');
19
+ insert into agent_board.boards(id,slug,name,created_by_agent_id,visibility,status)
20
+ values (empty_board,'xb-empty-' || suffix,'XB empty board',creator,'private','active');
21
+ insert into agent_board.board_memberships(board_id,agent_id,membership_state,created_by_agent_id,role)
22
+ values (board1,actor,'joined',creator,'member'), (board2,actor,'joined',creator,'member');
23
+ insert into agent_board.threads(id,thread_type,created_by_agent_id,subject)
24
+ values (th,'coordination',actor,'XB associated'),(private_th,'coordination',actor,'XB private');
25
+ perform set_config('request.jwt.claim.role','authenticated',true);
26
+ perform set_config('request.jwt.claim.sub',actor_user::text,true);
27
+ insert into xb_fixture values(board1,board2,empty_board,actor,actor_user,th,private_th);
28
+ end $$;
29
+ select lives_ok($$select * from agent_board.post_board_message((select thread from xb_fixture),(select actor from xb_fixture),(select board1 from xb_fixture),'first','first')$$,'XB empty first association succeeds');
30
+ select is((select board_id from agent_board.threads where id=(select thread from xb_fixture)),(select board1 from xb_fixture),'XB first association stamps the target');
31
+ select throws_ok($$select * from agent_board.post_board_message((select thread from xb_fixture),(select actor from xb_fixture),(select board2 from xb_fixture),'second','second')$$,'42501','thread belongs to a different board','XB mismatch refuses before delivery');
32
+ select is((select count(*) from agent_board.messages where thread_id=(select thread from xb_fixture)),1::bigint,'XB mismatch leaves message count unchanged');
33
+ select is((select board_id from agent_board.threads where id=(select thread from xb_fixture)),(select board1 from xb_fixture),'XB mismatch preserves original stamp');
34
+ select lives_ok($$select * from agent_board.post_message((select private_thread from xb_fixture),(select actor from xb_fixture),'private')$$,'XB setup writes private history');
35
+ select throws_ok($$select * from agent_board.post_board_message((select private_thread from xb_fixture),(select actor from xb_fixture),(select board1 from xb_fixture),'shared','shared')$$,'42501','thread has private history','XB private history cannot be associated');
36
+ select is((select board_id from agent_board.threads where id=(select private_thread from xb_fixture)),null::uuid,'XB private thread remains unstamped');
37
+ select throws_ok($$select set_config('request.jwt.claim.role','service_role',true); select * from agent_board.post_board_message((select thread from xb_fixture),(select actor from xb_fixture),(select board2 from xb_fixture),'service','service')$$,'42501','thread belongs to a different board','XB service transport cannot bypass mismatch');
38
+ select is((select count(*) from agent_board.messages where thread_id=(select thread from xb_fixture)),1::bigint,'XB service mismatch writes no message');
39
+ select throws_ok($$select set_config('request.jwt.claim.role','service_role',true); select agent_board._upsert_delivery_recipient('board',(select actor from xb_fixture),'message',(select id from agent_board.messages where thread_id=(select thread from xb_fixture)),null,null,(select board2 from xb_fixture),null)$$,'42501','thread belongs to a different board','XB sink independently refuses mismatch');
40
+ select is((select board_id from agent_board.threads where id=(select thread from xb_fixture)),(select board1 from xb_fixture),'XB sink mismatch preserves the stamp');
41
+ -- With no board memberships, no delivery sink runs: this independently pins
42
+ -- the sender's early boundary instead of accepting a later sink refusal.
43
+ select throws_ok($$select set_config('request.jwt.claim.role','service_role',true); select * from agent_board.post_board_message((select thread from xb_fixture),(select actor from xb_fixture),(select empty_board from xb_fixture),'early','early')$$,
44
+ '42501','thread belongs to a different board','XB early sender refuses mismatch without sink recipients');
45
+ select is((select count(*) from agent_board.messages where thread_id=(select thread from xb_fixture)),1::bigint,
46
+ 'XB early refusal leaves message count unchanged');
47
+ -- Isolate the task and event predicates: the event's task belongs to another
48
+ -- thread, so a task predicate on the event thread cannot mask its omission.
49
+ create temp table xb_history_case(kind text primary key,thread_id uuid not null,task_id uuid) on commit drop;
50
+ select lives_ok($q$
51
+ do $setup$
52
+ declare task_thread uuid:=gen_random_uuid(); event_thread uuid:=gen_random_uuid(); sink_thread uuid:=gen_random_uuid();
53
+ task_only uuid:=gen_random_uuid(); parent_task uuid:=gen_random_uuid();
54
+ begin
55
+ insert into agent_board.threads(id,thread_type,created_by_agent_id,subject)
56
+ values (task_thread,'coordination',(select actor from xb_fixture),'XB task only'),
57
+ (event_thread,'coordination',(select actor from xb_fixture),'XB event only'),
58
+ (sink_thread,'coordination',(select actor from xb_fixture),'XB sink source only');
59
+ insert into agent_board.messages(thread_id,author_agent_id,message_sequence,body) values (sink_thread,(select actor from xb_fixture),1,'XB private sink source');
60
+ insert into agent_board.tasks(id,thread_id,created_by_agent_id,title)
61
+ values (task_only,task_thread,(select actor from xb_fixture),'XB isolated task'),
62
+ (parent_task,(select private_thread from xb_fixture),(select actor from xb_fixture),'XB event parent');
63
+ insert into agent_board.task_events(task_id,thread_id,actor_agent_id,event_sequence,event_type,payload,origin_posture)
64
+ values (parent_task,event_thread,(select actor from xb_fixture),1,'created','{}'::jsonb,'unattested:signed-metadata-rpc');
65
+ insert into xb_history_case values ('task',task_thread,task_only),('event',event_thread,parent_task),('sink',sink_thread,null);
66
+ end $setup$;
67
+ $q$, 'XB isolated task and event histories are seeded');
68
+ select throws_ok($$select * from agent_board.post_board_message((select thread_id from xb_history_case where kind='task'),
69
+ (select actor from xb_fixture),(select board1 from xb_fixture),'task','body')$$,
70
+ '42501','thread has private history','XB task-only history refuses association');
71
+ select ok((select board_id is null from agent_board.threads where id=(select thread_id from xb_history_case where kind='task'))
72
+ and exists(select 1 from agent_board.tasks where id=(select task_id from xb_history_case where kind='task')),
73
+ 'XB task-only refusal preserves task and null stamp');
74
+ select throws_ok($$select * from agent_board.post_board_message((select thread_id from xb_history_case where kind='event'),
75
+ (select actor from xb_fixture),(select board1 from xb_fixture),'event','body')$$,
76
+ '42501','thread has private history','XB event-only history refuses association');
77
+ select ok((select board_id is null from agent_board.threads where id=(select thread_id from xb_history_case where kind='event'))
78
+ and exists(select 1 from agent_board.task_events where thread_id=(select thread_id from xb_history_case where kind='event')),
79
+ 'XB event-only refusal preserves event and null stamp');
80
+ select throws_ok($$select agent_board._upsert_delivery_recipient('board',(select actor from xb_fixture),'message',
81
+ (select id from agent_board.messages where thread_id=(select thread_id from xb_history_case where kind='sink')),null,null,(select board1 from xb_fixture),
82
+ (select id from agent_board.board_memberships where board_id=(select board1 from xb_fixture) and agent_id=(select actor from xb_fixture)))$$,
83
+ '42501','thread has private history','XB direct sink cannot associate an existing private source');
84
+ select ok((select board_id is null from agent_board.threads where id=(select thread_id from xb_history_case where kind='sink'))
85
+ and (select count(*) from agent_board.messages where thread_id=(select thread_id from xb_history_case where kind='sink'))=1,
86
+ 'XB direct sink refusal preserves source and null stamp');
87
+ select is((select count(*) from agent_board.delivery_recipients d join agent_board.messages m on m.id=d.source_message_id
88
+ where m.thread_id=(select thread_id from xb_history_case where kind='sink')),
89
+ 0::bigint,'XB direct sink refusal creates no delivery for its private source');
90
+ -- The existing board remains usable; replaying the same key returns the
91
+ -- original message rather than inserting a second copy.
92
+ create temp table xb_same_board_results(message_id uuid) on commit drop;
93
+ select lives_ok($$insert into xb_same_board_results select message_id from agent_board.post_board_message(
94
+ (select thread from xb_fixture),(select actor from xb_fixture),(select board1 from xb_fixture),
95
+ 'same board','replayed body',p_idempotency_key=>'xb-same-board')$$,
96
+ 'XB same-board keyed post succeeds');
97
+ select lives_ok($$insert into xb_same_board_results select message_id from agent_board.post_board_message(
98
+ (select thread from xb_fixture),(select actor from xb_fixture),(select board1 from xb_fixture),
99
+ 'same board','replayed body',p_idempotency_key=>'xb-same-board')$$,
100
+ 'XB same-board keyed replay succeeds');
101
+ select is((select count(distinct message_id) from xb_same_board_results),1::bigint,
102
+ 'XB same-board replay returns the original message');
103
+ select is((select count(*) from agent_board.messages where thread_id=(select thread from xb_fixture)),2::bigint,
104
+ 'XB same-board replay inserts exactly one new message');
105
+ select is((select board_id from agent_board.threads where id=(select thread from xb_fixture)),(select board1 from xb_fixture),
106
+ 'XB same-board replay preserves the original stamp');
107
+ -- A task and a delegation each carry their own real canonical parent. Their
108
+ -- private histories cannot be promoted by calling the sink directly; a
109
+ -- same-board source, however, remains a valid delivery.
110
+ create temp table xb_sink_sources(kind text primary key,thread_id uuid not null,source_id uuid not null) on commit drop;
111
+ select lives_ok($q$
112
+ do $setup$
113
+ declare private_thread uuid:=gen_random_uuid(); private_parent uuid:=gen_random_uuid(); private_child uuid:=gen_random_uuid();
114
+ private_delegation uuid:=gen_random_uuid(); board_parent uuid:=gen_random_uuid(); board_child uuid:=gen_random_uuid(); board_delegation uuid:=gen_random_uuid();
115
+ begin
116
+ insert into agent_board.threads(id,thread_type,created_by_agent_id,subject)
117
+ values(private_thread,'coordination',(select actor from xb_fixture),'XB private delegation');
118
+ insert into agent_board.tasks(id,thread_id,created_by_agent_id,title)
119
+ values(private_parent,private_thread,(select actor from xb_fixture),'private parent'),
120
+ (private_child,private_thread,(select actor from xb_fixture),'private child'),
121
+ (board_parent,(select thread from xb_fixture),(select actor from xb_fixture),'board parent'),
122
+ (board_child,(select thread from xb_fixture),(select actor from xb_fixture),'board child');
123
+ insert into agent_board.delegations(id,parent_task_id,child_task_id,delegated_by_agent_id,delegated_to_agent_id)
124
+ values(private_delegation,private_parent,private_child,(select actor from xb_fixture),(select actor from xb_fixture)),
125
+ (board_delegation,board_parent,board_child,(select actor from xb_fixture),(select actor from xb_fixture));
126
+ insert into xb_sink_sources values ('private_task',(select thread_id from xb_history_case where kind='task'),(select task_id from xb_history_case where kind='task')),
127
+ ('private_delegation',private_thread,private_delegation),('board_task',(select thread from xb_fixture),board_parent),
128
+ ('board_delegation',(select thread from xb_fixture),board_delegation);
129
+ end $setup$;
130
+ $q$, 'XB task and delegation sink sources are seeded');
131
+ select throws_ok($$select agent_board._upsert_delivery_recipient('tasking',(select actor from xb_fixture),'task',null,
132
+ (select source_id from xb_sink_sources where kind='private_task'),null,(select board1 from xb_fixture),
133
+ (select id from agent_board.board_memberships where board_id=(select board1 from xb_fixture) and agent_id=(select actor from xb_fixture)))$$,
134
+ '42501','thread has private history','XB direct task sink refuses private parent history');
135
+ select ok((select board_id is null from agent_board.threads where id=(select thread_id from xb_sink_sources where kind='private_task'))
136
+ and not exists(select 1 from agent_board.delivery_recipients where source_task_id=(select source_id from xb_sink_sources where kind='private_task')),
137
+ 'XB private task sink leaves stamp and delivery unchanged');
138
+ select throws_ok($$select agent_board._upsert_delivery_recipient('tasking',(select actor from xb_fixture),'delegation',null,null,
139
+ (select source_id from xb_sink_sources where kind='private_delegation'),(select board1 from xb_fixture),
140
+ (select id from agent_board.board_memberships where board_id=(select board1 from xb_fixture) and agent_id=(select actor from xb_fixture)))$$,
141
+ '42501','thread has private history','XB direct delegation sink refuses private child history');
142
+ select ok((select board_id is null from agent_board.threads where id=(select thread_id from xb_sink_sources where kind='private_delegation'))
143
+ and not exists(select 1 from agent_board.delivery_recipients where source_delegation_id=(select source_id from xb_sink_sources where kind='private_delegation'))
144
+ and exists(select 1 from agent_board.delegations d join agent_board.tasks c on c.id=d.child_task_id
145
+ where d.id=(select source_id from xb_sink_sources where kind='private_delegation') and c.thread_id=(select thread_id from xb_sink_sources where kind='private_delegation')),
146
+ 'XB private delegation sink preserves canonical child and leaves no delivery');
147
+ select lives_ok($$select agent_board._upsert_delivery_recipient('tasking',(select actor from xb_fixture),'task',null,
148
+ (select source_id from xb_sink_sources where kind='board_task'),null,(select board1 from xb_fixture),
149
+ (select id from agent_board.board_memberships where board_id=(select board1 from xb_fixture) and agent_id=(select actor from xb_fixture)))$$,
150
+ 'XB same-board direct task sink succeeds');
151
+ select ok(exists(select 1 from agent_board.delivery_recipients where source_task_id=(select source_id from xb_sink_sources where kind='board_task')
152
+ and board_id=(select board1 from xb_fixture)), 'XB same-board task sink retains source and board');
153
+ select lives_ok($$select agent_board._upsert_delivery_recipient('tasking',(select actor from xb_fixture),'delegation',null,null,
154
+ (select source_id from xb_sink_sources where kind='board_delegation'),(select board1 from xb_fixture),
155
+ (select id from agent_board.board_memberships where board_id=(select board1 from xb_fixture) and agent_id=(select actor from xb_fixture)))$$,
156
+ 'XB same-board direct delegation sink succeeds');
157
+ select ok(exists(select 1 from agent_board.delivery_recipients d join agent_board.delegations g on g.id=d.source_delegation_id
158
+ join agent_board.tasks c on c.id=g.child_task_id where g.id=(select source_id from xb_sink_sources where kind='board_delegation')
159
+ and d.board_id=(select board1 from xb_fixture) and c.thread_id=(select thread from xb_fixture)),
160
+ 'XB same-board delegation sink retains canonical child and board');
161
+ select * from finish();
162
+ rollback;
@@ -0,0 +1,91 @@
1
+ -- Native calls execute only when their full overload identity is present.
2
+ -- The plan is derived from those present cases; absence earns no call credit.
3
+ create extension if not exists pgtap with schema extensions;
4
+ set search_path=extensions,public,agent_board;
5
+ begin;
6
+ \ir fixtures/agent_board_667_conversion_guard.sql
7
+ select set_config('request.jwt.claim.role','service_role',true);
8
+ insert into agent_board.agents(id,agent_type,handle,display_name,status)
9
+ values('667f0000-0000-4000-8000-000000000001','agent','conversion-branches','Conversion branches','active');
10
+ insert into agent_board.threads(id,thread_type,created_by_agent_id,subject)
11
+ values('667f0000-0000-4000-8000-000000000010','coordination','667f0000-0000-4000-8000-000000000001','Quoted values are data');
12
+ create type pg_temp.xb_cf_pair as(i integer,t text);
13
+ create temp table xb_cf_cases(fn text,label text,expression text,expected jsonb,primary key(label)) on commit drop;
14
+ insert into xb_cf_cases values
15
+ ('realtime."cast"(text,regtype)','cast integer',$q$realtime."cast"('4','integer')$q$,'4'),
16
+ ('realtime."cast"(text,regtype)','cast quoted text',$q$realtime."cast"('a''b','text')$q$,to_jsonb('a''b'::text)),
17
+ ('realtime."cast"(text,regtype)','cast bytea preserves encoded text',$q$realtime."cast"('\x0041','bytea')$q$,to_jsonb('\x0041'::text)),
18
+ ('realtime."cast"(text,regtype)','cast bytea preserves plain text',$q$realtime."cast"('hello','bytea')$q$,to_jsonb('hello'::text)),
19
+ ('realtime."cast"(text,regtype)','cast integer array',$q$realtime."cast"('{1,2}','integer[]')$q$,'[1,2]'),
20
+ ('realtime."cast"(text,regtype)','cast composite',$q$realtime."cast"('(7,"a,b")','pg_temp.xb_cf_pair')$q$,'{"i":7,"t":"a,b"}'),
21
+ ('realtime."cast"(text,regtype)','cast reviewed domain',$q$realtime."cast"('4','information_schema.cardinal_number')$q$,'4'),
22
+ ('realtime.check_equality_op(realtime.equality_op,regtype,text,text)','four argument equality',$q$realtime.check_equality_op('eq','integer','4','4')$q$,'true'),
23
+ ('realtime.check_equality_op(realtime.equality_op,regtype,text,text)','four argument membership',$q$realtime.check_equality_op('in','integer','4','{3,4}')$q$,'true'),
24
+ ('realtime.check_equality_op(realtime.equality_op,regtype,text,text)','four argument inequality',$q$realtime.check_equality_op('neq','integer','4','4')$q$,'false'),
25
+ ('realtime.check_equality_op(realtime.equality_op,regtype,text,text,boolean)','five argument equality',$q$realtime.check_equality_op('eq','integer','4','4',false)$q$,'true'),
26
+ ('realtime.check_equality_op(realtime.equality_op,regtype,text,text,boolean)','five argument negation',$q$realtime.check_equality_op('eq','integer','4','4',true)$q$,'false'),
27
+ ('realtime.check_equality_op(realtime.equality_op,regtype,text,text,boolean)','distinct',$q$realtime.check_equality_op('isdistinct','integer','4','5',false)$q$,'true'),
28
+ ('realtime.check_equality_op(realtime.equality_op,regtype,text,text,boolean)','not distinct',$q$realtime.check_equality_op('isdistinct','integer','4','4',true)$q$,'true'),
29
+ ('realtime.check_equality_op(realtime.equality_op,regtype,text,text,boolean)','is null',$q$realtime.check_equality_op('is','integer',null::text,'null',false)$q$,'true'),
30
+ ('realtime.check_equality_op(realtime.equality_op,regtype,text,text,boolean)','is not null',$q$realtime.check_equality_op('is','integer',null::text,'null',true)$q$,'false'),
31
+ ('realtime.check_equality_op(realtime.equality_op,regtype,text,text,boolean)','like quoted text',$q$realtime.check_equality_op('like','text','a''b','%''%',false)$q$,'true'),
32
+ ('realtime.check_equality_op(realtime.equality_op,regtype,text,text,boolean)','case insensitive like',$q$realtime.check_equality_op('ilike','text','Alpha','a%',false)$q$,'true'),
33
+ ('realtime.check_equality_op(realtime.equality_op,regtype,text,text,boolean)','regular expression',$q$realtime.check_equality_op('match','text','alpha','^a.*',false)$q$,'true'),
34
+ ('realtime.check_equality_op(realtime.equality_op,regtype,text,text,boolean)','case insensitive regular expression',$q$realtime.check_equality_op('imatch','text','ALPHA','^a.*',false)$q$,'true'),
35
+ ('realtime.check_equality_op(realtime.equality_op,regtype,text,text,boolean)','negated membership',$q$realtime.check_equality_op('in','integer','4','{3,4}',true)$q$,'false');
36
+ -- Removing native literal quoting turns this valid text value into a successful
37
+ -- guarded task write. The final scoped assertion detects that side effect.
38
+ insert into xb_cf_cases
39
+ select 'realtime."cast"(text,regtype)','cast expression-looking text',
40
+ format('realtime."cast"(%L,''text'')',p),to_jsonb(p)
41
+ from(values($v$agent_board.create_task('667f0000-0000-4000-8000-000000000010','667f0000-0000-4000-8000-000000000001','escaped literal')$v$)) v(p);
42
+ insert into xb_cf_cases
43
+ select 'realtime.check_equality_op(realtime.equality_op,regtype,text,text,boolean)','distinct expression-looking text',
44
+ format('realtime.check_equality_op(''isdistinct'',''text'',%L,%L,false)',p,p),'false'::jsonb
45
+ from(values($v$agent_board.create_task('667f0000-0000-4000-8000-000000000010','667f0000-0000-4000-8000-000000000001','escaped literal')$v$)) v(p);
46
+ insert into xb_cf_cases
47
+ select 'realtime.check_equality_op(realtime.equality_op,regtype,text,text,boolean)','is-null expression-looking text',
48
+ format('realtime.check_equality_op(''is'',''text'',%L,''null'',false)',p),'false'::jsonb
49
+ from(values($v$agent_board.create_task('667f0000-0000-4000-8000-000000000010','667f0000-0000-4000-8000-000000000001','escaped literal')$v$)) v(p);
50
+ insert into xb_cf_cases
51
+ select 'realtime.check_equality_op(realtime.equality_op,regtype,text,text)','four argument expression-looking text',
52
+ format('realtime.check_equality_op(''eq'',''text'',%L,%L)',p,p),'true'::jsonb
53
+ from(values($v$agent_board.create_task('667f0000-0000-4000-8000-000000000010','667f0000-0000-4000-8000-000000000001','escaped literal')$v$)) v(p);
54
+ insert into xb_cf_cases
55
+ select 'realtime.check_equality_op(realtime.equality_op,regtype,text,text,boolean)','five argument expression-looking text',
56
+ format('realtime.check_equality_op(''eq'',''text'',%L,%L,false)',p,p),'true'::jsonb
57
+ from(values($v$agent_board.create_task('667f0000-0000-4000-8000-000000000010','667f0000-0000-4000-8000-000000000001','escaped literal')$v$)) v(p);
58
+ create temp table xb_cf_values(label text primary key,value jsonb) on commit drop;
59
+ create function pg_temp.xb_cf_run(p_label text,p_expression text,p_expected jsonb) returns setof text
60
+ language plpgsql volatile as $$
61
+ begin
62
+ return next lives_ok(format('insert into pg_temp.xb_cf_values select %L,to_jsonb((%s))',p_label,p_expression),
63
+ 'CB '||p_label||' call succeeds');
64
+ return next is((select value from pg_temp.xb_cf_values where label=p_label),p_expected,
65
+ 'CB '||p_label||' returns the expected value');
66
+ end$$;
67
+ select plan(4+2*(select count(*)::integer from xb_cf_cases where to_regprocedure(fn) is not null)
68
+ +(to_regprocedure('realtime."cast"(text,regtype)') is not null)::integer
69
+ +(to_regprocedure('realtime.check_equality_op(realtime.equality_op,regtype,text,text,boolean)') is not null)::integer);
70
+ select ok((select count(distinct fn) from xb_cf_cases)=3,'CB fixtures cover exactly three native overloads');
71
+ select is((select count(*) from pg_temp.xb_cf_problems()),0::bigint,'CB call fixtures retain the reviewed dispatch profile')
72
+ where exists(select 1 from xb_cf_cases where to_regprocedure(fn) is not null);
73
+ select * from skip('native converters absent; no primitive admission credit',1)
74
+ where not exists(select 1 from xb_cf_cases where to_regprocedure(fn) is not null);
75
+ select is((select count(*) from pg_temp.xb_cf_wrapper_problems()),0::bigint,'CB present native definitions and security metadata are exact');
76
+ select diag('Native calls absent; no native branch execution credit')
77
+ where not exists(select 1 from xb_cf_cases where to_regprocedure(fn) is not null);
78
+ select r.* from (select * from xb_cf_cases where to_regprocedure(fn) is not null order by label) c
79
+ cross join lateral pg_temp.xb_cf_run(c.label,c.expression,c.expected) r;
80
+ select throws_ok($$select realtime."cast"('-1','information_schema.cardinal_number')$$,'23514',
81
+ 'value for domain information_schema.cardinal_number violates check constraint "cardinal_number_domain_check"',
82
+ 'CB native cast preserves the reviewed domain refusal')
83
+ where to_regprocedure('realtime."cast"(text,regtype)') is not null;
84
+ select throws_ok($$select realtime.check_equality_op('is','integer','4','true; select 1',false)$$,'P0001',
85
+ 'invalid value for is filter: must be null, true, false, or unknown',
86
+ 'CB is-filter rejects a non-keyword before interpolation')
87
+ where to_regprocedure('realtime.check_equality_op(realtime.equality_op,regtype,text,text,boolean)') is not null;
88
+ select is((select count(*) from agent_board.tasks where thread_id='667f0000-0000-4000-8000-000000000010'),0::bigint,
89
+ 'CB native literal interpolation creates no fixture history');
90
+ select * from finish();
91
+ rollback;