@opengeni/db 0.36.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 (274) 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-GYO7ED2Q.js → chunk-EULAN2NP.js} +2 -2
  7. package/dist/{chunk-VTPRIXKG.js → chunk-NJVZPWXY.js} +3 -3
  8. package/dist/{chunk-HSOWSYOX.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-5KTIEDX7.js → chunk-PR2WMLOQ.js} +7714 -4443
  13. package/dist/chunk-PR2WMLOQ.js.map +1 -0
  14. package/dist/{chunk-Z7VYMNPZ.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.d.ts +10 -0
  36. package/dist/editable-artifact-durable-export.js +18 -3
  37. package/dist/editable-artifact-durable-export.js.map +1 -1
  38. package/dist/editable-artifacts.d.ts +2 -2
  39. package/dist/editable-artifacts.js +3 -3
  40. package/dist/governed-learning-activation-schema.d.ts +984 -0
  41. package/dist/governed-learning-activation.d.ts +98 -0
  42. package/dist/governed-learning-evaluator-schema.d.ts +633 -0
  43. package/dist/governed-learning-evaluator.d.ts +28 -0
  44. package/dist/index.d.ts +1633 -87
  45. package/dist/index.js +22004 -8109
  46. package/dist/index.js.map +1 -1
  47. package/dist/insights.d.ts +18 -0
  48. package/dist/integration-bindings.d.ts +1 -0
  49. package/dist/integration-facets.d.ts +30 -0
  50. package/dist/interaction-placement-loss.d.ts +22 -0
  51. package/dist/knowledge-source-sync-schema.d.ts +730 -0
  52. package/dist/knowledge-source-sync.d.ts +60 -0
  53. package/dist/managed-human-provisioning.d.ts +25 -0
  54. package/dist/memory-domain.d.ts +3 -3
  55. package/dist/migrate.d.ts +11 -1
  56. package/dist/migrate.js +1 -1
  57. package/dist/new-session-drafts.d.ts +43 -5
  58. package/dist/organization-membership-backfill.d.ts +174 -0
  59. package/dist/organization-membership-lifecycle.d.ts +162 -0
  60. package/dist/pr-review.d.ts +107 -0
  61. package/dist/preference-registry-schema.d.ts +2 -0
  62. package/dist/preference-registry.d.ts +2 -0
  63. package/dist/provision-roles.js +1 -1
  64. package/dist/runtime-posture.d.ts +23 -9
  65. package/dist/schema.d.ts +24925 -13697
  66. package/dist/schema.js +103 -1
  67. package/dist/scoped-knowledge-schema.d.ts +52 -1
  68. package/dist/session-background-commands.d.ts +125 -0
  69. package/dist/session-background-commands.js +34 -0
  70. package/dist/session-background-commands.js.map +1 -0
  71. package/dist/session-control.d.ts +156 -3
  72. package/dist/session-goal-pacing.d.ts +68 -0
  73. package/dist/session-queue-commands.d.ts +42 -5
  74. package/dist/session-realtime-ledger.d.ts +3 -0
  75. package/dist/session-tenancy.d.ts +71 -1
  76. package/dist/session-tenancy.js +25 -3
  77. package/dist/session-tool-call-settlement.d.ts +9 -4
  78. package/dist/slack-routing-personal-workspace.d.ts +23 -0
  79. package/dist/slack-routing.d.ts +153 -0
  80. package/dist/slack-task-policy-schema.d.ts +34 -0
  81. package/dist/slack-task-policy.d.ts +8 -0
  82. package/dist/slack-user-link-access.d.ts +1 -0
  83. package/dist/task-notes-schema.d.ts +594 -0
  84. package/dist/task-notes.d.ts +39 -0
  85. package/dist/turn-initiator.d.ts +2 -0
  86. package/dist/user-resource-authority.d.ts +109 -0
  87. package/dist/video-generation.js +3 -3
  88. package/dist/workspace-authority.d.ts +81 -0
  89. package/dist/workspace-instruction-policies-schema.d.ts +279 -0
  90. package/dist/workspace-instruction-policies.d.ts +62 -0
  91. package/dist/workspace-learning-policy.d.ts +1 -0
  92. package/dist/workspace-membership-permissions.d.ts +2 -0
  93. package/drizzle/0240_enrollment_connection_authority.sql +39 -0
  94. package/drizzle/0240_model_context_user_messages.sql +204 -0
  95. package/drizzle/0241_atomic_personal_resource_delegation.sql +1000 -0
  96. package/drizzle/0241_enrollment_agent_runtime.sql +70 -0
  97. package/drizzle/0243_google_drive_object_acl_authority.sql +554 -0
  98. package/drizzle/0244_slack_app_home_refresh_queue.sql +120 -0
  99. package/drizzle/0245_model_context_contribution_facts.sql +88 -0
  100. package/drizzle/0246_integration_personal_instance_authority.sql +70 -0
  101. package/drizzle/0247_terraform_stacks_provenance_repair.sql +428 -0
  102. package/drizzle/0248_terraform_stacks_component_resolution_fence.sql +83 -0
  103. package/drizzle/0249_personal_resource_delegation_authority_correction.sql +662 -0
  104. package/drizzle/0250_direct_retained_process_owner_liveness.sql +199 -0
  105. package/drizzle/0251_connected_machine_operation_policy.sql +45 -0
  106. package/drizzle/0252_scheduled_personal_resource_delegation.sql +1867 -0
  107. package/drizzle/0253_common_user_resource_authority_lifecycle.sql +1555 -0
  108. package/drizzle/0254_scoped_variable_set_authority.sql +1255 -0
  109. package/drizzle/0255_company_brain_governed_write_proposals.sql +524 -0
  110. package/drizzle/0256_connection_authority_delegation.sql +703 -0
  111. package/drizzle/0257_goal_revision_decisions_and_root_constraints.sql +436 -0
  112. package/drizzle/0258_three_scope_document_knowledge_authority.sql +1273 -0
  113. package/drizzle/0259_company_brain_context_selection_receipts.sql +869 -0
  114. package/drizzle/0260_task_note_knowledge_promotion.sql +820 -0
  115. package/drizzle/0261_preference_knowledge_proposal_actor_binding.sql +471 -0
  116. package/drizzle/0262_scoped_connected_machines_and_rigs.sql +1700 -0
  117. package/drizzle/0263_organization_membership_lifecycle.sql +3284 -0
  118. package/drizzle/0264_connection_authority_runtime_activation.sql +1084 -0
  119. package/drizzle/0266_company_brain_context_receipt_inspection.sql +156 -0
  120. package/drizzle/0268_governed_learning_decision_receipts.sql +825 -0
  121. package/drizzle/0269_governed_learning_activation_controller.sql +1874 -0
  122. package/drizzle/0270_governed_learning_history_inspection.sql +170 -0
  123. package/drizzle/0271_company_brain_retrieval_only_default.sql +524 -0
  124. package/drizzle/0272_human_confirmed_learning_activation.sql +591 -0
  125. package/drizzle/0273_scheduled_variable_set_materialization.sql +268 -0
  126. package/drizzle/0274_human_confirmed_knowledge_review.sql +310 -0
  127. package/drizzle/0275_scheduled_connection_authority.sql +5527 -0
  128. package/drizzle/0276_onboarding_proposal_initiating_human_guc.sql +198 -0
  129. package/drizzle/0277_workspace_writer_authority_attribution.sql +393 -0
  130. package/drizzle/0278_workspace_membership_removal_fencing.sql +1174 -0
  131. package/drizzle/0279_workspace_connection_use_lane.sql +343 -0
  132. package/drizzle/0280_connection_and_variable_set_audit_attribution.sql +773 -0
  133. package/drizzle/0281_viewer_holder_authority_claims.sql +52 -0
  134. package/drizzle/0282_variable_set_session_attach_attribution.sql +178 -0
  135. package/drizzle/0283_editable_spreadsheet_authored_state.sql +82 -0
  136. package/drizzle/0284_truthful_human_confirmed_review_reason.sql +930 -0
  137. package/drizzle/0285_organization_tenancy_inventory.sql +329 -0
  138. package/drizzle/0286_widen_task_note_expiry_ceiling.sql +391 -0
  139. package/drizzle/0287_open_suffix_pending_tool_calls.sql +18 -0
  140. package/drizzle/0288_attached_browser_reenrollment.sql +64 -0
  141. package/drizzle/0289_session_composer_policy_authority.sql +597 -0
  142. package/drizzle/0290_organization_membership_backfill.sql +268 -0
  143. package/drizzle/0291_resource_authority_classification_assertion.sql +371 -0
  144. package/drizzle/0292_truthful_tenancy_inventory_counters.sql +291 -0
  145. package/drizzle/0293_confirm_time_rule_rebaseline.sql +615 -0
  146. package/drizzle/0294_preference_activation_authority.sql +237 -0
  147. package/drizzle/0295_retire_legacy_standing_memory_mode.sql +476 -0
  148. package/drizzle/0296_force_rls_backfill_noop_repair.sql +215 -0
  149. package/drizzle/0297_session_ownership_classification_and_backfill.sql +752 -0
  150. package/drizzle/0298_organization_tenancy_parity.sql +609 -0
  151. package/drizzle/0299_organization_membership_lock_order.sql +291 -0
  152. package/drizzle/0300_tenancy_backfill_ledger.sql +459 -0
  153. package/drizzle/0301_session_snapshot_and_pin_visibility.sql +181 -0
  154. package/drizzle/0302_personal_workspace_session_ownership.sql +303 -0
  155. package/drizzle/0303_session_tenancy_product_activation.sql +919 -0
  156. package/drizzle/0304_personal_workspace_private_session_reads.sql +97 -0
  157. package/drizzle/0305_personal_resource_grant_management.sql +584 -0
  158. package/drizzle/0306_atomic_personal_resource_attachments.sql +1374 -0
  159. package/drizzle/0307_session_attention_state.sql +51 -0
  160. package/drizzle/0308_session_archives.sql +35 -0
  161. package/drizzle/0309_channel_project_pins.sql +6 -0
  162. package/drizzle/0310_channel_project_order.sql +27 -0
  163. package/drizzle/0311_company_scope_and_private_session_create.sql +656 -0
  164. package/drizzle/0312_quiescent_session_tree_deletion.sql +39 -0
  165. package/drizzle/0313_private_child_session_authority.sql +365 -0
  166. package/drizzle/0314_unregistered_organization_invitations.sql +606 -0
  167. package/drizzle/0315_personal_github_repository_selection.sql +624 -0
  168. package/drizzle/0316_human_confirmed_activation_resumed_generation.sql +869 -0
  169. package/drizzle/0317_session_goal_continuation_hold.sql +64 -0
  170. package/drizzle/0318_session_turn_startup_milestones.sql +115 -0
  171. package/drizzle/0319_event_triggered_automations.sql +254 -0
  172. package/drizzle/0320_pr_review_pack.sql +113 -0
  173. package/drizzle/0321_slack_bot_environment_display_name.sql +15 -0
  174. package/drizzle/0322_session_turns_unclaimed_prompt_trigger_index.sql +5 -0
  175. package/drizzle/0323_organization_private_session_enablement.sql +489 -0
  176. package/drizzle/0324_human_confirmed_company_profile_agent_admin.sql +856 -0
  177. package/drizzle/0325_child_lifecycle_notices.sql +44 -0
  178. package/drizzle/0326_interaction_operation_error_codes.sql +62 -0
  179. package/drizzle/0327_slack_first_task_hint.sql +29 -0
  180. package/drizzle/0328_session_background_commands.sql +590 -0
  181. package/drizzle/0329_slack_orchestration_delivery_events.sql +91 -0
  182. package/drizzle/0330_api_key_descriptions.sql +20 -0
  183. package/drizzle/0331_managed_organization_creation.sql +163 -0
  184. package/drizzle/0332_organization_shared_workspace_control_plane.sql +539 -0
  185. package/drizzle/0333_session_turn_prompt_routing.sql +45 -0
  186. package/drizzle/0334_connected_machine_workspace_root.sql +99 -0
  187. package/drizzle/0335_slack_workspace_routing.sql +460 -0
  188. package/drizzle/0336_atomic_session_fork_visibility.sql +667 -0
  189. package/drizzle/0337_slack_routed_action_handles.sql +189 -0
  190. package/drizzle/0338_atomic_connected_machine_attachments.sql +410 -0
  191. package/drizzle/0339_document_authority_reclassification.sql +1153 -0
  192. package/drizzle/0340_tenancy_backfill_activation_evidence.sql +1347 -0
  193. package/drizzle/0341_slack_routing_probe_organization_fence.sql +109 -0
  194. package/package.json +10 -6
  195. package/src/attached-browser-devices.ts +233 -5
  196. package/src/automations.ts +911 -0
  197. package/src/browser-sessions.ts +10 -0
  198. package/src/capability-integrations.ts +175 -1
  199. package/src/child-lifecycle-notices.ts +241 -0
  200. package/src/codemode-operations.ts +1 -35
  201. package/src/company-brain-context-selection-schema.ts +100 -0
  202. package/src/company-brain-context-selection.ts +359 -0
  203. package/src/company-brain-governed-writes.ts +1170 -0
  204. package/src/company-brain.ts +309 -0
  205. package/src/company-profile-agent-admin.ts +225 -0
  206. package/src/company-profile-schema.ts +121 -1
  207. package/src/company-profile.ts +21 -0
  208. package/src/computer-sessions.ts +10 -1
  209. package/src/connection-authority.ts +290 -0
  210. package/src/connection-scopes.ts +20 -0
  211. package/src/connection-tenancy-backfill.ts +81 -0
  212. package/src/connection-token-resolver.ts +263 -31
  213. package/src/database.ts +75 -2
  214. package/src/editable-artifact-durable-export.ts +27 -0
  215. package/src/editable-artifacts-schema.ts +10 -5
  216. package/src/editable-artifacts.ts +21 -11
  217. package/src/governed-learning-activation-schema.ts +121 -0
  218. package/src/governed-learning-activation.ts +588 -0
  219. package/src/governed-learning-evaluator-schema.ts +72 -0
  220. package/src/governed-learning-evaluator.ts +217 -0
  221. package/src/index.ts +12001 -1699
  222. package/src/insights.ts +107 -0
  223. package/src/integration-bindings.ts +88 -6
  224. package/src/integration-facets.ts +287 -84
  225. package/src/interaction-placement-loss.ts +234 -0
  226. package/src/knowledge-source-sync-schema.ts +83 -0
  227. package/src/knowledge-source-sync.ts +530 -0
  228. package/src/managed-human-provisioning.ts +116 -0
  229. package/src/memory-domain.ts +27 -32
  230. package/src/migrate.ts +77 -1
  231. package/src/new-session-drafts.ts +229 -35
  232. package/src/organization-membership-backfill.ts +735 -0
  233. package/src/organization-membership-lifecycle.ts +985 -0
  234. package/src/pr-review.ts +890 -0
  235. package/src/preference-registry.ts +31 -1
  236. package/src/provision-roles.ts +931 -24
  237. package/src/runtime-posture-cli.ts +2 -0
  238. package/src/runtime-posture.ts +1315 -52
  239. package/src/schema.ts +3336 -438
  240. package/src/scoped-knowledge-schema.ts +16 -8
  241. package/src/scoped-knowledge.ts +1 -1
  242. package/src/session-background-commands.ts +759 -0
  243. package/src/session-control.ts +640 -74
  244. package/src/session-goal-pacing.ts +126 -0
  245. package/src/session-queue-commands.ts +450 -98
  246. package/src/session-realtime-context.ts +19 -3
  247. package/src/session-realtime-ledger.ts +75 -12
  248. package/src/session-realtime-mirror.ts +1 -1
  249. package/src/session-tenancy.ts +403 -49
  250. package/src/session-tool-call-settlement.ts +38 -29
  251. package/src/slack-routing-personal-workspace.ts +67 -0
  252. package/src/slack-routing.ts +469 -0
  253. package/src/slack-task-policy-schema.ts +5 -0
  254. package/src/slack-task-policy.ts +18 -0
  255. package/src/slack-user-link-access.ts +17 -1
  256. package/src/task-notes-schema.ts +74 -0
  257. package/src/task-notes.ts +124 -2
  258. package/src/turn-initiator.ts +8 -0
  259. package/src/user-resource-authority.ts +377 -0
  260. package/src/workspace-authority.ts +160 -0
  261. package/src/workspace-instruction-policies-schema.ts +53 -1
  262. package/src/workspace-instruction-policies.ts +528 -77
  263. package/src/workspace-learning-policy.ts +21 -5
  264. package/src/workspace-membership-permissions.ts +9 -0
  265. package/dist/chunk-5KTIEDX7.js.map +0 -1
  266. package/dist/chunk-FPVAD4CO.js +0 -117
  267. package/dist/chunk-FPVAD4CO.js.map +0 -1
  268. package/dist/chunk-HSOWSYOX.js.map +0 -1
  269. package/dist/chunk-SK7YYHBI.js.map +0 -1
  270. package/dist/chunk-VKLB2ZGI.js +0 -2389
  271. package/dist/chunk-VKLB2ZGI.js.map +0 -1
  272. package/dist/chunk-Z7VYMNPZ.js.map +0 -1
  273. /package/dist/{chunk-GYO7ED2Q.js.map → chunk-EULAN2NP.js.map} +0 -0
  274. /package/dist/{chunk-VTPRIXKG.js.map → chunk-NJVZPWXY.js.map} +0 -0
@@ -0,0 +1,656 @@
1
+ -- deployment-mode: rolling
2
+ -- Activates the bounded organization administration projection and the atomic
3
+ -- private-session create seam. Existing session creation remains workspace-
4
+ -- shared unless an exact managed-human caller explicitly requests privacy.
5
+
6
+ SET LOCAL lock_timeout = '5s';
7
+ SET LOCAL statement_timeout = '10min';
8
+
9
+ CREATE TABLE organization_profile_events (
10
+ id uuid PRIMARY KEY,
11
+ account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
12
+ actor_membership_id uuid NOT NULL,
13
+ previous_name text NOT NULL,
14
+ requested_name text NOT NULL,
15
+ expected_updated_at timestamptz NOT NULL,
16
+ result_updated_at timestamptz NOT NULL,
17
+ created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
18
+ CONSTRAINT organization_profile_events_actor_fk
19
+ FOREIGN KEY (actor_membership_id, account_id)
20
+ REFERENCES organization_memberships(id, account_id) ON DELETE RESTRICT,
21
+ CONSTRAINT organization_profile_events_name_check CHECK (
22
+ octet_length(btrim(requested_name)) BETWEEN 1 AND 480
23
+ )
24
+ );
25
+ ALTER TABLE organization_profile_events ENABLE ROW LEVEL SECURITY;
26
+ ALTER TABLE organization_profile_events FORCE ROW LEVEL SECURITY;
27
+ REVOKE ALL ON TABLE organization_profile_events FROM PUBLIC;
28
+ CREATE POLICY organization_tenancy_lifecycle ON organization_profile_events
29
+ USING (current_setting('opengeni.organization_tenancy_lifecycle', true)
30
+ = 'organization_membership_lifecycle')
31
+ WITH CHECK (current_setting('opengeni.organization_tenancy_lifecycle', true)
32
+ = 'organization_membership_lifecycle');
33
+
34
+ ALTER TABLE sessions
35
+ ADD COLUMN create_requested_visibility text NOT NULL DEFAULT 'workspace_shared';
36
+ ALTER TABLE sessions
37
+ ADD CONSTRAINT sessions_create_requested_visibility_check
38
+ CHECK (create_requested_visibility IN ('user_private', 'workspace_shared'));
39
+
40
+ -- Private creation is intentionally separate from the generic visibility
41
+ -- lifecycle capability. The application role can ask the definer to mint one
42
+ -- exact, transaction-local INSERT authority, but cannot use it for UPDATE or
43
+ -- a second session.
44
+ CREATE TABLE private_session_create_capabilities (
45
+ backend_pid integer NOT NULL,
46
+ transaction_id xid8 NOT NULL,
47
+ capability_id uuid NOT NULL,
48
+ account_id uuid NOT NULL,
49
+ workspace_id uuid NOT NULL,
50
+ session_id uuid NOT NULL,
51
+ actor_subject_id text NOT NULL,
52
+ owner_membership_id uuid NOT NULL,
53
+ PRIMARY KEY (backend_pid, transaction_id, capability_id)
54
+ );
55
+ ALTER TABLE private_session_create_capabilities ENABLE ROW LEVEL SECURITY;
56
+ ALTER TABLE private_session_create_capabilities FORCE ROW LEVEL SECURITY;
57
+ REVOKE ALL ON TABLE private_session_create_capabilities FROM PUBLIC;
58
+ CREATE POLICY private_session_create_capability_owner
59
+ ON private_session_create_capabilities
60
+ USING (current_setting('opengeni.private_session_create_lifecycle', true)
61
+ = 'private_session_create')
62
+ WITH CHECK (current_setting('opengeni.private_session_create_lifecycle', true)
63
+ = 'private_session_create');
64
+
65
+ CREATE FUNCTION get_organization_administration_overview(
66
+ p_account_id uuid,
67
+ p_actor_subject_id text
68
+ ) RETURNS jsonb
69
+ LANGUAGE plpgsql SECURITY DEFINER
70
+ SET search_path FROM CURRENT
71
+ AS $body$
72
+ DECLARE
73
+ actor organization_memberships%ROWTYPE;
74
+ result jsonb;
75
+ workspace_count integer;
76
+ oversized_workspace boolean;
77
+ BEGIN
78
+ IF p_account_id IS NULL
79
+ OR p_actor_subject_id IS DISTINCT FROM opengeni_private.current_subject_id()
80
+ OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
81
+ THEN
82
+ RAISE EXCEPTION 'organization administration authority required' USING ERRCODE = '42501';
83
+ END IF;
84
+ PERFORM pg_catalog.set_config(
85
+ 'opengeni.organization_tenancy_lifecycle',
86
+ 'organization_membership_lifecycle', true
87
+ );
88
+ PERFORM 1 FROM managed_accounts account
89
+ WHERE account.id = p_account_id FOR SHARE;
90
+ IF NOT FOUND THEN RAISE EXCEPTION 'organization not found' USING ERRCODE = 'P0002'; END IF;
91
+ SELECT * INTO actor FROM organization_memberships membership
92
+ WHERE membership.account_id = p_account_id
93
+ AND membership.subject_id = p_actor_subject_id
94
+ FOR SHARE;
95
+ IF NOT FOUND OR actor.status <> 'active' OR actor.role NOT IN ('owner', 'admin') THEN
96
+ RAISE EXCEPTION 'organization administration required' USING ERRCODE = '42501';
97
+ END IF;
98
+ WITH shared_workspaces AS MATERIALIZED (
99
+ SELECT workspace.*,
100
+ (SELECT count(*)::integer FROM workspace_memberships access
101
+ WHERE access.workspace_id = workspace.id) AS member_count
102
+ FROM workspaces workspace
103
+ WHERE workspace.account_id = p_account_id
104
+ AND NOT EXISTS (
105
+ SELECT 1 FROM organization_memberships membership
106
+ WHERE membership.account_id = p_account_id
107
+ AND membership.personal_workspace_id = workspace.id
108
+ )
109
+ ),
110
+ bounds AS (
111
+ SELECT count(*)::integer AS workspace_count,
112
+ coalesce(bool_or(shared.member_count > 1000), false) AS oversized_workspace
113
+ FROM shared_workspaces shared
114
+ )
115
+ SELECT bounds.workspace_count, bounds.oversized_workspace,
116
+ CASE
117
+ WHEN bounds.workspace_count <= 500 AND NOT bounds.oversized_workspace
118
+ THEN pg_catalog.jsonb_build_object(
119
+ 'organization', pg_catalog.jsonb_build_object(
120
+ 'id', account.id,
121
+ 'name', account.name,
122
+ 'createdAt', account.created_at,
123
+ 'updatedAt', account.updated_at
124
+ ),
125
+ 'workspaces', coalesce((
126
+ SELECT pg_catalog.jsonb_agg(
127
+ pg_catalog.jsonb_build_object(
128
+ 'id', workspace.id,
129
+ 'name', workspace.name,
130
+ 'slug', workspace.slug,
131
+ 'createdAt', workspace.created_at,
132
+ 'updatedAt', workspace.updated_at,
133
+ 'members', coalesce((
134
+ SELECT pg_catalog.jsonb_agg(
135
+ pg_catalog.jsonb_build_object(
136
+ 'membershipId', access.id,
137
+ 'subjectId', access.subject_id,
138
+ 'subjectLabel', access.subject_label,
139
+ 'principalKind', CASE WHEN access.subject_id LIKE 'user:%'
140
+ THEN 'human' ELSE 'service' END,
141
+ 'role', access.role,
142
+ 'permissions', access.permissions,
143
+ 'createdAt', access.created_at
144
+ ) ORDER BY coalesce(access.subject_label, access.subject_id), access.id
145
+ ) FROM workspace_memberships access
146
+ WHERE access.workspace_id = workspace.id
147
+ ), '[]'::jsonb)
148
+ ) ORDER BY lower(workspace.name), workspace.id
149
+ ) FROM shared_workspaces workspace
150
+ ), '[]'::jsonb)
151
+ )
152
+ ELSE NULL
153
+ END
154
+ INTO workspace_count, oversized_workspace, result
155
+ FROM managed_accounts account CROSS JOIN bounds
156
+ WHERE account.id = p_account_id;
157
+ IF workspace_count > 500 THEN
158
+ RAISE EXCEPTION 'organization workspace inventory exceeds the bounded projection'
159
+ USING ERRCODE = '54000';
160
+ END IF;
161
+ IF oversized_workspace THEN
162
+ RAISE EXCEPTION 'workspace access inventory exceeds the bounded projection'
163
+ USING ERRCODE = '54000';
164
+ END IF;
165
+ RETURN result;
166
+ END
167
+ $body$;
168
+
169
+ CREATE FUNCTION update_organization_name(
170
+ p_account_id uuid,
171
+ p_actor_subject_id text,
172
+ p_name text,
173
+ p_expected_updated_at timestamptz,
174
+ p_operation_id uuid
175
+ ) RETURNS jsonb
176
+ LANGUAGE plpgsql SECURITY DEFINER
177
+ SET search_path FROM CURRENT
178
+ AS $body$
179
+ DECLARE
180
+ actor organization_memberships%ROWTYPE;
181
+ account managed_accounts%ROWTYPE;
182
+ prior organization_profile_events%ROWTYPE;
183
+ normalized_name text := btrim(p_name);
184
+ previous_name_value text;
185
+ BEGIN
186
+ IF p_account_id IS NULL OR p_name IS NULL OR p_operation_id IS NULL
187
+ OR p_expected_updated_at IS NULL
188
+ OR p_actor_subject_id IS DISTINCT FROM opengeni_private.current_subject_id()
189
+ OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
190
+ OR char_length(normalized_name) NOT BETWEEN 1 AND 120
191
+ OR octet_length(normalized_name) > 480
192
+ THEN
193
+ RAISE EXCEPTION 'organization name request is invalid' USING ERRCODE = '22023';
194
+ END IF;
195
+ PERFORM pg_catalog.set_config(
196
+ 'opengeni.organization_tenancy_lifecycle',
197
+ 'organization_membership_lifecycle', true
198
+ );
199
+ SELECT * INTO account FROM managed_accounts candidate
200
+ WHERE candidate.id = p_account_id FOR UPDATE;
201
+ IF NOT FOUND THEN RAISE EXCEPTION 'organization not found' USING ERRCODE = 'P0002'; END IF;
202
+ SELECT * INTO actor FROM organization_memberships membership
203
+ WHERE membership.account_id = p_account_id
204
+ AND membership.subject_id = p_actor_subject_id
205
+ FOR SHARE;
206
+ IF NOT FOUND OR actor.status <> 'active' OR actor.role NOT IN ('owner', 'admin') THEN
207
+ RAISE EXCEPTION 'organization administration required' USING ERRCODE = '42501';
208
+ END IF;
209
+ SELECT * INTO prior FROM organization_profile_events event
210
+ WHERE event.id = p_operation_id;
211
+ IF FOUND THEN
212
+ IF prior.account_id IS DISTINCT FROM p_account_id
213
+ OR prior.requested_name IS DISTINCT FROM normalized_name
214
+ OR prior.expected_updated_at IS DISTINCT FROM p_expected_updated_at
215
+ THEN
216
+ RAISE EXCEPTION 'organization name operation key was reused'
217
+ USING ERRCODE = '23505';
218
+ END IF;
219
+ account.name := prior.requested_name;
220
+ account.updated_at := prior.result_updated_at;
221
+ ELSE
222
+ IF account.updated_at IS DISTINCT FROM p_expected_updated_at THEN
223
+ RAISE EXCEPTION 'organization changed before rename' USING ERRCODE = '40001';
224
+ END IF;
225
+ previous_name_value := account.name;
226
+ UPDATE managed_accounts SET name = normalized_name, updated_at = clock_timestamp()
227
+ WHERE id = p_account_id RETURNING * INTO account;
228
+ INSERT INTO organization_profile_events (
229
+ id, account_id, actor_membership_id, previous_name, requested_name,
230
+ expected_updated_at, result_updated_at
231
+ ) VALUES (
232
+ p_operation_id, p_account_id, actor.id, previous_name_value, normalized_name,
233
+ p_expected_updated_at, account.updated_at
234
+ );
235
+ END IF;
236
+ RETURN pg_catalog.jsonb_build_object(
237
+ 'id', account.id,
238
+ 'name', account.name,
239
+ 'createdAt', account.created_at,
240
+ 'updatedAt', account.updated_at
241
+ );
242
+ END
243
+ $body$;
244
+
245
+ CREATE FUNCTION open_private_session_create_capability(
246
+ p_account_id uuid,
247
+ p_workspace_id uuid,
248
+ p_session_id uuid,
249
+ p_actor_subject_id text
250
+ ) RETURNS TABLE (capability_id uuid, owner_membership_id uuid)
251
+ LANGUAGE plpgsql SECURITY DEFINER
252
+ SET search_path FROM CURRENT
253
+ AS $body$
254
+ DECLARE
255
+ new_capability_id uuid := gen_random_uuid();
256
+ actor_membership_id uuid;
257
+ actor_personal_workspace boolean;
258
+ workspace_access_id uuid;
259
+ BEGIN
260
+ IF p_account_id IS NULL OR p_workspace_id IS NULL OR p_session_id IS NULL
261
+ OR p_actor_subject_id IS NULL OR p_actor_subject_id NOT LIKE 'user:%'
262
+ OR p_actor_subject_id IS DISTINCT FROM opengeni_private.current_subject_id()
263
+ OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
264
+ OR p_workspace_id IS DISTINCT FROM opengeni_private.current_workspace_id()
265
+ OR NOT session_tenancy_product_activated(p_account_id, 1)
266
+ THEN
267
+ RAISE EXCEPTION 'private session create authority required' USING ERRCODE = '42501';
268
+ END IF;
269
+ -- Match the organization lifecycle's workspace -> membership prefix so
270
+ -- suspension/offboarding cannot pass between authorization and INSERT.
271
+ PERFORM 1 FROM workspace_inference_controls control
272
+ WHERE control.account_id = p_account_id AND control.workspace_id = p_workspace_id
273
+ FOR SHARE;
274
+ PERFORM 1 FROM workspaces workspace
275
+ WHERE workspace.account_id = p_account_id AND workspace.id = p_workspace_id
276
+ FOR KEY SHARE;
277
+ SELECT membership.id, membership.personal_workspace_id = p_workspace_id
278
+ INTO actor_membership_id, actor_personal_workspace
279
+ FROM organization_memberships membership
280
+ WHERE membership.account_id = p_account_id
281
+ AND membership.subject_id = p_actor_subject_id
282
+ AND membership.status = 'active'
283
+ FOR KEY SHARE;
284
+ IF NOT FOUND THEN
285
+ RAISE EXCEPTION 'private session create authority required' USING ERRCODE = '42501';
286
+ END IF;
287
+ -- An ordinary workspace's exact access row is authority, not just evidence.
288
+ -- Lock it after the canonical workspace + organization-membership prefix so
289
+ -- workspace-membership removal cannot miss an uncommitted private session,
290
+ -- delete access, and let this transaction commit from an older snapshot.
291
+ IF NOT actor_personal_workspace THEN
292
+ SELECT access.id INTO workspace_access_id
293
+ FROM workspace_memberships access
294
+ WHERE access.account_id = p_account_id
295
+ AND access.workspace_id = p_workspace_id
296
+ AND access.subject_id = p_actor_subject_id
297
+ FOR KEY SHARE;
298
+ IF NOT FOUND THEN
299
+ RAISE EXCEPTION 'private session create authority required' USING ERRCODE = '42501';
300
+ END IF;
301
+ END IF;
302
+ PERFORM pg_catalog.set_config(
303
+ 'opengeni.private_session_create_lifecycle', 'private_session_create', true
304
+ );
305
+ INSERT INTO private_session_create_capabilities (
306
+ backend_pid, transaction_id, capability_id, account_id, workspace_id,
307
+ session_id, actor_subject_id, owner_membership_id
308
+ ) VALUES (
309
+ pg_backend_pid(), pg_current_xact_id(), new_capability_id, p_account_id,
310
+ p_workspace_id, p_session_id, p_actor_subject_id, actor_membership_id
311
+ );
312
+ PERFORM pg_catalog.set_config(
313
+ 'opengeni.private_session_create_capability', new_capability_id::text, true
314
+ );
315
+ capability_id := new_capability_id;
316
+ owner_membership_id := actor_membership_id;
317
+ RETURN NEXT;
318
+ END
319
+ $body$;
320
+
321
+ CREATE FUNCTION close_private_session_create_capability(p_capability_id uuid)
322
+ RETURNS void
323
+ LANGUAGE plpgsql SECURITY DEFINER
324
+ SET search_path FROM CURRENT
325
+ AS $body$
326
+ BEGIN
327
+ PERFORM pg_catalog.set_config(
328
+ 'opengeni.private_session_create_lifecycle', 'private_session_create', true
329
+ );
330
+ DELETE FROM private_session_create_capabilities capability
331
+ WHERE capability.backend_pid = pg_backend_pid()
332
+ AND capability.transaction_id = pg_current_xact_id()
333
+ AND capability.capability_id = p_capability_id;
334
+ PERFORM pg_catalog.set_config('opengeni.private_session_create_capability', '', true);
335
+ END
336
+ $body$;
337
+
338
+ CREATE FUNCTION fence_session_create_requested_visibility_update()
339
+ RETURNS trigger
340
+ LANGUAGE plpgsql SECURITY DEFINER
341
+ SET search_path FROM CURRENT
342
+ AS $body$
343
+ BEGIN
344
+ IF NEW.create_requested_visibility IS DISTINCT FROM OLD.create_requested_visibility THEN
345
+ RAISE EXCEPTION 'session requested visibility is immutable' USING ERRCODE = '42501';
346
+ END IF;
347
+ RETURN NEW;
348
+ END
349
+ $body$;
350
+
351
+ CREATE TRIGGER sessions_create_requested_visibility_immutable
352
+ BEFORE UPDATE ON sessions
353
+ FOR EACH ROW EXECUTE FUNCTION fence_session_create_requested_visibility_update();
354
+
355
+ REVOKE ALL ON FUNCTION get_organization_administration_overview(uuid,text) FROM PUBLIC;
356
+
357
+ DO $private_session_create_guard$
358
+ DECLARE
359
+ data_schema text := current_schema();
360
+ BEGIN
361
+ EXECUTE format($ddl$
362
+ CREATE OR REPLACE FUNCTION %1$I.guard_session_authority_write()
363
+ RETURNS trigger
364
+ LANGUAGE plpgsql
365
+ SECURITY DEFINER
366
+ SET search_path = %1$I, pg_catalog
367
+ AS $body$
368
+ DECLARE
369
+ resolved_owner_id uuid;
370
+ resolved_owner_subject_id text;
371
+ private_create_capability_id uuid;
372
+ creator_has_active_membership boolean;
373
+ owner_provenance_supplied boolean :=
374
+ NEW.owner_organization_membership_id IS NOT NULL
375
+ OR NEW.owner_subject_id IS NOT NULL;
376
+ previous_lifecycle_marker text := pg_catalog.current_setting(
377
+ 'opengeni.organization_tenancy_lifecycle', true
378
+ );
379
+ BEGIN
380
+ IF TG_OP = 'INSERT'
381
+ AND NEW.owner_organization_membership_id IS NULL
382
+ AND NEW.owner_subject_id IS NULL
383
+ THEN
384
+ IF NEW.parent_session_id IS NOT NULL THEN
385
+ SELECT parent.owner_organization_membership_id, parent.owner_subject_id
386
+ INTO resolved_owner_id, resolved_owner_subject_id
387
+ FROM sessions parent
388
+ WHERE parent.account_id = NEW.account_id
389
+ AND parent.workspace_id = NEW.workspace_id
390
+ AND parent.id = NEW.parent_session_id;
391
+ ELSIF NEW.created_by_kind = 'subject'
392
+ AND NEW.created_by_subject_id IS NOT NULL
393
+ THEN
394
+ PERFORM pg_catalog.set_config(
395
+ 'opengeni.organization_tenancy_lifecycle',
396
+ 'session_visibility_activation',
397
+ true
398
+ );
399
+ SELECT membership.id, membership.subject_id
400
+ INTO resolved_owner_id, resolved_owner_subject_id
401
+ FROM organization_memberships membership
402
+ WHERE membership.account_id = NEW.account_id
403
+ AND membership.subject_id = NEW.created_by_subject_id
404
+ AND membership.status = 'active'
405
+ AND (
406
+ -- Stated authority, read from the authority row itself: a managed
407
+ -- human's personal workspace has no workspace_memberships row by
408
+ -- design (0219), so the membership's own personal-workspace
409
+ -- pointer is the authority for exactly that one workspace. Same
410
+ -- shape as 0258's personal Document authority.
411
+ --
412
+ -- Restricted to `user:%%` because 0219 and 0263 gate the ENTIRE
413
+ -- managed-human lifecycle - provisioning, invitation, acceptance
414
+ -- - to that prefix. A personal-workspace pointer on an
415
+ -- `api_key:`/`configured:` membership is not a shape any
416
+ -- lifecycle can produce, and 0297's classifier calls that lane
417
+ -- (`external_lane_owns_row`) permanently unrepairable. The write
418
+ -- path must not attribute what the classifier refuses.
419
+ (
420
+ NEW.created_by_subject_id LIKE 'user:%%'
421
+ AND membership.personal_workspace_id = NEW.workspace_id
422
+ )
423
+ OR EXISTS (
424
+ SELECT 1
425
+ FROM workspace_memberships workspace_membership
426
+ WHERE workspace_membership.account_id = NEW.account_id
427
+ AND workspace_membership.workspace_id = NEW.workspace_id
428
+ AND workspace_membership.subject_id = membership.subject_id
429
+ )
430
+ );
431
+ IF NOT FOUND THEN
432
+ -- Classify the miss instead of writing an indistinguishable NULL.
433
+ SELECT EXISTS (
434
+ SELECT 1
435
+ FROM organization_memberships creator_membership
436
+ WHERE creator_membership.account_id = NEW.account_id
437
+ AND creator_membership.subject_id = NEW.created_by_subject_id
438
+ AND creator_membership.status = 'active'
439
+ AND NEW.created_by_subject_id LIKE 'user:%%'
440
+ ) INTO creator_has_active_membership;
441
+ IF creator_has_active_membership
442
+ AND EXISTS (
443
+ SELECT 1
444
+ FROM organization_memberships personal_owner
445
+ WHERE personal_owner.account_id = NEW.account_id
446
+ AND personal_owner.personal_workspace_id = NEW.workspace_id
447
+ AND personal_owner.status = 'active'
448
+ AND personal_owner.subject_id LIKE 'user:%%'
449
+ )
450
+ THEN
451
+ RAISE EXCEPTION
452
+ 'session owner authority is unresolved in a personal workspace'
453
+ USING ERRCODE = '55000';
454
+ END IF;
455
+ -- Otherwise genuinely ownerless: no active organization membership,
456
+ -- or no stated authority over an ordinary shared workspace.
457
+ END IF;
458
+ END IF;
459
+ NEW.owner_organization_membership_id := resolved_owner_id;
460
+ NEW.owner_subject_id := resolved_owner_subject_id;
461
+ PERFORM pg_catalog.set_config(
462
+ 'opengeni.organization_tenancy_lifecycle',
463
+ CASE WHEN previous_lifecycle_marker IS NULL THEN '' ELSE previous_lifecycle_marker END,
464
+ true
465
+ );
466
+ END IF;
467
+
468
+ IF (NEW.owner_organization_membership_id IS NULL)
469
+ <> (NEW.owner_subject_id IS NULL)
470
+ OR (NEW.visibility = 'user_private'
471
+ AND NEW.owner_organization_membership_id IS NULL)
472
+ THEN
473
+ RAISE EXCEPTION 'session authority owner provenance is incomplete'
474
+ USING ERRCODE = '23514';
475
+ END IF;
476
+
477
+ IF TG_OP = 'UPDATE'
478
+ AND (
479
+ NEW.visibility IS DISTINCT FROM OLD.visibility
480
+ OR NEW.owner_organization_membership_id
481
+ IS DISTINCT FROM OLD.owner_organization_membership_id
482
+ OR NEW.owner_subject_id IS DISTINCT FROM OLD.owner_subject_id
483
+ OR NEW.authority_epoch IS DISTINCT FROM OLD.authority_epoch
484
+ OR NEW.forked_from_session_id IS DISTINCT FROM OLD.forked_from_session_id
485
+ OR NEW.forked_from_authority_epoch
486
+ IS DISTINCT FROM OLD.forked_from_authority_epoch
487
+ OR NEW.forked_from_visibility IS DISTINCT FROM OLD.forked_from_visibility
488
+ OR NEW.forked_at IS DISTINCT FROM OLD.forked_at
489
+ OR NEW.forked_by_organization_membership_id
490
+ IS DISTINCT FROM OLD.forked_by_organization_membership_id
491
+ )
492
+ AND NOT EXISTS (
493
+ SELECT 1
494
+ FROM session_visibility_write_capabilities capability
495
+ WHERE capability.backend_pid = pg_catalog.pg_backend_pid()
496
+ AND capability.transaction_id = pg_catalog.pg_current_xact_id()
497
+ AND capability.capability_id = nullif(
498
+ pg_catalog.current_setting(
499
+ 'opengeni.session_visibility_write_capability', true
500
+ ),
501
+ ''
502
+ )::uuid
503
+ )
504
+ THEN
505
+ RAISE EXCEPTION 'session authority changes require the lifecycle capability'
506
+ USING ERRCODE = '42501';
507
+ END IF;
508
+
509
+ IF TG_OP = 'INSERT' THEN
510
+ PERFORM pg_catalog.set_config(
511
+ 'opengeni.private_session_create_lifecycle', 'private_session_create', true
512
+ );
513
+ DELETE FROM private_session_create_capabilities capability
514
+ WHERE capability.backend_pid = pg_catalog.pg_backend_pid()
515
+ AND capability.transaction_id = pg_catalog.pg_current_xact_id()
516
+ AND capability.capability_id = nullif(
517
+ pg_catalog.current_setting(
518
+ 'opengeni.private_session_create_capability', true
519
+ ),
520
+ ''
521
+ )::uuid
522
+ AND capability.account_id = NEW.account_id
523
+ AND capability.workspace_id = NEW.workspace_id
524
+ AND capability.session_id = NEW.id
525
+ AND capability.actor_subject_id = NEW.created_by_subject_id
526
+ AND capability.owner_membership_id = NEW.owner_organization_membership_id
527
+ AND NEW.created_by_kind = 'subject'
528
+ AND NEW.owner_subject_id = capability.actor_subject_id
529
+ AND NEW.visibility = 'user_private'
530
+ AND NEW.create_requested_visibility = 'user_private'
531
+ AND NEW.authority_epoch = 1
532
+ AND NEW.parent_session_id IS NULL
533
+ AND NEW.sandbox_group_id = NEW.id
534
+ AND NEW.forked_from_session_id IS NULL
535
+ AND NEW.forked_from_authority_epoch IS NULL
536
+ AND NEW.forked_from_visibility IS NULL
537
+ AND NEW.forked_at IS NULL
538
+ AND NEW.forked_by_organization_membership_id IS NULL
539
+ RETURNING capability.capability_id INTO private_create_capability_id;
540
+ IF private_create_capability_id IS NOT NULL THEN
541
+ PERFORM pg_catalog.set_config('opengeni.private_session_create_capability', '', true);
542
+ END IF;
543
+ END IF;
544
+
545
+ IF TG_OP = 'INSERT'
546
+ AND (
547
+ NEW.visibility <> 'workspace_shared'
548
+ OR NEW.authority_epoch <> 1
549
+ OR NEW.forked_from_session_id IS NOT NULL
550
+ OR NEW.forked_from_authority_epoch IS NOT NULL
551
+ OR NEW.forked_from_visibility IS NOT NULL
552
+ OR NEW.forked_at IS NOT NULL
553
+ OR NEW.forked_by_organization_membership_id IS NOT NULL
554
+ OR owner_provenance_supplied
555
+ )
556
+ AND private_create_capability_id IS NULL
557
+ AND NOT EXISTS (
558
+ SELECT 1
559
+ FROM session_visibility_write_capabilities capability
560
+ WHERE capability.backend_pid = pg_catalog.pg_backend_pid()
561
+ AND capability.transaction_id = pg_catalog.pg_current_xact_id()
562
+ AND capability.capability_id = nullif(
563
+ pg_catalog.current_setting(
564
+ 'opengeni.session_visibility_write_capability', true
565
+ ),
566
+ ''
567
+ )::uuid
568
+ )
569
+ THEN
570
+ RAISE EXCEPTION 'non-default session authority requires the lifecycle capability'
571
+ USING ERRCODE = '42501';
572
+ END IF;
573
+ RETURN NEW;
574
+ EXCEPTION WHEN OTHERS THEN
575
+ PERFORM pg_catalog.set_config(
576
+ 'opengeni.organization_tenancy_lifecycle',
577
+ CASE WHEN previous_lifecycle_marker IS NULL THEN '' ELSE previous_lifecycle_marker END,
578
+ true
579
+ );
580
+ RAISE;
581
+ END;
582
+ $body$;
583
+ $ddl$, data_schema);
584
+
585
+ EXECUTE format(
586
+ 'ALTER FUNCTION %I.guard_session_authority_write() SET search_path = pg_catalog, %I, pg_temp',
587
+ data_schema, data_schema
588
+ );
589
+ EXECUTE format(
590
+ 'COMMENT ON FUNCTION %I.guard_session_authority_write() IS %L',
591
+ data_schema,
592
+ 'Session authority write fence (0225, repaired by 0302 and 0311). '
593
+ || 'Private creation accepts one exact account/workspace/session/subject/owner capability '
594
+ || 'on INSERT only and consumes it before the write; existing visibility lifecycle '
595
+ || 'capabilities remain unchanged.'
596
+ );
597
+ END
598
+ $private_session_create_guard$;
599
+
600
+ REVOKE ALL ON FUNCTION update_organization_name(uuid,text,text,timestamptz,uuid) FROM PUBLIC;
601
+ REVOKE ALL ON FUNCTION open_private_session_create_capability(uuid,uuid,uuid,text) FROM PUBLIC;
602
+ REVOKE ALL ON FUNCTION close_private_session_create_capability(uuid) FROM PUBLIC;
603
+ REVOKE ALL ON FUNCTION fence_session_create_requested_visibility_update() FROM PUBLIC;
604
+
605
+ DO $pin_and_grant$
606
+ DECLARE data_schema text := current_schema();
607
+ BEGIN
608
+ EXECUTE format(
609
+ 'ALTER FUNCTION %I.get_organization_administration_overview(uuid,text) SET search_path = pg_catalog, %I, pg_temp',
610
+ data_schema, data_schema
611
+ );
612
+ EXECUTE format(
613
+ 'ALTER FUNCTION %I.update_organization_name(uuid,text,text,timestamptz,uuid) SET search_path = pg_catalog, %I, pg_temp',
614
+ data_schema, data_schema
615
+ );
616
+ EXECUTE format(
617
+ 'ALTER FUNCTION %I.open_private_session_create_capability(uuid,uuid,uuid,text) SET search_path = pg_catalog, %I, pg_temp',
618
+ data_schema, data_schema
619
+ );
620
+ EXECUTE format(
621
+ 'ALTER FUNCTION %I.close_private_session_create_capability(uuid) SET search_path = pg_catalog, %I, pg_temp',
622
+ data_schema, data_schema
623
+ );
624
+ EXECUTE format(
625
+ 'ALTER FUNCTION %I.fence_session_create_requested_visibility_update() SET search_path = pg_catalog, %I, pg_temp',
626
+ data_schema, data_schema
627
+ );
628
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
629
+ EXECUTE format(
630
+ 'REVOKE ALL ON TABLE %I.private_session_create_capabilities FROM opengeni_app',
631
+ data_schema
632
+ );
633
+ EXECUTE format(
634
+ 'GRANT EXECUTE ON FUNCTION %I.get_organization_administration_overview(uuid,text) TO opengeni_app',
635
+ data_schema
636
+ );
637
+ EXECUTE format(
638
+ 'GRANT EXECUTE ON FUNCTION %I.update_organization_name(uuid,text,text,timestamptz,uuid) TO opengeni_app',
639
+ data_schema
640
+ );
641
+ EXECUTE format(
642
+ 'GRANT EXECUTE ON FUNCTION %I.open_private_session_create_capability(uuid,uuid,uuid,text) TO opengeni_app',
643
+ data_schema
644
+ );
645
+ EXECUTE format(
646
+ 'GRANT EXECUTE ON FUNCTION %I.close_private_session_create_capability(uuid) TO opengeni_app',
647
+ data_schema
648
+ );
649
+ END IF;
650
+ END
651
+ $pin_and_grant$;
652
+
653
+ COMMENT ON FUNCTION get_organization_administration_overview(uuid,text) IS
654
+ 'Bounded organization-admin projection of canonical organization identity and shared workspace access; personal workspaces are excluded in the database.';
655
+ COMMENT ON FUNCTION open_private_session_create_capability(uuid,uuid,uuid,text) IS
656
+ 'Transaction-local capability for one activated managed-human private session insert.';
@@ -0,0 +1,39 @@
1
+ -- deployment-mode: rolling
2
+ -- Allow the explicit quiescent session-tree deletion lifecycle to remove the
3
+ -- rows that are owned exclusively by that tree. Cross-session and workspace
4
+ -- provenance keeps its existing RESTRICT fence, so published artifacts,
5
+ -- governed learning evidence, forks, and other durable external references
6
+ -- fail closed instead of being silently orphaned.
7
+
8
+ SET lock_timeout = '5s';
9
+ SET statement_timeout = '10min';
10
+
11
+ ALTER TABLE sessions
12
+ DROP CONSTRAINT sessions_workspace_parent_fk,
13
+ ADD CONSTRAINT sessions_workspace_parent_fk
14
+ FOREIGN KEY (workspace_id, parent_session_id)
15
+ REFERENCES sessions(workspace_id, id) ON DELETE CASCADE;
16
+
17
+ ALTER TABLE session_turn_attempts
18
+ DROP CONSTRAINT session_turn_attempts_workspace_session_fk,
19
+ ADD CONSTRAINT session_turn_attempts_workspace_session_fk
20
+ FOREIGN KEY (workspace_id, session_id)
21
+ REFERENCES sessions(workspace_id, id) ON DELETE CASCADE;
22
+
23
+ ALTER TABLE session_command_receipts
24
+ DROP CONSTRAINT session_command_receipts_target_session_fk,
25
+ ADD CONSTRAINT session_command_receipts_target_session_fk
26
+ FOREIGN KEY (workspace_id, target_session_id)
27
+ REFERENCES sessions(workspace_id, id) ON DELETE CASCADE;
28
+
29
+ ALTER TABLE workspace_control_events
30
+ DROP CONSTRAINT workspace_control_events_root_session_fk,
31
+ ADD CONSTRAINT workspace_control_events_root_session_fk
32
+ FOREIGN KEY (workspace_id, root_session_id)
33
+ REFERENCES sessions(workspace_id, id) ON DELETE CASCADE;
34
+
35
+ ALTER TABLE session_attempt_interruptions
36
+ DROP CONSTRAINT session_attempt_interruptions_workspace_session_fk,
37
+ ADD CONSTRAINT session_attempt_interruptions_workspace_session_fk
38
+ FOREIGN KEY (workspace_id, session_id)
39
+ REFERENCES sessions(workspace_id, id) ON DELETE CASCADE;