@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,160 @@
1
+ import { OrganizationMember } from "@opengeni/contracts";
2
+ import { sql } from "drizzle-orm";
3
+ import { rawRows, type Database } from "./database";
4
+
5
+ /**
6
+ * THE canonical live-workspace-authority resolver for one exact subject, run on
7
+ * a handle that is ALREADY inside the caller's transaction with that subject's
8
+ * RLS context applied.
9
+ *
10
+ * `getWorkspaceGrant` is a bare `workspace_memberships` join and is therefore
11
+ * NOT a complete authority answer: a managed human's personal workspace
12
+ * deliberately has no row in that table (migration 0219 raises on one), so its
13
+ * owner's access is the `organization_memberships.personal_workspace_id`
14
+ * pointer instead. Any seam that re-derives "does this subject still hold
15
+ * workspace authority here" from that bare join denies the one human who always
16
+ * belongs. This module exists so that rule has exactly one implementation.
17
+ *
18
+ * Seams that must serialize the answer with their own writes — session listing,
19
+ * session pins, the composer draft — call this variant so the read and the write
20
+ * share one transaction snapshot and one advisory fence. Seams that need to ask
21
+ * about a subject that is NOT the caller call
22
+ * {@link namedSubjectHasLiveWorkspaceAuthority} (`@opengeni/db`), which is a thin
23
+ * scope wrapper over exactly this body.
24
+ *
25
+ * True when the subject currently holds workspace authority the same way the
26
+ * route-time access builder derives it: a `workspace_memberships` row whose
27
+ * owning organization membership (when one exists) is active, or an active
28
+ * organization membership whose personal-workspace pointer is exactly this
29
+ * workspace. Never infers authority any other way; a deleted membership row and
30
+ * a suspended/revoked organization membership are both false. Only the
31
+ * membership's own `personal_workspace_id` counts as stated authority — nothing
32
+ * is read from `created_by`, a default workspace, a resource name, or current
33
+ * access.
34
+ *
35
+ * # This function is NOT an authorization. Read this before relying on it.
36
+ *
37
+ * It answers "does subject X hold authority over workspace W". It does NOT
38
+ * establish that the caller is X. Nothing here authenticates anybody.
39
+ *
40
+ * The scope assertion below is a **consistency check, not an authorization**.
41
+ * It prevents one thing: a caller naming a third-party subject while its
42
+ * transaction is scoped to someone else. It cannot detect a caller that scoped
43
+ * the transaction to an attacker-chosen subject in the first place, because
44
+ * `withWorkspaceSubjectRls(db, workspaceId, subjectId, ...)` takes that subject
45
+ * from its argument too. At today's three call sites both sides come from the
46
+ * same variable, so the check is a tautology there and fires only if a future
47
+ * refactor makes them diverge. Treat it as a tripwire against that refactor —
48
+ * never as evidence that the subject was authenticated.
49
+ *
50
+ * **The actual authorization for the personal-workspace exception lives in the
51
+ * API layer**: `AccessGrantAuthorization.canonicalManagedHumanSession`
52
+ * (`@opengeni/core`), stamped only where a Better Auth cookie was verified. A
53
+ * caller that has not proven that must not enable the exception. If you are
54
+ * adding a caller, that stamp is the part you have to get right; this function
55
+ * will happily answer `true` for any subject you scope a transaction to.
56
+ *
57
+ * Non-`user:` subjects (API keys, delegated service principals, configured and
58
+ * local principals) can never own an organization membership, so they
59
+ * short-circuit to the plain membership answer instead of tripping
60
+ * `list_self_organization_memberships`' `42501`.
61
+ */
62
+ export async function subjectHasLiveWorkspaceAuthorityInScope(
63
+ scopedDb: Database,
64
+ input: { accountId: string; workspaceId: string; subjectId: string },
65
+ ): Promise<boolean> {
66
+ // CONSISTENCY CHECK, NOT AN AUTHORIZATION. This function deliberately does not
67
+ // set `opengeni.subject_id`, so it cannot be handed a scope of its own — that
68
+ // makes the oracle shape unrepresentable here. But the scope it inherits was
69
+ // set by `withWorkspaceSubjectRls` from ITS caller's argument, so a match
70
+ // proves only that one caller used one subject consistently; it proves
71
+ // nothing about who that subject is. Authorization is the API layer's
72
+ // canonical-managed-cookie stamp. See the doc comment above.
73
+ const scopedSubjectId = await subjectIdInRlsScope(scopedDb);
74
+ if (scopedSubjectId !== input.subjectId) {
75
+ throw new Error(
76
+ "subjectHasLiveWorkspaceAuthorityInScope: subject does not match the applied RLS scope",
77
+ );
78
+ }
79
+ const [membershipRow] = await rawRows<{ present: number }>(
80
+ scopedDb,
81
+ sql`select 1 as present from workspace_memberships
82
+ where subject_id = ${input.subjectId}
83
+ and workspace_id = ${input.workspaceId}
84
+ limit 1`,
85
+ );
86
+ if (!input.subjectId.startsWith("user:")) {
87
+ return Boolean(membershipRow);
88
+ }
89
+ const [organizationMembershipResult] = await rawRows<{ result: unknown }>(
90
+ scopedDb,
91
+ sql`select list_self_organization_memberships(${input.subjectId}) as result`,
92
+ );
93
+ const organizationMemberships = OrganizationMember.array().parse(
94
+ organizationMembershipResult?.result ?? [],
95
+ );
96
+ const selfOrganizationMembership = organizationMemberships.find(
97
+ (organizationMembership) => organizationMembership.organizationId === input.accountId,
98
+ );
99
+ if (membershipRow) {
100
+ // Mirror the access builder's inactive-organization filter: a persisted
101
+ // membership row is dead while its organization membership is
102
+ // suspended/revoked. A subject with no organization membership at all
103
+ // (legacy/standalone) keeps its persisted row's authority.
104
+ return !selfOrganizationMembership || selfOrganizationMembership.status === "active";
105
+ }
106
+ return (
107
+ selfOrganizationMembership?.status === "active" &&
108
+ selfOrganizationMembership.personalWorkspaceId === input.workspaceId
109
+ );
110
+ }
111
+
112
+ /**
113
+ * The applied subject GUC, or `""` when none is set. `""` is the canonical
114
+ * "unset" for this GUC: `current_subject_id()` is
115
+ * `nullif(current_setting('opengeni.subject_id', true), '')` (0239 precedent), so
116
+ * a caller restoring `""` genuinely clears it rather than pinning an empty
117
+ * subject. Never throws, so it is safe for capturing a prior value that may
118
+ * legitimately be absent.
119
+ */
120
+ export async function rlsSubjectIdOrEmpty(scopedDb: Database): Promise<string> {
121
+ const [row] = await rawRows<{ subjectId: string | null }>(
122
+ scopedDb,
123
+ sql`select current_setting('opengeni.subject_id', true) as "subjectId"`,
124
+ );
125
+ return row?.subjectId ?? "";
126
+ }
127
+
128
+ /**
129
+ * The subject of the caller's ALREADY-applied RLS scope, required to be present.
130
+ * Used by the in-scope resolver to check that a caller is asking about its own
131
+ * scope rather than naming a third party — a consistency check, not proof that
132
+ * anything authenticated that subject.
133
+ */
134
+ async function subjectIdInRlsScope(scopedDb: Database): Promise<string> {
135
+ const subjectId = await rlsSubjectIdOrEmpty(scopedDb);
136
+ if (!subjectId) {
137
+ throw new Error("RLS subject context is not applied on the active backend");
138
+ }
139
+ return subjectId;
140
+ }
141
+
142
+ /**
143
+ * The tenant account of the caller's ALREADY-applied RLS scope. `withRlsContext`
144
+ * sets and read-back-verifies this GUC on the pinned backend before running the
145
+ * scoped body, so it is exact rather than a re-derivation — which lets a seam
146
+ * that only received a workspace id consult
147
+ * {@link subjectHasLiveWorkspaceAuthorityInScope} without a second round trip to
148
+ * resolve the account.
149
+ */
150
+ export async function accountIdInRlsScope(scopedDb: Database): Promise<string> {
151
+ const [row] = await rawRows<{ accountId: string | null }>(
152
+ scopedDb,
153
+ sql`select current_setting('opengeni.account_id', true) as "accountId"`,
154
+ );
155
+ const accountId = row?.accountId ?? "";
156
+ if (!accountId) {
157
+ throw new Error("RLS account context is not applied on the active backend");
158
+ }
159
+ return accountId;
160
+ }
@@ -172,6 +172,55 @@ export const workspaceInstructionPolicyActivationEvents = pgTable(
172
172
  }),
173
173
  );
174
174
 
175
+ export const workspaceInstructionPolicyDeactivationEvents = pgTable(
176
+ "workspace_instruction_policy_deactivation_events",
177
+ {
178
+ id: uuid("id").primaryKey().defaultRandom(),
179
+ operationId: uuid("operation_id").notNull(),
180
+ requestFingerprint: text("request_fingerprint").notNull(),
181
+ accountId: uuid("account_id").notNull(),
182
+ workspaceId: uuid("workspace_id").notNull(),
183
+ kind: text("kind").notNull(),
184
+ scope: text("scope").notNull(),
185
+ roleKey: text("role_key"),
186
+ type: text("type").notNull(),
187
+ activationVersion: bigint("activation_version", { mode: "number" }).notNull(),
188
+ oldRevisionId: uuid("old_revision_id").notNull(),
189
+ oldRevision: bigint("old_revision", { mode: "number" }).notNull(),
190
+ oldContentHash: text("old_content_hash").notNull(),
191
+ actorSubjectId: text("actor_subject_id").notNull(),
192
+ reason: text("reason").notNull(),
193
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
194
+ },
195
+ (table) => ({
196
+ workspaceOperation: uniqueIndex(
197
+ "workspace_instruction_policy_deactivate_workspace_operation_uq",
198
+ ).on(table.workspaceId, table.operationId),
199
+ workspaceActivationVersion: uniqueIndex(
200
+ "workspace_instruction_policy_deactivations_target_version_uq",
201
+ ).on(
202
+ table.workspaceId,
203
+ table.kind,
204
+ table.scope,
205
+ sql`coalesce(${table.roleKey}, '')`,
206
+ table.activationVersion,
207
+ ),
208
+ workspaceTimeline: index("workspace_instruction_policy_deactivations_workspace_time_idx").on(
209
+ table.workspaceId,
210
+ table.createdAt,
211
+ table.id,
212
+ ),
213
+ target: check(
214
+ "workspace_instruction_policy_deactivations_target_chk",
215
+ sql`(
216
+ (${table.kind} = 'charter' and ${table.scope} = 'global' and ${table.roleKey} is null)
217
+ or (${table.kind} = 'policy' and ${table.scope} = 'global' and ${table.roleKey} is null)
218
+ or (${table.kind} = 'policy' and ${table.scope} = 'role' and ${table.roleKey} is not null)
219
+ )`,
220
+ ),
221
+ }),
222
+ );
223
+
175
224
  export const workspaceInstructionPolicyOnboardingProposals = pgTable(
176
225
  "workspace_instruction_policy_onboarding_proposals",
177
226
  {
@@ -211,6 +260,9 @@ export const workspaceInstructionPolicyOnboardingProposals = pgTable(
211
260
  sql`coalesce(${table.roleKey}, '')`,
212
261
  table.sourceId,
213
262
  table.sourceVersion,
263
+ // One proposal per source per baseline: a rebaselined successor is
264
+ // admissible, a duplicate against the same baseline is not.
265
+ table.baselineActivationVersion,
214
266
  ),
215
267
  workspaceTimeline: index(
216
268
  "workspace_instruction_policy_onboarding_proposals_workspace_time_idx",
@@ -239,7 +291,7 @@ export const workspaceInstructionPolicyOnboardingProposals = pgTable(
239
291
  ${table.baselineRevisionId} is null
240
292
  and ${table.baselineRevision} is null
241
293
  and ${table.baselineContentHash} is null
242
- and ${table.baselineActivationVersion} = 0
294
+ and ${table.baselineActivationVersion} >= 0
243
295
  and ${table.baselineActivatedAt} is null
244
296
  ) or (
245
297
  ${table.baselineRevisionId} is not null