@opengeni/db 1.0.1 → 3.1.0-canary.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 (265) hide show
  1. package/dist/attached-browser-devices.d.ts +7 -0
  2. package/dist/automations.d.ts +154 -0
  3. package/dist/canonical-human-identities.js +3 -3
  4. package/dist/capability-integrations.d.ts +13 -0
  5. package/dist/child-lifecycle-notices.d.ts +68 -0
  6. package/dist/{chunk-LTG3S6ZY.js → chunk-EULAN2NP.js} +2 -2
  7. package/dist/{chunk-CUPM3FGB.js → chunk-NJVZPWXY.js} +3 -3
  8. package/dist/{chunk-4SR4HKKA.js → chunk-NMUBV7Z5.js} +19 -11
  9. package/dist/chunk-NMUBV7Z5.js.map +1 -0
  10. package/dist/chunk-OF5OBB76.js +4457 -0
  11. package/dist/chunk-OF5OBB76.js.map +1 -0
  12. package/dist/{chunk-6ZYKCRFO.js → chunk-PR2WMLOQ.js} +7580 -4417
  13. package/dist/chunk-PR2WMLOQ.js.map +1 -0
  14. package/dist/{chunk-ZWREYEWA.js → chunk-SCPUJS53.js} +41 -4
  15. package/dist/chunk-SCPUJS53.js.map +1 -0
  16. package/dist/{chunk-SK7YYHBI.js → chunk-SIBYBA2D.js} +46 -1
  17. package/dist/chunk-SIBYBA2D.js.map +1 -0
  18. package/dist/chunk-TIKF3KTI.js +383 -0
  19. package/dist/chunk-TIKF3KTI.js.map +1 -0
  20. package/dist/chunk-ZZ5LJEFX.js +516 -0
  21. package/dist/chunk-ZZ5LJEFX.js.map +1 -0
  22. package/dist/codemode-operations.d.ts +1 -6
  23. package/dist/company-brain-context-selection-schema.d.ts +705 -0
  24. package/dist/company-brain-context-selection.d.ts +94 -0
  25. package/dist/company-brain-governed-writes.d.ts +24 -0
  26. package/dist/company-brain.d.ts +55 -0
  27. package/dist/company-profile-agent-admin.d.ts +15 -0
  28. package/dist/company-profile-schema.d.ts +619 -2
  29. package/dist/company-profile.d.ts +9 -4
  30. package/dist/connection-authority.d.ts +95 -0
  31. package/dist/connection-scopes.d.ts +8 -0
  32. package/dist/connection-tenancy-backfill.d.ts +41 -0
  33. package/dist/connection-token-resolver.d.ts +30 -1
  34. package/dist/database.d.ts +19 -0
  35. package/dist/editable-artifact-durable-export.js +2 -2
  36. package/dist/editable-artifacts.d.ts +2 -2
  37. package/dist/editable-artifacts.js +3 -3
  38. package/dist/governed-learning-activation-schema.d.ts +984 -0
  39. package/dist/governed-learning-activation.d.ts +98 -0
  40. package/dist/governed-learning-evaluator-schema.d.ts +633 -0
  41. package/dist/governed-learning-evaluator.d.ts +28 -0
  42. package/dist/index.d.ts +1595 -81
  43. package/dist/index.js +21571 -8126
  44. package/dist/index.js.map +1 -1
  45. package/dist/insights.d.ts +18 -0
  46. package/dist/integration-bindings.d.ts +1 -0
  47. package/dist/integration-facets.d.ts +30 -0
  48. package/dist/interaction-placement-loss.d.ts +22 -0
  49. package/dist/managed-human-provisioning.d.ts +25 -0
  50. package/dist/memory-domain.d.ts +3 -3
  51. package/dist/migrate.d.ts +11 -1
  52. package/dist/migrate.js +1 -1
  53. package/dist/new-session-drafts.d.ts +43 -5
  54. package/dist/organization-membership-backfill.d.ts +174 -0
  55. package/dist/organization-membership-lifecycle.d.ts +162 -0
  56. package/dist/pr-review.d.ts +107 -0
  57. package/dist/preference-registry-schema.d.ts +2 -0
  58. package/dist/preference-registry.d.ts +2 -0
  59. package/dist/provision-roles.js +1 -1
  60. package/dist/runtime-posture.d.ts +22 -8
  61. package/dist/schema.d.ts +24914 -13703
  62. package/dist/schema.js +99 -1
  63. package/dist/scoped-knowledge-schema.d.ts +52 -1
  64. package/dist/session-background-commands.d.ts +125 -0
  65. package/dist/session-background-commands.js +34 -0
  66. package/dist/session-background-commands.js.map +1 -0
  67. package/dist/session-control.d.ts +156 -3
  68. package/dist/session-goal-pacing.d.ts +68 -0
  69. package/dist/session-queue-commands.d.ts +41 -4
  70. package/dist/session-realtime-ledger.d.ts +2 -0
  71. package/dist/session-tenancy.d.ts +71 -1
  72. package/dist/session-tenancy.js +25 -3
  73. package/dist/session-tool-call-settlement.d.ts +9 -4
  74. package/dist/slack-routing-personal-workspace.d.ts +23 -0
  75. package/dist/slack-routing.d.ts +153 -0
  76. package/dist/slack-task-policy-schema.d.ts +34 -0
  77. package/dist/slack-task-policy.d.ts +8 -0
  78. package/dist/slack-user-link-access.d.ts +1 -0
  79. package/dist/task-notes-schema.d.ts +594 -0
  80. package/dist/task-notes.d.ts +39 -0
  81. package/dist/turn-initiator.d.ts +2 -0
  82. package/dist/user-resource-authority.d.ts +109 -0
  83. package/dist/video-generation.js +3 -3
  84. package/dist/workspace-authority.d.ts +81 -0
  85. package/dist/workspace-instruction-policies-schema.d.ts +279 -0
  86. package/dist/workspace-instruction-policies.d.ts +62 -0
  87. package/dist/workspace-learning-policy.d.ts +1 -0
  88. package/dist/workspace-membership-permissions.d.ts +2 -0
  89. package/drizzle/0240_enrollment_connection_authority.sql +39 -0
  90. package/drizzle/0241_atomic_personal_resource_delegation.sql +1000 -0
  91. package/drizzle/0241_enrollment_agent_runtime.sql +70 -0
  92. package/drizzle/0244_slack_app_home_refresh_queue.sql +120 -0
  93. package/drizzle/0245_model_context_contribution_facts.sql +88 -0
  94. package/drizzle/0246_integration_personal_instance_authority.sql +70 -0
  95. package/drizzle/0247_terraform_stacks_provenance_repair.sql +428 -0
  96. package/drizzle/0248_terraform_stacks_component_resolution_fence.sql +83 -0
  97. package/drizzle/0249_personal_resource_delegation_authority_correction.sql +662 -0
  98. package/drizzle/0250_direct_retained_process_owner_liveness.sql +199 -0
  99. package/drizzle/0251_connected_machine_operation_policy.sql +45 -0
  100. package/drizzle/0252_scheduled_personal_resource_delegation.sql +1867 -0
  101. package/drizzle/0253_common_user_resource_authority_lifecycle.sql +1555 -0
  102. package/drizzle/0254_scoped_variable_set_authority.sql +1255 -0
  103. package/drizzle/0255_company_brain_governed_write_proposals.sql +524 -0
  104. package/drizzle/0256_connection_authority_delegation.sql +703 -0
  105. package/drizzle/0257_goal_revision_decisions_and_root_constraints.sql +436 -0
  106. package/drizzle/0258_three_scope_document_knowledge_authority.sql +1273 -0
  107. package/drizzle/0259_company_brain_context_selection_receipts.sql +869 -0
  108. package/drizzle/0260_task_note_knowledge_promotion.sql +820 -0
  109. package/drizzle/0261_preference_knowledge_proposal_actor_binding.sql +471 -0
  110. package/drizzle/0262_scoped_connected_machines_and_rigs.sql +1700 -0
  111. package/drizzle/0263_organization_membership_lifecycle.sql +3284 -0
  112. package/drizzle/0264_connection_authority_runtime_activation.sql +1084 -0
  113. package/drizzle/0266_company_brain_context_receipt_inspection.sql +156 -0
  114. package/drizzle/0268_governed_learning_decision_receipts.sql +825 -0
  115. package/drizzle/0269_governed_learning_activation_controller.sql +1874 -0
  116. package/drizzle/0270_governed_learning_history_inspection.sql +170 -0
  117. package/drizzle/0271_company_brain_retrieval_only_default.sql +524 -0
  118. package/drizzle/0272_human_confirmed_learning_activation.sql +591 -0
  119. package/drizzle/0273_scheduled_variable_set_materialization.sql +268 -0
  120. package/drizzle/0274_human_confirmed_knowledge_review.sql +310 -0
  121. package/drizzle/0275_scheduled_connection_authority.sql +5527 -0
  122. package/drizzle/0276_onboarding_proposal_initiating_human_guc.sql +198 -0
  123. package/drizzle/0277_workspace_writer_authority_attribution.sql +393 -0
  124. package/drizzle/0278_workspace_membership_removal_fencing.sql +1174 -0
  125. package/drizzle/0279_workspace_connection_use_lane.sql +343 -0
  126. package/drizzle/0280_connection_and_variable_set_audit_attribution.sql +773 -0
  127. package/drizzle/0281_viewer_holder_authority_claims.sql +52 -0
  128. package/drizzle/0282_variable_set_session_attach_attribution.sql +178 -0
  129. package/drizzle/0283_editable_spreadsheet_authored_state.sql +82 -0
  130. package/drizzle/0284_truthful_human_confirmed_review_reason.sql +930 -0
  131. package/drizzle/0285_organization_tenancy_inventory.sql +329 -0
  132. package/drizzle/0286_widen_task_note_expiry_ceiling.sql +391 -0
  133. package/drizzle/0287_open_suffix_pending_tool_calls.sql +18 -0
  134. package/drizzle/0288_attached_browser_reenrollment.sql +64 -0
  135. package/drizzle/0289_session_composer_policy_authority.sql +597 -0
  136. package/drizzle/0290_organization_membership_backfill.sql +268 -0
  137. package/drizzle/0291_resource_authority_classification_assertion.sql +371 -0
  138. package/drizzle/0292_truthful_tenancy_inventory_counters.sql +291 -0
  139. package/drizzle/0293_confirm_time_rule_rebaseline.sql +615 -0
  140. package/drizzle/0294_preference_activation_authority.sql +237 -0
  141. package/drizzle/0295_retire_legacy_standing_memory_mode.sql +476 -0
  142. package/drizzle/0296_force_rls_backfill_noop_repair.sql +215 -0
  143. package/drizzle/0297_session_ownership_classification_and_backfill.sql +752 -0
  144. package/drizzle/0298_organization_tenancy_parity.sql +609 -0
  145. package/drizzle/0299_organization_membership_lock_order.sql +291 -0
  146. package/drizzle/0300_tenancy_backfill_ledger.sql +459 -0
  147. package/drizzle/0301_session_snapshot_and_pin_visibility.sql +181 -0
  148. package/drizzle/0302_personal_workspace_session_ownership.sql +303 -0
  149. package/drizzle/0303_session_tenancy_product_activation.sql +919 -0
  150. package/drizzle/0304_personal_workspace_private_session_reads.sql +97 -0
  151. package/drizzle/0305_personal_resource_grant_management.sql +584 -0
  152. package/drizzle/0306_atomic_personal_resource_attachments.sql +1374 -0
  153. package/drizzle/0307_session_attention_state.sql +51 -0
  154. package/drizzle/0308_session_archives.sql +35 -0
  155. package/drizzle/0309_channel_project_pins.sql +6 -0
  156. package/drizzle/0310_channel_project_order.sql +27 -0
  157. package/drizzle/0311_company_scope_and_private_session_create.sql +656 -0
  158. package/drizzle/0312_quiescent_session_tree_deletion.sql +39 -0
  159. package/drizzle/0313_private_child_session_authority.sql +365 -0
  160. package/drizzle/0314_unregistered_organization_invitations.sql +606 -0
  161. package/drizzle/0315_personal_github_repository_selection.sql +624 -0
  162. package/drizzle/0316_human_confirmed_activation_resumed_generation.sql +869 -0
  163. package/drizzle/0317_session_goal_continuation_hold.sql +64 -0
  164. package/drizzle/0318_session_turn_startup_milestones.sql +115 -0
  165. package/drizzle/0319_event_triggered_automations.sql +254 -0
  166. package/drizzle/0320_pr_review_pack.sql +113 -0
  167. package/drizzle/0321_slack_bot_environment_display_name.sql +15 -0
  168. package/drizzle/0322_session_turns_unclaimed_prompt_trigger_index.sql +5 -0
  169. package/drizzle/0323_organization_private_session_enablement.sql +489 -0
  170. package/drizzle/0324_human_confirmed_company_profile_agent_admin.sql +856 -0
  171. package/drizzle/0325_child_lifecycle_notices.sql +44 -0
  172. package/drizzle/0326_interaction_operation_error_codes.sql +62 -0
  173. package/drizzle/0327_slack_first_task_hint.sql +29 -0
  174. package/drizzle/0328_session_background_commands.sql +590 -0
  175. package/drizzle/0329_slack_orchestration_delivery_events.sql +91 -0
  176. package/drizzle/0330_api_key_descriptions.sql +20 -0
  177. package/drizzle/0331_managed_organization_creation.sql +163 -0
  178. package/drizzle/0332_organization_shared_workspace_control_plane.sql +539 -0
  179. package/drizzle/0333_session_turn_prompt_routing.sql +45 -0
  180. package/drizzle/0334_connected_machine_workspace_root.sql +99 -0
  181. package/drizzle/0335_slack_workspace_routing.sql +460 -0
  182. package/drizzle/0336_atomic_session_fork_visibility.sql +667 -0
  183. package/drizzle/0337_slack_routed_action_handles.sql +189 -0
  184. package/drizzle/0338_atomic_connected_machine_attachments.sql +410 -0
  185. package/drizzle/0339_document_authority_reclassification.sql +1153 -0
  186. package/drizzle/0340_tenancy_backfill_activation_evidence.sql +1347 -0
  187. package/drizzle/0341_slack_routing_probe_organization_fence.sql +109 -0
  188. package/package.json +10 -6
  189. package/src/attached-browser-devices.ts +233 -5
  190. package/src/automations.ts +911 -0
  191. package/src/browser-sessions.ts +10 -0
  192. package/src/capability-integrations.ts +175 -1
  193. package/src/child-lifecycle-notices.ts +241 -0
  194. package/src/codemode-operations.ts +1 -35
  195. package/src/company-brain-context-selection-schema.ts +100 -0
  196. package/src/company-brain-context-selection.ts +359 -0
  197. package/src/company-brain-governed-writes.ts +1170 -0
  198. package/src/company-brain.ts +309 -0
  199. package/src/company-profile-agent-admin.ts +225 -0
  200. package/src/company-profile-schema.ts +121 -1
  201. package/src/company-profile.ts +21 -0
  202. package/src/computer-sessions.ts +10 -1
  203. package/src/connection-authority.ts +290 -0
  204. package/src/connection-scopes.ts +20 -0
  205. package/src/connection-tenancy-backfill.ts +81 -0
  206. package/src/connection-token-resolver.ts +263 -31
  207. package/src/database.ts +75 -2
  208. package/src/editable-artifacts-schema.ts +10 -5
  209. package/src/editable-artifacts.ts +21 -11
  210. package/src/governed-learning-activation-schema.ts +121 -0
  211. package/src/governed-learning-activation.ts +588 -0
  212. package/src/governed-learning-evaluator-schema.ts +72 -0
  213. package/src/governed-learning-evaluator.ts +217 -0
  214. package/src/index.ts +11651 -1548
  215. package/src/insights.ts +107 -0
  216. package/src/integration-bindings.ts +88 -6
  217. package/src/integration-facets.ts +287 -84
  218. package/src/interaction-placement-loss.ts +234 -0
  219. package/src/managed-human-provisioning.ts +116 -0
  220. package/src/memory-domain.ts +27 -32
  221. package/src/migrate.ts +77 -1
  222. package/src/new-session-drafts.ts +229 -35
  223. package/src/organization-membership-backfill.ts +735 -0
  224. package/src/organization-membership-lifecycle.ts +985 -0
  225. package/src/pr-review.ts +890 -0
  226. package/src/preference-registry.ts +31 -1
  227. package/src/provision-roles.ts +877 -6
  228. package/src/runtime-posture-cli.ts +2 -0
  229. package/src/runtime-posture.ts +1291 -76
  230. package/src/schema.ts +3303 -428
  231. package/src/scoped-knowledge-schema.ts +16 -8
  232. package/src/scoped-knowledge.ts +1 -1
  233. package/src/session-background-commands.ts +759 -0
  234. package/src/session-control.ts +640 -74
  235. package/src/session-goal-pacing.ts +126 -0
  236. package/src/session-queue-commands.ts +437 -84
  237. package/src/session-realtime-context.ts +3 -3
  238. package/src/session-realtime-ledger.ts +22 -11
  239. package/src/session-realtime-mirror.ts +1 -1
  240. package/src/session-tenancy.ts +403 -49
  241. package/src/session-tool-call-settlement.ts +38 -29
  242. package/src/slack-routing-personal-workspace.ts +67 -0
  243. package/src/slack-routing.ts +469 -0
  244. package/src/slack-task-policy-schema.ts +5 -0
  245. package/src/slack-task-policy.ts +18 -0
  246. package/src/slack-user-link-access.ts +17 -1
  247. package/src/task-notes-schema.ts +74 -0
  248. package/src/task-notes.ts +124 -2
  249. package/src/turn-initiator.ts +8 -0
  250. package/src/user-resource-authority.ts +377 -0
  251. package/src/workspace-authority.ts +160 -0
  252. package/src/workspace-instruction-policies-schema.ts +53 -1
  253. package/src/workspace-instruction-policies.ts +528 -77
  254. package/src/workspace-learning-policy.ts +21 -5
  255. package/src/workspace-membership-permissions.ts +9 -0
  256. package/dist/chunk-4SR4HKKA.js.map +0 -1
  257. package/dist/chunk-6ZYKCRFO.js.map +0 -1
  258. package/dist/chunk-NXAFLCQX.js +0 -2468
  259. package/dist/chunk-NXAFLCQX.js.map +0 -1
  260. package/dist/chunk-SK7YYHBI.js.map +0 -1
  261. package/dist/chunk-WD5ROSLL.js +0 -117
  262. package/dist/chunk-WD5ROSLL.js.map +0 -1
  263. package/dist/chunk-ZWREYEWA.js.map +0 -1
  264. /package/dist/{chunk-LTG3S6ZY.js.map → chunk-EULAN2NP.js.map} +0 -0
  265. /package/dist/{chunk-CUPM3FGB.js.map → chunk-NJVZPWXY.js.map} +0 -0
@@ -0,0 +1,1874 @@
1
+ -- deployment-mode: rolling
2
+ SET lock_timeout = '5s';
3
+ SET statement_timeout = '60s';
4
+
5
+ -- Consume one final governed-learning automatic verdict without turning that
6
+ -- inert receipt into a reusable grant. The controller revalidates every live
7
+ -- authority and calls only destination-owned service lifecycle functions.
8
+
9
+ CREATE TABLE "governed_learning_activation_receipts" (
10
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
11
+ "account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
12
+ "workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
13
+ "operation_id" uuid NOT NULL,
14
+ "input_hash" text NOT NULL,
15
+ "decision_receipt_id" uuid NOT NULL
16
+ REFERENCES "governed_learning_decision_receipts"("id") ON DELETE RESTRICT,
17
+ "initiating_human_subject_id" text NOT NULL,
18
+ "service_actor_subject_id" text NOT NULL,
19
+ "policy_revision_id" uuid NOT NULL
20
+ REFERENCES "workspace_learning_policy_revisions"("id") ON DELETE RESTRICT,
21
+ "policy_hash" text NOT NULL,
22
+ "policy_activation_version" bigint NOT NULL,
23
+ "source_kind" text NOT NULL,
24
+ "source_id" uuid NOT NULL,
25
+ "source_authority_hash" text NOT NULL,
26
+ "proposal_id" uuid NOT NULL REFERENCES "knowledge_change_proposals"("id") ON DELETE RESTRICT,
27
+ "claim_id" uuid NOT NULL REFERENCES "knowledge_claims"("id") ON DELETE RESTRICT,
28
+ "evidence_id" uuid NOT NULL REFERENCES "knowledge_claim_evidence"("id") ON DELETE RESTRICT,
29
+ "knowledge_previous_review_id" uuid NOT NULL
30
+ REFERENCES "knowledge_claim_reviews"("id") ON DELETE RESTRICT,
31
+ "knowledge_previous_review_revision" bigint NOT NULL,
32
+ "knowledge_approval_review_id" uuid NOT NULL
33
+ REFERENCES "knowledge_claim_reviews"("id") ON DELETE RESTRICT,
34
+ "knowledge_approval_review_revision" bigint NOT NULL,
35
+ "knowledge_approval_input_hash" text NOT NULL,
36
+ "destination" text NOT NULL,
37
+ "destination_proposal_id" uuid NOT NULL,
38
+ "destination_revision_id" uuid NOT NULL,
39
+ "destination_old_revision_id" uuid,
40
+ "destination_old_content_hash" text,
41
+ "destination_old_version" bigint NOT NULL,
42
+ "destination_new_content_hash" text NOT NULL,
43
+ "destination_new_version" bigint NOT NULL,
44
+ "destination_event_id" uuid NOT NULL,
45
+ "effective_at" timestamptz NOT NULL,
46
+ "created_at" timestamptz NOT NULL DEFAULT transaction_timestamp(),
47
+ CONSTRAINT "governed_learning_activation_receipts_operation_uq"
48
+ UNIQUE ("workspace_id", "operation_id"),
49
+ CONSTRAINT "governed_learning_activation_receipts_decision_uq"
50
+ UNIQUE ("decision_receipt_id"),
51
+ CONSTRAINT "governed_learning_activation_receipts_identity_chk" CHECK (
52
+ length(btrim("initiating_human_subject_id")) BETWEEN 1 AND 1024
53
+ AND octet_length(convert_to("initiating_human_subject_id", 'UTF8')) <= 4096
54
+ AND length(btrim("service_actor_subject_id")) BETWEEN 1 AND 1024
55
+ AND "service_actor_subject_id" LIKE 'service:governed-learning-activation:%'
56
+ ),
57
+ CONSTRAINT "governed_learning_activation_receipts_hash_chk" CHECK (
58
+ "input_hash" ~ '^[0-9a-f]{64}$'
59
+ AND "policy_hash" ~ '^[0-9a-f]{64}$'
60
+ AND "source_authority_hash" ~ '^[0-9a-f]{64}$'
61
+ AND "knowledge_approval_input_hash" ~ '^[0-9a-f]{64}$'
62
+ AND "destination_new_content_hash" ~ '^[0-9a-f]{64}$'
63
+ AND ("destination_old_content_hash" IS NULL
64
+ OR "destination_old_content_hash" ~ '^[0-9a-f]{64}$')
65
+ ),
66
+ CONSTRAINT "governed_learning_activation_receipts_boundary_chk" CHECK (
67
+ "policy_activation_version" > 0
68
+ AND "source_kind" IN ('scoped-knowledge-evidence', 'task-note')
69
+ AND "knowledge_previous_review_revision" > 0
70
+ AND "knowledge_approval_review_revision" > "knowledge_previous_review_revision"
71
+ AND "destination" IN ('instruction_policy', 'preference')
72
+ AND "destination_old_version" >= 0
73
+ AND "destination_new_version" > "destination_old_version"
74
+ AND (("destination_old_revision_id" IS NULL AND "destination_old_content_hash" IS NULL)
75
+ OR ("destination_old_revision_id" IS NOT NULL
76
+ AND "destination_old_content_hash" IS NOT NULL))
77
+ )
78
+ );
79
+
80
+ CREATE INDEX "governed_learning_activation_receipts_workspace_time_idx"
81
+ ON "governed_learning_activation_receipts" ("workspace_id", "created_at" DESC, "id" DESC);
82
+
83
+ CREATE TABLE "governed_learning_activation_undo_receipts" (
84
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
85
+ "account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
86
+ "workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
87
+ "operation_id" uuid NOT NULL,
88
+ "input_hash" text NOT NULL,
89
+ "activation_receipt_id" uuid NOT NULL
90
+ REFERENCES "governed_learning_activation_receipts"("id") ON DELETE RESTRICT,
91
+ "initiating_human_subject_id" text NOT NULL,
92
+ "service_actor_subject_id" text NOT NULL,
93
+ "destination" text NOT NULL,
94
+ "knowledge_approval_review_id" uuid NOT NULL
95
+ REFERENCES "knowledge_claim_reviews"("id") ON DELETE RESTRICT,
96
+ "knowledge_revocation_review_id" uuid NOT NULL
97
+ REFERENCES "knowledge_claim_reviews"("id") ON DELETE RESTRICT,
98
+ "knowledge_revocation_review_revision" bigint NOT NULL,
99
+ "knowledge_revocation_input_hash" text NOT NULL,
100
+ "destination_activated_revision_id" uuid NOT NULL,
101
+ "destination_restored_revision_id" uuid,
102
+ "destination_activated_content_hash" text NOT NULL,
103
+ "destination_restored_content_hash" text,
104
+ "destination_old_version" bigint NOT NULL,
105
+ "destination_new_version" bigint NOT NULL,
106
+ "destination_event_id" uuid NOT NULL,
107
+ "effective_at" timestamptz NOT NULL,
108
+ "created_at" timestamptz NOT NULL DEFAULT transaction_timestamp(),
109
+ CONSTRAINT "governed_learning_activation_undo_receipts_operation_uq"
110
+ UNIQUE ("workspace_id", "operation_id"),
111
+ CONSTRAINT "governed_learning_activation_undo_receipts_activation_uq"
112
+ UNIQUE ("activation_receipt_id"),
113
+ CONSTRAINT "governed_learning_activation_undo_receipts_identity_chk" CHECK (
114
+ length(btrim("initiating_human_subject_id")) BETWEEN 1 AND 1024
115
+ AND length(btrim("service_actor_subject_id")) BETWEEN 1 AND 1024
116
+ AND "service_actor_subject_id" LIKE 'service:governed-learning-activation:%'
117
+ ),
118
+ CONSTRAINT "governed_learning_activation_undo_receipts_hash_chk" CHECK (
119
+ "input_hash" ~ '^[0-9a-f]{64}$'
120
+ AND "knowledge_revocation_input_hash" ~ '^[0-9a-f]{64}$'
121
+ AND "destination_activated_content_hash" ~ '^[0-9a-f]{64}$'
122
+ AND ("destination_restored_content_hash" IS NULL
123
+ OR "destination_restored_content_hash" ~ '^[0-9a-f]{64}$')
124
+ ),
125
+ CONSTRAINT "governed_learning_activation_undo_receipts_boundary_chk" CHECK (
126
+ "destination" IN ('instruction_policy', 'preference')
127
+ AND "knowledge_revocation_review_revision" > 0
128
+ AND "destination_old_version" > 0
129
+ AND "destination_new_version" > "destination_old_version"
130
+ AND (("destination_restored_revision_id" IS NULL
131
+ AND "destination_restored_content_hash" IS NULL)
132
+ OR ("destination_restored_revision_id" IS NOT NULL
133
+ AND "destination_restored_content_hash" IS NOT NULL))
134
+ )
135
+ );
136
+
137
+ CREATE INDEX "governed_learning_activation_undo_receipts_workspace_time_idx"
138
+ ON "governed_learning_activation_undo_receipts" ("workspace_id", "created_at" DESC, "id" DESC);
139
+
140
+ ALTER TABLE "governed_learning_activation_receipts" ENABLE ROW LEVEL SECURITY;
141
+ ALTER TABLE "governed_learning_activation_receipts" FORCE ROW LEVEL SECURITY;
142
+ ALTER TABLE "governed_learning_activation_undo_receipts" ENABLE ROW LEVEL SECURITY;
143
+ ALTER TABLE "governed_learning_activation_undo_receipts" FORCE ROW LEVEL SECURITY;
144
+
145
+ CREATE POLICY governed_learning_activation_receipts_tenant
146
+ ON "governed_learning_activation_receipts"
147
+ USING (opengeni_private.workspace_rls_visible("account_id", "workspace_id"))
148
+ WITH CHECK (opengeni_private.workspace_rls_visible("account_id", "workspace_id"));
149
+ CREATE POLICY governed_learning_activation_receipts_subject
150
+ ON "governed_learning_activation_receipts" AS RESTRICTIVE
151
+ USING ("initiating_human_subject_id" = nullif(current_setting('opengeni.subject_id', true), ''))
152
+ WITH CHECK ("initiating_human_subject_id" = nullif(current_setting('opengeni.subject_id', true), ''));
153
+ CREATE POLICY governed_learning_activation_undo_receipts_tenant
154
+ ON "governed_learning_activation_undo_receipts"
155
+ USING (opengeni_private.workspace_rls_visible("account_id", "workspace_id"))
156
+ WITH CHECK (opengeni_private.workspace_rls_visible("account_id", "workspace_id"));
157
+ CREATE POLICY governed_learning_activation_undo_receipts_subject
158
+ ON "governed_learning_activation_undo_receipts" AS RESTRICTIVE
159
+ USING ("initiating_human_subject_id" = nullif(current_setting('opengeni.subject_id', true), ''))
160
+ WITH CHECK ("initiating_human_subject_id" = nullif(current_setting('opengeni.subject_id', true), ''));
161
+
162
+ CREATE OR REPLACE FUNCTION governed_learning_reject_activation_receipt_mutation()
163
+ RETURNS trigger LANGUAGE plpgsql AS $$
164
+ BEGIN
165
+ IF TG_OP = 'DELETE' AND pg_trigger_depth() > 1 AND (
166
+ NOT EXISTS (SELECT 1 FROM managed_accounts WHERE id = OLD.account_id)
167
+ OR NOT EXISTS (SELECT 1 FROM workspaces WHERE id = OLD.workspace_id)
168
+ ) THEN
169
+ RETURN OLD;
170
+ END IF;
171
+ RAISE EXCEPTION 'governed-learning activation receipts are immutable'
172
+ USING ERRCODE = '55000';
173
+ END;
174
+ $$;
175
+
176
+ CREATE TRIGGER governed_learning_activation_receipts_immutable
177
+ BEFORE UPDATE OR DELETE ON "governed_learning_activation_receipts"
178
+ FOR EACH ROW EXECUTE FUNCTION governed_learning_reject_activation_receipt_mutation();
179
+ CREATE TRIGGER governed_learning_activation_undo_receipts_immutable
180
+ BEFORE UPDATE OR DELETE ON "governed_learning_activation_undo_receipts"
181
+ FOR EACH ROW EXECUTE FUNCTION governed_learning_reject_activation_receipt_mutation();
182
+
183
+ CREATE OR REPLACE FUNCTION opengeni_private.governed_learning_derived_uuid(
184
+ p_operation_id uuid,
185
+ p_label text
186
+ ) RETURNS uuid LANGUAGE sql IMMUTABLE STRICT AS $$
187
+ SELECT (
188
+ substr(value, 1, 8) || '-' || substr(value, 9, 4) || '-5' || substr(value, 14, 3)
189
+ || '-' || substr('89ab', 1 + (get_byte(decode(substr(value, 17, 2), 'hex'), 0) & 3), 1)
190
+ || substr(value, 18, 3) || '-' || substr(value, 21, 12)
191
+ )::uuid
192
+ FROM (
193
+ SELECT encode(sha256(convert_to(
194
+ concat('governed-learning-activation:v1:', p_operation_id, ':', p_label), 'UTF8'
195
+ )), 'hex') AS value
196
+ ) digest;
197
+ $$;
198
+ REVOKE ALL ON FUNCTION opengeni_private.governed_learning_derived_uuid(uuid, text) FROM PUBLIC;
199
+
200
+ -- Service approvals/revocations are admitted only through the private native
201
+ -- adapter below. Broadening the row check without this trigger would let any
202
+ -- service-shaped direct insert manufacture accepted Knowledge authority.
203
+ ALTER TABLE "knowledge_claim_reviews"
204
+ DROP CONSTRAINT "knowledge_claim_reviews_review_chk";
205
+ ALTER TABLE "knowledge_claim_reviews"
206
+ ADD CONSTRAINT "knowledge_claim_reviews_review_chk" CHECK (
207
+ "state" IN ('proposed', 'approved', 'rejected', 'revoked')
208
+ AND "review_revision" > 0
209
+ AND length(btrim("reason")) BETWEEN 1 AND 4096
210
+ AND (
211
+ "state" = 'proposed'
212
+ OR ("actor_kind" = 'human' AND "actor_subject_id" = "initiating_human_subject_id")
213
+ OR (
214
+ "actor_kind" = 'service'
215
+ AND "actor_subject_id" LIKE 'service:governed-learning-activation:%'
216
+ AND "initiating_human_subject_id" IS NOT NULL
217
+ )
218
+ )
219
+ );
220
+
221
+ CREATE OR REPLACE FUNCTION governed_learning_guard_service_review()
222
+ RETURNS trigger LANGUAGE plpgsql AS $$
223
+ DECLARE capability_writer boolean;
224
+ BEGIN
225
+ SELECT current_user = pg_catalog.pg_get_userbyid(procedure_row.proowner)
226
+ INTO capability_writer
227
+ FROM pg_catalog.pg_proc procedure_row
228
+ WHERE procedure_row.oid = pg_catalog.to_regprocedure(pg_catalog.format(
229
+ '%I.governed_learning_apply_knowledge_review(uuid,uuid,uuid,uuid,uuid,bigint,text,text,text)',
230
+ TG_TABLE_SCHEMA
231
+ ));
232
+ IF NEW.state <> 'proposed' AND NEW.actor_kind = 'service' AND (
233
+ NOT coalesce(capability_writer, false)
234
+ OR current_setting('opengeni.governed_learning_review_operation', true)
235
+ IS DISTINCT FROM NEW.operation_id
236
+ OR current_setting('opengeni.governed_learning_review_claim', true)
237
+ IS DISTINCT FROM NEW.claim_id::text
238
+ ) THEN
239
+ RAISE EXCEPTION 'service Knowledge review requires exact governed-learning capability'
240
+ USING ERRCODE = '42501';
241
+ END IF;
242
+ RETURN NEW;
243
+ END;
244
+ $$;
245
+ CREATE TRIGGER knowledge_claim_reviews_05_governed_learning_guard
246
+ BEFORE INSERT ON "knowledge_claim_reviews"
247
+ FOR EACH ROW EXECUTE FUNCTION governed_learning_guard_service_review();
248
+
249
+ CREATE OR REPLACE FUNCTION governed_learning_apply_knowledge_review(
250
+ p_account_id uuid,
251
+ p_workspace_id uuid,
252
+ p_operation_id uuid,
253
+ p_claim_id uuid,
254
+ p_expected_review_id uuid,
255
+ p_expected_review_revision bigint,
256
+ p_state text,
257
+ p_actor_subject_id text,
258
+ p_initiating_human_subject_id text
259
+ ) RETURNS SETOF knowledge_claim_reviews
260
+ LANGUAGE plpgsql SECURITY DEFINER AS $$
261
+ DECLARE
262
+ claim_row knowledge_claims%ROWTYPE;
263
+ current_review knowledge_claim_reviews%ROWTYPE;
264
+ result_row knowledge_claim_reviews%ROWTYPE;
265
+ input_hash_value text;
266
+ BEGIN
267
+ IF p_state NOT IN ('approved', 'revoked')
268
+ OR p_actor_subject_id NOT LIKE 'service:governed-learning-activation:%'
269
+ OR length(btrim(p_initiating_human_subject_id)) NOT BETWEEN 1 AND 1024
270
+ THEN
271
+ RAISE EXCEPTION 'governed-learning Knowledge review input is invalid'
272
+ USING ERRCODE = '22023';
273
+ END IF;
274
+ SELECT * INTO claim_row FROM knowledge_claims claim
275
+ WHERE claim.account_id = p_account_id AND claim.id = p_claim_id
276
+ AND claim.scope_kind = 'workspace'
277
+ AND claim.scope_workspace_id = p_workspace_id
278
+ AND claim.scope_subject_id IS NULL
279
+ AND claim.initiating_human_subject_id = p_initiating_human_subject_id
280
+ FOR UPDATE;
281
+ IF NOT FOUND THEN
282
+ RAISE EXCEPTION 'governed-learning Knowledge claim is unavailable' USING ERRCODE = '42501';
283
+ END IF;
284
+ SELECT * INTO current_review FROM knowledge_claim_reviews review
285
+ WHERE review.account_id = p_account_id AND review.claim_id = p_claim_id
286
+ ORDER BY review.review_revision DESC LIMIT 1 FOR SHARE;
287
+ IF current_review.id IS DISTINCT FROM p_expected_review_id
288
+ OR current_review.review_revision IS DISTINCT FROM p_expected_review_revision
289
+ OR (p_state = 'approved' AND current_review.state <> 'proposed')
290
+ OR (p_state = 'revoked' AND current_review.state <> 'approved')
291
+ THEN
292
+ RAISE EXCEPTION 'governed-learning Knowledge review was superseded'
293
+ USING ERRCODE = '40001';
294
+ END IF;
295
+ input_hash_value := encode(sha256(convert_to(jsonb_build_array(
296
+ 'governed-learning-native-review', 1, p_account_id, p_workspace_id,
297
+ p_operation_id, p_claim_id, p_expected_review_id,
298
+ p_expected_review_revision, p_state, p_actor_subject_id,
299
+ p_initiating_human_subject_id
300
+ )::text, 'UTF8')), 'hex');
301
+ PERFORM set_config('opengeni.governed_learning_review_operation', p_operation_id::text, true);
302
+ PERFORM set_config('opengeni.governed_learning_review_claim', p_claim_id::text, true);
303
+ INSERT INTO knowledge_claim_reviews (
304
+ account_id, scope_kind, scope_workspace_id, scope_subject_id, scope_key,
305
+ claim_id, state, reason, operation_id, input_hash, actor_kind,
306
+ actor_subject_id, initiating_human_subject_id
307
+ ) VALUES (
308
+ p_account_id, claim_row.scope_kind, claim_row.scope_workspace_id,
309
+ claim_row.scope_subject_id, claim_row.scope_key, p_claim_id, p_state,
310
+ CASE p_state WHEN 'approved' THEN 'Automatic governed-learning activation.'
311
+ ELSE 'Exact governed-learning activation undo.' END,
312
+ p_operation_id::text, input_hash_value, 'service', p_actor_subject_id,
313
+ p_initiating_human_subject_id
314
+ ) RETURNING * INTO result_row;
315
+ PERFORM set_config('opengeni.governed_learning_review_operation', '', true);
316
+ PERFORM set_config('opengeni.governed_learning_review_claim', '', true);
317
+ RETURN NEXT result_row;
318
+ END;
319
+ $$;
320
+ REVOKE ALL ON FUNCTION governed_learning_apply_knowledge_review(
321
+ uuid, uuid, uuid, uuid, uuid, bigint, text, text, text
322
+ ) FROM PUBLIC;
323
+
324
+ -- Deactivation history is additive so rolling old API binaries continue to
325
+ -- read the legacy activation-event shape with a required new revision.
326
+ CREATE TABLE "workspace_instruction_policy_deactivation_events" (
327
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
328
+ "operation_id" uuid NOT NULL,
329
+ "request_fingerprint" text NOT NULL,
330
+ "account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
331
+ "workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
332
+ "kind" text NOT NULL,
333
+ "scope" text NOT NULL,
334
+ "role_key" text,
335
+ "type" text NOT NULL DEFAULT 'automatic_deactivate',
336
+ "activation_version" bigint NOT NULL,
337
+ "old_revision_id" uuid NOT NULL
338
+ REFERENCES "workspace_instruction_policy_revisions"("id") ON DELETE RESTRICT,
339
+ "old_revision" bigint NOT NULL,
340
+ "old_content_hash" text NOT NULL,
341
+ "actor_subject_id" text NOT NULL,
342
+ "reason" text NOT NULL,
343
+ "created_at" timestamptz NOT NULL DEFAULT transaction_timestamp(),
344
+ CONSTRAINT "workspace_instruction_policy_deactivations_receipt_chk" CHECK (
345
+ "request_fingerprint" ~ '^[0-9a-f]{64}$'
346
+ ),
347
+ CONSTRAINT "workspace_instruction_policy_deactivations_target_chk" CHECK (
348
+ ("kind" = 'charter' AND "scope" = 'global' AND "role_key" IS NULL)
349
+ OR ("kind" = 'policy' AND "scope" = 'global' AND "role_key" IS NULL)
350
+ OR ("kind" = 'policy' AND "scope" = 'role' AND "role_key" IS NOT NULL)
351
+ ),
352
+ CONSTRAINT "workspace_instruction_policy_deactivations_boundary_chk" CHECK (
353
+ "type" = 'automatic_deactivate'
354
+ AND "activation_version" > 0
355
+ AND "old_revision" > 0
356
+ AND "old_content_hash" ~ '^[0-9a-f]{64}$'
357
+ AND "actor_subject_id" LIKE 'service:governed-learning-activation:%'
358
+ AND length(btrim("actor_subject_id")) BETWEEN 1 AND 1024
359
+ AND length(btrim("reason")) BETWEEN 1 AND 4096
360
+ ),
361
+ CONSTRAINT "workspace_instruction_policy_deactivate_workspace_operation_uq"
362
+ UNIQUE ("workspace_id", "operation_id")
363
+ );
364
+ CREATE UNIQUE INDEX "workspace_instruction_policy_deactivations_target_version_uq"
365
+ ON "workspace_instruction_policy_deactivation_events"
366
+ ("workspace_id", "kind", "scope", coalesce("role_key", ''), "activation_version");
367
+ CREATE INDEX "workspace_instruction_policy_deactivations_workspace_time_idx"
368
+ ON "workspace_instruction_policy_deactivation_events"
369
+ ("workspace_id", "created_at" DESC, "id" DESC);
370
+
371
+ ALTER TABLE "workspace_instruction_policy_deactivation_events" ENABLE ROW LEVEL SECURITY;
372
+ ALTER TABLE "workspace_instruction_policy_deactivation_events" FORCE ROW LEVEL SECURITY;
373
+ CREATE POLICY workspace_isolation ON "workspace_instruction_policy_deactivation_events"
374
+ USING (opengeni_private.workspace_rls_visible("account_id", "workspace_id"))
375
+ WITH CHECK (opengeni_private.workspace_rls_visible("account_id", "workspace_id"));
376
+ CREATE TRIGGER workspace_instruction_policy_deactivations_immutable
377
+ BEFORE UPDATE OR DELETE ON "workspace_instruction_policy_deactivation_events"
378
+ FOR EACH ROW EXECUTE FUNCTION workspace_instruction_policy_reject_mutation();
379
+
380
+ CREATE OR REPLACE FUNCTION workspace_instruction_policy_validate_deactivation_event()
381
+ RETURNS trigger LANGUAGE plpgsql AS $$
382
+ BEGIN
383
+ IF NOT EXISTS (
384
+ SELECT 1 FROM workspace_instruction_policy_revisions revision
385
+ WHERE revision.id = NEW.old_revision_id
386
+ AND revision.account_id = NEW.account_id
387
+ AND revision.workspace_id = NEW.workspace_id
388
+ AND revision.kind = NEW.kind
389
+ AND revision.scope = NEW.scope
390
+ AND revision.role_key IS NOT DISTINCT FROM NEW.role_key
391
+ AND revision.revision = NEW.old_revision
392
+ AND revision.content_hash = NEW.old_content_hash
393
+ ) THEN
394
+ RAISE EXCEPTION 'instruction-policy deactivation event has an invalid old revision'
395
+ USING ERRCODE = '23514';
396
+ END IF;
397
+ RETURN NEW;
398
+ END;
399
+ $$;
400
+ CREATE TRIGGER workspace_instruction_policy_deactivations_validate_revision
401
+ BEFORE INSERT ON "workspace_instruction_policy_deactivation_events"
402
+ FOR EACH ROW EXECUTE FUNCTION workspace_instruction_policy_validate_deactivation_event();
403
+
404
+ CREATE OR REPLACE FUNCTION workspace_instruction_policy_guard_lifecycle_operation()
405
+ RETURNS trigger LANGUAGE plpgsql AS $$
406
+ BEGIN
407
+ IF TG_TABLE_NAME = 'workspace_instruction_policy_deactivation_events' THEN
408
+ IF EXISTS (
409
+ SELECT 1 FROM workspace_instruction_policy_activation_events event
410
+ WHERE event.workspace_id = NEW.workspace_id
411
+ AND event.operation_id = NEW.operation_id
412
+ ) THEN
413
+ RAISE EXCEPTION 'instruction-policy lifecycle operation was already used'
414
+ USING ERRCODE = '23505';
415
+ END IF;
416
+ ELSIF NEW.operation_id IS NOT NULL AND EXISTS (
417
+ SELECT 1 FROM workspace_instruction_policy_deactivation_events event
418
+ WHERE event.workspace_id = NEW.workspace_id
419
+ AND event.operation_id = NEW.operation_id
420
+ ) THEN
421
+ RAISE EXCEPTION 'instruction-policy lifecycle operation was already used'
422
+ USING ERRCODE = '23505';
423
+ END IF;
424
+ RETURN NEW;
425
+ END;
426
+ $$;
427
+ CREATE TRIGGER workspace_instruction_policy_activations_operation_guard
428
+ BEFORE INSERT ON "workspace_instruction_policy_activation_events"
429
+ FOR EACH ROW EXECUTE FUNCTION workspace_instruction_policy_guard_lifecycle_operation();
430
+ CREATE TRIGGER workspace_instruction_policy_deactivations_operation_guard
431
+ BEFORE INSERT ON "workspace_instruction_policy_deactivation_events"
432
+ FOR EACH ROW EXECUTE FUNCTION workspace_instruction_policy_guard_lifecycle_operation();
433
+
434
+ CREATE OR REPLACE FUNCTION workspace_instruction_policy_guard_head_delete()
435
+ RETURNS trigger LANGUAGE plpgsql AS $$
436
+ DECLARE capability_writer boolean;
437
+ BEGIN
438
+ IF pg_trigger_depth() > 1 AND NOT EXISTS (
439
+ SELECT 1 FROM workspaces workspace WHERE workspace.id = OLD.workspace_id
440
+ ) THEN
441
+ RETURN OLD;
442
+ END IF;
443
+ SELECT current_user = pg_catalog.pg_get_userbyid(procedure_row.proowner)
444
+ INTO capability_writer
445
+ FROM pg_catalog.pg_proc procedure_row
446
+ WHERE procedure_row.oid = pg_catalog.to_regprocedure(pg_catalog.format(
447
+ '%I.governed_learning_deactivate_instruction_policy(uuid,uuid,uuid,uuid,uuid,bigint,text)',
448
+ TG_TABLE_SCHEMA
449
+ ));
450
+ IF NOT coalesce(capability_writer, false)
451
+ OR current_setting('opengeni.governed_learning_deactivation_operation', true) IS NULL
452
+ OR current_setting('opengeni.governed_learning_deactivation_operation', true) = ''
453
+ THEN
454
+ RAISE EXCEPTION 'instruction-policy head deletion requires governed-learning capability'
455
+ USING ERRCODE = '42501';
456
+ END IF;
457
+ RETURN OLD;
458
+ END;
459
+ $$;
460
+ CREATE TRIGGER workspace_instruction_policy_heads_deactivation_guard
461
+ BEFORE DELETE ON "workspace_instruction_policy_heads"
462
+ FOR EACH ROW EXECUTE FUNCTION workspace_instruction_policy_guard_head_delete();
463
+
464
+ ALTER TABLE "workspace_instruction_policy_onboarding_proposals"
465
+ DROP CONSTRAINT "workspace_instruction_policy_onboarding_proposals_baseline_chk";
466
+ ALTER TABLE "workspace_instruction_policy_onboarding_proposals"
467
+ ADD CONSTRAINT "workspace_instruction_policy_onboarding_proposals_baseline_chk" CHECK (
468
+ (
469
+ "baseline_revision_id" IS NULL
470
+ AND "baseline_revision" IS NULL
471
+ AND "baseline_content_hash" IS NULL
472
+ AND "baseline_activation_version" >= 0
473
+ AND "baseline_activated_at" IS NULL
474
+ ) OR (
475
+ "baseline_revision_id" IS NOT NULL
476
+ AND "baseline_revision" > 0
477
+ AND "baseline_content_hash" ~ '^[0-9a-f]{64}$'
478
+ AND "baseline_activation_version" > 0
479
+ AND "baseline_activated_at" IS NOT NULL
480
+ )
481
+ );
482
+
483
+ CREATE OR REPLACE FUNCTION workspace_instruction_policy_validate_onboarding_proposal()
484
+ RETURNS trigger LANGUAGE plpgsql AS $$
485
+ BEGIN
486
+ IF NEW.baseline_revision_id IS NULL THEN
487
+ IF EXISTS (
488
+ SELECT 1 FROM workspace_instruction_policy_heads head
489
+ WHERE head.account_id = NEW.account_id
490
+ AND head.workspace_id = NEW.workspace_id
491
+ AND head.kind = NEW.kind
492
+ AND head.scope = NEW.scope
493
+ AND head.role_key IS NOT DISTINCT FROM NEW.role_key
494
+ ) THEN
495
+ RAISE EXCEPTION 'instruction-policy proposal must capture the exact active head baseline'
496
+ USING ERRCODE = '23514';
497
+ END IF;
498
+ IF NOT (
499
+ (
500
+ NEW.baseline_activation_version = 0
501
+ AND NOT EXISTS (
502
+ SELECT 1 FROM workspace_instruction_policy_heads head
503
+ WHERE head.account_id = NEW.account_id
504
+ AND head.workspace_id = NEW.workspace_id
505
+ AND head.kind = NEW.kind
506
+ AND head.scope = NEW.scope
507
+ AND head.role_key IS NOT DISTINCT FROM NEW.role_key
508
+ )
509
+ AND NOT EXISTS (
510
+ SELECT 1 FROM workspace_instruction_policy_deactivation_events event
511
+ WHERE event.account_id = NEW.account_id
512
+ AND event.workspace_id = NEW.workspace_id
513
+ AND event.kind = NEW.kind
514
+ AND event.scope = NEW.scope
515
+ AND event.role_key IS NOT DISTINCT FROM NEW.role_key
516
+ )
517
+ ) OR (
518
+ NEW.baseline_activation_version > 0
519
+ AND NOT EXISTS (
520
+ SELECT 1 FROM workspace_instruction_policy_heads head
521
+ WHERE head.account_id = NEW.account_id
522
+ AND head.workspace_id = NEW.workspace_id
523
+ AND head.kind = NEW.kind
524
+ AND head.scope = NEW.scope
525
+ AND head.role_key IS NOT DISTINCT FROM NEW.role_key
526
+ )
527
+ AND EXISTS (
528
+ SELECT 1 FROM workspace_instruction_policy_deactivation_events event
529
+ WHERE event.account_id = NEW.account_id
530
+ AND event.workspace_id = NEW.workspace_id
531
+ AND event.kind = NEW.kind
532
+ AND event.scope = NEW.scope
533
+ AND event.role_key IS NOT DISTINCT FROM NEW.role_key
534
+ AND event.activation_version = NEW.baseline_activation_version
535
+ AND NOT EXISTS (
536
+ SELECT 1 FROM workspace_instruction_policy_deactivation_events newer
537
+ WHERE newer.account_id = event.account_id
538
+ AND newer.workspace_id = event.workspace_id
539
+ AND newer.kind = event.kind
540
+ AND newer.scope = event.scope
541
+ AND newer.role_key IS NOT DISTINCT FROM event.role_key
542
+ AND newer.activation_version > event.activation_version
543
+ )
544
+ )
545
+ )
546
+ ) THEN
547
+ RAISE EXCEPTION 'instruction-policy proposal must capture the exact inactive boundary'
548
+ USING ERRCODE = '23514';
549
+ END IF;
550
+ ELSIF NOT EXISTS (
551
+ SELECT 1 FROM workspace_instruction_policy_heads head
552
+ WHERE head.account_id = NEW.account_id
553
+ AND head.workspace_id = NEW.workspace_id
554
+ AND head.kind = NEW.kind
555
+ AND head.scope = NEW.scope
556
+ AND head.role_key IS NOT DISTINCT FROM NEW.role_key
557
+ AND head.revision_id = NEW.baseline_revision_id
558
+ AND head.revision = NEW.baseline_revision
559
+ AND head.content_hash = NEW.baseline_content_hash
560
+ AND head.activation_version = NEW.baseline_activation_version
561
+ AND head.activated_at = NEW.baseline_activated_at
562
+ ) THEN
563
+ RAISE EXCEPTION 'instruction-policy proposal must capture the exact active head baseline'
564
+ USING ERRCODE = '23514';
565
+ END IF;
566
+
567
+ IF NEW.baseline_revision_id IS NOT NULL AND NOT EXISTS (
568
+ SELECT 1 FROM workspace_instruction_policy_revisions revision
569
+ WHERE revision.id = NEW.baseline_revision_id
570
+ AND revision.account_id = NEW.account_id
571
+ AND revision.workspace_id = NEW.workspace_id
572
+ AND revision.kind = NEW.kind
573
+ AND revision.scope = NEW.scope
574
+ AND revision.role_key IS NOT DISTINCT FROM NEW.role_key
575
+ AND revision.revision = NEW.baseline_revision
576
+ AND revision.content_hash = NEW.baseline_content_hash
577
+ ) THEN
578
+ RAISE EXCEPTION 'instruction-policy proposal has an invalid baseline revision'
579
+ USING ERRCODE = '23514';
580
+ END IF;
581
+
582
+ IF NOT EXISTS (
583
+ SELECT 1 FROM workspace_instruction_policy_revisions revision
584
+ WHERE revision.id = NEW.draft_revision_id
585
+ AND revision.operation_id = NEW.operation_id
586
+ AND revision.request_fingerprint = NEW.request_fingerprint
587
+ AND revision.account_id = NEW.account_id
588
+ AND revision.workspace_id = NEW.workspace_id
589
+ AND revision.kind = NEW.kind
590
+ AND revision.scope = NEW.scope
591
+ AND revision.role_key IS NOT DISTINCT FROM NEW.role_key
592
+ AND revision.revision = NEW.draft_revision
593
+ AND revision.content_hash = NEW.draft_content_hash
594
+ AND revision.supersedes_revision_id IS NOT DISTINCT FROM NEW.baseline_revision_id
595
+ AND revision.created_by_subject_id = NEW.created_by_subject_id
596
+ AND (
597
+ (
598
+ revision.provenance_source = 'onboarding'
599
+ AND revision.provenance_source_id = NEW.id::text
600
+ ) OR (
601
+ revision.provenance_source = 'knowledge_proposal'
602
+ AND revision.provenance_source_id = NEW.source_id
603
+ AND NEW.source_version = NEW.draft_content_hash
604
+ AND EXISTS (
605
+ SELECT 1 FROM knowledge_change_proposals proposal
606
+ WHERE proposal.id::text = NEW.source_id
607
+ AND proposal.account_id = NEW.account_id
608
+ AND proposal.scope_kind = 'workspace'
609
+ AND proposal.scope_workspace_id = NEW.workspace_id
610
+ AND proposal.scope_subject_id IS NULL
611
+ AND proposal.target_kind = 'instruction_policy'
612
+ AND proposal.target_scope = NEW.scope
613
+ AND proposal.target_key IS NOT DISTINCT FROM CASE
614
+ WHEN NEW.scope = 'role' THEN NEW.role_key
615
+ ELSE NULL
616
+ END
617
+ AND proposal.content_hash = NEW.source_version
618
+ AND proposal.actor_kind = 'service'
619
+ AND proposal.actor_subject_id = NEW.created_by_subject_id
620
+ AND proposal.initiating_human_subject_id
621
+ = current_setting('opengeni.subject_id', true)
622
+ AND proposal.claim_id IS NOT NULL
623
+ AND proposal.evidence_id IS NOT NULL
624
+ AND proposal.status = 'proposed'
625
+ )
626
+ )
627
+ )
628
+ ) THEN
629
+ RAISE EXCEPTION 'instruction-policy proposal must identify its exact inactive draft'
630
+ USING ERRCODE = '23514';
631
+ END IF;
632
+
633
+ IF EXISTS (
634
+ SELECT 1 FROM workspace_instruction_policy_heads head
635
+ WHERE head.account_id = NEW.account_id
636
+ AND head.workspace_id = NEW.workspace_id
637
+ AND head.revision_id = NEW.draft_revision_id
638
+ ) OR EXISTS (
639
+ SELECT 1 FROM workspace_instruction_policy_activation_events event
640
+ WHERE event.account_id = NEW.account_id
641
+ AND event.workspace_id = NEW.workspace_id
642
+ AND event.new_revision_id = NEW.draft_revision_id
643
+ ) THEN
644
+ RAISE EXCEPTION 'instruction-policy proposal must identify a never-activated inactive draft'
645
+ USING ERRCODE = '23514';
646
+ END IF;
647
+ RETURN NEW;
648
+ END;
649
+ $$;
650
+
651
+ CREATE OR REPLACE FUNCTION governed_learning_apply_instruction_policy(
652
+ p_action text,
653
+ p_account_id uuid,
654
+ p_workspace_id uuid,
655
+ p_operation_id uuid,
656
+ p_proposal_id uuid,
657
+ p_expected_current_revision_id uuid,
658
+ p_expected_activation_version bigint,
659
+ p_target_revision_id uuid,
660
+ p_actor_subject_id text
661
+ ) RETURNS SETOF workspace_instruction_policy_activation_events
662
+ LANGUAGE plpgsql SECURITY DEFINER AS $$
663
+ DECLARE
664
+ proposal_row workspace_instruction_policy_onboarding_proposals%ROWTYPE;
665
+ current_head workspace_instruction_policy_heads%ROWTYPE;
666
+ active_revision workspace_instruction_policy_revisions%ROWTYPE;
667
+ target_revision workspace_instruction_policy_revisions%ROWTYPE;
668
+ event_row workspace_instruction_policy_activation_events%ROWTYPE;
669
+ inactive_activation_version bigint;
670
+ current_activation_version bigint;
671
+ next_version bigint;
672
+ fingerprint text;
673
+ event_time timestamptz;
674
+ BEGIN
675
+ IF p_action NOT IN ('activate', 'undo')
676
+ OR p_actor_subject_id NOT LIKE 'service:governed-learning-activation:%'
677
+ OR p_expected_activation_version < 0
678
+ OR (p_action = 'undo' AND p_target_revision_id IS NULL)
679
+ THEN
680
+ RAISE EXCEPTION 'governed-learning instruction-policy input is invalid'
681
+ USING ERRCODE = '22023';
682
+ END IF;
683
+ PERFORM 1 FROM workspaces workspace
684
+ WHERE workspace.account_id = p_account_id AND workspace.id = p_workspace_id
685
+ FOR UPDATE;
686
+ IF NOT FOUND THEN
687
+ RAISE EXCEPTION 'governed-learning workspace is unavailable' USING ERRCODE = '42501';
688
+ END IF;
689
+ SELECT * INTO proposal_row FROM workspace_instruction_policy_onboarding_proposals proposal
690
+ WHERE proposal.account_id = p_account_id
691
+ AND proposal.workspace_id = p_workspace_id
692
+ AND proposal.id = p_proposal_id
693
+ AND proposal.status = 'proposed'
694
+ AND proposal.source_id ~ '^[0-9a-f-]{36}$'
695
+ FOR SHARE;
696
+ IF NOT FOUND THEN
697
+ RAISE EXCEPTION 'governed-learning instruction-policy proposal is unavailable'
698
+ USING ERRCODE = '42501';
699
+ END IF;
700
+ SELECT * INTO active_revision FROM workspace_instruction_policy_revisions revision
701
+ WHERE revision.account_id = p_account_id
702
+ AND revision.workspace_id = p_workspace_id
703
+ AND revision.id = proposal_row.draft_revision_id
704
+ AND revision.content_hash = proposal_row.draft_content_hash
705
+ AND revision.provenance_source = 'knowledge_proposal'
706
+ AND revision.provenance_source_id = proposal_row.source_id
707
+ FOR SHARE;
708
+ IF NOT FOUND THEN
709
+ RAISE EXCEPTION 'governed-learning instruction-policy draft is unavailable'
710
+ USING ERRCODE = '42501';
711
+ END IF;
712
+ SELECT * INTO current_head FROM workspace_instruction_policy_heads head
713
+ WHERE head.account_id = p_account_id AND head.workspace_id = p_workspace_id
714
+ AND head.kind = proposal_row.kind AND head.scope = proposal_row.scope
715
+ AND head.role_key IS NOT DISTINCT FROM proposal_row.role_key
716
+ FOR UPDATE;
717
+ IF NOT FOUND THEN
718
+ SELECT event.activation_version INTO inactive_activation_version
719
+ FROM workspace_instruction_policy_deactivation_events event
720
+ WHERE event.account_id = p_account_id AND event.workspace_id = p_workspace_id
721
+ AND event.kind = proposal_row.kind AND event.scope = proposal_row.scope
722
+ AND event.role_key IS NOT DISTINCT FROM proposal_row.role_key
723
+ ORDER BY event.activation_version DESC, event.id DESC
724
+ LIMIT 1;
725
+ END IF;
726
+ current_activation_version := coalesce(current_head.activation_version, inactive_activation_version, 0);
727
+ IF current_head.revision_id IS DISTINCT FROM p_expected_current_revision_id
728
+ OR current_activation_version IS DISTINCT FROM p_expected_activation_version
729
+ THEN
730
+ RAISE EXCEPTION 'governed-learning instruction-policy head changed'
731
+ USING ERRCODE = '40001';
732
+ END IF;
733
+ IF p_action = 'activate' THEN
734
+ IF p_target_revision_id IS DISTINCT FROM proposal_row.draft_revision_id
735
+ OR proposal_row.baseline_revision_id IS DISTINCT FROM p_expected_current_revision_id
736
+ OR proposal_row.baseline_activation_version IS DISTINCT FROM p_expected_activation_version
737
+ THEN
738
+ RAISE EXCEPTION 'governed-learning instruction-policy proposal baseline changed'
739
+ USING ERRCODE = '40001';
740
+ END IF;
741
+ target_revision := active_revision;
742
+ ELSE
743
+ IF current_head.revision_id IS DISTINCT FROM proposal_row.draft_revision_id THEN
744
+ RAISE EXCEPTION 'governed-learning instruction-policy activation was superseded'
745
+ USING ERRCODE = '40001';
746
+ END IF;
747
+ IF p_target_revision_id IS NOT NULL THEN
748
+ SELECT * INTO target_revision FROM workspace_instruction_policy_revisions revision
749
+ WHERE revision.account_id = p_account_id AND revision.workspace_id = p_workspace_id
750
+ AND revision.id = p_target_revision_id
751
+ AND revision.kind = proposal_row.kind AND revision.scope = proposal_row.scope
752
+ AND revision.role_key IS NOT DISTINCT FROM proposal_row.role_key
753
+ FOR SHARE;
754
+ IF NOT FOUND THEN
755
+ RAISE EXCEPTION 'governed-learning rollback target is unavailable'
756
+ USING ERRCODE = '42501';
757
+ END IF;
758
+ END IF;
759
+ END IF;
760
+ fingerprint := encode(sha256(convert_to(jsonb_build_array(
761
+ 'governed-learning-instruction-policy', 1, p_action, p_account_id,
762
+ p_workspace_id, p_operation_id, p_proposal_id, p_expected_current_revision_id,
763
+ p_expected_activation_version, p_target_revision_id, p_actor_subject_id
764
+ )::text, 'UTF8')), 'hex');
765
+ event_time := clock_timestamp();
766
+ next_version := current_activation_version + 1;
767
+ IF p_action = 'activate' THEN
768
+ IF current_head.id IS NULL THEN
769
+ INSERT INTO workspace_instruction_policy_heads (
770
+ account_id, workspace_id, kind, scope, role_key, revision_id, revision,
771
+ content_hash, activation_version, activated_at
772
+ ) VALUES (
773
+ p_account_id, p_workspace_id, proposal_row.kind, proposal_row.scope,
774
+ proposal_row.role_key, target_revision.id, target_revision.revision,
775
+ target_revision.content_hash, next_version, event_time
776
+ );
777
+ ELSE
778
+ UPDATE workspace_instruction_policy_heads SET
779
+ revision_id = target_revision.id, revision = target_revision.revision,
780
+ content_hash = target_revision.content_hash,
781
+ activation_version = next_version, activated_at = event_time
782
+ WHERE id = current_head.id;
783
+ END IF;
784
+ ELSE
785
+ UPDATE workspace_instruction_policy_heads SET
786
+ revision_id = target_revision.id, revision = target_revision.revision,
787
+ content_hash = target_revision.content_hash,
788
+ activation_version = next_version, activated_at = event_time
789
+ WHERE id = current_head.id;
790
+ END IF;
791
+ INSERT INTO workspace_instruction_policy_activation_events (
792
+ operation_id, request_fingerprint, account_id, workspace_id, kind, scope,
793
+ role_key, type, activation_version, old_revision_id, old_revision,
794
+ old_content_hash, new_revision_id, new_revision, new_content_hash,
795
+ actor_subject_id, reason, created_at
796
+ ) VALUES (
797
+ p_operation_id, fingerprint, p_account_id, p_workspace_id, proposal_row.kind,
798
+ proposal_row.scope, proposal_row.role_key,
799
+ CASE WHEN p_action = 'activate' THEN 'activate' ELSE 'rollback' END,
800
+ next_version, current_head.revision_id, current_head.revision,
801
+ current_head.content_hash, target_revision.id, target_revision.revision,
802
+ target_revision.content_hash, p_actor_subject_id,
803
+ CASE WHEN p_action = 'activate' THEN 'Automatic governed-learning activation.'
804
+ ELSE 'Exact governed-learning activation undo.' END,
805
+ event_time
806
+ ) RETURNING * INTO event_row;
807
+ RETURN NEXT event_row;
808
+ END;
809
+ $$;
810
+ REVOKE ALL ON FUNCTION governed_learning_apply_instruction_policy(
811
+ text, uuid, uuid, uuid, uuid, uuid, bigint, uuid, text
812
+ ) FROM PUBLIC;
813
+
814
+ CREATE OR REPLACE FUNCTION governed_learning_deactivate_instruction_policy(
815
+ p_account_id uuid,
816
+ p_workspace_id uuid,
817
+ p_operation_id uuid,
818
+ p_proposal_id uuid,
819
+ p_expected_current_revision_id uuid,
820
+ p_expected_activation_version bigint,
821
+ p_actor_subject_id text
822
+ ) RETURNS SETOF workspace_instruction_policy_deactivation_events
823
+ LANGUAGE plpgsql SECURITY DEFINER AS $$
824
+ DECLARE
825
+ proposal_row workspace_instruction_policy_onboarding_proposals%ROWTYPE;
826
+ current_head workspace_instruction_policy_heads%ROWTYPE;
827
+ result_row workspace_instruction_policy_deactivation_events%ROWTYPE;
828
+ fingerprint text;
829
+ event_time timestamptz;
830
+ next_version bigint;
831
+ BEGIN
832
+ IF p_expected_current_revision_id IS NULL
833
+ OR p_expected_activation_version <= 0
834
+ OR p_actor_subject_id NOT LIKE 'service:governed-learning-activation:%'
835
+ THEN
836
+ RAISE EXCEPTION 'governed-learning instruction-policy deactivation input is invalid'
837
+ USING ERRCODE = '22023';
838
+ END IF;
839
+ PERFORM 1 FROM workspaces workspace
840
+ WHERE workspace.account_id = p_account_id AND workspace.id = p_workspace_id
841
+ FOR UPDATE;
842
+ IF NOT FOUND THEN
843
+ RAISE EXCEPTION 'governed-learning workspace is unavailable' USING ERRCODE = '42501';
844
+ END IF;
845
+ SELECT * INTO proposal_row FROM workspace_instruction_policy_onboarding_proposals proposal
846
+ WHERE proposal.account_id = p_account_id AND proposal.workspace_id = p_workspace_id
847
+ AND proposal.id = p_proposal_id AND proposal.status = 'proposed'
848
+ FOR SHARE;
849
+ IF NOT FOUND OR proposal_row.draft_revision_id IS DISTINCT FROM p_expected_current_revision_id
850
+ THEN
851
+ RAISE EXCEPTION 'governed-learning instruction-policy proposal is unavailable'
852
+ USING ERRCODE = '42501';
853
+ END IF;
854
+ SELECT * INTO current_head FROM workspace_instruction_policy_heads head
855
+ WHERE head.account_id = p_account_id AND head.workspace_id = p_workspace_id
856
+ AND head.kind = proposal_row.kind AND head.scope = proposal_row.scope
857
+ AND head.role_key IS NOT DISTINCT FROM proposal_row.role_key
858
+ FOR UPDATE;
859
+ IF current_head.revision_id IS DISTINCT FROM p_expected_current_revision_id
860
+ OR current_head.activation_version IS DISTINCT FROM p_expected_activation_version
861
+ THEN
862
+ RAISE EXCEPTION 'governed-learning instruction-policy activation was superseded'
863
+ USING ERRCODE = '40001';
864
+ END IF;
865
+ fingerprint := encode(sha256(convert_to(jsonb_build_array(
866
+ 'governed-learning-instruction-policy-deactivation', 1, p_account_id,
867
+ p_workspace_id, p_operation_id, p_proposal_id,
868
+ p_expected_current_revision_id, p_expected_activation_version,
869
+ p_actor_subject_id
870
+ )::text, 'UTF8')), 'hex');
871
+ event_time := clock_timestamp();
872
+ next_version := p_expected_activation_version + 1;
873
+ PERFORM set_config(
874
+ 'opengeni.governed_learning_deactivation_operation', p_operation_id::text, true
875
+ );
876
+ DELETE FROM workspace_instruction_policy_heads WHERE id = current_head.id;
877
+ INSERT INTO workspace_instruction_policy_deactivation_events (
878
+ operation_id, request_fingerprint, account_id, workspace_id, kind, scope,
879
+ role_key, type, activation_version, old_revision_id, old_revision,
880
+ old_content_hash, actor_subject_id, reason, created_at
881
+ ) VALUES (
882
+ p_operation_id, fingerprint, p_account_id, p_workspace_id, proposal_row.kind,
883
+ proposal_row.scope, proposal_row.role_key, 'automatic_deactivate', next_version,
884
+ current_head.revision_id, current_head.revision, current_head.content_hash,
885
+ p_actor_subject_id, 'Exact governed-learning activation undo.', event_time
886
+ ) RETURNING * INTO result_row;
887
+ PERFORM set_config('opengeni.governed_learning_deactivation_operation', '', true);
888
+ RETURN NEXT result_row;
889
+ END;
890
+ $$;
891
+ REVOKE ALL ON FUNCTION governed_learning_deactivate_instruction_policy(
892
+ uuid, uuid, uuid, uuid, uuid, bigint, text
893
+ ) FROM PUBLIC;
894
+
895
+ CREATE OR REPLACE FUNCTION workspace_instruction_policy_canonical_snapshot_entries(
896
+ p_account_id uuid,
897
+ p_workspace_id uuid,
898
+ p_policy_role text,
899
+ p_accepted_at timestamptz
900
+ ) RETURNS jsonb
901
+ LANGUAGE sql STABLE AS $$
902
+ SELECT coalesce(jsonb_agg(candidate.entry ORDER BY candidate.ordinal), '[]'::jsonb)
903
+ FROM (
904
+ SELECT
905
+ CASE
906
+ WHEN event.kind = 'charter' THEN 0
907
+ WHEN event.scope = 'global' THEN 1
908
+ ELSE 2
909
+ END AS ordinal,
910
+ jsonb_build_object(
911
+ 'kind', event.kind,
912
+ 'scope', event.scope,
913
+ 'roleKey', event.role_key,
914
+ 'revisionId', revision.id::text,
915
+ 'revision', revision.revision,
916
+ 'contentHash', revision.content_hash,
917
+ 'activationVersion', event.activation_version,
918
+ 'activatedAt', to_char(
919
+ event.created_at AT TIME ZONE 'UTC',
920
+ 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
921
+ ),
922
+ 'provenance', jsonb_build_object(
923
+ 'source', revision.provenance_source,
924
+ 'sourceIdHash', CASE
925
+ WHEN revision.provenance_source_id IS NULL THEN NULL
926
+ ELSE encode(sha256(convert_to(revision.provenance_source_id, 'UTF8')), 'hex')
927
+ END
928
+ )
929
+ ) AS entry
930
+ FROM (
931
+ SELECT DISTINCT ON (lifecycle.kind, lifecycle.scope, coalesce(lifecycle.role_key, ''))
932
+ lifecycle.*
933
+ FROM (
934
+ SELECT
935
+ activation.id, activation.account_id, activation.workspace_id,
936
+ activation.kind, activation.scope, activation.role_key,
937
+ activation.activation_version, activation.new_revision_id,
938
+ activation.new_revision, activation.new_content_hash,
939
+ activation.created_at
940
+ FROM workspace_instruction_policy_activation_events activation
941
+ UNION ALL
942
+ SELECT
943
+ deactivation.id, deactivation.account_id, deactivation.workspace_id,
944
+ deactivation.kind, deactivation.scope, deactivation.role_key,
945
+ deactivation.activation_version, NULL::uuid, NULL::bigint, NULL::text,
946
+ deactivation.created_at
947
+ FROM workspace_instruction_policy_deactivation_events deactivation
948
+ ) lifecycle
949
+ WHERE lifecycle.account_id = p_account_id
950
+ AND lifecycle.workspace_id = p_workspace_id
951
+ AND lifecycle.created_at <= p_accepted_at
952
+ AND (
953
+ (lifecycle.kind = 'charter' AND lifecycle.scope = 'global')
954
+ OR (lifecycle.kind = 'policy' AND lifecycle.scope = 'global')
955
+ OR (
956
+ p_policy_role IS NOT NULL
957
+ AND lifecycle.kind = 'policy'
958
+ AND lifecycle.scope = 'role'
959
+ AND lifecycle.role_key = p_policy_role
960
+ )
961
+ )
962
+ ORDER BY
963
+ lifecycle.kind,
964
+ lifecycle.scope,
965
+ coalesce(lifecycle.role_key, ''),
966
+ lifecycle.activation_version DESC,
967
+ lifecycle.created_at DESC,
968
+ lifecycle.id DESC
969
+ ) event
970
+ JOIN workspace_instruction_policy_revisions revision
971
+ ON revision.account_id = event.account_id
972
+ AND revision.workspace_id = event.workspace_id
973
+ AND revision.id = event.new_revision_id
974
+ AND revision.revision = event.new_revision
975
+ AND revision.content_hash = event.new_content_hash
976
+ ) candidate;
977
+ $$;
978
+ REVOKE ALL ON FUNCTION workspace_instruction_policy_canonical_snapshot_entries(
979
+ uuid, uuid, text, timestamptz
980
+ ) FROM PUBLIC;
981
+
982
+ CREATE OR REPLACE FUNCTION governed_learning_apply_preference(
983
+ p_action text,
984
+ p_account_id uuid,
985
+ p_workspace_id uuid,
986
+ p_operation_id uuid,
987
+ p_knowledge_proposal_id uuid,
988
+ p_preference_id uuid,
989
+ p_revision_id uuid,
990
+ p_expected_active_revision_id uuid,
991
+ p_expected_activation_version integer,
992
+ p_actor_subject_id text
993
+ ) RETURNS SETOF preference_registry_events
994
+ LANGUAGE plpgsql SECURITY DEFINER AS $$
995
+ DECLARE
996
+ proposal_receipt company_brain_preference_proposal_receipts%ROWTYPE;
997
+ preference_row preference_registry_preferences%ROWTYPE;
998
+ revision_row preference_registry_revisions%ROWTYPE;
999
+ event_row preference_registry_events%ROWTYPE;
1000
+ next_event_version integer;
1001
+ BEGIN
1002
+ IF p_action NOT IN ('activate', 'undo')
1003
+ OR p_actor_subject_id NOT LIKE 'service:governed-learning-activation:%'
1004
+ OR p_expected_activation_version < 0
1005
+ THEN
1006
+ RAISE EXCEPTION 'governed-learning preference input is invalid' USING ERRCODE = '22023';
1007
+ END IF;
1008
+ SELECT * INTO proposal_receipt FROM company_brain_preference_proposal_receipts receipt
1009
+ WHERE receipt.account_id = p_account_id AND receipt.workspace_id = p_workspace_id
1010
+ AND receipt.knowledge_proposal_id = p_knowledge_proposal_id
1011
+ AND receipt.preference_id = p_preference_id AND receipt.revision_id = p_revision_id
1012
+ FOR SHARE;
1013
+ IF NOT FOUND THEN
1014
+ RAISE EXCEPTION 'governed-learning preference proposal is unavailable' USING ERRCODE = '42501';
1015
+ END IF;
1016
+ SELECT * INTO preference_row FROM preference_registry_preferences preference
1017
+ WHERE preference.account_id = p_account_id AND preference.id = p_preference_id
1018
+ AND preference.scope = 'workspace' AND preference.scope_workspace_id = p_workspace_id
1019
+ AND preference.scope_subject_id IS NULL
1020
+ FOR UPDATE;
1021
+ IF NOT FOUND OR preference_row.scope_version <> 1
1022
+ OR preference_row.active_revision_id IS DISTINCT FROM p_expected_active_revision_id
1023
+ OR preference_row.activation_version IS DISTINCT FROM p_expected_activation_version
1024
+ THEN
1025
+ RAISE EXCEPTION 'governed-learning preference head changed' USING ERRCODE = '40001';
1026
+ END IF;
1027
+ SELECT * INTO revision_row FROM preference_registry_revisions revision
1028
+ WHERE revision.account_id = p_account_id AND revision.preference_id = p_preference_id
1029
+ AND revision.id = p_revision_id
1030
+ AND revision.provenance_source = 'knowledge_proposal'
1031
+ AND revision.provenance_source_id = p_knowledge_proposal_id::text
1032
+ AND revision.trust = 'untrusted_proposal'
1033
+ AND (revision.expires_at IS NULL OR revision.expires_at > transaction_timestamp())
1034
+ FOR SHARE;
1035
+ IF NOT FOUND THEN
1036
+ RAISE EXCEPTION 'governed-learning preference revision is unavailable' USING ERRCODE = '42501';
1037
+ END IF;
1038
+ IF (p_action = 'activate' AND (
1039
+ preference_row.status <> 'proposed'
1040
+ OR p_expected_active_revision_id IS NOT NULL
1041
+ OR p_expected_activation_version <> 0
1042
+ )) OR (p_action = 'undo' AND (
1043
+ preference_row.status <> 'active'
1044
+ OR p_expected_active_revision_id IS DISTINCT FROM p_revision_id
1045
+ ))
1046
+ THEN
1047
+ RAISE EXCEPTION 'governed-learning preference lifecycle was superseded'
1048
+ USING ERRCODE = '40001';
1049
+ END IF;
1050
+ PERFORM set_config('opengeni.preference_lifecycle_head_id', preference_row.id::text, true);
1051
+ PERFORM set_config('opengeni.preference_lifecycle_operation',
1052
+ CASE WHEN p_action = 'activate' THEN 'activate' ELSE 'deactivate' END, true);
1053
+ IF p_action = 'activate' THEN
1054
+ UPDATE preference_registry_preferences SET
1055
+ status = 'active', active_revision_id = revision_row.id,
1056
+ active_revision = revision_row.revision,
1057
+ active_content_hash = revision_row.content_hash,
1058
+ activation_version = activation_version + 1,
1059
+ updated_at = clock_timestamp()
1060
+ WHERE id = preference_row.id;
1061
+ ELSE
1062
+ UPDATE preference_registry_preferences SET
1063
+ status = 'inactive', active_revision_id = NULL, active_revision = NULL,
1064
+ active_content_hash = NULL, activation_version = activation_version + 1,
1065
+ updated_at = clock_timestamp()
1066
+ WHERE id = preference_row.id;
1067
+ END IF;
1068
+ PERFORM set_config('opengeni.preference_lifecycle_head_id', '', true);
1069
+ PERFORM set_config('opengeni.preference_lifecycle_operation', '', true);
1070
+ SELECT coalesce(max(event.version), 0) + 1 INTO next_event_version
1071
+ FROM preference_registry_events event WHERE event.preference_id = preference_row.id;
1072
+ INSERT INTO preference_registry_events (
1073
+ account_id, preference_id, type, version, old_revision_id, new_revision_id,
1074
+ actor_subject_id, reason
1075
+ ) VALUES (
1076
+ p_account_id, p_preference_id,
1077
+ CASE WHEN p_action = 'activate' THEN 'activated' ELSE 'deactivated' END,
1078
+ next_event_version,
1079
+ CASE WHEN p_action = 'undo' THEN revision_row.id ELSE NULL END,
1080
+ CASE WHEN p_action = 'activate' THEN revision_row.id ELSE NULL END,
1081
+ p_actor_subject_id,
1082
+ CASE WHEN p_action = 'activate' THEN 'Automatic governed-learning activation.'
1083
+ ELSE 'Exact governed-learning activation undo.' END
1084
+ ) RETURNING * INTO event_row;
1085
+ RETURN NEXT event_row;
1086
+ END;
1087
+ $$;
1088
+ REVOKE ALL ON FUNCTION governed_learning_apply_preference(
1089
+ text, uuid, uuid, uuid, uuid, uuid, uuid, uuid, integer, text
1090
+ ) FROM PUBLIC;
1091
+
1092
+ CREATE OR REPLACE FUNCTION activate_governed_learning_decision(
1093
+ p_account_id uuid,
1094
+ p_workspace_id uuid,
1095
+ p_operation_id uuid,
1096
+ p_decision_receipt_id uuid
1097
+ ) RETURNS SETOF governed_learning_activation_receipts
1098
+ LANGUAGE plpgsql SECURITY DEFINER AS $$
1099
+ DECLARE
1100
+ caller_subject_id text := nullif(current_setting('opengeni.subject_id', true), '');
1101
+ decision_row governed_learning_decision_receipts%ROWTYPE;
1102
+ result_row governed_learning_activation_receipts%ROWTYPE;
1103
+ proposal_row knowledge_change_proposals%ROWTYPE;
1104
+ claim_row knowledge_claims%ROWTYPE;
1105
+ evidence_row knowledge_claim_evidence%ROWTYPE;
1106
+ review_row knowledge_claim_reviews%ROWTYPE;
1107
+ approval_row knowledge_claim_reviews%ROWTYPE;
1108
+ policy_head workspace_learning_policy_heads%ROWTYPE;
1109
+ policy_revision workspace_learning_policy_revisions%ROWTYPE;
1110
+ policy_snapshot workspace_learning_policy_snapshots%ROWTYPE;
1111
+ task_note_row task_notes%ROWTYPE;
1112
+ document_authority record;
1113
+ instruction_proposal workspace_instruction_policy_onboarding_proposals%ROWTYPE;
1114
+ instruction_event workspace_instruction_policy_activation_events%ROWTYPE;
1115
+ preference_receipt company_brain_preference_proposal_receipts%ROWTYPE;
1116
+ preference_row preference_registry_preferences%ROWTYPE;
1117
+ preference_revision preference_registry_revisions%ROWTYPE;
1118
+ preference_event preference_registry_events%ROWTYPE;
1119
+ current_instruction_head workspace_instruction_policy_heads%ROWTYPE;
1120
+ current_instruction_activation_version bigint;
1121
+ input_hash_value text;
1122
+ service_actor text;
1123
+ current_authority_hash text;
1124
+ current_mode text;
1125
+ conflict_count integer;
1126
+ destination_value text;
1127
+ destination_proposal_id_value uuid;
1128
+ destination_revision_id_value uuid;
1129
+ destination_old_revision_id_value uuid;
1130
+ destination_old_content_hash_value text;
1131
+ destination_old_version_value bigint;
1132
+ destination_new_content_hash_value text;
1133
+ destination_new_version_value bigint;
1134
+ destination_event_id_value uuid;
1135
+ effective_at_value timestamptz;
1136
+ review_operation_id uuid;
1137
+ destination_operation_id uuid;
1138
+ BEGIN
1139
+ IF p_account_id IS NULL OR p_workspace_id IS NULL OR p_operation_id IS NULL
1140
+ OR p_decision_receipt_id IS NULL OR caller_subject_id IS NULL
1141
+ OR length(btrim(caller_subject_id)) NOT BETWEEN 1 AND 1024
1142
+ OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
1143
+ OR p_workspace_id IS DISTINCT FROM opengeni_private.current_workspace_id()
1144
+ THEN
1145
+ RAISE EXCEPTION 'governed-learning activation requires exact tenant authority'
1146
+ USING ERRCODE = '42501';
1147
+ END IF;
1148
+ input_hash_value := encode(sha256(convert_to(jsonb_build_array(
1149
+ 'governed-learning-activation', 1, p_account_id, p_workspace_id,
1150
+ p_operation_id, p_decision_receipt_id
1151
+ )::text, 'UTF8')), 'hex');
1152
+ SELECT * INTO result_row FROM governed_learning_activation_receipts receipt
1153
+ WHERE receipt.workspace_id = p_workspace_id AND receipt.operation_id = p_operation_id
1154
+ FOR SHARE;
1155
+ IF FOUND THEN
1156
+ IF result_row.account_id <> p_account_id OR result_row.input_hash <> input_hash_value
1157
+ OR result_row.decision_receipt_id <> p_decision_receipt_id
1158
+ OR result_row.initiating_human_subject_id <> caller_subject_id
1159
+ THEN
1160
+ RAISE EXCEPTION 'governed-learning activation operation conflicts with immutable receipt'
1161
+ USING ERRCODE = '23505';
1162
+ END IF;
1163
+ RETURN NEXT result_row;
1164
+ RETURN;
1165
+ END IF;
1166
+
1167
+ -- Canonical shared prefix. The workspace UPDATE lock also gives all three
1168
+ -- destination adapters one order, avoiding claim/head inversion.
1169
+ PERFORM 1 FROM workspaces workspace
1170
+ WHERE workspace.account_id = p_account_id AND workspace.id = p_workspace_id
1171
+ FOR UPDATE;
1172
+ IF NOT FOUND THEN
1173
+ RAISE EXCEPTION 'governed-learning workspace is unavailable' USING ERRCODE = '42501';
1174
+ END IF;
1175
+ SELECT * INTO result_row FROM governed_learning_activation_receipts receipt
1176
+ WHERE receipt.workspace_id = p_workspace_id AND receipt.operation_id = p_operation_id
1177
+ FOR SHARE;
1178
+ IF FOUND THEN
1179
+ IF result_row.account_id <> p_account_id OR result_row.input_hash <> input_hash_value
1180
+ OR result_row.decision_receipt_id <> p_decision_receipt_id
1181
+ OR result_row.initiating_human_subject_id <> caller_subject_id
1182
+ THEN
1183
+ RAISE EXCEPTION 'governed-learning activation operation conflicts with immutable receipt'
1184
+ USING ERRCODE = '23505';
1185
+ END IF;
1186
+ RETURN NEXT result_row;
1187
+ RETURN;
1188
+ END IF;
1189
+ SELECT * INTO decision_row FROM governed_learning_decision_receipts receipt
1190
+ WHERE receipt.id = p_decision_receipt_id AND receipt.account_id = p_account_id
1191
+ AND receipt.workspace_id = p_workspace_id
1192
+ AND receipt.initiating_human_subject_id = caller_subject_id
1193
+ AND receipt.outcome = 'automatic' AND receipt.automatic_eligible
1194
+ FOR UPDATE;
1195
+ IF NOT FOUND OR NOT session_reference_visible(
1196
+ decision_row.account_id, decision_row.workspace_id, decision_row.session_id
1197
+ ) THEN
1198
+ RAISE EXCEPTION 'final automatic evaluator receipt is unavailable'
1199
+ USING ERRCODE = '42501';
1200
+ END IF;
1201
+ IF EXISTS (
1202
+ SELECT 1 FROM governed_learning_activation_receipts receipt
1203
+ WHERE receipt.decision_receipt_id = p_decision_receipt_id
1204
+ ) THEN
1205
+ RAISE EXCEPTION 'automatic evaluator receipt was consumed by another operation'
1206
+ USING ERRCODE = '23505';
1207
+ END IF;
1208
+ PERFORM 1 FROM sessions session
1209
+ WHERE session.account_id = p_account_id AND session.workspace_id = p_workspace_id
1210
+ AND session.id = decision_row.session_id AND session.active_turn_id = decision_row.turn_id
1211
+ AND session_reference_visible(session.account_id, session.workspace_id, session.id)
1212
+ FOR SHARE;
1213
+ IF NOT FOUND THEN
1214
+ RAISE EXCEPTION 'governed-learning activation session is unavailable' USING ERRCODE = '42501';
1215
+ END IF;
1216
+ PERFORM 1 FROM session_turns turn
1217
+ WHERE turn.account_id = p_account_id AND turn.workspace_id = p_workspace_id
1218
+ AND turn.session_id = decision_row.session_id AND turn.id = decision_row.turn_id
1219
+ AND turn.active_attempt_id = decision_row.attempt_id
1220
+ AND turn.execution_generation = decision_row.execution_generation
1221
+ AND turn.status IN ('running', 'requires_action', 'recovering', 'waiting_capacity')
1222
+ AND coalesce(turn.initiating_human_subject_id,
1223
+ CASE WHEN turn.initiator_kind = 'subject' THEN turn.initiator_subject_id END
1224
+ ) = caller_subject_id
1225
+ FOR SHARE;
1226
+ IF NOT FOUND THEN
1227
+ RAISE EXCEPTION 'governed-learning activation turn is unavailable' USING ERRCODE = '42501';
1228
+ END IF;
1229
+ PERFORM 1 FROM session_turn_attempts attempt
1230
+ WHERE attempt.account_id = p_account_id AND attempt.workspace_id = p_workspace_id
1231
+ AND attempt.session_id = decision_row.session_id AND attempt.turn_id = decision_row.turn_id
1232
+ AND attempt.id = decision_row.attempt_id
1233
+ AND attempt.execution_generation = decision_row.execution_generation
1234
+ AND attempt.state IN ('claimed', 'running')
1235
+ AND NOT EXISTS (
1236
+ SELECT 1 FROM session_attempt_interruptions interruption
1237
+ WHERE interruption.workspace_id = p_workspace_id
1238
+ AND interruption.attempt_id = decision_row.attempt_id
1239
+ AND interruption.state IN ('pending', 'delivered', 'acknowledged')
1240
+ )
1241
+ FOR SHARE;
1242
+ IF NOT FOUND THEN
1243
+ RAISE EXCEPTION 'governed-learning activation attempt is unavailable' USING ERRCODE = '42501';
1244
+ END IF;
1245
+
1246
+ SELECT * INTO policy_snapshot FROM workspace_learning_policy_snapshots snapshot
1247
+ WHERE snapshot.id = decision_row.policy_snapshot_id
1248
+ AND snapshot.account_id = p_account_id AND snapshot.workspace_id = p_workspace_id
1249
+ AND snapshot.session_id = decision_row.session_id
1250
+ AND snapshot.turn_id = decision_row.turn_id
1251
+ AND snapshot.attempt_id = decision_row.attempt_id
1252
+ AND snapshot.execution_generation = decision_row.execution_generation
1253
+ AND snapshot.snapshot_hash = decision_row.policy_snapshot_hash
1254
+ FOR SHARE;
1255
+ SELECT * INTO policy_head FROM workspace_learning_policy_heads head
1256
+ WHERE head.account_id = p_account_id AND head.workspace_id = p_workspace_id
1257
+ FOR SHARE;
1258
+ SELECT * INTO policy_revision FROM workspace_learning_policy_revisions revision
1259
+ WHERE revision.account_id = p_account_id AND revision.workspace_id = p_workspace_id
1260
+ AND revision.id = policy_head.revision_id
1261
+ FOR SHARE;
1262
+ IF policy_snapshot.id IS NULL
1263
+ OR policy_head.revision_id IS DISTINCT FROM decision_row.policy_revision_id
1264
+ OR policy_head.policy_hash IS DISTINCT FROM policy_snapshot.policy_hash
1265
+ OR policy_head.activation_version IS DISTINCT FROM decision_row.policy_activation_version
1266
+ OR policy_revision.policy_hash IS DISTINCT FROM policy_snapshot.policy_hash
1267
+ THEN
1268
+ RAISE EXCEPTION 'governed-learning policy authority changed after evaluation'
1269
+ USING ERRCODE = '40001';
1270
+ END IF;
1271
+ current_mode := coalesce((
1272
+ SELECT override.value->>'mode'
1273
+ FROM jsonb_array_elements(policy_revision.source_overrides) override(value)
1274
+ WHERE override.value->>'kind' = decision_row.source_kind
1275
+ AND override.value->>'id' = decision_row.source_id::text LIMIT 1
1276
+ ), policy_revision.workspace_mode);
1277
+ IF current_mode <> 'automatic' THEN
1278
+ RAISE EXCEPTION 'governed-learning source is no longer automatic' USING ERRCODE = '42501';
1279
+ END IF;
1280
+
1281
+ SELECT * INTO proposal_row FROM knowledge_change_proposals proposal
1282
+ WHERE proposal.id = decision_row.proposal_id AND proposal.account_id = p_account_id
1283
+ AND proposal.scope_kind = 'workspace' AND proposal.scope_workspace_id = p_workspace_id
1284
+ AND proposal.scope_subject_id IS NULL AND proposal.status = 'proposed'
1285
+ AND proposal.input_hash = decision_row.proposal_input_hash
1286
+ AND proposal.content_hash = decision_row.proposal_content_hash
1287
+ AND proposal.claim_id = decision_row.claim_id
1288
+ AND proposal.evidence_id = decision_row.evidence_id
1289
+ AND proposal.initiating_human_subject_id = caller_subject_id
1290
+ FOR SHARE;
1291
+ SELECT * INTO claim_row FROM knowledge_claims claim
1292
+ WHERE claim.id = decision_row.claim_id AND claim.account_id = p_account_id
1293
+ AND claim.scope_kind = 'workspace' AND claim.scope_workspace_id = p_workspace_id
1294
+ AND claim.scope_subject_id IS NULL AND claim.input_hash = decision_row.claim_input_hash
1295
+ AND claim.initiating_human_subject_id = caller_subject_id
1296
+ AND claim.confidence_bps >= decision_row.confidence_floor_bps
1297
+ AND claim.effective_at <= transaction_timestamp()
1298
+ AND (claim.expires_at IS NULL OR claim.expires_at > transaction_timestamp())
1299
+ FOR UPDATE;
1300
+ SELECT * INTO evidence_row FROM knowledge_claim_evidence evidence
1301
+ WHERE evidence.id = decision_row.evidence_id AND evidence.account_id = p_account_id
1302
+ AND evidence.scope_kind = 'workspace' AND evidence.scope_workspace_id = p_workspace_id
1303
+ AND evidence.scope_subject_id IS NULL AND evidence.claim_id = decision_row.claim_id
1304
+ AND evidence.polarity = 'supports' AND evidence.input_hash = decision_row.evidence_input_hash
1305
+ AND evidence.content_hash = decision_row.evidence_content_hash
1306
+ AND evidence.initiating_human_subject_id = caller_subject_id
1307
+ FOR SHARE;
1308
+ IF proposal_row.id IS NULL OR claim_row.id IS NULL OR evidence_row.id IS NULL THEN
1309
+ RAISE EXCEPTION 'governed-learning proposal lineage changed after evaluation'
1310
+ USING ERRCODE = '42501';
1311
+ END IF;
1312
+ SELECT * INTO review_row FROM knowledge_claim_reviews review
1313
+ WHERE review.account_id = p_account_id AND review.claim_id = decision_row.claim_id
1314
+ ORDER BY review.review_revision DESC LIMIT 1 FOR SHARE;
1315
+ IF review_row.id IS NULL OR review_row.state <> 'proposed'
1316
+ OR review_row.review_revision <> decision_row.review_revision
1317
+ THEN
1318
+ RAISE EXCEPTION 'governed-learning review was superseded after evaluation'
1319
+ USING ERRCODE = '40001';
1320
+ END IF;
1321
+ SELECT least(1000, count(*))::integer INTO conflict_count FROM (
1322
+ SELECT relation.id FROM knowledge_claim_relations relation
1323
+ WHERE relation.account_id = p_account_id AND relation.scope_kind = 'workspace'
1324
+ AND relation.scope_workspace_id = p_workspace_id AND relation.scope_subject_id IS NULL
1325
+ AND relation.relation_type = 'conflicts_with'
1326
+ AND (relation.from_claim_id = decision_row.claim_id
1327
+ OR relation.to_claim_id = decision_row.claim_id)
1328
+ UNION ALL
1329
+ SELECT contradiction.id FROM knowledge_claim_evidence contradiction
1330
+ WHERE contradiction.account_id = p_account_id AND contradiction.scope_kind = 'workspace'
1331
+ AND contradiction.scope_workspace_id = p_workspace_id
1332
+ AND contradiction.scope_subject_id IS NULL
1333
+ AND contradiction.claim_id = decision_row.claim_id
1334
+ AND contradiction.polarity = 'contradicts'
1335
+ ) conflicts;
1336
+ IF conflict_count <> 0 THEN
1337
+ RAISE EXCEPTION 'governed-learning evidence now conflicts' USING ERRCODE = '23514';
1338
+ END IF;
1339
+
1340
+ IF evidence_row.task_note_id IS NOT NULL THEN
1341
+ SELECT * INTO task_note_row FROM task_notes note
1342
+ WHERE note.id = evidence_row.task_note_id AND note.account_id = p_account_id
1343
+ AND note.workspace_id = p_workspace_id
1344
+ AND note.root_session_id = evidence_row.task_note_root_session_id
1345
+ AND note.version = evidence_row.task_note_version
1346
+ AND note.text_hash = evidence_row.content_hash
1347
+ AND note.status = 'active' AND note.expires_at > transaction_timestamp()
1348
+ AND session_reference_visible(note.account_id, note.workspace_id, note.root_session_id)
1349
+ FOR SHARE;
1350
+ IF NOT FOUND THEN
1351
+ RAISE EXCEPTION 'governed-learning Task-note authority was revoked' USING ERRCODE = '42501';
1352
+ END IF;
1353
+ current_authority_hash := encode(sha256(convert_to(jsonb_build_array(
1354
+ 'task-note-authority', 1, evidence_row.task_note_id,
1355
+ evidence_row.task_note_root_session_id, evidence_row.task_note_version,
1356
+ evidence_row.content_hash, task_note_row.status, task_note_row.expires_at
1357
+ )::text, 'UTF8')), 'hex');
1358
+ ELSE
1359
+ SELECT provider.id AS provider_id, provider.lifecycle_state AS provider_state,
1360
+ provider.lifecycle_generation AS provider_generation, source.id AS source_id,
1361
+ source.lifecycle_state AS source_state, source.lifecycle_generation AS source_generation,
1362
+ source.current_acl_generation, object.id AS object_id,
1363
+ object.lifecycle_state AS object_state, object.lifecycle_generation AS object_generation,
1364
+ object.version_generation AS object_version_generation, object.current_version_id,
1365
+ version.id AS version_id, version.version_generation, version.content_sha256,
1366
+ version.acl_version_id, version.acl_generation, version.document_id,
1367
+ evidence_acl.acl_hash AS evidence_acl_hash,
1368
+ evidence_acl.agent_access AS evidence_agent_access,
1369
+ current_acl.id AS current_acl_id, current_acl.acl_hash AS current_acl_hash,
1370
+ current_acl.agent_access AS current_agent_access
1371
+ INTO document_authority
1372
+ FROM knowledge_document_versions version
1373
+ JOIN knowledge_source_objects object ON object.account_id = version.account_id
1374
+ AND object.id = version.object_id AND object.scope_key = version.scope_key
1375
+ JOIN knowledge_sources source ON source.account_id = version.account_id
1376
+ AND source.id = version.source_id AND source.scope_key = version.scope_key
1377
+ JOIN knowledge_providers provider ON provider.account_id = source.account_id
1378
+ AND provider.id = source.provider_id AND provider.scope_key = source.scope_key
1379
+ JOIN knowledge_source_acl_versions evidence_acl ON evidence_acl.account_id = version.account_id
1380
+ AND evidence_acl.id = version.acl_version_id AND evidence_acl.source_id = version.source_id
1381
+ AND evidence_acl.generation = version.acl_generation
1382
+ LEFT JOIN knowledge_source_acl_versions current_acl ON current_acl.account_id = source.account_id
1383
+ AND current_acl.source_id = source.id AND current_acl.generation = source.current_acl_generation
1384
+ WHERE version.id = evidence_row.document_version_id AND version.account_id = p_account_id
1385
+ AND version.scope_kind = 'workspace' AND version.scope_workspace_id = p_workspace_id
1386
+ AND version.scope_subject_id IS NULL
1387
+ FOR SHARE OF version, object, source, provider, evidence_acl;
1388
+ IF document_authority.version_id IS NULL
1389
+ OR document_authority.provider_state IS DISTINCT FROM 'active'
1390
+ OR document_authority.source_state IS DISTINCT FROM 'active'
1391
+ OR document_authority.object_state IS DISTINCT FROM 'active'
1392
+ OR document_authority.current_version_id IS DISTINCT FROM document_authority.version_id
1393
+ OR document_authority.object_version_generation IS DISTINCT FROM document_authority.version_generation
1394
+ OR document_authority.current_acl_id IS NULL
1395
+ OR NOT coalesce(document_authority.evidence_agent_access, false)
1396
+ OR NOT coalesce(document_authority.current_agent_access, false)
1397
+ OR (document_authority.document_id IS NOT NULL AND NOT EXISTS (
1398
+ SELECT 1 FROM documents document WHERE document.id = document_authority.document_id
1399
+ AND document.account_id = p_account_id AND document.workspace_id = p_workspace_id
1400
+ AND document.status = 'ready' AND document.agent_access
1401
+ AND (document.visibility <> 'private' OR document.created_by = caller_subject_id)
1402
+ ))
1403
+ OR (evidence_row.document_chunk_id IS NOT NULL AND NOT EXISTS (
1404
+ SELECT 1 FROM document_chunks chunk WHERE chunk.id = evidence_row.document_chunk_id
1405
+ AND chunk.account_id = p_account_id AND chunk.workspace_id = p_workspace_id
1406
+ AND chunk.document_id = document_authority.document_id
1407
+ ))
1408
+ THEN
1409
+ RAISE EXCEPTION 'governed-learning document authority was revoked' USING ERRCODE = '42501';
1410
+ END IF;
1411
+ current_authority_hash := encode(sha256(convert_to(jsonb_build_array(
1412
+ 'document-evidence-authority', 1, evidence_row.document_version_id,
1413
+ document_authority.provider_id, document_authority.provider_state,
1414
+ document_authority.provider_generation, document_authority.source_id,
1415
+ document_authority.source_state, document_authority.source_generation,
1416
+ document_authority.current_acl_generation, document_authority.object_id,
1417
+ document_authority.object_state, document_authority.object_generation,
1418
+ document_authority.object_version_generation, document_authority.current_version_id,
1419
+ document_authority.version_id, document_authority.version_generation,
1420
+ document_authority.content_sha256, document_authority.acl_version_id,
1421
+ document_authority.acl_generation, document_authority.evidence_acl_hash,
1422
+ document_authority.current_acl_id, document_authority.current_acl_hash,
1423
+ evidence_row.document_chunk_id, false
1424
+ )::text, 'UTF8')), 'hex');
1425
+ END IF;
1426
+ IF current_authority_hash IS DISTINCT FROM decision_row.evidence_authority_hash THEN
1427
+ RAISE EXCEPTION 'governed-learning evidence authority changed after evaluation'
1428
+ USING ERRCODE = '40001';
1429
+ END IF;
1430
+
1431
+ IF proposal_row.target_kind = 'instruction_policy' THEN
1432
+ SELECT * INTO instruction_proposal
1433
+ FROM workspace_instruction_policy_onboarding_proposals proposal
1434
+ WHERE proposal.account_id = p_account_id AND proposal.workspace_id = p_workspace_id
1435
+ AND proposal.source_id = proposal_row.id::text
1436
+ AND proposal.source_version = proposal_row.content_hash
1437
+ AND proposal.status = 'proposed'
1438
+ FOR SHARE;
1439
+ IF NOT FOUND OR instruction_proposal.draft_content_hash <> proposal_row.content_hash THEN
1440
+ RAISE EXCEPTION 'exact inactive instruction-policy proposal is unavailable'
1441
+ USING ERRCODE = '42501';
1442
+ END IF;
1443
+ SELECT * INTO current_instruction_head FROM workspace_instruction_policy_heads head
1444
+ WHERE head.account_id = p_account_id AND head.workspace_id = p_workspace_id
1445
+ AND head.kind = instruction_proposal.kind AND head.scope = instruction_proposal.scope
1446
+ AND head.role_key IS NOT DISTINCT FROM instruction_proposal.role_key FOR SHARE;
1447
+ IF NOT FOUND THEN
1448
+ SELECT event.activation_version INTO current_instruction_activation_version
1449
+ FROM workspace_instruction_policy_deactivation_events event
1450
+ WHERE event.account_id = p_account_id AND event.workspace_id = p_workspace_id
1451
+ AND event.kind = instruction_proposal.kind AND event.scope = instruction_proposal.scope
1452
+ AND event.role_key IS NOT DISTINCT FROM instruction_proposal.role_key
1453
+ ORDER BY event.activation_version DESC, event.id DESC
1454
+ LIMIT 1;
1455
+ END IF;
1456
+ current_instruction_activation_version := coalesce(
1457
+ current_instruction_head.activation_version,
1458
+ current_instruction_activation_version,
1459
+ 0
1460
+ );
1461
+ IF current_instruction_head.revision_id IS DISTINCT FROM instruction_proposal.baseline_revision_id
1462
+ OR current_instruction_activation_version
1463
+ IS DISTINCT FROM instruction_proposal.baseline_activation_version
1464
+ THEN
1465
+ RAISE EXCEPTION 'instruction-policy proposal baseline is stale' USING ERRCODE = '40001';
1466
+ END IF;
1467
+ destination_value := 'instruction_policy';
1468
+ destination_proposal_id_value := instruction_proposal.id;
1469
+ destination_revision_id_value := instruction_proposal.draft_revision_id;
1470
+ destination_old_revision_id_value := instruction_proposal.baseline_revision_id;
1471
+ destination_old_content_hash_value := instruction_proposal.baseline_content_hash;
1472
+ destination_old_version_value := instruction_proposal.baseline_activation_version;
1473
+ destination_new_content_hash_value := instruction_proposal.draft_content_hash;
1474
+ ELSIF proposal_row.target_kind = 'preference' THEN
1475
+ SELECT * INTO preference_receipt FROM company_brain_preference_proposal_receipts receipt
1476
+ WHERE receipt.account_id = p_account_id AND receipt.workspace_id = p_workspace_id
1477
+ AND receipt.knowledge_proposal_id = proposal_row.id FOR SHARE;
1478
+ SELECT * INTO preference_row FROM preference_registry_preferences preference
1479
+ WHERE preference.account_id = p_account_id AND preference.id = preference_receipt.preference_id
1480
+ AND preference.scope = 'workspace' AND preference.scope_workspace_id = p_workspace_id
1481
+ AND preference.scope_subject_id IS NULL AND preference.status = 'proposed'
1482
+ AND preference.active_revision_id IS NULL AND preference.activation_version = 0
1483
+ AND preference.scope_version = 1 FOR SHARE;
1484
+ SELECT * INTO preference_revision FROM preference_registry_revisions revision
1485
+ WHERE revision.account_id = p_account_id AND revision.id = preference_receipt.revision_id
1486
+ AND revision.preference_id = preference_receipt.preference_id
1487
+ AND revision.provenance_source = 'knowledge_proposal'
1488
+ AND revision.provenance_source_id = proposal_row.id::text
1489
+ AND revision.trust = 'untrusted_proposal'
1490
+ AND (revision.expires_at IS NULL OR revision.expires_at > transaction_timestamp())
1491
+ FOR SHARE;
1492
+ IF preference_receipt.id IS NULL OR preference_row.id IS NULL OR preference_revision.id IS NULL
1493
+ OR proposal_row.target_scope <> 'workspace'
1494
+ OR proposal_row.target_key <> preference_row.stable_key
1495
+ THEN
1496
+ RAISE EXCEPTION 'exact inactive preference proposal is unavailable'
1497
+ USING ERRCODE = '42501';
1498
+ END IF;
1499
+ destination_value := 'preference';
1500
+ destination_proposal_id_value := preference_row.id;
1501
+ destination_revision_id_value := preference_revision.id;
1502
+ destination_old_revision_id_value := NULL;
1503
+ destination_old_content_hash_value := NULL;
1504
+ destination_old_version_value := 0;
1505
+ destination_new_content_hash_value := preference_revision.content_hash;
1506
+ ELSE
1507
+ RAISE EXCEPTION 'unsupported governed-learning destination' USING ERRCODE = '23514';
1508
+ END IF;
1509
+
1510
+ service_actor := 'service:governed-learning-activation:' || input_hash_value;
1511
+ review_operation_id := opengeni_private.governed_learning_derived_uuid(
1512
+ p_operation_id, 'knowledge-approve'
1513
+ );
1514
+ destination_operation_id := opengeni_private.governed_learning_derived_uuid(
1515
+ p_operation_id, 'destination-activate'
1516
+ );
1517
+ SELECT * INTO approval_row FROM governed_learning_apply_knowledge_review(
1518
+ p_account_id, p_workspace_id, review_operation_id, decision_row.claim_id,
1519
+ review_row.id, review_row.review_revision, 'approved', service_actor, caller_subject_id
1520
+ );
1521
+ IF destination_value = 'instruction_policy' THEN
1522
+ SELECT * INTO instruction_event FROM governed_learning_apply_instruction_policy(
1523
+ 'activate', p_account_id, p_workspace_id, destination_operation_id,
1524
+ destination_proposal_id_value, destination_old_revision_id_value,
1525
+ destination_old_version_value, destination_revision_id_value, service_actor
1526
+ );
1527
+ destination_new_version_value := instruction_event.activation_version;
1528
+ destination_event_id_value := instruction_event.id;
1529
+ effective_at_value := instruction_event.created_at;
1530
+ ELSE
1531
+ SELECT * INTO preference_event FROM governed_learning_apply_preference(
1532
+ 'activate', p_account_id, p_workspace_id, destination_operation_id,
1533
+ proposal_row.id, destination_proposal_id_value, destination_revision_id_value,
1534
+ NULL, 0, service_actor
1535
+ );
1536
+ SELECT activation_version INTO destination_new_version_value
1537
+ FROM preference_registry_preferences WHERE id = destination_proposal_id_value;
1538
+ destination_event_id_value := preference_event.id;
1539
+ effective_at_value := preference_event.created_at;
1540
+ END IF;
1541
+
1542
+ INSERT INTO governed_learning_activation_receipts (
1543
+ account_id, workspace_id, operation_id, input_hash, decision_receipt_id,
1544
+ initiating_human_subject_id, service_actor_subject_id, policy_revision_id,
1545
+ policy_hash, policy_activation_version, source_kind, source_id,
1546
+ source_authority_hash, proposal_id, claim_id, evidence_id,
1547
+ knowledge_previous_review_id, knowledge_previous_review_revision,
1548
+ knowledge_approval_review_id, knowledge_approval_review_revision,
1549
+ knowledge_approval_input_hash, destination, destination_proposal_id,
1550
+ destination_revision_id, destination_old_revision_id,
1551
+ destination_old_content_hash, destination_old_version,
1552
+ destination_new_content_hash, destination_new_version, destination_event_id,
1553
+ effective_at
1554
+ ) VALUES (
1555
+ p_account_id, p_workspace_id, p_operation_id, input_hash_value,
1556
+ p_decision_receipt_id, caller_subject_id, service_actor, policy_revision.id,
1557
+ policy_revision.policy_hash, policy_head.activation_version,
1558
+ decision_row.source_kind, decision_row.source_id, current_authority_hash,
1559
+ decision_row.proposal_id, decision_row.claim_id, decision_row.evidence_id,
1560
+ review_row.id, review_row.review_revision, approval_row.id,
1561
+ approval_row.review_revision, approval_row.input_hash, destination_value,
1562
+ destination_proposal_id_value, destination_revision_id_value,
1563
+ destination_old_revision_id_value, destination_old_content_hash_value,
1564
+ destination_old_version_value, destination_new_content_hash_value,
1565
+ destination_new_version_value, destination_event_id_value, effective_at_value
1566
+ ) RETURNING * INTO result_row;
1567
+ RETURN NEXT result_row;
1568
+ END;
1569
+ $$;
1570
+ REVOKE ALL ON FUNCTION activate_governed_learning_decision(uuid, uuid, uuid, uuid) FROM PUBLIC;
1571
+
1572
+ CREATE OR REPLACE FUNCTION undo_governed_learning_activation(
1573
+ p_account_id uuid,
1574
+ p_workspace_id uuid,
1575
+ p_operation_id uuid,
1576
+ p_activation_receipt_id uuid
1577
+ ) RETURNS SETOF governed_learning_activation_undo_receipts
1578
+ LANGUAGE plpgsql SECURITY DEFINER AS $$
1579
+ DECLARE
1580
+ caller_subject_id text := nullif(current_setting('opengeni.subject_id', true), '');
1581
+ activation_row governed_learning_activation_receipts%ROWTYPE;
1582
+ decision_row governed_learning_decision_receipts%ROWTYPE;
1583
+ result_row governed_learning_activation_undo_receipts%ROWTYPE;
1584
+ latest_review knowledge_claim_reviews%ROWTYPE;
1585
+ revocation_row knowledge_claim_reviews%ROWTYPE;
1586
+ instruction_head workspace_instruction_policy_heads%ROWTYPE;
1587
+ instruction_event workspace_instruction_policy_activation_events%ROWTYPE;
1588
+ instruction_deactivation workspace_instruction_policy_deactivation_events%ROWTYPE;
1589
+ preference_row preference_registry_preferences%ROWTYPE;
1590
+ preference_event preference_registry_events%ROWTYPE;
1591
+ input_hash_value text;
1592
+ service_actor text;
1593
+ review_operation_id uuid;
1594
+ destination_operation_id uuid;
1595
+ destination_new_version_value bigint;
1596
+ destination_event_id_value uuid;
1597
+ effective_at_value timestamptz;
1598
+ BEGIN
1599
+ IF p_account_id IS NULL OR p_workspace_id IS NULL OR p_operation_id IS NULL
1600
+ OR p_activation_receipt_id IS NULL OR caller_subject_id IS NULL
1601
+ OR length(btrim(caller_subject_id)) NOT BETWEEN 1 AND 1024
1602
+ OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
1603
+ OR p_workspace_id IS DISTINCT FROM opengeni_private.current_workspace_id()
1604
+ THEN
1605
+ RAISE EXCEPTION 'governed-learning undo requires exact tenant authority'
1606
+ USING ERRCODE = '42501';
1607
+ END IF;
1608
+ input_hash_value := encode(sha256(convert_to(jsonb_build_array(
1609
+ 'governed-learning-activation-undo', 1, p_account_id, p_workspace_id,
1610
+ p_operation_id, p_activation_receipt_id
1611
+ )::text, 'UTF8')), 'hex');
1612
+ SELECT * INTO result_row FROM governed_learning_activation_undo_receipts receipt
1613
+ WHERE receipt.workspace_id = p_workspace_id AND receipt.operation_id = p_operation_id
1614
+ FOR SHARE;
1615
+ IF FOUND THEN
1616
+ IF result_row.account_id <> p_account_id OR result_row.input_hash <> input_hash_value
1617
+ OR result_row.activation_receipt_id <> p_activation_receipt_id
1618
+ OR result_row.initiating_human_subject_id <> caller_subject_id
1619
+ THEN
1620
+ RAISE EXCEPTION 'governed-learning undo operation conflicts with immutable receipt'
1621
+ USING ERRCODE = '23505';
1622
+ END IF;
1623
+ RETURN NEXT result_row;
1624
+ RETURN;
1625
+ END IF;
1626
+ PERFORM 1 FROM workspaces workspace
1627
+ WHERE workspace.account_id = p_account_id AND workspace.id = p_workspace_id
1628
+ FOR UPDATE;
1629
+ IF NOT FOUND THEN
1630
+ RAISE EXCEPTION 'governed-learning workspace is unavailable' USING ERRCODE = '42501';
1631
+ END IF;
1632
+ SELECT * INTO result_row FROM governed_learning_activation_undo_receipts receipt
1633
+ WHERE receipt.workspace_id = p_workspace_id AND receipt.operation_id = p_operation_id
1634
+ FOR SHARE;
1635
+ IF FOUND THEN
1636
+ IF result_row.account_id <> p_account_id OR result_row.input_hash <> input_hash_value
1637
+ OR result_row.activation_receipt_id <> p_activation_receipt_id
1638
+ OR result_row.initiating_human_subject_id <> caller_subject_id
1639
+ THEN
1640
+ RAISE EXCEPTION 'governed-learning undo operation conflicts with immutable receipt'
1641
+ USING ERRCODE = '23505';
1642
+ END IF;
1643
+ RETURN NEXT result_row;
1644
+ RETURN;
1645
+ END IF;
1646
+ SELECT * INTO activation_row FROM governed_learning_activation_receipts receipt
1647
+ WHERE receipt.id = p_activation_receipt_id AND receipt.account_id = p_account_id
1648
+ AND receipt.workspace_id = p_workspace_id
1649
+ AND receipt.initiating_human_subject_id = caller_subject_id
1650
+ FOR UPDATE;
1651
+ IF NOT FOUND THEN
1652
+ RAISE EXCEPTION 'governed-learning activation receipt is unavailable' USING ERRCODE = '42501';
1653
+ END IF;
1654
+ SELECT * INTO decision_row FROM governed_learning_decision_receipts receipt
1655
+ WHERE receipt.id = activation_row.decision_receipt_id
1656
+ AND receipt.account_id = p_account_id AND receipt.workspace_id = p_workspace_id
1657
+ AND receipt.initiating_human_subject_id = caller_subject_id FOR SHARE;
1658
+ IF NOT FOUND OR NOT session_reference_visible(
1659
+ decision_row.account_id, decision_row.workspace_id, decision_row.session_id
1660
+ ) THEN
1661
+ RAISE EXCEPTION 'governed-learning undo source is unavailable' USING ERRCODE = '42501';
1662
+ END IF;
1663
+ IF EXISTS (
1664
+ SELECT 1 FROM governed_learning_activation_undo_receipts receipt
1665
+ WHERE receipt.activation_receipt_id = p_activation_receipt_id
1666
+ ) THEN
1667
+ RAISE EXCEPTION 'governed-learning activation was undone by another operation'
1668
+ USING ERRCODE = '23505';
1669
+ END IF;
1670
+ SELECT * INTO latest_review FROM knowledge_claim_reviews review
1671
+ WHERE review.account_id = p_account_id AND review.claim_id = activation_row.claim_id
1672
+ ORDER BY review.review_revision DESC LIMIT 1 FOR UPDATE;
1673
+ IF latest_review.id IS DISTINCT FROM activation_row.knowledge_approval_review_id
1674
+ OR latest_review.review_revision IS DISTINCT FROM activation_row.knowledge_approval_review_revision
1675
+ OR latest_review.state <> 'approved'
1676
+ THEN
1677
+ RAISE EXCEPTION 'automatic Knowledge activation was superseded' USING ERRCODE = '40001';
1678
+ END IF;
1679
+ service_actor := 'service:governed-learning-activation:' || input_hash_value;
1680
+ review_operation_id := opengeni_private.governed_learning_derived_uuid(
1681
+ p_operation_id, 'knowledge-revoke'
1682
+ );
1683
+ destination_operation_id := opengeni_private.governed_learning_derived_uuid(
1684
+ p_operation_id, 'destination-undo'
1685
+ );
1686
+
1687
+ IF activation_row.destination = 'instruction_policy' THEN
1688
+ SELECT head.* INTO instruction_head FROM workspace_instruction_policy_heads head
1689
+ JOIN workspace_instruction_policy_onboarding_proposals proposal
1690
+ ON proposal.account_id = head.account_id AND proposal.workspace_id = head.workspace_id
1691
+ AND proposal.id = activation_row.destination_proposal_id
1692
+ AND proposal.kind = head.kind AND proposal.scope = head.scope
1693
+ AND proposal.role_key IS NOT DISTINCT FROM head.role_key
1694
+ WHERE head.account_id = p_account_id AND head.workspace_id = p_workspace_id
1695
+ AND head.revision_id = activation_row.destination_revision_id
1696
+ AND head.content_hash = activation_row.destination_new_content_hash
1697
+ AND head.activation_version = activation_row.destination_new_version
1698
+ FOR UPDATE OF head;
1699
+ IF NOT FOUND THEN
1700
+ RAISE EXCEPTION 'automatic instruction-policy activation was superseded'
1701
+ USING ERRCODE = '40001';
1702
+ END IF;
1703
+ IF activation_row.destination_old_revision_id IS NULL THEN
1704
+ SELECT * INTO instruction_deactivation
1705
+ FROM governed_learning_deactivate_instruction_policy(
1706
+ p_account_id, p_workspace_id, destination_operation_id,
1707
+ activation_row.destination_proposal_id, activation_row.destination_revision_id,
1708
+ activation_row.destination_new_version, service_actor
1709
+ );
1710
+ destination_new_version_value := instruction_deactivation.activation_version;
1711
+ destination_event_id_value := instruction_deactivation.id;
1712
+ effective_at_value := instruction_deactivation.created_at;
1713
+ ELSE
1714
+ SELECT * INTO instruction_event FROM governed_learning_apply_instruction_policy(
1715
+ 'undo', p_account_id, p_workspace_id, destination_operation_id,
1716
+ activation_row.destination_proposal_id, activation_row.destination_revision_id,
1717
+ activation_row.destination_new_version, activation_row.destination_old_revision_id,
1718
+ service_actor
1719
+ );
1720
+ destination_new_version_value := instruction_event.activation_version;
1721
+ destination_event_id_value := instruction_event.id;
1722
+ effective_at_value := instruction_event.created_at;
1723
+ END IF;
1724
+ ELSIF activation_row.destination = 'preference' THEN
1725
+ SELECT * INTO preference_row FROM preference_registry_preferences preference
1726
+ WHERE preference.account_id = p_account_id AND preference.id = activation_row.destination_proposal_id
1727
+ AND preference.scope = 'workspace' AND preference.scope_workspace_id = p_workspace_id
1728
+ AND preference.status = 'active'
1729
+ AND preference.active_revision_id = activation_row.destination_revision_id
1730
+ AND preference.active_content_hash = activation_row.destination_new_content_hash
1731
+ AND preference.activation_version = activation_row.destination_new_version
1732
+ FOR UPDATE;
1733
+ IF NOT FOUND THEN
1734
+ RAISE EXCEPTION 'automatic preference activation was superseded' USING ERRCODE = '40001';
1735
+ END IF;
1736
+ SELECT * INTO preference_event FROM governed_learning_apply_preference(
1737
+ 'undo', p_account_id, p_workspace_id, destination_operation_id,
1738
+ activation_row.proposal_id, activation_row.destination_proposal_id,
1739
+ activation_row.destination_revision_id, activation_row.destination_revision_id,
1740
+ activation_row.destination_new_version::integer, service_actor
1741
+ );
1742
+ SELECT activation_version INTO destination_new_version_value
1743
+ FROM preference_registry_preferences WHERE id = activation_row.destination_proposal_id;
1744
+ destination_event_id_value := preference_event.id;
1745
+ effective_at_value := preference_event.created_at;
1746
+ ELSE
1747
+ RAISE EXCEPTION 'governed-learning activation destination is invalid' USING ERRCODE = '23514';
1748
+ END IF;
1749
+ SELECT * INTO revocation_row FROM governed_learning_apply_knowledge_review(
1750
+ p_account_id, p_workspace_id, review_operation_id, activation_row.claim_id,
1751
+ latest_review.id, latest_review.review_revision, 'revoked', service_actor,
1752
+ caller_subject_id
1753
+ );
1754
+ INSERT INTO governed_learning_activation_undo_receipts (
1755
+ account_id, workspace_id, operation_id, input_hash, activation_receipt_id,
1756
+ initiating_human_subject_id, service_actor_subject_id, destination,
1757
+ knowledge_approval_review_id, knowledge_revocation_review_id,
1758
+ knowledge_revocation_review_revision, knowledge_revocation_input_hash,
1759
+ destination_activated_revision_id, destination_restored_revision_id,
1760
+ destination_activated_content_hash, destination_restored_content_hash,
1761
+ destination_old_version, destination_new_version, destination_event_id,
1762
+ effective_at
1763
+ ) VALUES (
1764
+ p_account_id, p_workspace_id, p_operation_id, input_hash_value,
1765
+ activation_row.id, caller_subject_id, service_actor, activation_row.destination,
1766
+ activation_row.knowledge_approval_review_id, revocation_row.id,
1767
+ revocation_row.review_revision, revocation_row.input_hash,
1768
+ activation_row.destination_revision_id, activation_row.destination_old_revision_id,
1769
+ activation_row.destination_new_content_hash, activation_row.destination_old_content_hash,
1770
+ activation_row.destination_new_version, destination_new_version_value,
1771
+ destination_event_id_value, effective_at_value
1772
+ ) RETURNING * INTO result_row;
1773
+ RETURN NEXT result_row;
1774
+ END;
1775
+ $$;
1776
+ REVOKE ALL ON FUNCTION undo_governed_learning_activation(uuid, uuid, uuid, uuid) FROM PUBLIC;
1777
+
1778
+ -- Target-schema hardening is mandatory because runtime roles may have TEMP.
1779
+ DO $governed_learning_activation_hardening$
1780
+ DECLARE
1781
+ target_schema text := current_schema();
1782
+ runtime_role text := nullif(current_setting('opengeni.runtime_role', true), '');
1783
+ BEGIN
1784
+ IF runtime_role IS NULL AND EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
1785
+ runtime_role := 'opengeni_app';
1786
+ END IF;
1787
+ EXECUTE format(
1788
+ 'ALTER FUNCTION %I.governed_learning_reject_activation_receipt_mutation() '
1789
+ || 'SET search_path = pg_catalog, %I, pg_temp', target_schema, target_schema
1790
+ );
1791
+ EXECUTE format(
1792
+ 'ALTER FUNCTION %I.governed_learning_guard_service_review() '
1793
+ || 'SET search_path = pg_catalog, %I, pg_temp', target_schema, target_schema
1794
+ );
1795
+ EXECUTE format(
1796
+ 'ALTER FUNCTION %I.governed_learning_apply_knowledge_review('
1797
+ || 'uuid,uuid,uuid,uuid,uuid,bigint,text,text,text) '
1798
+ || 'SET search_path = pg_catalog, %I, pg_temp', target_schema, target_schema
1799
+ );
1800
+ EXECUTE format(
1801
+ 'ALTER FUNCTION %I.workspace_instruction_policy_reject_mutation() '
1802
+ || 'SET search_path = pg_catalog, %I, pg_temp', target_schema, target_schema
1803
+ );
1804
+ EXECUTE format(
1805
+ 'ALTER FUNCTION %I.workspace_instruction_policy_guard_lifecycle_operation() '
1806
+ || 'SET search_path = pg_catalog, %I, pg_temp', target_schema, target_schema
1807
+ );
1808
+ EXECUTE format(
1809
+ 'ALTER FUNCTION %I.workspace_instruction_policy_guard_head_delete() '
1810
+ || 'SET search_path = pg_catalog, %I, pg_temp', target_schema, target_schema
1811
+ );
1812
+ EXECUTE format(
1813
+ 'ALTER FUNCTION %I.workspace_instruction_policy_validate_head() '
1814
+ || 'SET search_path = pg_catalog, %I, pg_temp', target_schema, target_schema
1815
+ );
1816
+ EXECUTE format(
1817
+ 'ALTER FUNCTION %I.workspace_instruction_policy_validate_onboarding_proposal() '
1818
+ || 'SET search_path = pg_catalog, %I, pg_temp', target_schema, target_schema
1819
+ );
1820
+ EXECUTE format(
1821
+ 'ALTER FUNCTION %I.workspace_instruction_policy_validate_event() '
1822
+ || 'SET search_path = pg_catalog, %I, pg_temp', target_schema, target_schema
1823
+ );
1824
+ EXECUTE format(
1825
+ 'ALTER FUNCTION %I.workspace_instruction_policy_validate_deactivation_event() '
1826
+ || 'SET search_path = pg_catalog, %I, pg_temp', target_schema, target_schema
1827
+ );
1828
+ EXECUTE format(
1829
+ 'ALTER FUNCTION %I.governed_learning_apply_instruction_policy('
1830
+ || 'text,uuid,uuid,uuid,uuid,uuid,bigint,uuid,text) '
1831
+ || 'SET search_path = pg_catalog, %I, pg_temp', target_schema, target_schema
1832
+ );
1833
+ EXECUTE format(
1834
+ 'ALTER FUNCTION %I.governed_learning_deactivate_instruction_policy('
1835
+ || 'uuid,uuid,uuid,uuid,uuid,bigint,text) '
1836
+ || 'SET search_path = pg_catalog, %I, pg_temp', target_schema, target_schema
1837
+ );
1838
+ EXECUTE format(
1839
+ 'ALTER FUNCTION %I.workspace_instruction_policy_canonical_snapshot_entries('
1840
+ || 'uuid,uuid,text,timestamp with time zone) '
1841
+ || 'SET search_path = pg_catalog, %I, pg_temp', target_schema, target_schema
1842
+ );
1843
+ EXECUTE format(
1844
+ 'ALTER FUNCTION %I.governed_learning_apply_preference('
1845
+ || 'text,uuid,uuid,uuid,uuid,uuid,uuid,uuid,integer,text) '
1846
+ || 'SET search_path = pg_catalog, %I, pg_temp', target_schema, target_schema
1847
+ );
1848
+ EXECUTE format(
1849
+ 'ALTER FUNCTION %I.activate_governed_learning_decision(uuid,uuid,uuid,uuid) '
1850
+ || 'SET search_path = pg_catalog, %I, pg_temp', target_schema, target_schema
1851
+ );
1852
+ EXECUTE format(
1853
+ 'ALTER FUNCTION %I.undo_governed_learning_activation(uuid,uuid,uuid,uuid) '
1854
+ || 'SET search_path = pg_catalog, %I, pg_temp', target_schema, target_schema
1855
+ );
1856
+ EXECUTE format('REVOKE ALL ON TABLE %I.governed_learning_activation_receipts FROM PUBLIC', target_schema);
1857
+ EXECUTE format('REVOKE ALL ON TABLE %I.governed_learning_activation_undo_receipts FROM PUBLIC', target_schema);
1858
+ EXECUTE format('REVOKE ALL ON TABLE %I.workspace_instruction_policy_deactivation_events FROM PUBLIC', target_schema);
1859
+ IF runtime_role IS NOT NULL THEN
1860
+ EXECUTE format('REVOKE ALL ON TABLE %I.governed_learning_activation_receipts FROM %I', target_schema, runtime_role);
1861
+ EXECUTE format('REVOKE ALL ON TABLE %I.governed_learning_activation_undo_receipts FROM %I', target_schema, runtime_role);
1862
+ EXECUTE format('REVOKE ALL ON TABLE %I.workspace_instruction_policy_deactivation_events FROM %I', target_schema, runtime_role);
1863
+ EXECUTE format('GRANT SELECT ON TABLE %I.workspace_instruction_policy_deactivation_events TO %I', target_schema, runtime_role);
1864
+ EXECUTE format(
1865
+ 'GRANT EXECUTE ON FUNCTION %I.activate_governed_learning_decision(uuid,uuid,uuid,uuid) TO %I',
1866
+ target_schema, runtime_role
1867
+ );
1868
+ EXECUTE format(
1869
+ 'GRANT EXECUTE ON FUNCTION %I.undo_governed_learning_activation(uuid,uuid,uuid,uuid) TO %I',
1870
+ target_schema, runtime_role
1871
+ );
1872
+ END IF;
1873
+ END
1874
+ $governed_learning_activation_hardening$;