@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,163 @@
1
+ -- deployment-mode: rolling
2
+ -- Self-service organization creation for an authenticated managed human. One
3
+ -- lifecycle seam creates the organization, its first shared workspace, the
4
+ -- caller's personal workspace, and both authority anchors atomically.
5
+
6
+ SET LOCAL lock_timeout = '5s';
7
+ SET LOCAL statement_timeout = '10min';
8
+
9
+ CREATE OR REPLACE FUNCTION create_managed_organization(
10
+ p_subject_id text,
11
+ p_subject_label text,
12
+ p_name text,
13
+ p_operation_id uuid
14
+ ) RETURNS jsonb
15
+ LANGUAGE plpgsql
16
+ SECURITY DEFINER
17
+ SET search_path FROM CURRENT
18
+ AS $body$
19
+ DECLARE
20
+ requested_name text := btrim(p_name);
21
+ account_row managed_accounts%ROWTYPE;
22
+ shared_workspace workspaces%ROWTYPE;
23
+ personal_workspace workspaces%ROWTYPE;
24
+ membership_row organization_memberships%ROWTYPE;
25
+ workspace_permissions jsonb := pg_catalog.jsonb_build_array(
26
+ 'workspace:read', 'workspace:admin', 'members:manage',
27
+ 'sessions:create', 'sessions:read', 'sessions:control',
28
+ 'files:upload', 'files:read', 'documents:manage', 'documents:search',
29
+ 'scheduled_tasks:manage', 'scheduled_tasks:run',
30
+ 'github:manage', 'github:use', 'api_keys:manage',
31
+ 'connections:read', 'connections:write',
32
+ 'variable-sets:list', 'variable-sets:read', 'variable-sets:write',
33
+ 'variable-sets:manage', 'variable-sets:attach', 'variable-sets:use',
34
+ 'secrets:list', 'secrets:read', 'secrets:write',
35
+ 'mcp_servers:attach', 'goals:manage',
36
+ 'enrollments:read', 'enrollments:manage',
37
+ 'artifacts:read', 'artifacts:publish'
38
+ );
39
+ BEGIN
40
+ IF p_operation_id IS NULL
41
+ OR p_subject_id IS NULL
42
+ OR p_subject_id NOT LIKE 'user:%'
43
+ OR p_subject_id IS DISTINCT FROM opengeni_private.current_subject_id()
44
+ OR requested_name IS NULL
45
+ OR length(requested_name) NOT BETWEEN 1 AND 120
46
+ OR NOT EXISTS (
47
+ SELECT 1 FROM auth_users auth_user
48
+ WHERE auth_user.id = substr(p_subject_id, length('user:') + 1)
49
+ )
50
+ THEN
51
+ RAISE EXCEPTION 'managed organization creation input is invalid'
52
+ USING ERRCODE = '22023';
53
+ END IF;
54
+
55
+ -- One operation id is one durable creation attempt. Serialize before the
56
+ -- replay lookup so concurrent retries cannot both miss the account row and
57
+ -- race its unique external identity.
58
+ PERFORM pg_catalog.pg_advisory_xact_lock(pg_catalog.hashtextextended(
59
+ 'managed-organization-creation:' || p_operation_id::text, 0
60
+ ));
61
+
62
+ SELECT * INTO account_row
63
+ FROM managed_accounts account
64
+ WHERE account.external_source = 'opengeni:managed-organization'
65
+ AND account.external_id = p_operation_id::text
66
+ FOR UPDATE;
67
+
68
+ IF FOUND THEN
69
+ IF account_row.name IS DISTINCT FROM requested_name THEN
70
+ RAISE EXCEPTION 'organization operation id was reused with different input'
71
+ USING ERRCODE = '23505';
72
+ END IF;
73
+ IF NOT EXISTS (
74
+ SELECT 1
75
+ FROM organization_memberships membership
76
+ WHERE membership.account_id = account_row.id
77
+ AND membership.subject_id = p_subject_id
78
+ AND membership.role = 'owner'
79
+ AND membership.status = 'active'
80
+ ) THEN
81
+ RAISE EXCEPTION 'organization operation id belongs to another subject'
82
+ USING ERRCODE = '23505';
83
+ END IF;
84
+ SELECT * INTO shared_workspace
85
+ FROM workspaces workspace
86
+ WHERE workspace.account_id = account_row.id
87
+ AND workspace.external_source = 'opengeni:managed-organization-default';
88
+ IF NOT FOUND THEN
89
+ RAISE EXCEPTION 'managed organization creation is incomplete' USING ERRCODE = '55000';
90
+ END IF;
91
+ ELSE
92
+ INSERT INTO managed_accounts (name, external_source, external_id)
93
+ VALUES (requested_name, 'opengeni:managed-organization', p_operation_id::text)
94
+ RETURNING * INTO account_row;
95
+
96
+ PERFORM pg_catalog.set_config('opengeni.account_id', account_row.id::text, true);
97
+ INSERT INTO workspaces (account_id, name, slug, external_source, external_id)
98
+ VALUES (
99
+ account_row.id, 'Default workspace', NULL,
100
+ 'opengeni:managed-organization-default', account_row.id::text
101
+ ) RETURNING * INTO shared_workspace;
102
+ INSERT INTO workspace_inference_controls (workspace_id, account_id)
103
+ VALUES (shared_workspace.id, account_row.id);
104
+
105
+ INSERT INTO workspaces (account_id, name, slug, external_source, external_id)
106
+ VALUES (
107
+ account_row.id, 'Personal workspace', NULL,
108
+ 'opengeni:organization-membership', account_row.id::text || ':' || p_subject_id
109
+ ) RETURNING * INTO personal_workspace;
110
+ INSERT INTO workspace_inference_controls (workspace_id, account_id)
111
+ VALUES (personal_workspace.id, account_row.id);
112
+
113
+ PERFORM pg_catalog.set_config(
114
+ 'opengeni.organization_tenancy_lifecycle',
115
+ 'organization_membership_lifecycle',
116
+ true
117
+ );
118
+ INSERT INTO organization_memberships (
119
+ account_id, subject_id, role, status, personal_workspace_id
120
+ ) VALUES (
121
+ account_row.id, p_subject_id, 'owner', 'active', personal_workspace.id
122
+ ) RETURNING * INTO membership_row;
123
+
124
+ INSERT INTO workspace_memberships (
125
+ account_id, workspace_id, subject_id, subject_label, role, permissions
126
+ ) VALUES (
127
+ account_row.id, shared_workspace.id, p_subject_id,
128
+ nullif(btrim(p_subject_label), ''), 'owner', workspace_permissions
129
+ );
130
+ END IF;
131
+
132
+ RETURN pg_catalog.jsonb_build_object(
133
+ 'organization', pg_catalog.jsonb_build_object(
134
+ 'id', account_row.id,
135
+ 'name', account_row.name,
136
+ 'createdAt', account_row.created_at,
137
+ 'updatedAt', account_row.updated_at
138
+ ),
139
+ 'workspaceId', shared_workspace.id
140
+ );
141
+ END
142
+ $body$;
143
+
144
+ REVOKE ALL ON FUNCTION create_managed_organization(text, text, text, uuid) FROM PUBLIC;
145
+ DO $body$
146
+ DECLARE
147
+ target_schema text := pg_catalog.current_schema();
148
+ BEGIN
149
+ EXECUTE pg_catalog.format(
150
+ 'ALTER FUNCTION %I.create_managed_organization(text,text,text,uuid) SET search_path = pg_catalog, %I, pg_temp',
151
+ target_schema, target_schema
152
+ );
153
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
154
+ EXECUTE pg_catalog.format(
155
+ 'GRANT EXECUTE ON FUNCTION %I.create_managed_organization(text,text,text,uuid) TO opengeni_app',
156
+ target_schema
157
+ );
158
+ END IF;
159
+ END
160
+ $body$;
161
+
162
+ COMMENT ON FUNCTION create_managed_organization(text, text, text, uuid) IS
163
+ 'Creates one managed organization and its initial authority graph atomically.';
@@ -0,0 +1,539 @@
1
+ -- deployment-mode: rolling
2
+ -- Active direct managed-human organization owners and administrators may
3
+ -- administer a shared workspace without acquiring operational workspace
4
+ -- access. Personal workspaces are excluded at the database capability.
5
+
6
+ SET LOCAL lock_timeout = '5s';
7
+ SET LOCAL statement_timeout = '5min';
8
+
9
+ ALTER TABLE organization_membership_operation_receipts
10
+ DROP CONSTRAINT organization_membership_operation_receipts_action_check;
11
+ ALTER TABLE organization_membership_operation_receipts
12
+ ADD CONSTRAINT organization_membership_operation_receipts_action_check CHECK (
13
+ action IN (
14
+ 'invite', 'accept', 'revoke_invitation', 'change_role', 'suspend',
15
+ 'reactivate', 'offboard', 'retention', 'create_workspace'
16
+ )
17
+ ) NOT VALID;
18
+ ALTER TABLE organization_membership_operation_receipts
19
+ VALIDATE CONSTRAINT organization_membership_operation_receipts_action_check;
20
+
21
+ -- The organization-only exception to the last durable workspace-admin guard
22
+ -- must carry route provenance, not merely an organization role. This
23
+ -- transaction-scoped capability is opened only by the direct managed-cookie
24
+ -- organization route and is invisible to ordinary/delegated workspace paths.
25
+ CREATE TABLE organization_shared_workspace_administration_capabilities (
26
+ backend_pid integer NOT NULL,
27
+ transaction_id xid8 NOT NULL,
28
+ capability_id uuid NOT NULL,
29
+ account_id uuid NOT NULL,
30
+ workspace_id uuid NOT NULL,
31
+ actor_subject_id text NOT NULL,
32
+ PRIMARY KEY (backend_pid, transaction_id, capability_id)
33
+ );
34
+
35
+ ALTER TABLE organization_shared_workspace_administration_capabilities ENABLE ROW LEVEL SECURITY;
36
+ ALTER TABLE organization_shared_workspace_administration_capabilities FORCE ROW LEVEL SECURITY;
37
+
38
+ DO $capability_policy$
39
+ DECLARE
40
+ target_schema text := pg_catalog.current_schema();
41
+ migration_owner text := current_user;
42
+ BEGIN
43
+ EXECUTE pg_catalog.format(
44
+ 'CREATE POLICY organization_shared_workspace_administration_capability_owner '
45
+ || 'ON %I.organization_shared_workspace_administration_capabilities '
46
+ || 'FOR ALL USING (current_user = %L) WITH CHECK (current_user = %L)',
47
+ target_schema, migration_owner, migration_owner
48
+ );
49
+ END
50
+ $capability_policy$;
51
+
52
+ CREATE FUNCTION opengeni_private.assert_organization_shared_workspace_administrator(
53
+ p_account_id uuid,
54
+ p_workspace_id uuid,
55
+ p_actor_subject_id text
56
+ ) RETURNS void
57
+ LANGUAGE plpgsql
58
+ SET search_path FROM CURRENT
59
+ AS $body$
60
+ BEGIN
61
+ IF p_account_id IS NULL
62
+ OR p_workspace_id IS NULL
63
+ OR p_actor_subject_id IS NULL
64
+ OR p_actor_subject_id NOT LIKE 'user:%'
65
+ OR p_actor_subject_id IS DISTINCT FROM opengeni_private.current_subject_id()
66
+ OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
67
+ THEN
68
+ RAISE EXCEPTION 'organization shared-workspace administration authority is invalid'
69
+ USING ERRCODE = '42501';
70
+ END IF;
71
+
72
+ -- Serialize role/personal-workspace changes with this transaction. The lock
73
+ -- has no row-level dependency and therefore keeps the canonical lifecycle
74
+ -- ordering intact.
75
+ PERFORM pg_catalog.pg_advisory_xact_lock(pg_catalog.hashtextextended(
76
+ 'organization-membership:' || p_account_id::text, 0
77
+ ));
78
+
79
+ IF NOT EXISTS (
80
+ SELECT 1
81
+ FROM organization_memberships actor
82
+ WHERE actor.account_id = p_account_id
83
+ AND actor.subject_id = p_actor_subject_id
84
+ AND actor.status = 'active'
85
+ AND actor.role IN ('owner', 'admin')
86
+ ) THEN
87
+ RAISE EXCEPTION 'organization administration required' USING ERRCODE = '42501';
88
+ END IF;
89
+
90
+ IF NOT EXISTS (
91
+ SELECT 1
92
+ FROM workspaces workspace
93
+ WHERE workspace.account_id = p_account_id
94
+ AND workspace.id = p_workspace_id
95
+ AND NOT EXISTS (
96
+ SELECT 1
97
+ FROM organization_memberships membership
98
+ WHERE membership.account_id = p_account_id
99
+ AND membership.personal_workspace_id = workspace.id
100
+ )
101
+ ) THEN
102
+ -- Cross-organization, missing, and Personal workspaces intentionally share
103
+ -- one non-enumerating result.
104
+ RAISE EXCEPTION 'shared workspace not found' USING ERRCODE = 'P0002';
105
+ END IF;
106
+ END
107
+ $body$;
108
+
109
+ CREATE FUNCTION assert_organization_shared_workspace_administrator(
110
+ p_account_id uuid,
111
+ p_workspace_id uuid,
112
+ p_actor_subject_id text
113
+ ) RETURNS void
114
+ LANGUAGE plpgsql
115
+ SECURITY DEFINER
116
+ SET search_path FROM CURRENT
117
+ AS $body$
118
+ BEGIN
119
+ PERFORM opengeni_private.assert_organization_shared_workspace_administrator(
120
+ p_account_id, p_workspace_id, p_actor_subject_id
121
+ );
122
+ END
123
+ $body$;
124
+
125
+ CREATE FUNCTION open_organization_shared_workspace_administration_capability(
126
+ p_account_id uuid,
127
+ p_workspace_id uuid,
128
+ p_actor_subject_id text
129
+ ) RETURNS uuid
130
+ LANGUAGE plpgsql
131
+ SECURITY DEFINER
132
+ SET search_path FROM CURRENT
133
+ AS $body$
134
+ DECLARE
135
+ capability_id_value uuid := pg_catalog.gen_random_uuid();
136
+ BEGIN
137
+ PERFORM opengeni_private.assert_organization_shared_workspace_administrator(
138
+ p_account_id, p_workspace_id, p_actor_subject_id
139
+ );
140
+ INSERT INTO organization_shared_workspace_administration_capabilities (
141
+ backend_pid, transaction_id, capability_id,
142
+ account_id, workspace_id, actor_subject_id
143
+ ) VALUES (
144
+ pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), capability_id_value,
145
+ p_account_id, p_workspace_id, p_actor_subject_id
146
+ );
147
+ PERFORM pg_catalog.set_config(
148
+ 'opengeni.organization_shared_workspace_administration_capability',
149
+ capability_id_value::text,
150
+ true
151
+ );
152
+ RETURN capability_id_value;
153
+ END
154
+ $body$;
155
+
156
+ CREATE FUNCTION close_organization_shared_workspace_administration_capability(
157
+ p_capability_id uuid
158
+ ) RETURNS void
159
+ LANGUAGE plpgsql
160
+ SECURITY DEFINER
161
+ SET search_path FROM CURRENT
162
+ AS $body$
163
+ BEGIN
164
+ DELETE FROM organization_shared_workspace_administration_capabilities capability
165
+ WHERE capability.backend_pid = pg_catalog.pg_backend_pid()
166
+ AND capability.transaction_id = pg_catalog.pg_current_xact_id()
167
+ AND capability.capability_id = p_capability_id;
168
+ IF pg_catalog.current_setting(
169
+ 'opengeni.organization_shared_workspace_administration_capability', true
170
+ ) = p_capability_id::text THEN
171
+ PERFORM pg_catalog.set_config(
172
+ 'opengeni.organization_shared_workspace_administration_capability', '', true
173
+ );
174
+ END IF;
175
+ END
176
+ $body$;
177
+
178
+ CREATE FUNCTION create_organization_shared_workspace(
179
+ p_account_id uuid,
180
+ p_actor_subject_id text,
181
+ p_name text,
182
+ p_slug text,
183
+ p_agent_instructions text,
184
+ p_operation_id uuid
185
+ ) RETURNS jsonb
186
+ LANGUAGE plpgsql
187
+ SECURITY DEFINER
188
+ SET search_path FROM CURRENT
189
+ AS $body$
190
+ DECLARE
191
+ requested_name text := pg_catalog.btrim(p_name);
192
+ requested_slug text := nullif(pg_catalog.btrim(p_slug), '');
193
+ requested_agent_instructions text := nullif(
194
+ pg_catalog.btrim(p_agent_instructions), ''
195
+ );
196
+ input_hash_value text;
197
+ receipt_row organization_membership_operation_receipts%ROWTYPE;
198
+ workspace_row workspaces%ROWTYPE;
199
+ result_value jsonb;
200
+ BEGIN
201
+ IF p_account_id IS NULL
202
+ OR p_operation_id IS NULL
203
+ OR p_actor_subject_id IS NULL
204
+ OR p_actor_subject_id NOT LIKE 'user:%'
205
+ OR p_actor_subject_id IS DISTINCT FROM opengeni_private.current_subject_id()
206
+ OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
207
+ OR requested_name IS NULL
208
+ OR pg_catalog.length(requested_name) NOT BETWEEN 1 AND 120
209
+ OR (requested_slug IS NOT NULL AND pg_catalog.length(requested_slug) > 120)
210
+ THEN
211
+ RAISE EXCEPTION 'organization shared-workspace creation input is invalid'
212
+ USING ERRCODE = '22023';
213
+ END IF;
214
+
215
+ -- Match the organization lifecycle lock order: advisory organization lock,
216
+ -- account key-share fence, then organization membership and workspace rows.
217
+ PERFORM pg_catalog.pg_advisory_xact_lock(pg_catalog.hashtextextended(
218
+ 'organization-membership:' || p_account_id::text, 0
219
+ ));
220
+ PERFORM 1 FROM managed_accounts account
221
+ WHERE account.id = p_account_id FOR KEY SHARE;
222
+ IF NOT FOUND THEN
223
+ RAISE EXCEPTION 'organization not found' USING ERRCODE = 'P0002';
224
+ END IF;
225
+ IF NOT EXISTS (
226
+ SELECT 1 FROM organization_memberships actor
227
+ WHERE actor.account_id = p_account_id
228
+ AND actor.subject_id = p_actor_subject_id
229
+ AND actor.status = 'active'
230
+ AND actor.role IN ('owner', 'admin')
231
+ ) THEN
232
+ RAISE EXCEPTION 'organization administration required' USING ERRCODE = '42501';
233
+ END IF;
234
+
235
+ input_hash_value := pg_catalog.encode(pg_catalog.sha256(pg_catalog.convert_to(
236
+ pg_catalog.jsonb_build_object(
237
+ 'actorSubjectId', p_actor_subject_id,
238
+ 'name', requested_name,
239
+ 'slug', requested_slug,
240
+ 'agentInstructions', requested_agent_instructions
241
+ )::text,
242
+ 'UTF8'
243
+ )), 'hex');
244
+ PERFORM pg_catalog.set_config(
245
+ 'opengeni.organization_tenancy_lifecycle',
246
+ 'organization_membership_lifecycle',
247
+ true
248
+ );
249
+ SELECT * INTO receipt_row
250
+ FROM organization_membership_operation_receipts receipt
251
+ WHERE receipt.account_id = p_account_id
252
+ AND receipt.operation_id = p_operation_id
253
+ FOR UPDATE;
254
+ IF FOUND THEN
255
+ IF receipt_row.action IS DISTINCT FROM 'create_workspace'
256
+ OR receipt_row.input_hash IS DISTINCT FROM input_hash_value
257
+ THEN
258
+ RAISE EXCEPTION 'organization operation id was reused with different input'
259
+ USING ERRCODE = '23505';
260
+ END IF;
261
+ RETURN receipt_row.result;
262
+ END IF;
263
+
264
+ INSERT INTO workspaces (
265
+ account_id, name, slug, external_source, external_id, agent_instructions
266
+ ) VALUES (
267
+ p_account_id, requested_name, requested_slug,
268
+ 'opengeni:organization-shared-workspace',
269
+ p_account_id::text || ':' || p_operation_id::text,
270
+ requested_agent_instructions
271
+ ) RETURNING * INTO workspace_row;
272
+ INSERT INTO workspace_inference_controls (workspace_id, account_id)
273
+ VALUES (workspace_row.id, p_account_id);
274
+
275
+ result_value := pg_catalog.jsonb_build_object('workspaceId', workspace_row.id);
276
+ INSERT INTO organization_membership_operation_receipts (
277
+ account_id, operation_id, action, input_hash, result
278
+ ) VALUES (
279
+ p_account_id, p_operation_id, 'create_workspace', input_hash_value, result_value
280
+ );
281
+ RETURN result_value;
282
+ END
283
+ $body$;
284
+
285
+ CREATE FUNCTION upsert_organization_shared_workspace_member(
286
+ p_account_id uuid,
287
+ p_workspace_id uuid,
288
+ p_actor_subject_id text,
289
+ p_target_membership_id uuid,
290
+ p_subject_label text,
291
+ p_role text,
292
+ p_permissions jsonb,
293
+ p_require_existing boolean
294
+ ) RETURNS jsonb
295
+ LANGUAGE plpgsql
296
+ SECURITY DEFINER
297
+ SET search_path FROM CURRENT
298
+ AS $body$
299
+ DECLARE
300
+ target organization_memberships%ROWTYPE;
301
+ access workspace_memberships%ROWTYPE;
302
+ normalized_role text;
303
+ existing_role text;
304
+ BEGIN
305
+ IF p_target_membership_id IS NULL
306
+ OR p_require_existing IS NULL
307
+ OR p_permissions IS NULL
308
+ OR pg_catalog.jsonb_typeof(p_permissions) <> 'array'
309
+ OR pg_catalog.jsonb_array_length(p_permissions) > 128
310
+ OR EXISTS (
311
+ SELECT 1 FROM pg_catalog.jsonb_array_elements(p_permissions) item(value)
312
+ WHERE pg_catalog.jsonb_typeof(item.value) <> 'string'
313
+ OR char_length(item.value #>> '{}') NOT BETWEEN 1 AND 128
314
+ )
315
+ THEN
316
+ RAISE EXCEPTION 'organization shared-workspace member request is invalid'
317
+ USING ERRCODE = '22023';
318
+ END IF;
319
+
320
+ PERFORM opengeni_private.assert_organization_shared_workspace_administrator(
321
+ p_account_id, p_workspace_id, p_actor_subject_id
322
+ );
323
+
324
+ SELECT * INTO target
325
+ FROM organization_memberships membership
326
+ WHERE membership.account_id = p_account_id
327
+ AND membership.id = p_target_membership_id
328
+ AND membership.status = 'active'
329
+ AND membership.subject_id LIKE 'user:%'
330
+ FOR SHARE;
331
+ IF NOT FOUND THEN
332
+ RAISE EXCEPTION 'active organization member not found' USING ERRCODE = 'P0002';
333
+ END IF;
334
+
335
+ SELECT membership.role INTO existing_role
336
+ FROM workspace_memberships membership
337
+ WHERE membership.account_id = p_account_id
338
+ AND membership.workspace_id = p_workspace_id
339
+ AND membership.subject_id = target.subject_id;
340
+ IF p_require_existing AND existing_role IS NULL THEN
341
+ RAISE EXCEPTION 'workspace member not found' USING ERRCODE = 'P0002';
342
+ END IF;
343
+ normalized_role := coalesce(nullif(btrim(p_role), ''), existing_role, 'member');
344
+ IF char_length(normalized_role) NOT BETWEEN 1 AND 64 THEN
345
+ RAISE EXCEPTION 'organization shared-workspace member role is invalid'
346
+ USING ERRCODE = '22023';
347
+ END IF;
348
+
349
+ INSERT INTO workspace_memberships (
350
+ account_id, workspace_id, subject_id, subject_label, role, permissions
351
+ ) VALUES (
352
+ p_account_id, p_workspace_id, target.subject_id,
353
+ nullif(btrim(p_subject_label), ''), normalized_role, p_permissions
354
+ )
355
+ ON CONFLICT (subject_id, workspace_id) DO UPDATE SET
356
+ subject_label = coalesce(EXCLUDED.subject_label, workspace_memberships.subject_label),
357
+ role = EXCLUDED.role,
358
+ permissions = EXCLUDED.permissions,
359
+ updated_at = pg_catalog.clock_timestamp()
360
+ RETURNING * INTO access;
361
+
362
+ RETURN pg_catalog.jsonb_build_object(
363
+ 'subjectId', access.subject_id,
364
+ 'subjectLabel', access.subject_label,
365
+ 'role', access.role,
366
+ 'permissions', access.permissions,
367
+ 'createdAt', access.created_at
368
+ );
369
+ END
370
+ $body$;
371
+
372
+ -- Migration 0278 allowed an organization administrator to remove a workspace
373
+ -- member, but still required another durable workspace administrator to remain.
374
+ -- Once organization administration is a complete shared-workspace control
375
+ -- plane, that durable operational grant is no longer required. Preserve the
376
+ -- guard for workspace-only administrators and exclude Personal workspaces for
377
+ -- both actor paths.
378
+ CREATE OR REPLACE FUNCTION opengeni_private.assert_workspace_membership_removal_actor(
379
+ p_account_id uuid,
380
+ p_workspace_id uuid,
381
+ p_actor_subject text,
382
+ p_target_subject text
383
+ ) RETURNS void
384
+ LANGUAGE plpgsql
385
+ AS $body$
386
+ DECLARE
387
+ actor_is_organization_administrator boolean;
388
+ actor_can_administer boolean;
389
+ BEGIN
390
+ IF p_actor_subject = p_target_subject THEN
391
+ RAISE EXCEPTION 'a member cannot remove their own workspace membership'
392
+ USING ERRCODE = '55000';
393
+ END IF;
394
+ IF NOT EXISTS (
395
+ SELECT 1 FROM workspaces workspace
396
+ WHERE workspace.account_id = p_account_id
397
+ AND workspace.id = p_workspace_id
398
+ AND NOT EXISTS (
399
+ SELECT 1 FROM organization_memberships membership
400
+ WHERE membership.account_id = p_account_id
401
+ AND membership.personal_workspace_id = workspace.id
402
+ )
403
+ ) THEN
404
+ RAISE EXCEPTION 'shared workspace not found' USING ERRCODE = 'P0002';
405
+ END IF;
406
+ SELECT EXISTS (
407
+ SELECT 1
408
+ FROM organization_memberships org_actor
409
+ JOIN organization_shared_workspace_administration_capabilities capability
410
+ ON capability.account_id = org_actor.account_id
411
+ AND capability.workspace_id = p_workspace_id
412
+ AND capability.actor_subject_id = org_actor.subject_id
413
+ AND capability.backend_pid = pg_catalog.pg_backend_pid()
414
+ AND capability.transaction_id = pg_catalog.pg_current_xact_id()
415
+ AND capability.capability_id::text = pg_catalog.current_setting(
416
+ 'opengeni.organization_shared_workspace_administration_capability', true
417
+ )
418
+ WHERE org_actor.account_id = p_account_id
419
+ AND org_actor.subject_id = p_actor_subject
420
+ AND p_actor_subject LIKE 'user:%'
421
+ AND org_actor.status = 'active'
422
+ AND org_actor.role IN ('owner', 'admin')
423
+ ) INTO actor_is_organization_administrator;
424
+ SELECT actor_is_organization_administrator OR EXISTS (
425
+ SELECT 1 FROM workspace_memberships actor_row
426
+ WHERE actor_row.account_id = p_account_id
427
+ AND actor_row.workspace_id = p_workspace_id
428
+ AND actor_row.subject_id = p_actor_subject
429
+ AND actor_row.permissions ?| ARRAY['workspace:admin', 'members:manage']
430
+ ) INTO actor_can_administer;
431
+ IF NOT actor_can_administer THEN
432
+ RAISE EXCEPTION 'workspace member administration required' USING ERRCODE = '42501';
433
+ END IF;
434
+ IF NOT actor_is_organization_administrator
435
+ AND EXISTS (
436
+ SELECT 1 FROM workspace_memberships target_row
437
+ WHERE target_row.account_id = p_account_id
438
+ AND target_row.workspace_id = p_workspace_id
439
+ AND target_row.subject_id = p_target_subject
440
+ AND target_row.permissions ?| ARRAY['workspace:admin', 'members:manage']
441
+ ) AND NOT EXISTS (
442
+ SELECT 1 FROM workspace_memberships other
443
+ WHERE other.account_id = p_account_id
444
+ AND other.workspace_id = p_workspace_id
445
+ AND other.subject_id <> p_target_subject
446
+ AND other.permissions ?| ARRAY['workspace:admin', 'members:manage']
447
+ )
448
+ THEN
449
+ RAISE EXCEPTION 'cannot remove the last administering workspace member'
450
+ USING ERRCODE = '55000';
451
+ END IF;
452
+ END
453
+ $body$;
454
+
455
+ REVOKE ALL ON FUNCTION opengeni_private.assert_organization_shared_workspace_administrator(
456
+ uuid, uuid, text
457
+ ) FROM PUBLIC;
458
+ REVOKE ALL ON FUNCTION assert_organization_shared_workspace_administrator(uuid, uuid, text)
459
+ FROM PUBLIC;
460
+ REVOKE ALL ON FUNCTION open_organization_shared_workspace_administration_capability(
461
+ uuid, uuid, text
462
+ ) FROM PUBLIC;
463
+ REVOKE ALL ON FUNCTION close_organization_shared_workspace_administration_capability(uuid)
464
+ FROM PUBLIC;
465
+ REVOKE ALL ON FUNCTION create_organization_shared_workspace(
466
+ uuid, text, text, text, text, uuid
467
+ ) FROM PUBLIC;
468
+ REVOKE ALL ON FUNCTION upsert_organization_shared_workspace_member(
469
+ uuid, uuid, text, uuid, text, text, jsonb, boolean
470
+ ) FROM PUBLIC;
471
+ REVOKE ALL ON TABLE organization_shared_workspace_administration_capabilities FROM PUBLIC;
472
+
473
+ DO $body$
474
+ DECLARE
475
+ target_schema text := pg_catalog.current_schema();
476
+ BEGIN
477
+ EXECUTE pg_catalog.format(
478
+ 'ALTER FUNCTION opengeni_private.assert_organization_shared_workspace_administrator(uuid,uuid,text) SET search_path = pg_catalog, %I, pg_temp',
479
+ target_schema
480
+ );
481
+ EXECUTE pg_catalog.format(
482
+ 'ALTER FUNCTION opengeni_private.assert_workspace_membership_removal_actor(uuid,uuid,text,text) SET search_path = pg_catalog, %I, pg_temp',
483
+ target_schema
484
+ );
485
+ EXECUTE pg_catalog.format(
486
+ 'ALTER FUNCTION %I.assert_organization_shared_workspace_administrator(uuid,uuid,text) SET search_path = pg_catalog, %I, pg_temp',
487
+ target_schema, target_schema
488
+ );
489
+ EXECUTE pg_catalog.format(
490
+ 'ALTER FUNCTION %I.open_organization_shared_workspace_administration_capability(uuid,uuid,text) SET search_path = pg_catalog, %I, pg_temp',
491
+ target_schema, target_schema
492
+ );
493
+ EXECUTE pg_catalog.format(
494
+ 'ALTER FUNCTION %I.close_organization_shared_workspace_administration_capability(uuid) SET search_path = pg_catalog, %I, pg_temp',
495
+ target_schema, target_schema
496
+ );
497
+ EXECUTE pg_catalog.format(
498
+ 'ALTER FUNCTION %I.create_organization_shared_workspace(uuid,text,text,text,text,uuid) SET search_path = pg_catalog, %I, pg_temp',
499
+ target_schema, target_schema
500
+ );
501
+ EXECUTE pg_catalog.format(
502
+ 'ALTER FUNCTION %I.upsert_organization_shared_workspace_member(uuid,uuid,text,uuid,text,text,jsonb,boolean) SET search_path = pg_catalog, %I, pg_temp',
503
+ target_schema, target_schema
504
+ );
505
+ IF EXISTS (SELECT 1 FROM pg_catalog.pg_roles WHERE rolname = 'opengeni_app') THEN
506
+ EXECUTE pg_catalog.format(
507
+ 'REVOKE ALL ON TABLE %I.organization_shared_workspace_administration_capabilities FROM opengeni_app',
508
+ target_schema
509
+ );
510
+ EXECUTE pg_catalog.format(
511
+ 'GRANT EXECUTE ON FUNCTION %I.assert_organization_shared_workspace_administrator(uuid,uuid,text) TO opengeni_app',
512
+ target_schema
513
+ );
514
+ EXECUTE pg_catalog.format(
515
+ 'GRANT EXECUTE ON FUNCTION %I.upsert_organization_shared_workspace_member(uuid,uuid,text,uuid,text,text,jsonb,boolean) TO opengeni_app',
516
+ target_schema
517
+ );
518
+ EXECUTE pg_catalog.format(
519
+ 'GRANT EXECUTE ON FUNCTION %I.open_organization_shared_workspace_administration_capability(uuid,uuid,text) TO opengeni_app',
520
+ target_schema
521
+ );
522
+ EXECUTE pg_catalog.format(
523
+ 'GRANT EXECUTE ON FUNCTION %I.close_organization_shared_workspace_administration_capability(uuid) TO opengeni_app',
524
+ target_schema
525
+ );
526
+ EXECUTE pg_catalog.format(
527
+ 'GRANT EXECUTE ON FUNCTION %I.create_organization_shared_workspace(uuid,text,text,text,text,uuid) TO opengeni_app',
528
+ target_schema
529
+ );
530
+ END IF;
531
+ END
532
+ $body$;
533
+
534
+ COMMENT ON FUNCTION assert_organization_shared_workspace_administrator(uuid,uuid,text) IS
535
+ 'Authorizes the direct managed-human organization control plane for one shared workspace; Personal workspaces are excluded.';
536
+ COMMENT ON FUNCTION upsert_organization_shared_workspace_member(uuid,uuid,text,uuid,text,text,jsonb,boolean) IS
537
+ 'Assigns or updates one active organization member in a shared workspace without granting the actor operational access.';
538
+ COMMENT ON FUNCTION create_organization_shared_workspace(uuid,text,text,text,text,uuid) IS
539
+ 'Idempotently creates a shared workspace through the direct organization control plane without granting operational workspace access to the actor.';