@opengeni/db 1.0.1 → 3.2.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 (271) 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-4SR4HKKA.js → chunk-2EH4WRHL.js} +19 -11
  7. package/dist/chunk-2EH4WRHL.js.map +1 -0
  8. package/dist/{chunk-CUPM3FGB.js → chunk-2PRS4J5Q.js} +3 -3
  9. package/dist/chunk-6HMM2R5T.js +390 -0
  10. package/dist/chunk-6HMM2R5T.js.map +1 -0
  11. package/dist/{chunk-ZWREYEWA.js → chunk-AZHW7R5V.js} +56 -11
  12. package/dist/chunk-AZHW7R5V.js.map +1 -0
  13. package/dist/{chunk-LTG3S6ZY.js → chunk-E6CLMKEE.js} +2 -2
  14. package/dist/{chunk-6ZYKCRFO.js → chunk-KRLPEMLH.js} +7591 -4417
  15. package/dist/chunk-KRLPEMLH.js.map +1 -0
  16. package/dist/chunk-NAB6FKFN.js +516 -0
  17. package/dist/chunk-NAB6FKFN.js.map +1 -0
  18. package/dist/{chunk-SK7YYHBI.js → chunk-SIBYBA2D.js} +46 -1
  19. package/dist/chunk-SIBYBA2D.js.map +1 -0
  20. package/dist/chunk-UZNXG44K.js +4508 -0
  21. package/dist/chunk-UZNXG44K.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 +85 -0
  33. package/dist/connection-token-resolver.d.ts +30 -1
  34. package/dist/database.d.ts +23 -4
  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 +1606 -81
  43. package/dist/index.js +21898 -8147
  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 +29 -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 +43 -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 +268 -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 +28 -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/drizzle/0342_slack_route_prompt_single_pending.sql +32 -0
  189. package/drizzle/0343_personal_document_force_rls_lock_repair.sql +146 -0
  190. package/drizzle/0344_private_session_visibility_transition_gate.sql +85 -0
  191. package/drizzle/0345_tenant_scoped_session_tenancy_fence.sql +2839 -0
  192. package/drizzle/0346_document_migration_audit_surface.sql +454 -0
  193. package/drizzle/0347_connection_authority_convergence_evidence.sql +396 -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 +167 -0
  212. package/src/connection-token-resolver.ts +263 -31
  213. package/src/database.ts +87 -2
  214. package/src/editable-artifacts-schema.ts +10 -5
  215. package/src/editable-artifacts.ts +21 -11
  216. package/src/governed-learning-activation-schema.ts +121 -0
  217. package/src/governed-learning-activation.ts +588 -0
  218. package/src/governed-learning-evaluator-schema.ts +72 -0
  219. package/src/governed-learning-evaluator.ts +217 -0
  220. package/src/index.ts +11676 -1550
  221. package/src/insights.ts +107 -0
  222. package/src/integration-bindings.ts +88 -6
  223. package/src/integration-facets.ts +287 -84
  224. package/src/interaction-placement-loss.ts +234 -0
  225. package/src/managed-human-provisioning.ts +116 -0
  226. package/src/memory-domain.ts +27 -32
  227. package/src/migrate.ts +77 -1
  228. package/src/new-session-drafts.ts +229 -35
  229. package/src/organization-membership-backfill.ts +735 -0
  230. package/src/organization-membership-lifecycle.ts +995 -0
  231. package/src/pr-review.ts +890 -0
  232. package/src/preference-registry.ts +31 -1
  233. package/src/provision-roles.ts +905 -6
  234. package/src/runtime-posture-cli.ts +2 -0
  235. package/src/runtime-posture.ts +1317 -72
  236. package/src/schema.ts +3316 -428
  237. package/src/scoped-knowledge-schema.ts +16 -8
  238. package/src/scoped-knowledge.ts +1 -1
  239. package/src/session-background-commands.ts +759 -0
  240. package/src/session-control.ts +640 -74
  241. package/src/session-goal-pacing.ts +126 -0
  242. package/src/session-queue-commands.ts +441 -85
  243. package/src/session-realtime-context.ts +4 -6
  244. package/src/session-realtime-ledger.ts +28 -14
  245. package/src/session-realtime-mirror.ts +1 -1
  246. package/src/session-tenancy.ts +410 -49
  247. package/src/session-tool-call-settlement.ts +38 -29
  248. package/src/slack-routing-personal-workspace.ts +67 -0
  249. package/src/slack-routing.ts +867 -0
  250. package/src/slack-task-policy-schema.ts +5 -0
  251. package/src/slack-task-policy.ts +18 -0
  252. package/src/slack-user-link-access.ts +138 -11
  253. package/src/task-notes-schema.ts +74 -0
  254. package/src/task-notes.ts +124 -2
  255. package/src/turn-initiator.ts +8 -0
  256. package/src/user-resource-authority.ts +377 -0
  257. package/src/workspace-authority.ts +160 -0
  258. package/src/workspace-instruction-policies-schema.ts +53 -1
  259. package/src/workspace-instruction-policies.ts +528 -77
  260. package/src/workspace-learning-policy.ts +21 -5
  261. package/src/workspace-membership-permissions.ts +9 -0
  262. package/dist/chunk-4SR4HKKA.js.map +0 -1
  263. package/dist/chunk-6ZYKCRFO.js.map +0 -1
  264. package/dist/chunk-NXAFLCQX.js +0 -2468
  265. package/dist/chunk-NXAFLCQX.js.map +0 -1
  266. package/dist/chunk-SK7YYHBI.js.map +0 -1
  267. package/dist/chunk-WD5ROSLL.js +0 -117
  268. package/dist/chunk-WD5ROSLL.js.map +0 -1
  269. package/dist/chunk-ZWREYEWA.js.map +0 -1
  270. /package/dist/{chunk-CUPM3FGB.js.map → chunk-2PRS4J5Q.js.map} +0 -0
  271. /package/dist/{chunk-LTG3S6ZY.js.map → chunk-E6CLMKEE.js.map} +0 -0
@@ -0,0 +1,1170 @@
1
+ import { createHash } from "node:crypto";
2
+ import {
3
+ AGENT_AUTHORED_INSTRUCTION_POLICY_CONTENT_MAX_CHARS,
4
+ AGENT_AUTHORED_PREFERENCE_CONTENT_MAX_CHARS,
5
+ CompanyBrainGovernedWriteAttempt,
6
+ CompanyBrainGovernedWriteReceipt,
7
+ CompanyBrainGovernedWriteRequest,
8
+ PREFERENCE_REGISTRY_DESCRIPTOR_DESCRIPTION_MAX_CHARS,
9
+ PREFERENCE_REGISTRY_TITLE_MAX_CHARS,
10
+ agentAuthoredDurableTextTooLongMessage,
11
+ type CompanyBrainGovernedWriteReceipt as CompanyBrainGovernedWriteReceiptType,
12
+ type CompanyBrainGovernedWriteRequest as CompanyBrainGovernedWriteRequestType,
13
+ type ScopedKnowledgeActor,
14
+ } from "@opengeni/contracts";
15
+ import { and, eq, inArray, sql } from "drizzle-orm";
16
+ import type { Database } from "./database";
17
+ import { rawRows, setSubjectRlsContext, withRlsContext } from "./database";
18
+ import { sanitizePreferenceDescriptorText } from "./preference-registry";
19
+ import {
20
+ appendKnowledgeClaim,
21
+ appendKnowledgeClaimReview,
22
+ createKnowledgeChangeProposal,
23
+ linkKnowledgeClaims,
24
+ scopedKnowledgeInputHash,
25
+ ScopedKnowledgeAuthorityError,
26
+ ScopedKnowledgeInvalidOperationError,
27
+ upsertKnowledgeEntity,
28
+ upsertKnowledgeFact,
29
+ } from "./scoped-knowledge";
30
+ import * as schema from "./schema";
31
+ import { createWorkspaceInstructionPolicyKnowledgeProposal } from "./workspace-instruction-policies";
32
+
33
+ export class CompanyBrainGovernedWriteAuthorityError extends Error {
34
+ readonly name = "CompanyBrainGovernedWriteAuthorityError";
35
+ }
36
+
37
+ export class CompanyBrainGovernedWriteInvalidOperationError extends Error {
38
+ readonly name = "CompanyBrainGovernedWriteInvalidOperationError";
39
+ }
40
+
41
+ type AttemptAuthority = CompanyBrainGovernedWriteAttempt & {
42
+ initiatingHumanSubjectId: string;
43
+ };
44
+
45
+ type WorkspaceEvidence = {
46
+ claimId: string;
47
+ evidenceId: string;
48
+ confidenceBps: number;
49
+ };
50
+
51
+ type TaskNotePromotionSource = {
52
+ noteId: string;
53
+ rootSessionId: string;
54
+ noteVersion: 1;
55
+ noteText: string;
56
+ noteTextHash: string;
57
+ noteCreatedAt: string;
58
+ initiatingHumanSubjectId: string;
59
+ };
60
+
61
+ type TaskNotePromotionRequest = Extract<
62
+ CompanyBrainGovernedWriteRequestType,
63
+ {
64
+ kind:
65
+ | "promote_task_note_knowledge"
66
+ | "promote_task_note_instruction_policy"
67
+ | "promote_task_note_preference";
68
+ }
69
+ >;
70
+
71
+ type InstructionPolicyProposalRequest = Extract<
72
+ CompanyBrainGovernedWriteRequestType,
73
+ { kind: "propose_instruction_policy" | "promote_task_note_instruction_policy" }
74
+ >;
75
+
76
+ type PreferenceProposalRequest = Extract<
77
+ CompanyBrainGovernedWriteRequestType,
78
+ { kind: "propose_preference" | "promote_task_note_preference" }
79
+ >;
80
+
81
+ type TaskNoteKnowledgeMaterialization = WorkspaceEvidence & {
82
+ reviewId: string;
83
+ source: TaskNotePromotionSource;
84
+ };
85
+
86
+ type NormalizedPreferenceProposal = {
87
+ stableKey: string;
88
+ title: string;
89
+ description: string;
90
+ content: string;
91
+ precedenceRank: number;
92
+ conflictStrategy: "override" | "merge" | "reject" | "inform";
93
+ conflictsWith: string[];
94
+ expiresAt: string | null;
95
+ };
96
+
97
+ function isTaskNotePromotion(
98
+ request: CompanyBrainGovernedWriteRequestType,
99
+ ): request is TaskNotePromotionRequest {
100
+ return (
101
+ request.kind === "promote_task_note_knowledge" ||
102
+ request.kind === "promote_task_note_instruction_policy" ||
103
+ request.kind === "promote_task_note_preference"
104
+ );
105
+ }
106
+
107
+ function derivedOperationId(operationId: string, label: string): string {
108
+ const bytes = Buffer.from(
109
+ createHash("sha256")
110
+ .update(`company-brain-governed-write:v1:${operationId}:${label}`, "utf8")
111
+ .digest("hex")
112
+ .slice(0, 32),
113
+ "hex",
114
+ );
115
+ bytes[6] = (bytes[6]! & 0x0f) | 0x50;
116
+ bytes[8] = (bytes[8]! & 0x3f) | 0x80;
117
+ const hex = bytes.toString("hex");
118
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
119
+ }
120
+
121
+ function preferenceProposalContent(request: PreferenceProposalRequest, content: string): string {
122
+ return JSON.stringify(normalizePreferenceProposal(request, content));
123
+ }
124
+
125
+ function normalizePreferenceProposal(
126
+ request: PreferenceProposalRequest,
127
+ content: string,
128
+ ): NormalizedPreferenceProposal {
129
+ const title = sanitizePreferenceDescriptorText(request.title)
130
+ .slice(0, PREFERENCE_REGISTRY_TITLE_MAX_CHARS)
131
+ .trim();
132
+ const description = sanitizePreferenceDescriptorText(request.description)
133
+ .slice(0, PREFERENCE_REGISTRY_DESCRIPTOR_DESCRIPTION_MAX_CHARS)
134
+ .trim();
135
+ if (!title || !description) {
136
+ throw new CompanyBrainGovernedWriteInvalidOperationError(
137
+ "Preference proposal title and description must contain visible plain text",
138
+ );
139
+ }
140
+ return {
141
+ stableKey: request.stableKey,
142
+ title,
143
+ description,
144
+ content,
145
+ precedenceRank: request.precedenceRank,
146
+ conflictStrategy: request.conflictStrategy,
147
+ conflictsWith: [...new Set(request.conflictsWith)].sort(),
148
+ expiresAt: request.expiresAt ? new Date(request.expiresAt).toISOString() : null,
149
+ };
150
+ }
151
+
152
+ async function withCompanyBrainGovernedWriteAuthority<T, P>(
153
+ db: Database,
154
+ attempt: CompanyBrainGovernedWriteAttempt,
155
+ destinationWorkspaceLock: "key_share" | "update",
156
+ prelock: (db: Database) => Promise<P>,
157
+ fn: (db: Database, authority: AttemptAuthority, prelocked: P) => Promise<T>,
158
+ ): Promise<T> {
159
+ return await withRlsContext(
160
+ db,
161
+ { accountId: attempt.accountId, workspaceId: attempt.workspaceId },
162
+ async (scopedDb) => {
163
+ // Instruction-policy lifecycle writes serialize on the workspace row.
164
+ // Acquire that destination lock before Task-note root/session locks so a
165
+ // later nested lifecycle call only re-enters a lock already held by this
166
+ // transaction. Taking KEY SHARE here and upgrading after distinct roots
167
+ // are locked lets two valid promotions deadlock each other.
168
+ if (destinationWorkspaceLock === "update") {
169
+ const lockedWorkspace = await rawRows<{ id: string }>(
170
+ scopedDb,
171
+ sql`
172
+ SELECT workspace.id
173
+ FROM workspaces workspace
174
+ WHERE workspace.id = ${attempt.workspaceId}::uuid
175
+ AND workspace.account_id = ${attempt.accountId}::uuid
176
+ FOR UPDATE OF workspace
177
+ `,
178
+ );
179
+ if (!lockedWorkspace[0]) {
180
+ throw new CompanyBrainGovernedWriteAuthorityError(
181
+ "Governed Company Brain write workspace is unavailable",
182
+ );
183
+ }
184
+ }
185
+ // Tree-scoped authorities own the root/current-session lock prefix. Run
186
+ // them before the generic current-session locks below to avoid inverting
187
+ // the canonical Task-note lifecycle order.
188
+ const prelocked = await prelock(scopedDb);
189
+ const rows = await rawRows<{ initiating_human_subject_id: string }>(
190
+ scopedDb,
191
+ sql`
192
+ WITH locked_workspace AS MATERIALIZED (
193
+ SELECT workspace.id, workspace.account_id
194
+ FROM workspaces workspace
195
+ WHERE workspace.id = ${attempt.workspaceId}::uuid
196
+ AND workspace.account_id = ${attempt.accountId}::uuid
197
+ FOR KEY SHARE OF workspace
198
+ ), locked_session AS MATERIALIZED (
199
+ SELECT session.id, session.account_id, session.workspace_id, session.active_turn_id
200
+ FROM sessions session
201
+ JOIN locked_workspace workspace
202
+ ON workspace.id = session.workspace_id
203
+ AND workspace.account_id = session.account_id
204
+ WHERE session.id = ${attempt.sessionId}::uuid
205
+ AND session.active_turn_id = ${attempt.turnId}::uuid
206
+ FOR SHARE OF session
207
+ ), locked_turn AS MATERIALIZED (
208
+ SELECT turn.id, turn.account_id, turn.workspace_id, turn.session_id,
209
+ turn.active_attempt_id, turn.execution_generation,
210
+ coalesce(
211
+ turn.initiating_human_subject_id,
212
+ case when turn.initiator_kind = 'subject' then turn.initiator_subject_id end
213
+ ) as initiating_human_subject_id
214
+ FROM session_turns turn
215
+ JOIN locked_session session
216
+ ON session.id = turn.session_id
217
+ AND session.workspace_id = turn.workspace_id
218
+ AND session.account_id = turn.account_id
219
+ WHERE turn.id = ${attempt.turnId}::uuid
220
+ AND turn.active_attempt_id = ${attempt.attemptId}::uuid
221
+ AND turn.execution_generation = ${attempt.executionGeneration}
222
+ AND turn.status IN ('running', 'requires_action', 'recovering', 'waiting_capacity')
223
+ AND length(btrim(coalesce(
224
+ turn.initiating_human_subject_id,
225
+ case when turn.initiator_kind = 'subject' then turn.initiator_subject_id end
226
+ ))) BETWEEN 1 AND 1024
227
+ FOR SHARE OF turn
228
+ ), locked_attempt AS MATERIALIZED (
229
+ SELECT attempt.id, attempt.account_id, attempt.workspace_id,
230
+ attempt.session_id, attempt.turn_id, attempt.execution_generation
231
+ FROM session_turn_attempts attempt
232
+ JOIN locked_turn turn
233
+ ON turn.id = attempt.turn_id
234
+ AND turn.session_id = attempt.session_id
235
+ AND turn.workspace_id = attempt.workspace_id
236
+ AND turn.account_id = attempt.account_id
237
+ WHERE attempt.id = ${attempt.attemptId}::uuid
238
+ AND attempt.execution_generation = ${attempt.executionGeneration}
239
+ AND attempt.state IN ('claimed', 'running')
240
+ FOR SHARE OF attempt
241
+ )
242
+ SELECT turn.initiating_human_subject_id
243
+ FROM locked_workspace workspace
244
+ JOIN locked_session session ON true
245
+ JOIN locked_turn turn ON true
246
+ JOIN locked_attempt attempt ON true
247
+ WHERE workspace.account_id = attempt.account_id
248
+ AND workspace.id = attempt.workspace_id
249
+ AND session.id = attempt.session_id
250
+ AND turn.id = attempt.turn_id
251
+ `,
252
+ );
253
+ const initiatingHumanSubjectId = rows[0]?.initiating_human_subject_id;
254
+ if (!initiatingHumanSubjectId) {
255
+ throw new CompanyBrainGovernedWriteAuthorityError(
256
+ "Governed Company Brain writes require the exact current attempt, generation, immutable human initiator, and no live interruption",
257
+ );
258
+ }
259
+ // Lock acquisition may wait behind Pause/Steer. Under READ COMMITTED, a
260
+ // NOT EXISTS evaluated in that same statement can retain its pre-wait
261
+ // snapshot and miss the newly committed interruption. Recheck in a fresh
262
+ // statement after all canonical rows are locked; interruption writers now
263
+ // cannot advance until this transaction completes.
264
+ const revalidated = await rawRows<{ id: string }>(
265
+ scopedDb,
266
+ sql`
267
+ SELECT attempt.id
268
+ FROM sessions session
269
+ JOIN session_turns turn
270
+ ON turn.account_id = session.account_id
271
+ AND turn.workspace_id = session.workspace_id
272
+ AND turn.session_id = session.id
273
+ JOIN session_turn_attempts attempt
274
+ ON attempt.account_id = turn.account_id
275
+ AND attempt.workspace_id = turn.workspace_id
276
+ AND attempt.session_id = turn.session_id
277
+ AND attempt.turn_id = turn.id
278
+ WHERE session.account_id = ${attempt.accountId}::uuid
279
+ AND session.workspace_id = ${attempt.workspaceId}::uuid
280
+ AND session.id = ${attempt.sessionId}::uuid
281
+ AND session.active_turn_id = ${attempt.turnId}::uuid
282
+ AND turn.id = ${attempt.turnId}::uuid
283
+ AND turn.active_attempt_id = ${attempt.attemptId}::uuid
284
+ AND turn.execution_generation = ${attempt.executionGeneration}
285
+ AND turn.status IN ('running', 'requires_action', 'recovering', 'waiting_capacity')
286
+ AND attempt.id = ${attempt.attemptId}::uuid
287
+ AND attempt.execution_generation = ${attempt.executionGeneration}
288
+ AND attempt.state IN ('claimed', 'running')
289
+ AND NOT EXISTS (
290
+ SELECT 1
291
+ FROM session_attempt_interruptions interruption
292
+ WHERE interruption.workspace_id = attempt.workspace_id
293
+ AND interruption.attempt_id = attempt.id
294
+ AND interruption.state IN ('pending', 'delivered', 'acknowledged')
295
+ )
296
+ `,
297
+ );
298
+ if (!revalidated[0]) {
299
+ throw new CompanyBrainGovernedWriteAuthorityError(
300
+ "Governed Company Brain write authority changed while admission was waiting",
301
+ );
302
+ }
303
+ await setSubjectRlsContext(scopedDb, initiatingHumanSubjectId);
304
+ return await fn(scopedDb, { ...attempt, initiatingHumanSubjectId }, prelocked);
305
+ },
306
+ );
307
+ }
308
+
309
+ async function requireWorkspaceEvidence(
310
+ db: Database,
311
+ authority: AttemptAuthority,
312
+ claimId: string,
313
+ evidenceId: string,
314
+ replacesClaimId: string | null,
315
+ ): Promise<WorkspaceEvidence> {
316
+ const claimIds = replacesClaimId === null ? [claimId] : [claimId, replacesClaimId];
317
+ const [claims, evidenceRows] = await Promise.all([
318
+ db
319
+ .select()
320
+ .from(schema.knowledgeClaims)
321
+ .where(
322
+ and(
323
+ eq(schema.knowledgeClaims.accountId, authority.accountId),
324
+ inArray(schema.knowledgeClaims.id, claimIds),
325
+ ),
326
+ ),
327
+ db
328
+ .select()
329
+ .from(schema.knowledgeClaimEvidence)
330
+ .where(
331
+ and(
332
+ eq(schema.knowledgeClaimEvidence.accountId, authority.accountId),
333
+ eq(schema.knowledgeClaimEvidence.id, evidenceId),
334
+ ),
335
+ )
336
+ .limit(1),
337
+ ]);
338
+ const claim = claims.find((candidate) => candidate.id === claimId);
339
+ const replaced = replacesClaimId
340
+ ? claims.find((candidate) => candidate.id === replacesClaimId)
341
+ : null;
342
+ const evidence = evidenceRows[0];
343
+ const isExactWorkspaceClaim = (candidate: typeof claim | null): boolean =>
344
+ candidate != null &&
345
+ candidate.scopeKind === "workspace" &&
346
+ candidate.scopeWorkspaceId === authority.workspaceId &&
347
+ candidate.scopeSubjectId === null;
348
+ if (
349
+ !isExactWorkspaceClaim(claim) ||
350
+ !evidence ||
351
+ evidence.claimId !== claimId ||
352
+ evidence.polarity !== "supports" ||
353
+ evidence.scopeKind !== "workspace" ||
354
+ evidence.scopeWorkspaceId !== authority.workspaceId ||
355
+ evidence.scopeSubjectId !== null
356
+ ) {
357
+ throw new CompanyBrainGovernedWriteInvalidOperationError(
358
+ "Governed writes require exact supporting evidence for a workspace-local Knowledge claim",
359
+ );
360
+ }
361
+ if (
362
+ replacesClaimId !== null &&
363
+ (replacesClaimId === claimId || !isExactWorkspaceClaim(replaced))
364
+ ) {
365
+ throw new CompanyBrainGovernedWriteInvalidOperationError(
366
+ "Knowledge correction requires a different replaced claim in the same workspace scope",
367
+ );
368
+ }
369
+ return { claimId, evidenceId, confidenceBps: claim!.confidenceBps };
370
+ }
371
+
372
+ async function createWorkspacePreferenceProposalForAttempt(
373
+ db: Database,
374
+ authority: AttemptAuthority,
375
+ request: PreferenceProposalRequest,
376
+ content: string,
377
+ knowledgeProposalId: string,
378
+ inputHash: string,
379
+ ): Promise<{ preferenceId: string; revisionId: string }> {
380
+ const normalized = normalizePreferenceProposal(request, content);
381
+ const [result] = await rawRows<{ preferenceId: string; revisionId: string }>(
382
+ db,
383
+ sql`
384
+ SELECT
385
+ proposal.preference_id AS "preferenceId",
386
+ proposal.revision_id AS "revisionId"
387
+ FROM preference_registry_create_knowledge_proposal_for_attempt(
388
+ ${authority.accountId}::uuid,
389
+ ${authority.workspaceId}::uuid,
390
+ ${authority.sessionId}::uuid,
391
+ ${authority.turnId}::uuid,
392
+ ${authority.attemptId}::uuid,
393
+ ${authority.executionGeneration},
394
+ ${request.operationId}::uuid,
395
+ ${inputHash},
396
+ ${knowledgeProposalId}::uuid,
397
+ ${normalized.stableKey},
398
+ ${normalized.title},
399
+ ${normalized.description},
400
+ ${normalized.content},
401
+ ${normalized.precedenceRank},
402
+ ${normalized.conflictStrategy},
403
+ ${JSON.stringify(normalized.conflictsWith)}::jsonb,
404
+ ${normalized.expiresAt}::timestamptz,
405
+ ${request.reason}
406
+ ) proposal
407
+ `,
408
+ );
409
+ if (!result) throw new Error("Preference Knowledge proposal returned no durable receipt");
410
+ return result;
411
+ }
412
+
413
+ function receipt(
414
+ input: Omit<CompanyBrainGovernedWriteReceiptType, "outcome" | "effectiveBoundary" | "rollback">,
415
+ ): CompanyBrainGovernedWriteReceiptType {
416
+ return CompanyBrainGovernedWriteReceipt.parse({
417
+ ...input,
418
+ outcome: "proposed",
419
+ effectiveBoundary: "human_review_required",
420
+ rollback: { supported: false, mechanism: "not_applicable_proposal_only" },
421
+ });
422
+ }
423
+
424
+ async function resolveTaskNotePromotionSource(
425
+ db: Database,
426
+ authority: CompanyBrainGovernedWriteAttempt,
427
+ noteId: string,
428
+ expectedNoteVersion: 1,
429
+ evidenceOperationId: string,
430
+ claimOperationId: string,
431
+ actorSubjectId: string,
432
+ ): Promise<TaskNotePromotionSource> {
433
+ const [row] = await rawRows<{
434
+ noteId: string;
435
+ rootSessionId: string;
436
+ noteVersion: number;
437
+ noteText: string;
438
+ noteTextHash: string;
439
+ noteCreatedAt: Date | string;
440
+ initiatingHumanSubjectId: string;
441
+ }>(
442
+ db,
443
+ sql`
444
+ SELECT
445
+ source.note_id AS "noteId",
446
+ source.root_session_id AS "rootSessionId",
447
+ source.note_version AS "noteVersion",
448
+ source.note_text AS "noteText",
449
+ source.note_text_hash AS "noteTextHash",
450
+ source.note_created_at AS "noteCreatedAt",
451
+ source.initiating_human_subject_id AS "initiatingHumanSubjectId"
452
+ FROM resolve_task_note_knowledge_promotion_source(
453
+ ${authority.accountId}::uuid,
454
+ ${authority.workspaceId}::uuid,
455
+ ${authority.sessionId}::uuid,
456
+ ${authority.turnId}::uuid,
457
+ ${authority.attemptId}::uuid,
458
+ ${authority.executionGeneration},
459
+ ${noteId}::uuid,
460
+ ${expectedNoteVersion},
461
+ ${evidenceOperationId},
462
+ ${claimOperationId},
463
+ ${actorSubjectId}
464
+ ) source
465
+ `,
466
+ );
467
+ if (!row || row.noteVersion !== 1) {
468
+ throw new CompanyBrainGovernedWriteAuthorityError(
469
+ "Task-note promotion source did not preserve exact attempt authority",
470
+ );
471
+ }
472
+ return {
473
+ noteId: row.noteId,
474
+ rootSessionId: row.rootSessionId,
475
+ noteVersion: 1,
476
+ noteText: row.noteText,
477
+ noteTextHash: row.noteTextHash,
478
+ noteCreatedAt: new Date(row.noteCreatedAt).toISOString(),
479
+ initiatingHumanSubjectId: row.initiatingHumanSubjectId,
480
+ };
481
+ }
482
+
483
+ async function taskNotePromotionAlreadyMaterialized(
484
+ db: Database,
485
+ accountId: string,
486
+ operationId: string,
487
+ ): Promise<boolean> {
488
+ const [row] = await db
489
+ .select({ id: schema.knowledgeClaimEvidence.id })
490
+ .from(schema.knowledgeClaimEvidence)
491
+ .where(
492
+ and(
493
+ eq(schema.knowledgeClaimEvidence.accountId, accountId),
494
+ eq(schema.knowledgeClaimEvidence.operationId, operationId),
495
+ ),
496
+ )
497
+ .limit(1);
498
+ return row !== undefined;
499
+ }
500
+
501
+ async function appendTaskNoteKnowledgeEvidence(
502
+ db: Database,
503
+ input: {
504
+ authority: AttemptAuthority;
505
+ operationId: string;
506
+ actor: ScopedKnowledgeActor;
507
+ claimId: string;
508
+ source: TaskNotePromotionSource;
509
+ },
510
+ ): Promise<{ id: string }> {
511
+ const inputHash = scopedKnowledgeInputHash({
512
+ claimId: input.claimId,
513
+ taskNoteId: input.source.noteId,
514
+ taskNoteRootSessionId: input.source.rootSessionId,
515
+ taskNoteVersion: input.source.noteVersion,
516
+ contentHash: input.source.noteTextHash,
517
+ polarity: "supports",
518
+ actor: input.actor,
519
+ });
520
+ const [claim] = await db
521
+ .select()
522
+ .from(schema.knowledgeClaims)
523
+ .where(eq(schema.knowledgeClaims.id, input.claimId))
524
+ .limit(1);
525
+ if (
526
+ !claim ||
527
+ claim.accountId !== input.authority.accountId ||
528
+ claim.scopeKind !== "workspace" ||
529
+ claim.scopeWorkspaceId !== input.authority.workspaceId ||
530
+ claim.scopeSubjectId !== null
531
+ ) {
532
+ throw new CompanyBrainGovernedWriteInvalidOperationError(
533
+ "Task-note promotion claim must remain in the source workspace",
534
+ );
535
+ }
536
+ const [created] = await db
537
+ .insert(schema.knowledgeClaimEvidence)
538
+ .values({
539
+ accountId: input.authority.accountId,
540
+ scopeKind: claim.scopeKind,
541
+ scopeWorkspaceId: claim.scopeWorkspaceId,
542
+ scopeSubjectId: claim.scopeSubjectId,
543
+ scopeKey: claim.scopeKey,
544
+ claimId: claim.id,
545
+ documentVersionId: null,
546
+ taskNoteId: input.source.noteId,
547
+ taskNoteRootSessionId: input.source.rootSessionId,
548
+ taskNoteVersion: input.source.noteVersion,
549
+ polarity: "supports",
550
+ documentChunkId: null,
551
+ chunkIndex: null,
552
+ locator: null,
553
+ quoteHash: null,
554
+ contentHash: input.source.noteTextHash,
555
+ operationId: input.operationId,
556
+ inputHash,
557
+ actorKind: input.actor.kind,
558
+ actorSubjectId: input.actor.subjectId,
559
+ initiatingHumanSubjectId: input.actor.initiatingHumanSubjectId,
560
+ })
561
+ .onConflictDoNothing()
562
+ .returning({ id: schema.knowledgeClaimEvidence.id });
563
+ if (created) return created;
564
+ const [replayed] = await db
565
+ .select({
566
+ id: schema.knowledgeClaimEvidence.id,
567
+ inputHash: schema.knowledgeClaimEvidence.inputHash,
568
+ })
569
+ .from(schema.knowledgeClaimEvidence)
570
+ .where(
571
+ and(
572
+ eq(schema.knowledgeClaimEvidence.accountId, input.authority.accountId),
573
+ eq(schema.knowledgeClaimEvidence.operationId, input.operationId),
574
+ ),
575
+ )
576
+ .limit(1);
577
+ if (!replayed || replayed.inputHash !== inputHash) {
578
+ throw new CompanyBrainGovernedWriteInvalidOperationError(
579
+ "Task-note evidence operation was replayed with different immutable input",
580
+ );
581
+ }
582
+ return { id: replayed.id };
583
+ }
584
+
585
+ async function replayTaskNoteKnowledgeMaterialization(
586
+ db: Database,
587
+ authority: AttemptAuthority,
588
+ operationId: string,
589
+ topLevelInputHash: string,
590
+ ): Promise<TaskNoteKnowledgeMaterialization | null> {
591
+ const evidenceOperationId = derivedOperationId(operationId, "task-note-evidence");
592
+ const [evidence] = await db
593
+ .select()
594
+ .from(schema.knowledgeClaimEvidence)
595
+ .where(
596
+ and(
597
+ eq(schema.knowledgeClaimEvidence.accountId, authority.accountId),
598
+ eq(schema.knowledgeClaimEvidence.operationId, evidenceOperationId),
599
+ ),
600
+ )
601
+ .limit(1);
602
+ if (!evidence) return null;
603
+ const expectedActor = `service:company-brain-governed-write:${topLevelInputHash}`;
604
+ if (
605
+ evidence.actorSubjectId !== expectedActor ||
606
+ evidence.initiatingHumanSubjectId !== authority.initiatingHumanSubjectId ||
607
+ evidence.scopeKind !== "workspace" ||
608
+ evidence.scopeWorkspaceId !== authority.workspaceId ||
609
+ evidence.scopeSubjectId !== null ||
610
+ evidence.documentVersionId !== null ||
611
+ evidence.taskNoteId === null ||
612
+ evidence.taskNoteRootSessionId === null ||
613
+ evidence.taskNoteVersion !== 1
614
+ ) {
615
+ throw new CompanyBrainGovernedWriteInvalidOperationError(
616
+ "Task-note promotion operation was replayed with different immutable input",
617
+ );
618
+ }
619
+ const [claim] = await db
620
+ .select()
621
+ .from(schema.knowledgeClaims)
622
+ .where(
623
+ and(
624
+ eq(schema.knowledgeClaims.accountId, authority.accountId),
625
+ eq(schema.knowledgeClaims.id, evidence.claimId),
626
+ ),
627
+ )
628
+ .limit(1);
629
+ const [fact] = claim
630
+ ? await db
631
+ .select()
632
+ .from(schema.knowledgeFacts)
633
+ .where(
634
+ and(
635
+ eq(schema.knowledgeFacts.accountId, authority.accountId),
636
+ eq(schema.knowledgeFacts.id, claim.factId),
637
+ ),
638
+ )
639
+ .limit(1)
640
+ : [];
641
+ const noteText = fact?.objectValue;
642
+ if (
643
+ !claim ||
644
+ !fact ||
645
+ claim.extractionMethod !== "task-note-promotion-v1" ||
646
+ claim.scopeKind !== "workspace" ||
647
+ claim.scopeWorkspaceId !== authority.workspaceId ||
648
+ claim.scopeSubjectId !== null ||
649
+ claim.actorKind !== "service" ||
650
+ claim.actorSubjectId !== expectedActor ||
651
+ claim.initiatingHumanSubjectId !== authority.initiatingHumanSubjectId ||
652
+ fact.scopeKind !== "workspace" ||
653
+ fact.scopeWorkspaceId !== authority.workspaceId ||
654
+ fact.scopeSubjectId !== null ||
655
+ fact.actorKind !== "service" ||
656
+ fact.actorSubjectId !== expectedActor ||
657
+ fact.initiatingHumanSubjectId !== authority.initiatingHumanSubjectId ||
658
+ fact.objectKind !== "text" ||
659
+ typeof noteText !== "string" ||
660
+ createHash("sha256").update(noteText, "utf8").digest("hex") !== evidence.contentHash
661
+ ) {
662
+ throw new CompanyBrainGovernedWriteInvalidOperationError(
663
+ "Task-note promotion Knowledge lineage was replayed with different immutable input",
664
+ );
665
+ }
666
+ const [review] = await db
667
+ .select()
668
+ .from(schema.knowledgeClaimReviews)
669
+ .where(
670
+ and(
671
+ eq(schema.knowledgeClaimReviews.accountId, authority.accountId),
672
+ eq(schema.knowledgeClaimReviews.operationId, derivedOperationId(operationId, "guard")),
673
+ eq(schema.knowledgeClaimReviews.claimId, evidence.claimId),
674
+ ),
675
+ )
676
+ .limit(1);
677
+ if (
678
+ !review ||
679
+ review.scopeKind !== "workspace" ||
680
+ review.scopeWorkspaceId !== authority.workspaceId ||
681
+ review.scopeSubjectId !== null ||
682
+ review.state !== "proposed" ||
683
+ review.actorKind !== "service" ||
684
+ review.actorSubjectId !== expectedActor ||
685
+ review.initiatingHumanSubjectId !== authority.initiatingHumanSubjectId
686
+ ) {
687
+ throw new CompanyBrainGovernedWriteInvalidOperationError(
688
+ "Task-note promotion evidence has no matching proposal review receipt",
689
+ );
690
+ }
691
+ return {
692
+ claimId: evidence.claimId,
693
+ evidenceId: evidence.id,
694
+ confidenceBps: claim.confidenceBps,
695
+ reviewId: review.id,
696
+ source: {
697
+ noteId: evidence.taskNoteId,
698
+ rootSessionId: evidence.taskNoteRootSessionId,
699
+ noteVersion: 1,
700
+ noteText,
701
+ noteTextHash: evidence.contentHash,
702
+ noteCreatedAt: new Date(claim.effectiveAt).toISOString(),
703
+ initiatingHumanSubjectId: authority.initiatingHumanSubjectId,
704
+ },
705
+ };
706
+ }
707
+
708
+ async function materializeTaskNoteKnowledge(
709
+ db: Database,
710
+ input: {
711
+ authority: AttemptAuthority;
712
+ request: TaskNotePromotionRequest;
713
+ actor: ScopedKnowledgeActor;
714
+ source: TaskNotePromotionSource;
715
+ evidenceOperationId: string;
716
+ claimOperationId: string;
717
+ },
718
+ ): Promise<TaskNoteKnowledgeMaterialization> {
719
+ const entity = await upsertKnowledgeEntity(db, {
720
+ accountId: input.authority.accountId,
721
+ workspaceId: input.authority.workspaceId,
722
+ scope: {
723
+ kind: "workspace",
724
+ workspaceId: input.authority.workspaceId,
725
+ subjectId: null,
726
+ },
727
+ operationId: derivedOperationId(input.request.operationId, "task-note-entity"),
728
+ actor: input.actor,
729
+ entityType: input.request.entityType,
730
+ normalizedKey: input.request.normalizedKey,
731
+ displayName: input.request.displayName,
732
+ });
733
+ const fact = await upsertKnowledgeFact(db, {
734
+ accountId: input.authority.accountId,
735
+ workspaceId: input.authority.workspaceId,
736
+ operationId: derivedOperationId(input.request.operationId, "task-note-fact"),
737
+ actor: input.actor,
738
+ subjectEntityId: entity.id,
739
+ predicateKey: input.request.predicateKey,
740
+ object: { kind: "text", value: input.source.noteText },
741
+ });
742
+ const claim = await appendKnowledgeClaim(db, {
743
+ accountId: input.authority.accountId,
744
+ workspaceId: input.authority.workspaceId,
745
+ operationId: input.claimOperationId,
746
+ actor: input.actor,
747
+ factId: fact.id,
748
+ origin: "inferred",
749
+ confidenceBps: input.request.confidenceBps,
750
+ effectiveAt: input.source.noteCreatedAt,
751
+ expiresAt: null,
752
+ extractionMethod: "task-note-promotion-v1",
753
+ extractionMetadata: {
754
+ taskNoteId: input.source.noteId,
755
+ taskNoteRootSessionId: input.source.rootSessionId,
756
+ taskNoteVersion: input.source.noteVersion,
757
+ taskNoteTextHash: input.source.noteTextHash,
758
+ },
759
+ });
760
+ const evidence = await appendTaskNoteKnowledgeEvidence(db, {
761
+ authority: input.authority,
762
+ operationId: input.evidenceOperationId,
763
+ actor: input.actor,
764
+ claimId: claim.id,
765
+ source: input.source,
766
+ });
767
+ const review = await appendKnowledgeClaimReview(db, {
768
+ accountId: input.authority.accountId,
769
+ workspaceId: input.authority.workspaceId,
770
+ operationId: derivedOperationId(input.request.operationId, "guard"),
771
+ actor: input.actor,
772
+ claimId: claim.id,
773
+ state: "proposed",
774
+ reason: input.request.reason,
775
+ });
776
+ return {
777
+ claimId: claim.id,
778
+ evidenceId: evidence.id,
779
+ confidenceBps: claim.confidenceBps,
780
+ reviewId: review.id,
781
+ source: input.source,
782
+ };
783
+ }
784
+
785
+ /**
786
+ * The identity of a governed write is what was asked for, not the
787
+ * compare-and-set baseline it happened to read on the way in. An
788
+ * instruction-policy proposal carries the live head revision/version so the CAS
789
+ * can reject a stale write, but hashing those into the operation identity made
790
+ * an ordinary turn-recovery replay of the same operationId look like a
791
+ * different operation once the head moved, which surfaced as a replayed-with-
792
+ * different-immutable-input failure. Strip exactly those two fields, and only
793
+ * for that request kind, so every other lane's hash stays byte-identical.
794
+ */
795
+ function identityRequest(request: CompanyBrainGovernedWriteRequestType): unknown {
796
+ if (!isInstructionPolicyProposal(request)) return request;
797
+ const {
798
+ expectedCurrentRevisionId: _revision,
799
+ expectedActivationVersion: _version,
800
+ ...rest
801
+ } = request;
802
+ // Deliberately no longer the request union: this value only ever feeds the
803
+ // identity hash, never a code path that reads those fields.
804
+ return rest;
805
+ }
806
+
807
+ /**
808
+ * Task-note promotion lands in exactly the same destination materialization as
809
+ * a direct `propose_instruction_policy` / `propose_preference`, so the
810
+ * agent-authored prompt budget has to be enforced here too. Without it the note
811
+ * (bounded only by `TASK_NOTE_TEXT_MAX_BYTES`) is a way to write a long
812
+ * procedure into an always-composed rule through the back door.
813
+ *
814
+ * The note bytes stay exact evidence: this rejects the promotion rather than
815
+ * truncating or rewriting anything, and it runs before any evidence, claim, or
816
+ * proposal row is written. A convergent replay of a promotion that was already
817
+ * accepted is deliberately not re-checked, so stored rows are never invalidated
818
+ * after the fact.
819
+ */
820
+ function assertAgentAuthoredPromotionBudget(
821
+ request: CompanyBrainGovernedWriteRequestType,
822
+ noteText: string,
823
+ ): void {
824
+ const kind =
825
+ request.kind === "promote_task_note_instruction_policy"
826
+ ? ("instruction_policy" as const)
827
+ : request.kind === "promote_task_note_preference"
828
+ ? ("preference" as const)
829
+ : null;
830
+ if (kind === null) return;
831
+ const maxChars =
832
+ kind === "instruction_policy"
833
+ ? AGENT_AUTHORED_INSTRUCTION_POLICY_CONTENT_MAX_CHARS
834
+ : AGENT_AUTHORED_PREFERENCE_CONTENT_MAX_CHARS;
835
+ if (noteText.length <= maxChars) return;
836
+ throw new CompanyBrainGovernedWriteInvalidOperationError(
837
+ agentAuthoredDurableTextTooLongMessage({ kind, actualChars: noteText.length }),
838
+ );
839
+ }
840
+
841
+ function isInstructionPolicyProposal(
842
+ request: CompanyBrainGovernedWriteRequestType,
843
+ ): request is InstructionPolicyProposalRequest {
844
+ return (
845
+ request.kind === "propose_instruction_policy" ||
846
+ request.kind === "promote_task_note_instruction_policy"
847
+ );
848
+ }
849
+
850
+ async function materializeWaysOfWorkingProposal(
851
+ db: Database,
852
+ input: {
853
+ authority: AttemptAuthority;
854
+ request: InstructionPolicyProposalRequest | PreferenceProposalRequest;
855
+ content: string;
856
+ evidence: WorkspaceEvidence;
857
+ reviewId: string;
858
+ actor: ScopedKnowledgeActor;
859
+ inputHash: string;
860
+ taskNoteSource?: TaskNotePromotionSource;
861
+ },
862
+ ): Promise<CompanyBrainGovernedWriteReceiptType> {
863
+ const taskNoteSource = input.taskNoteSource
864
+ ? {
865
+ noteId: input.taskNoteSource.noteId,
866
+ rootSessionId: input.taskNoteSource.rootSessionId,
867
+ noteVersion: input.taskNoteSource.noteVersion,
868
+ textHash: input.taskNoteSource.noteTextHash,
869
+ }
870
+ : undefined;
871
+
872
+ if (isInstructionPolicyProposal(input.request)) {
873
+ const changeProposal = await createKnowledgeChangeProposal(db, {
874
+ accountId: input.authority.accountId,
875
+ workspaceId: input.authority.workspaceId,
876
+ operationId: derivedOperationId(input.request.operationId, "knowledge-change-proposal"),
877
+ actor: input.actor,
878
+ claimId: input.evidence.claimId,
879
+ evidenceId: input.evidence.evidenceId,
880
+ targetKind: "instruction_policy",
881
+ targetScope: input.request.target.scope,
882
+ targetKey: input.request.target.scope === "role" ? input.request.target.roleKey : null,
883
+ content: input.content,
884
+ });
885
+ const proposal = await createWorkspaceInstructionPolicyKnowledgeProposal(db, {
886
+ operationId: derivedOperationId(input.request.operationId, "instruction-proposal"),
887
+ accountId: input.authority.accountId,
888
+ workspaceId: input.authority.workspaceId,
889
+ ...input.request.target,
890
+ content: input.content,
891
+ knowledgeProposalId: changeProposal.id,
892
+ knowledgeProposalContentHash: changeProposal.contentHash,
893
+ confidenceBps: input.evidence.confidenceBps,
894
+ expectedCurrentRevisionId: input.request.expectedCurrentRevisionId,
895
+ expectedActivationVersion: input.request.expectedActivationVersion,
896
+ createdBySubjectId: input.actor.subjectId,
897
+ });
898
+ return receipt({
899
+ operationId: input.request.operationId,
900
+ inputHash: input.inputHash,
901
+ workspaceId: input.authority.workspaceId,
902
+ destination: "instruction_policy",
903
+ claimId: input.evidence.claimId,
904
+ evidenceId: input.evidence.evidenceId,
905
+ relationId: null,
906
+ reviewId: input.reviewId,
907
+ knowledgeChangeProposalId: changeProposal.id,
908
+ destinationProposalId: proposal.id,
909
+ destinationRevisionId: proposal.draft.id,
910
+ taskNoteSource,
911
+ });
912
+ }
913
+
914
+ const changeProposal = await createKnowledgeChangeProposal(db, {
915
+ accountId: input.authority.accountId,
916
+ workspaceId: input.authority.workspaceId,
917
+ operationId: derivedOperationId(input.request.operationId, "knowledge-change-proposal"),
918
+ actor: input.actor,
919
+ claimId: input.evidence.claimId,
920
+ evidenceId: input.evidence.evidenceId,
921
+ targetKind: "preference",
922
+ targetScope: "workspace",
923
+ targetKey: input.request.stableKey,
924
+ content: preferenceProposalContent(input.request, input.content),
925
+ });
926
+ const proposal = await createWorkspacePreferenceProposalForAttempt(
927
+ db,
928
+ input.authority,
929
+ input.request,
930
+ input.content,
931
+ changeProposal.id,
932
+ input.inputHash,
933
+ );
934
+ return receipt({
935
+ operationId: input.request.operationId,
936
+ inputHash: input.inputHash,
937
+ workspaceId: input.authority.workspaceId,
938
+ destination: "preference",
939
+ claimId: input.evidence.claimId,
940
+ evidenceId: input.evidence.evidenceId,
941
+ relationId: null,
942
+ reviewId: input.reviewId,
943
+ knowledgeChangeProposalId: changeProposal.id,
944
+ destinationProposalId: proposal.preferenceId,
945
+ destinationRevisionId: proposal.revisionId,
946
+ taskNoteSource,
947
+ });
948
+ }
949
+
950
+ /**
951
+ * Route one explicit workspace-local proposal. This owns write admission only;
952
+ * selector snapshots, logical-turn receipts, tools, and activation remain out of scope.
953
+ */
954
+ /**
955
+ * Write result: the agent-facing receipt plus the exact initiating human the
956
+ * accepted attempt froze. The subject id is internal routing authority for the
957
+ * governed-learning evaluator/controller and is stripped from public receipts.
958
+ */
959
+ export type CompanyBrainGovernedWriteResult = CompanyBrainGovernedWriteReceiptType & {
960
+ initiatingHumanSubjectId: string;
961
+ };
962
+
963
+ export async function writeCompanyBrainGovernedProposal(
964
+ db: Database,
965
+ rawInput: {
966
+ attempt: CompanyBrainGovernedWriteAttempt;
967
+ request: CompanyBrainGovernedWriteRequestType;
968
+ },
969
+ ): Promise<CompanyBrainGovernedWriteResult> {
970
+ const attempt = CompanyBrainGovernedWriteAttempt.parse(rawInput.attempt);
971
+ const request = CompanyBrainGovernedWriteRequest.parse(rawInput.request);
972
+ const inputHash = scopedKnowledgeInputHash({
973
+ attempt,
974
+ request: identityRequest(request),
975
+ });
976
+ const actorSubjectId = `service:company-brain-governed-write:${inputHash}`;
977
+ const evidenceOperationId = derivedOperationId(request.operationId, "task-note-evidence");
978
+ const claimOperationId = derivedOperationId(request.operationId, "task-note-claim");
979
+ return await withCompanyBrainGovernedWriteAuthority(
980
+ db,
981
+ attempt,
982
+ isInstructionPolicyProposal(request) ? "update" : "key_share",
983
+ async (scopedDb): Promise<TaskNotePromotionSource | null> => {
984
+ if (!isTaskNotePromotion(request)) return null;
985
+ if (
986
+ await taskNotePromotionAlreadyMaterialized(scopedDb, attempt.accountId, evidenceOperationId)
987
+ ) {
988
+ return null;
989
+ }
990
+ return await resolveTaskNotePromotionSource(
991
+ scopedDb,
992
+ attempt,
993
+ request.noteId,
994
+ request.expectedNoteVersion,
995
+ evidenceOperationId,
996
+ claimOperationId,
997
+ actorSubjectId,
998
+ );
999
+ },
1000
+ async (scopedDb, authority, prelockedSource) => ({
1001
+ ...(await writeGovernedProposalBody(scopedDb, authority, prelockedSource)),
1002
+ initiatingHumanSubjectId: authority.initiatingHumanSubjectId,
1003
+ }),
1004
+ ).catch((error: unknown) => {
1005
+ if (
1006
+ error instanceof CompanyBrainGovernedWriteAuthorityError ||
1007
+ error instanceof CompanyBrainGovernedWriteInvalidOperationError
1008
+ ) {
1009
+ throw error;
1010
+ }
1011
+ if (error instanceof ScopedKnowledgeAuthorityError) {
1012
+ throw new CompanyBrainGovernedWriteAuthorityError(error.message);
1013
+ }
1014
+ if (error instanceof ScopedKnowledgeInvalidOperationError) {
1015
+ throw new CompanyBrainGovernedWriteInvalidOperationError(error.message);
1016
+ }
1017
+ throw error;
1018
+ });
1019
+
1020
+ async function writeGovernedProposalBody(
1021
+ scopedDb: Database,
1022
+ authority: AttemptAuthority,
1023
+ prelockedSource: TaskNotePromotionSource | null,
1024
+ ): Promise<CompanyBrainGovernedWriteReceiptType> {
1025
+ {
1026
+ const actor: ScopedKnowledgeActor = {
1027
+ kind: "service",
1028
+ subjectId: actorSubjectId,
1029
+ initiatingHumanSubjectId: authority.initiatingHumanSubjectId,
1030
+ };
1031
+ if (isTaskNotePromotion(request)) {
1032
+ let materialization: TaskNoteKnowledgeMaterialization;
1033
+ if (prelockedSource === null) {
1034
+ const replayed = await replayTaskNoteKnowledgeMaterialization(
1035
+ scopedDb,
1036
+ authority,
1037
+ request.operationId,
1038
+ inputHash,
1039
+ );
1040
+ if (!replayed) {
1041
+ throw new CompanyBrainGovernedWriteInvalidOperationError(
1042
+ "Task-note promotion source changed before exact authority admission",
1043
+ );
1044
+ }
1045
+ materialization = replayed;
1046
+ } else {
1047
+ if (prelockedSource.initiatingHumanSubjectId !== authority.initiatingHumanSubjectId) {
1048
+ throw new CompanyBrainGovernedWriteAuthorityError(
1049
+ "Task-note promotion source did not preserve exact attempt authority",
1050
+ );
1051
+ }
1052
+ assertAgentAuthoredPromotionBudget(request, prelockedSource.noteText);
1053
+ materialization = await materializeTaskNoteKnowledge(scopedDb, {
1054
+ authority,
1055
+ request,
1056
+ actor,
1057
+ source: prelockedSource,
1058
+ evidenceOperationId,
1059
+ claimOperationId,
1060
+ });
1061
+ }
1062
+ if (
1063
+ materialization.source.initiatingHumanSubjectId !== authority.initiatingHumanSubjectId
1064
+ ) {
1065
+ throw new CompanyBrainGovernedWriteAuthorityError(
1066
+ "Task-note promotion source did not preserve exact attempt authority",
1067
+ );
1068
+ }
1069
+ if (request.kind === "promote_task_note_knowledge") {
1070
+ return receipt({
1071
+ operationId: request.operationId,
1072
+ inputHash,
1073
+ workspaceId: authority.workspaceId,
1074
+ destination: "knowledge",
1075
+ claimId: materialization.claimId,
1076
+ evidenceId: materialization.evidenceId,
1077
+ relationId: null,
1078
+ reviewId: materialization.reviewId,
1079
+ knowledgeChangeProposalId: null,
1080
+ destinationProposalId: null,
1081
+ destinationRevisionId: null,
1082
+ taskNoteSource: {
1083
+ noteId: materialization.source.noteId,
1084
+ rootSessionId: materialization.source.rootSessionId,
1085
+ noteVersion: materialization.source.noteVersion,
1086
+ textHash: materialization.source.noteTextHash,
1087
+ },
1088
+ });
1089
+ }
1090
+ return await materializeWaysOfWorkingProposal(scopedDb, {
1091
+ authority,
1092
+ request,
1093
+ content: materialization.source.noteText,
1094
+ evidence: materialization,
1095
+ reviewId: materialization.reviewId,
1096
+ actor,
1097
+ inputHash,
1098
+ taskNoteSource: materialization.source,
1099
+ });
1100
+ }
1101
+ const evidence = await requireWorkspaceEvidence(
1102
+ scopedDb,
1103
+ authority,
1104
+ request.claimId,
1105
+ request.evidenceId,
1106
+ request.kind === "correct_knowledge" ? request.replacesClaimId : null,
1107
+ );
1108
+ const review = await appendKnowledgeClaimReview(scopedDb, {
1109
+ accountId: authority.accountId,
1110
+ workspaceId: authority.workspaceId,
1111
+ operationId: derivedOperationId(request.operationId, "guard"),
1112
+ actor,
1113
+ claimId: request.claimId,
1114
+ state: "proposed",
1115
+ reason: request.reason,
1116
+ });
1117
+
1118
+ if (request.kind === "propose_knowledge") {
1119
+ return receipt({
1120
+ operationId: request.operationId,
1121
+ inputHash,
1122
+ workspaceId: authority.workspaceId,
1123
+ destination: "knowledge",
1124
+ claimId: evidence.claimId,
1125
+ evidenceId: evidence.evidenceId,
1126
+ relationId: null,
1127
+ reviewId: review.id,
1128
+ knowledgeChangeProposalId: null,
1129
+ destinationProposalId: null,
1130
+ destinationRevisionId: null,
1131
+ });
1132
+ }
1133
+
1134
+ if (request.kind === "correct_knowledge") {
1135
+ const relation = await linkKnowledgeClaims(scopedDb, {
1136
+ accountId: authority.accountId,
1137
+ workspaceId: authority.workspaceId,
1138
+ operationId: derivedOperationId(request.operationId, "knowledge-correction"),
1139
+ actor,
1140
+ relationType: "supersedes",
1141
+ fromClaimId: request.claimId,
1142
+ toClaimId: request.replacesClaimId,
1143
+ });
1144
+ return receipt({
1145
+ operationId: request.operationId,
1146
+ inputHash,
1147
+ workspaceId: authority.workspaceId,
1148
+ destination: "knowledge",
1149
+ claimId: evidence.claimId,
1150
+ evidenceId: evidence.evidenceId,
1151
+ relationId: relation.id,
1152
+ reviewId: review.id,
1153
+ knowledgeChangeProposalId: null,
1154
+ destinationProposalId: null,
1155
+ destinationRevisionId: null,
1156
+ });
1157
+ }
1158
+
1159
+ return await materializeWaysOfWorkingProposal(scopedDb, {
1160
+ authority,
1161
+ request,
1162
+ content: request.content,
1163
+ evidence,
1164
+ reviewId: review.id,
1165
+ actor,
1166
+ inputHash,
1167
+ });
1168
+ }
1169
+ }
1170
+ }