thinkwork-cli 0.12.12 → 0.12.16

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 (475) hide show
  1. package/README.md +42 -38
  2. package/dist/{api-client-ASJWIG3Z.js → api-client-JUBCQQDF.js} +1 -1
  3. package/dist/{chunk-JEHV35EU.js → chunk-STY44FQO.js} +353 -30
  4. package/dist/cli.js +23823 -10151
  5. package/dist/commands/enterprise/templates/deploy-repo/.github/workflows/deploy.yml +17 -6
  6. package/dist/commands/enterprise/templates/deploy-repo/docs/runbook.md +81 -9
  7. package/dist/commands/enterprise/templates/deploy-repo/scripts/apply-release.mjs +183 -21
  8. package/dist/commands/enterprise/templates/deploy-repo/scripts/smoke.mjs +1 -2
  9. package/dist/commands/enterprise/templates/deploy-repo/terraform/main.tf +286 -0
  10. package/dist/drizzle/0000_petite_marvel_apes.sql +1188 -0
  11. package/dist/drizzle/0001_milky_surge.sql +103 -0
  12. package/dist/drizzle/0002_aberrant_mattie_franklin.sql +41 -0
  13. package/dist/drizzle/0003_exotic_ogun.sql +3 -0
  14. package/dist/drizzle/0004_glamorous_magneto.sql +2 -0
  15. package/dist/drizzle/0005_ancient_nocturne.sql +2 -0
  16. package/dist/drizzle/0006_new_colonel_america.sql +2 -0
  17. package/dist/drizzle/0007_unique_external_task_id.sql +31 -0
  18. package/dist/drizzle/0008_external_task_id_column.sql +43 -0
  19. package/dist/drizzle/0009_evaluations.sql +64 -0
  20. package/dist/drizzle/0010_eval_agent_template.sql +2 -0
  21. package/dist/drizzle/0011_eval_run_template.sql +2 -0
  22. package/dist/drizzle/0012_eval_seed_unique.sql +16 -0
  23. package/dist/drizzle/0013_motionless_white_queen.sql +140 -0
  24. package/dist/drizzle/0014_wiki_aggregation.sql +11 -0
  25. package/dist/drizzle/0015_pg_trgm_alias_title_indexes.sql +9 -0
  26. package/dist/drizzle/0016_wiki_schema_drops.sql +24 -0
  27. package/dist/drizzle/0017_wiki_places.sql +46 -0
  28. package/dist/drizzle/0018_agent_workspace_overlay.sql +26 -0
  29. package/dist/drizzle/0018_cool_firebird.sql +24 -0
  30. package/dist/drizzle/0018_skill_runs.sql +84 -0
  31. package/dist/drizzle/0019_condemned_dragon_man.sql +95 -0
  32. package/dist/drizzle/0019_webhook_skill_runs.sql +58 -0
  33. package/dist/drizzle/0020_crazy_the_anarchist.sql +9 -0
  34. package/dist/drizzle/0020_mutation_idempotency.sql +64 -0
  35. package/dist/drizzle/0021_crm_work_links.sql +76 -0
  36. package/dist/drizzle/0021_skill_runs_completion_hmac.sql +33 -0
  37. package/dist/drizzle/0022_add_billing_tables.sql +70 -0
  38. package/dist/drizzle/0022_seed_thinkwork_admin_permissions.sql +160 -0
  39. package/dist/drizzle/0022_stripe_billing_indexes.sql +48 -0
  40. package/dist/drizzle/0023_tenants_deactivation.sql +27 -0
  41. package/dist/drizzle/0024_tenant_mcp_admin_keys.sql +63 -0
  42. package/dist/drizzle/0025_v1_agent_architecture.sql +182 -0
  43. package/dist/drizzle/0026_thread_turns_add_kind.sql +28 -0
  44. package/dist/drizzle/0027_capability_catalog_and_manifests.sql +219 -0
  45. package/dist/drizzle/0028_aws_s3_extension.sql +26 -0
  46. package/dist/drizzle/0029_collapse_execution_types.sql +42 -0
  47. package/dist/drizzle/0030_retire_composition_primitives.sql +48 -0
  48. package/dist/drizzle/0031_thread_cleanup_drops.sql +131 -0
  49. package/dist/drizzle/0032_add_browser_automation_template_config.sql +24 -0
  50. package/dist/drizzle/0033_agent_operation_leases.sql +32 -0
  51. package/dist/drizzle/0034_agent_workspace_events.sql +116 -0
  52. package/dist/drizzle/0035_workspace_review_decisions.sql +19 -0
  53. package/dist/drizzle/0036_user_scoped_memory_wiki.sql +152 -0
  54. package/dist/drizzle/0037_user_profile_operating_model.sql +34 -0
  55. package/dist/drizzle/0042_agent_runtime_selector.sql +41 -0
  56. package/dist/drizzle/0043_wiki_search_tsv_tokenization.sql +48 -0
  57. package/dist/drizzle/0044_add_web_search_template_config.sql +7 -0
  58. package/dist/drizzle/0045_add_send_email_template_config.sql +7 -0
  59. package/dist/drizzle/0046_tenant_mcp_context_tools.sql +26 -0
  60. package/dist/drizzle/0047_add_context_engine_template_config.sql +7 -0
  61. package/dist/drizzle/0048_tenant_context_provider_settings.sql +22 -0
  62. package/dist/drizzle/0050_brain_v0_entity_subtype.sql +29 -0
  63. package/dist/drizzle/0051_brain_v0_tenant_entity_pages.sql +170 -0
  64. package/dist/drizzle/0052_brain_v0_external_refs.sql +41 -0
  65. package/dist/drizzle/0053_brain_v0_unresolved_mentions_subtype.sql +48 -0
  66. package/dist/drizzle/0054_wiki_compile_jobs_input.sql +28 -0
  67. package/dist/drizzle/0055_routines_stepfunctions_substrate.sql +202 -0
  68. package/dist/drizzle/0056_routine_step_events_dedup_idx.sql +25 -0
  69. package/dist/drizzle/0057_archive_legacy_python_routines.sql +36 -0
  70. package/dist/drizzle/0058_routines_visibility_and_owning_agent.sql +61 -0
  71. package/dist/drizzle/0061_routine_execution_asl_version_id.sql +29 -0
  72. package/dist/drizzle/0062_migrate_pi_to_flue.sql +110 -0
  73. package/dist/drizzle/0063_threads_session_data.sql +23 -0
  74. package/dist/drizzle/0064_tenant_credentials.sql +65 -0
  75. package/dist/drizzle/0067_thinkwork_computers_phase_one.sql +174 -0
  76. package/dist/drizzle/0068_drop_system_workflows_and_activation.sql +115 -0
  77. package/dist/drizzle/0069_compliance_schema.sql +364 -0
  78. package/dist/drizzle/0070_compliance_aurora_roles.sql +171 -0
  79. package/dist/drizzle/0072_threads_computer_ownership.sql +24 -0
  80. package/dist/drizzle/0073_compliance_tenant_anchor_state.sql +105 -0
  81. package/dist/drizzle/0074_compliance_event_hash_index.sql +49 -0
  82. package/dist/drizzle/0075_scheduled_jobs_computer_ownership.sql +27 -0
  83. package/dist/drizzle/0076_scheduled_jobs_marco_backfill.sql +69 -0
  84. package/dist/drizzle/0077_computers_primary_agent_id.sql +38 -0
  85. package/dist/drizzle/0078_tenant_customize_catalog.sql +46 -0
  86. package/dist/drizzle/0079_seed_tenant_customize_catalog.sql +53 -0
  87. package/dist/drizzle/0081_routines_catalog_slug.sql +42 -0
  88. package/dist/drizzle/0082_messages_parts_jsonb.sql +52 -0
  89. package/dist/drizzle/0083_computer_runbooks.sql +103 -0
  90. package/dist/drizzle/0084_artifacts_favorited_at.sql +32 -0
  91. package/dist/drizzle/0085_seed_thinkwork_computer_default_template.sql +46 -0
  92. package/dist/drizzle/0086_update_default_template_models.sql +19 -0
  93. package/dist/drizzle/0087_retire_oss_connectors.sql +77 -0
  94. package/dist/drizzle/0088_compliance_event_types_finance_pilot.sql +37 -0
  95. package/dist/drizzle/0089_remove_maniflow_eval_seeds.sql +81 -0
  96. package/dist/drizzle/0089_wiki_schema_extraction.sql +315 -0
  97. package/dist/drizzle/0090_brain_schema_extraction.sql +274 -0
  98. package/dist/drizzle/0091_drop_wiki_brain_compat_views.sql +118 -0
  99. package/dist/drizzle/0092_finish_oss_connector_retirement.sql +97 -0
  100. package/dist/drizzle/0093_eval_runs_scheduled_job_id.sql +23 -0
  101. package/dist/drizzle/0094_slack_workspace_app.sql +140 -0
  102. package/dist/drizzle/0095_eval_runs_computer_id.sql +22 -0
  103. package/dist/drizzle/0096_true_redteam_eval_seed_cleanup.sql +67 -0
  104. package/dist/drizzle/0097_shared_computers.sql +191 -0
  105. package/dist/drizzle/0098_business_ontology.sql +587 -0
  106. package/dist/drizzle/0099_thread_idle_learning.sql +145 -0
  107. package/dist/drizzle/0100_ontology_gated_brain_dynamic_subtypes.sql +7 -0
  108. package/dist/drizzle/0101_ontology_gated_unresolved_mentions_dynamic_subtypes.sql +7 -0
  109. package/dist/drizzle/0102_wiki_brain_owner_repair.sql +87 -0
  110. package/dist/drizzle/0103_brain_section_source_trigger_repair.sql +75 -0
  111. package/dist/drizzle/0104_expand_seed_ontology_for_user_memory.sql +166 -0
  112. package/dist/drizzle/0105_spaces_domain.sql +349 -0
  113. package/dist/drizzle/0106_expand_seed_ontology_for_triples.sql +173 -0
  114. package/dist/drizzle/0106_space_threads_participants.sql +197 -0
  115. package/dist/drizzle/0107_linked_task_mirror.sql +245 -0
  116. package/dist/drizzle/0108_message_mentions.sql +95 -0
  117. package/dist/drizzle/0109_space_owned_threads.sql +217 -0
  118. package/dist/drizzle/0110_thread_participant_read_state.sql +21 -0
  119. package/dist/drizzle/0111_agents_tenant_name_unique.sql +38 -0
  120. package/dist/drizzle/0112_recast_spaces_as_contextual_workrooms.sql +138 -0
  121. package/dist/drizzle/0113_agents_own_runtime_fields.sql +48 -0
  122. package/dist/drizzle/0114_migrate_templates_to_agents_and_spaces.sql +361 -0
  123. package/dist/drizzle/0115_remove_computer_eval_template_dependencies.sql +69 -0
  124. package/dist/drizzle/0116_backfill_thread_participants_access.sql +126 -0
  125. package/dist/drizzle/0117_space_access_mode.sql +40 -0
  126. package/dist/drizzle/0118_normalize_tenant_member_principal_type.sql +41 -0
  127. package/dist/drizzle/0119_space_knowledge_bases.sql +126 -0
  128. package/dist/drizzle/0120_space_automations.sql +58 -0
  129. package/dist/drizzle/0121_drop_computer_runbooks.sql +26 -0
  130. package/dist/drizzle/0122_space_email_triggers.sql +24 -0
  131. package/dist/drizzle/0123_single_platform_agent_and_overrides.sql +58 -0
  132. package/dist/drizzle/0124_thread_turns_finalized_at.sql +17 -0
  133. package/dist/drizzle/0125_drop_space_agent_assignments.sql +42 -0
  134. package/dist/drizzle/0126_migrate_flue_to_pi.sql +42 -0
  135. package/dist/drizzle/0127_runtime_type_observability.sql +28 -0
  136. package/dist/drizzle/0128_drop_eval_test_cases_agent_id.sql +37 -0
  137. package/dist/drizzle/0129_drop_code_factory_tables.sql +43 -0
  138. package/dist/drizzle/0129_eval_results_system_prompt.sql +30 -0
  139. package/dist/drizzle/0130_drop_teams_and_team_id_columns.sql +54 -0
  140. package/dist/drizzle/0131_drop_skill_catalog_and_tenant_skills.sql +33 -0
  141. package/dist/drizzle/0132_drop_computer_tables.sql +31 -0
  142. package/dist/drizzle/0133_drop_seed_view_tombstones.sql +79 -0
  143. package/dist/drizzle/0133_thread_turns_system_prompt.sql +22 -0
  144. package/dist/drizzle/0134_drop_dead_agent_runtime_tables.sql +53 -0
  145. package/dist/drizzle/0135_native_checklist_linked_tasks.sql +48 -0
  146. package/dist/drizzle/0136_goal_ledger.sql +55 -0
  147. package/dist/drizzle/0136_space_email_trigger_status.sql +66 -0
  148. package/dist/drizzle/0137_thread_participant_pins.sql +25 -0
  149. package/dist/drizzle/0138_users_cognito_sub.sql +30 -0
  150. package/dist/drizzle/0139_mobile_turn_client_id.sql +23 -0
  151. package/dist/drizzle/0140_workspace_folder_names.sql +36 -0
  152. package/dist/drizzle/0141_eval_runs_desktop_pi_provenance.sql +31 -0
  153. package/dist/drizzle/0142_pi_only_agent_runtime.sql +61 -0
  154. package/dist/drizzle/0143_add_web_extract_builtin_config.sql +28 -0
  155. package/dist/drizzle/0144_skill_catalog_index.sql +40 -0
  156. package/dist/drizzle/0145_knowledge_graph_thread_ingest.sql +512 -0
  157. package/dist/drizzle/0146_knowledge_graph_source_scope.sql +413 -0
  158. package/dist/drizzle/0147_knowledge_graph_scope_trigger_qualification.sql +211 -0
  159. package/dist/drizzle/0148_user_cost_attribution.sql +171 -0
  160. package/dist/drizzle/0149_managed_mcp_servers.sql +64 -0
  161. package/dist/drizzle/0149_user_model_approvals.sql +89 -0
  162. package/dist/drizzle/0150_bootstrap_first_admin_claim.sql +11 -0
  163. package/dist/drizzle/0151_managed_deployments.sql +91 -0
  164. package/dist/drizzle/0152_agent_profiles.sql +81 -0
  165. package/dist/drizzle/0153_message_mentions_agent_profile.sql +9 -0
  166. package/dist/drizzle/0154_customer_deployment_sessions.sql +61 -0
  167. package/dist/drizzle/0155_tenant_model_catalog.sql +152 -0
  168. package/dist/drizzle/0156_bootstrap_credential_leases.sql +52 -0
  169. package/dist/drizzle/0157_knowledge_graph_observations_source.sql +79 -0
  170. package/dist/drizzle/0158_pending_user_questions.sql +80 -0
  171. package/dist/drizzle/0158_wiki_tenant_scope.sql +38 -0
  172. package/dist/drizzle/0159_agent_profiles_space_local.sql +49 -0
  173. package/dist/drizzle/0159_application_plugins.sql +193 -0
  174. package/dist/drizzle/0159_eval_scoring_version.sql +46 -0
  175. package/dist/drizzle/0160_compliance_event_types_plugins.sql +37 -0
  176. package/dist/drizzle/0160_eval_datasets.sql +75 -0
  177. package/dist/drizzle/0160_eval_results_thread_turn.sql +35 -0
  178. package/dist/drizzle/0161_eval_run_pinning.sql +54 -0
  179. package/dist/drizzle/0161_tenant_mcp_servers_plugin_source.sql +62 -0
  180. package/dist/drizzle/0162_eval_result_override.sql +49 -0
  181. package/dist/drizzle/0163_eval_replay_tool_allowlist.sql +50 -0
  182. package/dist/drizzle/0164_eval_replay_tool_override_mode.sql +52 -0
  183. package/dist/drizzle/0165_plugin_premium_entitlements.sql +153 -0
  184. package/dist/drizzle/0166_company_brain_substrate_contract.sql +386 -0
  185. package/dist/drizzle/0167_company_brain_artifact_manifest_runtime.sql +85 -0
  186. package/dist/drizzle/0168_eval_skill_gate.sql +44 -0
  187. package/dist/drizzle/0169_release_update_jobs.sql +67 -0
  188. package/dist/drizzle/0170_email_channel_plugin.sql +291 -0
  189. package/dist/drizzle/0171_linked_task_external_providers.sql +30 -0
  190. package/dist/drizzle/0172_email_channel_sendgrid_provider.sql +8 -0
  191. package/dist/drizzle/0173_auth_provider_resources.sql +83 -0
  192. package/dist/drizzle/0174_workos_auth_bridges.sql +37 -0
  193. package/dist/drizzle/0175_workos_auth_sessions.sql +38 -0
  194. package/dist/drizzle/0176_n8n_agent_step_runs.sql +107 -0
  195. package/dist/drizzle/0177_workflow_control_plane.sql +281 -0
  196. package/dist/drizzle/0178_workflow_backfill_existing_routines.sql +525 -0
  197. package/dist/drizzle/0179_tenant_goal_budget_settings.sql +21 -0
  198. package/dist/drizzle/0180_skill_drafts.sql +89 -0
  199. package/dist/drizzle/0181_skill_draft_publish_status.sql +21 -0
  200. package/dist/drizzle/0182_agent_loops.sql +260 -0
  201. package/dist/drizzle/0183_okf_artifact_manifests.sql +52 -0
  202. package/dist/drizzle/0184_skill_catalog_trust_cache.sql +35 -0
  203. package/dist/drizzle/0185_agent_loop_space.sql +16 -0
  204. package/dist/drizzle/0186_tenant_mcp_runtime_metadata.sql +10 -0
  205. package/dist/drizzle/0187_native_work_items.sql +220 -0
  206. package/dist/drizzle/0188_company_etl_plugin_rename.sql +99 -0
  207. package/dist/drizzle/0189_trace_cost_substrate.sql +247 -0
  208. package/dist/drizzle/0190_billing_export_reconciliation.sql +101 -0
  209. package/dist/drizzle/0191_open_engine_work_item_queue.sql +66 -0
  210. package/dist/drizzle/0192_work_item_labels.sql +44 -0
  211. package/dist/drizzle/0193_work_item_documents.sql +31 -0
  212. package/dist/drizzle/0194_memory_retain_attempts.sql +103 -0
  213. package/dist/drizzle/0194_work_item_comments.sql +35 -0
  214. package/dist/drizzle/0195_plugin_app_overlays.sql +45 -0
  215. package/dist/drizzle/0196_pi_extensions.sql +118 -0
  216. package/dist/drizzle/0197_eval_profiles.sql +150 -0
  217. package/dist/drizzle/0197_rcm_context_columns.sql +28 -0
  218. package/dist/drizzle/0198_eval_runs_unstable.sql +27 -0
  219. package/dist/drizzle/0199_eval_runs_cost_partial.sql +27 -0
  220. package/dist/drizzle/meta/_journal.json +160 -0
  221. package/dist/plugins/README.md +162 -0
  222. package/dist/plugins/catalog/package.json +40 -0
  223. package/dist/plugins/catalog/scripts/build-catalog.ts +120 -0
  224. package/dist/plugins/catalog/scripts/generate-plugin-registry.ts +144 -0
  225. package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +86 -0
  226. package/dist/plugins/catalog/src/__tests__/catalog.test.ts +308 -0
  227. package/dist/plugins/catalog/src/__tests__/contracts.test.ts +861 -0
  228. package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +124 -0
  229. package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +120 -0
  230. package/dist/plugins/catalog/src/catalog.ts +434 -0
  231. package/dist/plugins/catalog/src/contracts.ts +1498 -0
  232. package/dist/plugins/catalog/src/index.ts +4 -0
  233. package/dist/plugins/catalog/src/plugin-package.ts +171 -0
  234. package/dist/plugins/catalog/src/registry/generated-first-party.ts +76 -0
  235. package/dist/plugins/catalog/src/registry/index.ts +1 -0
  236. package/dist/plugins/catalog/tsconfig.json +8 -0
  237. package/dist/plugins/company-brain/README.md +37 -0
  238. package/dist/plugins/company-brain/package.json +33 -0
  239. package/dist/plugins/company-brain/runtime/cognee/Dockerfile +11 -0
  240. package/dist/plugins/company-brain/smoke/cognee-managed-app-smoke.mjs +458 -0
  241. package/dist/plugins/company-brain/smoke/cognee-memory-cutover-smoke.mjs +539 -0
  242. package/dist/plugins/company-brain/smoke/company-brain-context-engine-smoke.mjs +312 -0
  243. package/dist/plugins/company-brain/smoke/company-brain-operations-smoke.mjs +449 -0
  244. package/dist/plugins/company-brain/smoke/company-brain-plugin-smoke.mjs +655 -0
  245. package/dist/plugins/company-brain/smoke/hindsight-memory-isolation-smoke.mjs +474 -0
  246. package/dist/plugins/company-brain/src/api/cognee-client.ts +896 -0
  247. package/dist/plugins/company-brain/src/api/cognee-cluster-identity.ts +49 -0
  248. package/dist/plugins/company-brain/src/api/cognee-memory-scope.ts +84 -0
  249. package/dist/plugins/company-brain/src/api/context-engine-provider.ts +888 -0
  250. package/dist/plugins/company-brain/src/api/migration.ts +665 -0
  251. package/dist/plugins/company-brain/src/deployment/cognee-managed-app.ts +396 -0
  252. package/dist/plugins/company-brain/src/index.ts +62 -0
  253. package/dist/plugins/company-brain/src/manifest.ts +112 -0
  254. package/dist/plugins/company-brain/terraform/cognee/README.md +263 -0
  255. package/dist/plugins/company-brain/terraform/cognee/main.tf +766 -0
  256. package/dist/plugins/company-brain/terraform/cognee/outputs.tf +144 -0
  257. package/dist/plugins/company-brain/terraform/cognee/variables.tf +402 -0
  258. package/dist/plugins/company-brain/test/api/cognee-client.test.ts +562 -0
  259. package/dist/plugins/company-brain/test/api/cognee-memory-scope.test.ts +74 -0
  260. package/dist/plugins/company-brain/test/api/context-engine-provider.test.ts +447 -0
  261. package/dist/plugins/company-brain/test/api/migration.test.ts +465 -0
  262. package/dist/plugins/company-brain/test/manifest.test.ts +95 -0
  263. package/dist/plugins/company-brain/tsconfig.json +8 -0
  264. package/dist/plugins/company-data/README.md +49 -0
  265. package/dist/plugins/company-data/package.json +21 -0
  266. package/dist/plugins/company-data/src/index.ts +30 -0
  267. package/dist/plugins/company-data/src/manifest.ts +26 -0
  268. package/dist/plugins/company-data/test/manifest.test.ts +116 -0
  269. package/dist/plugins/company-data/tsconfig.json +8 -0
  270. package/dist/plugins/company-etl/README.md +46 -0
  271. package/dist/plugins/company-etl/package.json +21 -0
  272. package/dist/plugins/company-etl/src/index.ts +29 -0
  273. package/dist/plugins/company-etl/src/manifest.ts +26 -0
  274. package/dist/plugins/company-etl/test/manifest.test.ts +108 -0
  275. package/dist/plugins/company-etl/tsconfig.json +8 -0
  276. package/dist/plugins/email-channel/README.md +41 -0
  277. package/dist/plugins/email-channel/package.json +22 -0
  278. package/dist/plugins/email-channel/src/index.ts +36 -0
  279. package/dist/plugins/email-channel/src/manifest.ts +44 -0
  280. package/dist/plugins/email-channel/src/provider-contract.ts +36 -0
  281. package/dist/plugins/email-channel/test/manifest.test.ts +119 -0
  282. package/dist/plugins/email-channel/tsconfig.json +8 -0
  283. package/dist/plugins/lastmile/README.md +50 -0
  284. package/dist/plugins/lastmile/package.json +23 -0
  285. package/dist/plugins/lastmile/smoke/lastmile-plugin-smoke.mjs +661 -0
  286. package/dist/plugins/lastmile/src/api/tasks-adapter.ts +572 -0
  287. package/dist/plugins/lastmile/src/discovery.fixture.ts +64 -0
  288. package/dist/plugins/lastmile/src/index.ts +46 -0
  289. package/dist/plugins/lastmile/src/manifest.ts +139 -0
  290. package/dist/plugins/lastmile/test/api/tasks-adapter.test.ts +266 -0
  291. package/dist/plugins/lastmile/test/discovery.test.ts +88 -0
  292. package/dist/plugins/lastmile/tsconfig.json +8 -0
  293. package/dist/plugins/n8n/README.md +201 -0
  294. package/dist/plugins/n8n/n8n-app/README.md +33 -0
  295. package/dist/plugins/n8n/n8n-app/package.json +23 -0
  296. package/dist/plugins/n8n/n8n-app/src/application-config.ts +45 -0
  297. package/dist/plugins/n8n/n8n-app/src/front-components/thinkwork-workflows.front-component.tsx +970 -0
  298. package/dist/plugins/n8n/n8n-app/src/lib/n8n-app-data.ts +190 -0
  299. package/dist/plugins/n8n/n8n-app/src/vite-env.d.ts +7 -0
  300. package/dist/plugins/n8n/n8n-app/tsconfig.json +15 -0
  301. package/dist/plugins/n8n/package.json +35 -0
  302. package/dist/plugins/n8n/runtime/Dockerfile +18 -0
  303. package/dist/plugins/n8n/runtime/n8n-task-runners.json +14 -0
  304. package/dist/plugins/n8n/runtime/n8n-task-runners.json.template +14 -0
  305. package/dist/plugins/n8n/runtime/package-lock.json +13 -0
  306. package/dist/plugins/n8n/runtime/package.json +7 -0
  307. package/dist/plugins/n8n/scripts/sync-thinkwork-app.mjs +268 -0
  308. package/dist/plugins/n8n/smoke/README.md +95 -0
  309. package/dist/plugins/n8n/smoke/n8n-agent-step-bridge-smoke.mjs +866 -0
  310. package/dist/plugins/n8n/smoke/n8n-integrated-app-smoke.mjs +262 -0
  311. package/dist/plugins/n8n/smoke/n8n-managed-app-smoke.mjs +409 -0
  312. package/dist/plugins/n8n/smoke/n8n-mcp-smoke.mjs +624 -0
  313. package/dist/plugins/n8n/src/deployment/image-build.ts +172 -0
  314. package/dist/plugins/n8n/src/deployment/managed-app.ts +429 -0
  315. package/dist/plugins/n8n/src/index.ts +79 -0
  316. package/dist/plugins/n8n/src/manifest.ts +403 -0
  317. package/dist/plugins/n8n/src/package-config.ts +186 -0
  318. package/dist/plugins/n8n/src/skills/n8n-workflow-operator/SKILL.md +76 -0
  319. package/dist/plugins/n8n/src/skills/n8n-workflow-operator/references/mcp-tooling.md +40 -0
  320. package/dist/plugins/n8n/src/skills/n8n-workflow-operator/references/validation-and-handoff.md +40 -0
  321. package/dist/plugins/n8n/src/skills/n8n-workflow-operator/references/workflow-authoring.md +45 -0
  322. package/dist/plugins/n8n/terraform/n8n/README.md +113 -0
  323. package/dist/plugins/n8n/terraform/n8n/main.tf +918 -0
  324. package/dist/plugins/n8n/terraform/n8n/outputs.tf +124 -0
  325. package/dist/plugins/n8n/terraform/n8n/scripts/sync-database.py +250 -0
  326. package/dist/plugins/n8n/terraform/n8n/scripts/test_sync_database.py +55 -0
  327. package/dist/plugins/n8n/terraform/n8n/variables.tf +354 -0
  328. package/dist/plugins/n8n/test/image-build.test.ts +118 -0
  329. package/dist/plugins/n8n/test/integrated-app-contract.test.ts +353 -0
  330. package/dist/plugins/n8n/test/manifest.test.ts +278 -0
  331. package/dist/plugins/n8n/test/package-config.test.ts +57 -0
  332. package/dist/plugins/n8n/tsconfig.json +8 -0
  333. package/dist/plugins/sendgrid/README.md +30 -0
  334. package/dist/plugins/sendgrid/package.json +22 -0
  335. package/dist/plugins/sendgrid/src/index.ts +36 -0
  336. package/dist/plugins/sendgrid/src/manifest.ts +37 -0
  337. package/dist/plugins/sendgrid/src/provider-contract.ts +14 -0
  338. package/dist/plugins/sendgrid/test/manifest.test.ts +80 -0
  339. package/dist/plugins/sendgrid/tsconfig.json +8 -0
  340. package/dist/plugins/twenty/README.md +78 -0
  341. package/dist/plugins/twenty/package.json +27 -0
  342. package/dist/plugins/twenty/scripts/__tests__/sync-thinkwork-app.test.mjs +67 -0
  343. package/dist/plugins/twenty/scripts/sync-thinkwork-app.mjs +185 -0
  344. package/dist/plugins/twenty/scripts/wire-thinkwork-workflow.mjs +658 -0
  345. package/dist/plugins/twenty/smoke/twenty-managed-app-smoke.mjs +359 -0
  346. package/dist/plugins/twenty/smoke/twenty-mcp-oauth-smoke.mjs +574 -0
  347. package/dist/plugins/twenty/src/api/cutover.ts +209 -0
  348. package/dist/plugins/twenty/src/deployment/managed-app.ts +253 -0
  349. package/dist/plugins/twenty/src/index.ts +55 -0
  350. package/dist/plugins/twenty/src/manifest.ts +259 -0
  351. package/dist/plugins/twenty/terraform/twenty/README.md +97 -0
  352. package/dist/plugins/twenty/terraform/twenty/main.tf +829 -0
  353. package/dist/plugins/twenty/terraform/twenty/outputs.tf +79 -0
  354. package/dist/plugins/twenty/terraform/twenty/variables.tf +268 -0
  355. package/dist/plugins/twenty/test/api/cutover.test.ts +183 -0
  356. package/dist/plugins/twenty/test/manifest.test.ts +463 -0
  357. package/dist/plugins/twenty/tsconfig.json +9 -0
  358. package/dist/plugins/twenty/twenty-app/.nvmrc +1 -0
  359. package/dist/plugins/twenty/twenty-app/README.md +148 -0
  360. package/dist/plugins/twenty/twenty-app/package.json +30 -0
  361. package/dist/plugins/twenty/twenty-app/src/application-config.ts +44 -0
  362. package/dist/plugins/twenty/twenty-app/src/constants/universal-identifiers.ts +18 -0
  363. package/dist/plugins/twenty/twenty-app/src/default-role.ts +24 -0
  364. package/dist/plugins/twenty/twenty-app/src/front-components/thinkwork-settings.front-component.tsx +299 -0
  365. package/dist/plugins/twenty/twenty-app/src/logic-functions/thinkwork-webhook.logic-function.ts +354 -0
  366. package/dist/plugins/twenty/twenty-app/tsconfig.json +14 -0
  367. package/dist/plugins/twenty/twenty-app/yarn.lock +4144 -0
  368. package/dist/plugins/workos-auth/package.json +22 -0
  369. package/dist/plugins/workos-auth/src/index.ts +30 -0
  370. package/dist/plugins/workos-auth/src/manifest.ts +40 -0
  371. package/dist/plugins/workos-auth/src/provider-contract.ts +39 -0
  372. package/dist/plugins/workos-auth/test/manifest.test.ts +94 -0
  373. package/dist/plugins/workos-auth/tsconfig.json +8 -0
  374. package/dist/scripts/bootstrap-workspace.sh +162 -0
  375. package/dist/scripts/post-deploy.sh +290 -0
  376. package/dist/terraform/examples/greenfield/main.tf +903 -75
  377. package/dist/terraform/examples/greenfield/terraform.tfvars.example +67 -8
  378. package/dist/terraform/modules/app/agentcore-code-interpreter/Dockerfile.sandbox-base +2 -2
  379. package/dist/terraform/modules/app/agentcore-code-interpreter/README.md +8 -9
  380. package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/sitecustomize.py +227 -0
  381. package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/test_sitecustomize.py +277 -0
  382. package/dist/terraform/modules/app/agentcore-memory/README.md +6 -6
  383. package/dist/terraform/modules/app/agentcore-memory/main.tf +1 -1
  384. package/dist/terraform/modules/app/agentcore-memory/scripts/create_or_find_memory.sh +40 -9
  385. package/dist/terraform/modules/app/agentcore-pi/README.md +61 -0
  386. package/dist/terraform/modules/app/agentcore-pi/main.tf +466 -0
  387. package/dist/terraform/modules/app/agentcore-pi/outputs.tf +38 -0
  388. package/dist/terraform/modules/app/agentcore-pi/variables.tf +145 -0
  389. package/dist/terraform/modules/app/agentcore-platform/main.tf +83 -0
  390. package/dist/terraform/modules/app/agentcore-runtime/main.tf +9 -11
  391. package/dist/terraform/modules/app/appsync-subscriptions/main.tf +5 -1
  392. package/dist/terraform/modules/app/customer-domain/main.tf +483 -0
  393. package/dist/terraform/modules/app/deployment-control-plane/README.md +47 -0
  394. package/dist/terraform/modules/app/deployment-control-plane/buildspec.yml +24 -0
  395. package/dist/terraform/modules/app/deployment-control-plane/main.tf +614 -0
  396. package/dist/terraform/modules/app/deployment-control-plane/outputs.tf +54 -0
  397. package/dist/terraform/modules/app/deployment-control-plane/runner.py +4720 -0
  398. package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +3026 -0
  399. package/dist/terraform/modules/app/deployment-control-plane/variables.tf +96 -0
  400. package/dist/terraform/modules/app/hindsight-memory/README.md +25 -23
  401. package/dist/terraform/modules/app/hindsight-memory/main.tf +26 -2
  402. package/dist/terraform/modules/app/lambda-api/eval-fanout.tf +17 -50
  403. package/dist/terraform/modules/app/lambda-api/handlers.tf +1290 -679
  404. package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +933 -0
  405. package/dist/terraform/modules/app/lambda-api/main.tf +14 -527
  406. package/dist/terraform/modules/app/lambda-api/mcp-oauth.tf +3 -19
  407. package/dist/terraform/modules/app/lambda-api/outputs.tf +50 -6
  408. package/dist/terraform/modules/app/lambda-api/runtime-config.tf +163 -0
  409. package/dist/terraform/modules/app/lambda-api/slack-app-secrets.tf +3 -1
  410. package/dist/terraform/modules/app/lambda-api/stripe-secrets.tf +3 -1
  411. package/dist/terraform/modules/app/lambda-api/variables.tf +240 -53
  412. package/dist/terraform/modules/app/lambda-api/workspace-events.tf +2 -25
  413. package/dist/terraform/modules/app/ses-email/main.tf +143 -30
  414. package/dist/terraform/modules/app/static-site/main.tf +4 -3
  415. package/dist/terraform/modules/app/www-dns/main.tf +45 -5
  416. package/dist/terraform/modules/app/www-dns/outputs.tf +10 -0
  417. package/dist/terraform/modules/app/www-dns/variables.tf +24 -0
  418. package/dist/terraform/modules/data/aurora-postgres/main.tf +15 -1
  419. package/dist/terraform/modules/data/compliance-exports-bucket/main.tf +1 -1
  420. package/dist/terraform/modules/data/compliance-exports-bucket/variables.tf +6 -0
  421. package/dist/terraform/modules/foundation/cognito/main.tf +167 -11
  422. package/dist/terraform/modules/foundation/cognito/outputs.tf +5 -0
  423. package/dist/terraform/modules/foundation/cognito/variables.tf +110 -0
  424. package/dist/terraform/modules/foundation/vpc/main.tf +45 -3
  425. package/dist/terraform/modules/foundation/vpc/outputs.tf +20 -0
  426. package/dist/terraform/modules/foundation/vpc/variables.tf +18 -0
  427. package/dist/terraform/modules/thinkwork/README.md +71 -0
  428. package/dist/terraform/modules/thinkwork/main.tf +1325 -174
  429. package/dist/terraform/modules/thinkwork/outputs.tf +457 -24
  430. package/dist/terraform/modules/thinkwork/variables.tf +1169 -93
  431. package/dist/terraform/schema.graphql +82 -9
  432. package/dist/workspace-defaults/files/AGENTS.md +344 -0
  433. package/dist/workspace-defaults/files/CONTEXT.md +8 -0
  434. package/dist/workspace-defaults/files/GUARDRAILS.md +51 -0
  435. package/dist/workspace-defaults/files/MEMORY_GUIDE.md +65 -0
  436. package/dist/workspace-defaults/files/ROUTER.md +24 -0
  437. package/dist/workspace-defaults/files/SPACE.md +62 -0
  438. package/dist/workspace-defaults/files/TOOLS.md +65 -0
  439. package/dist/workspace-defaults/files/USER.md +40 -0
  440. package/dist/workspace-defaults/files/mcp.json +3 -0
  441. package/dist/workspace-defaults/files/memory/contacts.md +13 -0
  442. package/dist/workspace-defaults/files/memory/lessons.md +13 -0
  443. package/dist/workspace-defaults/files/memory/preferences.md +12 -0
  444. package/dist/workspace-defaults/files/skills/.gitkeep +1 -0
  445. package/dist/workspace-defaults/files/skills/artifact-builder/SKILL.md +79 -0
  446. package/dist/workspace-defaults/files/skills/artifact-builder/references/crm-dashboard.md +166 -0
  447. package/dist/workspace-defaults/files/skills/automation-loop-designer/SKILL.md +120 -0
  448. package/dist/workspace-defaults/files/skills/skill-creator/LICENSE.txt +202 -0
  449. package/dist/workspace-defaults/files/skills/skill-creator/SKILL.md +485 -0
  450. package/dist/workspace-defaults/files/skills/skill-creator/UPSTREAM.json +124 -0
  451. package/dist/workspace-defaults/files/skills/skill-creator/agents/analyzer.md +274 -0
  452. package/dist/workspace-defaults/files/skills/skill-creator/agents/comparator.md +202 -0
  453. package/dist/workspace-defaults/files/skills/skill-creator/agents/grader.md +223 -0
  454. package/dist/workspace-defaults/files/skills/skill-creator/assets/eval_review.html +146 -0
  455. package/dist/workspace-defaults/files/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  456. package/dist/workspace-defaults/files/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  457. package/dist/workspace-defaults/files/skills/skill-creator/references/schemas.md +430 -0
  458. package/dist/workspace-defaults/files/skills/skill-creator/scripts/__init__.py +0 -0
  459. package/dist/workspace-defaults/files/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  460. package/dist/workspace-defaults/files/skills/skill-creator/scripts/generate_report.py +326 -0
  461. package/dist/workspace-defaults/files/skills/skill-creator/scripts/improve_description.py +247 -0
  462. package/dist/workspace-defaults/files/skills/skill-creator/scripts/package_skill.py +136 -0
  463. package/dist/workspace-defaults/files/skills/skill-creator/scripts/quick_validate.py +103 -0
  464. package/dist/workspace-defaults/files/skills/skill-creator/scripts/run_eval.py +310 -0
  465. package/dist/workspace-defaults/files/skills/skill-creator/scripts/run_loop.py +328 -0
  466. package/dist/workspace-defaults/files/skills/skill-creator/scripts/utils.py +47 -0
  467. package/package.json +2 -1
  468. package/dist/terraform/modules/app/agentcore-flue/README.md +0 -58
  469. package/dist/terraform/modules/app/agentcore-flue/main.tf +0 -323
  470. package/dist/terraform/modules/app/agentcore-flue/outputs.tf +0 -23
  471. package/dist/terraform/modules/app/agentcore-flue/variables.tf +0 -97
  472. package/dist/terraform/modules/app/computer-runtime/README.md +0 -15
  473. package/dist/terraform/modules/app/computer-runtime/main.tf +0 -406
  474. package/dist/terraform/modules/app/computer-runtime/outputs.tf +0 -75
  475. package/dist/terraform/modules/app/computer-runtime/variables.tf +0 -66
@@ -0,0 +1,364 @@
1
+ -- 0069_compliance_schema.sql
2
+ --
3
+ -- Phase 3 U1 of the System Workflows revert + Compliance reframe.
4
+ -- Greenfield `compliance.*` Postgres schema for the SOC2 Type 1
5
+ -- evidence-foundation: append-only audit-event log with per-tenant
6
+ -- hash chain (computed app-side by the U4 drainer Lambda), an outbox
7
+ -- table providing same-transaction durability for control-evidence
8
+ -- writes, an opaque actor pseudonym table erasable on GDPR RTBF, and
9
+ -- an export-jobs table for the admin async-export flow.
10
+ --
11
+ -- The 11 Postgres tables that backed System Workflows + Activation
12
+ -- were dropped in Phase 2 U6 (drizzle/0068_drop_system_workflows_and_activation.sql,
13
+ -- shipped via PR #873 + #878). This migration is greenfield in the
14
+ -- new `compliance.*` namespace; nothing is renamed or repointed.
15
+ --
16
+ -- Plan reference:
17
+ -- docs/plans/2026-05-06-011-feat-compliance-audit-event-log-plan.md
18
+ --
19
+ -- Origin brainstorm:
20
+ -- docs/brainstorms/2026-05-06-system-workflows-revert-compliance-reframe-requirements.md
21
+ --
22
+ -- Apply manually:
23
+ -- psql "$DATABASE_URL" -f packages/database-pg/drizzle/0069_compliance_schema.sql
24
+ -- Then verify:
25
+ -- pnpm db:migrate-manual
26
+ -- psql "$DATABASE_URL" -c "\dt compliance.*"
27
+ --
28
+ -- Role separation note: this migration creates the schema and tables
29
+ -- with default permissions. Aurora role provisioning (compliance_writer,
30
+ -- compliance_reader) lands in U2 via Terraform; until then the dev DB's
31
+ -- default app role can read and write `compliance.*` freely. Phase B
32
+ -- writers (U3-U6) and the drainer (U4) are inert pre-U2 — no rows
33
+ -- accumulate in dev before the role lockdown ships.
34
+ --
35
+ -- Markers (consumed by scripts/db-migrate-manual.sh as the post-deploy drift gate):
36
+ --
37
+ -- creates: compliance.audit_outbox
38
+ -- creates: compliance.audit_events
39
+ -- creates: compliance.actor_pseudonym
40
+ -- creates: compliance.export_jobs
41
+ -- creates: compliance.idx_audit_outbox_pending
42
+ -- creates: compliance.idx_audit_outbox_tenant_enqueued
43
+ -- creates: compliance.idx_audit_events_tenant_occurred
44
+ -- creates: compliance.idx_audit_events_tenant_event_type
45
+ -- creates: compliance.idx_audit_events_actor
46
+ -- creates: compliance.idx_audit_events_control_ids
47
+ -- creates: compliance.uq_audit_events_event_id
48
+ -- creates: compliance.uq_audit_events_outbox_id
49
+ -- creates: compliance.uq_audit_outbox_event_id
50
+ -- creates: compliance.idx_actor_pseudonym_user
51
+ -- creates: compliance.idx_actor_pseudonym_email_hash
52
+ -- creates: compliance.idx_export_jobs_tenant_requested
53
+ -- creates: compliance.idx_export_jobs_actor_requested
54
+ -- creates-constraint: compliance.audit_events.audit_events_actor_type_allowed
55
+ -- These two prefix constraints were renamed to *_v2 by 0088 (finance pilot
56
+ -- extended the event_type prefixes). Markers updated to the post-0088 names
57
+ -- so the drift reporter resolves them via pg_constraint; the original names
58
+ -- are guaranteed absent in any DB where 0088 has applied.
59
+ -- creates-constraint: compliance.audit_events.audit_events_event_type_prefix_v2
60
+ -- creates-constraint: compliance.audit_outbox.audit_outbox_event_type_prefix_v2
61
+ -- creates-constraint: compliance.export_jobs.export_jobs_format_allowed
62
+ -- creates-constraint: compliance.export_jobs.export_jobs_status_allowed
63
+ -- creates-constraint: compliance.actor_pseudonym.actor_pseudonym_actor_type_allowed
64
+ -- creates-function: compliance.raise_immutable
65
+ -- creates-trigger: compliance.audit_events.audit_events_block_update
66
+ -- creates-trigger: compliance.audit_events.audit_events_block_delete
67
+ -- creates-trigger: compliance.audit_events.audit_events_block_truncate
68
+
69
+ \set ON_ERROR_STOP on
70
+
71
+ BEGIN;
72
+
73
+ SET LOCAL lock_timeout = '5s';
74
+ SET LOCAL statement_timeout = '60s';
75
+
76
+ -- Refuse to apply against an unexpected DB. Hand-rolled migrations are
77
+ -- applied by an operator and a stale DATABASE_URL pointing at a non-dev
78
+ -- target would create the schema in the wrong place.
79
+ DO $$
80
+ BEGIN
81
+ IF current_database() != 'thinkwork' THEN
82
+ RAISE EXCEPTION 'wrong database: %, expected thinkwork', current_database();
83
+ END IF;
84
+ END $$;
85
+
86
+ CREATE SCHEMA IF NOT EXISTS compliance;
87
+ COMMENT ON SCHEMA compliance IS
88
+ 'SOC2 audit-event log + tamper evidence. App role writes to audit_outbox; drainer writes to audit_events; admin role reads. See docs/plans/2026-05-06-011-feat-compliance-audit-event-log-plan.md.';
89
+
90
+ -- ---------------------------------------------------------------------------
91
+ -- compliance.audit_outbox
92
+ --
93
+ -- Same-transaction durability tier for control-evidence writes (R6).
94
+ -- A resolver or handler inserts here inside its primary `db.transaction`;
95
+ -- if the originating action commits, the outbox row is visible. The U4
96
+ -- single-writer drainer Lambda (reserved-concurrency=1) polls
97
+ -- `FOR UPDATE SKIP LOCKED LIMIT 1`, computes per-tenant prev_hash +
98
+ -- event_hash, writes to audit_events keyed on outbox_id (UNIQUE), and
99
+ -- marks drained_at. Idempotent on outbox_id replay.
100
+ --
101
+ -- The full envelope (R5) is stored here so the drainer is a pure
102
+ -- chain-and-copy operation; outbox rows are not summary records but
103
+ -- the canonical pre-chain representation.
104
+ -- ---------------------------------------------------------------------------
105
+
106
+ CREATE TABLE IF NOT EXISTS compliance.audit_outbox (
107
+ outbox_id uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
108
+ event_id uuid NOT NULL,
109
+ tenant_id uuid NOT NULL,
110
+ occurred_at timestamp with time zone NOT NULL,
111
+ enqueued_at timestamp with time zone NOT NULL DEFAULT now(),
112
+ drained_at timestamp with time zone,
113
+ drainer_error text,
114
+ actor text NOT NULL,
115
+ actor_type text NOT NULL,
116
+ source text NOT NULL,
117
+ event_type text NOT NULL,
118
+ resource_type text,
119
+ resource_id text,
120
+ action text,
121
+ outcome text,
122
+ request_id text,
123
+ thread_id uuid,
124
+ agent_id uuid,
125
+ payload jsonb NOT NULL DEFAULT '{}'::jsonb,
126
+ payload_schema_version integer NOT NULL DEFAULT 1,
127
+ control_ids text[] NOT NULL DEFAULT ARRAY[]::text[],
128
+ payload_redacted_fields text[] NOT NULL DEFAULT ARRAY[]::text[],
129
+ payload_oversize_s3_key text,
130
+ CONSTRAINT audit_outbox_event_type_prefix CHECK (
131
+ event_type ~ '^(auth|user|agent|mcp|workspace|data|policy|approval)\.'
132
+ )
133
+ );
134
+
135
+ COMMENT ON TABLE compliance.audit_outbox IS
136
+ 'Pre-chain durability tier. Writers insert in caller transaction; U4 drainer Lambda single-writer polls FOR UPDATE SKIP LOCKED, computes per-tenant chain, inserts to audit_events.';
137
+
138
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_audit_outbox_event_id
139
+ ON compliance.audit_outbox (event_id);
140
+
141
+ -- Partial index over un-drained rows so the drainer's poll query stays
142
+ -- O(N pending) regardless of total outbox volume.
143
+ CREATE INDEX IF NOT EXISTS idx_audit_outbox_pending
144
+ ON compliance.audit_outbox (enqueued_at)
145
+ WHERE drained_at IS NULL;
146
+
147
+ CREATE INDEX IF NOT EXISTS idx_audit_outbox_tenant_enqueued
148
+ ON compliance.audit_outbox (tenant_id, enqueued_at);
149
+
150
+ -- ---------------------------------------------------------------------------
151
+ -- compliance.audit_events
152
+ --
153
+ -- Append-only audit-event log (R5). Per-tenant hash chain via prev_hash
154
+ -- + event_hash, both computed app-side by the U4 drainer Lambda. INSERT
155
+ -- is the only legal mutation; UPDATE and DELETE are rejected by the
156
+ -- compliance.raise_immutable() trigger below as defense-in-depth on top
157
+ -- of the role grants U2 ships.
158
+ --
159
+ -- The 22-field envelope plus payload_oversize_s3_key (oversized payloads
160
+ -- spill to S3 with the key reference recorded here) and outbox_id (link
161
+ -- back to the originating outbox row, idempotency key for drainer
162
+ -- replay).
163
+ --
164
+ -- prev_hash is nullable: the genesis event in each tenant's chain has
165
+ -- no prior hash. event_hash is non-nullable: every row commits to its
166
+ -- own hash so the chain is verifiable from any anchor backwards.
167
+ -- ---------------------------------------------------------------------------
168
+
169
+ CREATE TABLE IF NOT EXISTS compliance.audit_events (
170
+ event_id uuid PRIMARY KEY NOT NULL,
171
+ outbox_id uuid NOT NULL,
172
+ tenant_id uuid NOT NULL,
173
+ occurred_at timestamp with time zone NOT NULL,
174
+ recorded_at timestamp with time zone NOT NULL DEFAULT now(),
175
+ actor text NOT NULL,
176
+ actor_type text NOT NULL,
177
+ source text NOT NULL,
178
+ event_type text NOT NULL,
179
+ resource_type text,
180
+ resource_id text,
181
+ action text,
182
+ outcome text,
183
+ request_id text,
184
+ thread_id uuid,
185
+ agent_id uuid,
186
+ payload jsonb NOT NULL DEFAULT '{}'::jsonb,
187
+ payload_schema_version integer NOT NULL DEFAULT 1,
188
+ control_ids text[] NOT NULL DEFAULT ARRAY[]::text[],
189
+ payload_redacted_fields text[] NOT NULL DEFAULT ARRAY[]::text[],
190
+ payload_oversize_s3_key text,
191
+ prev_hash char(64),
192
+ event_hash char(64) NOT NULL,
193
+ CONSTRAINT audit_events_actor_type_allowed CHECK (
194
+ actor_type IN ('user', 'system', 'agent')
195
+ ),
196
+ CONSTRAINT audit_events_event_type_prefix CHECK (
197
+ event_type ~ '^(auth|user|agent|mcp|workspace|data|policy|approval)\.'
198
+ )
199
+ );
200
+
201
+ COMMENT ON TABLE compliance.audit_events IS
202
+ 'Append-only audit-event log. Written ONLY by the U4 drainer Lambda. INSERT-only by role + immutability triggers. Per-tenant hash chain (prev_hash, event_hash). Tamper evidence anchored periodically by U8 to S3 Object Lock.';
203
+
204
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_audit_events_event_id
205
+ ON compliance.audit_events (event_id);
206
+
207
+ -- outbox_id UNIQUE provides drainer-replay idempotency: a re-invocation
208
+ -- on the same outbox row attempts an INSERT that conflicts and is
209
+ -- handled by ON CONFLICT (outbox_id) DO NOTHING in the drainer.
210
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_audit_events_outbox_id
211
+ ON compliance.audit_events (outbox_id);
212
+
213
+ -- Per-tenant time-ordered listing — admin Events list default sort.
214
+ CREATE INDEX IF NOT EXISTS idx_audit_events_tenant_occurred
215
+ ON compliance.audit_events (tenant_id, occurred_at DESC);
216
+
217
+ -- Filter by event_type (R8) often combined with tenant + time.
218
+ CREATE INDEX IF NOT EXISTS idx_audit_events_tenant_event_type
219
+ ON compliance.audit_events (tenant_id, event_type, occurred_at DESC);
220
+
221
+ -- Filter by actor (R8) — actor stored as opaque text id.
222
+ CREATE INDEX IF NOT EXISTS idx_audit_events_actor
223
+ ON compliance.audit_events (actor);
224
+
225
+ -- Control-evidence exports filter by control_ids[] (e.g. CC6.1, CC8.1).
226
+ -- GIN supports `control_ids @> ARRAY['CC6.1']` membership lookups.
227
+ CREATE INDEX IF NOT EXISTS idx_audit_events_control_ids
228
+ ON compliance.audit_events USING GIN (control_ids);
229
+
230
+ -- ---------------------------------------------------------------------------
231
+ -- compliance.actor_pseudonym
232
+ --
233
+ -- GDPR right-to-be-forgotten enabler (Decision #5). The audit chain
234
+ -- hashes the opaque `actor` text id, never PII. This table maps
235
+ -- actor_id → user_id + email_hash. RTBF erasure deletes the pseudonym
236
+ -- row; the chain remains valid (no audit_events row mutates) but
237
+ -- actor → person resolution is lost. EDPB recognizes this as effective
238
+ -- erasure when re-identification is no longer reasonably possible.
239
+ --
240
+ -- email_hash is sha256 of the lowercased email — supports typeahead
241
+ -- lookup in the admin Events list filter without storing plaintext.
242
+ -- ---------------------------------------------------------------------------
243
+
244
+ CREATE TABLE IF NOT EXISTS compliance.actor_pseudonym (
245
+ actor_id uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
246
+ user_id uuid,
247
+ email_hash char(64),
248
+ actor_type text NOT NULL,
249
+ display_name text,
250
+ created_at timestamp with time zone NOT NULL DEFAULT now(),
251
+ CONSTRAINT actor_pseudonym_actor_type_allowed CHECK (
252
+ actor_type IN ('user', 'system', 'agent')
253
+ )
254
+ );
255
+
256
+ COMMENT ON TABLE compliance.actor_pseudonym IS
257
+ 'Opaque actor_id ↔ user_id + email_hash mapping. GDPR RTBF: deleting a row breaks actor→person resolution while leaving the audit chain intact. Hash chain hashes actor_id, never PII.';
258
+
259
+ CREATE INDEX IF NOT EXISTS idx_actor_pseudonym_user
260
+ ON compliance.actor_pseudonym (user_id)
261
+ WHERE user_id IS NOT NULL;
262
+
263
+ CREATE INDEX IF NOT EXISTS idx_actor_pseudonym_email_hash
264
+ ON compliance.actor_pseudonym (email_hash)
265
+ WHERE email_hash IS NOT NULL;
266
+
267
+ -- ---------------------------------------------------------------------------
268
+ -- compliance.export_jobs
269
+ --
270
+ -- Async CSV/JSON export tracking (R9, U11). createAuditExport mutation
271
+ -- inserts a queued row, sends an SQS message, and returns job_id. The
272
+ -- export-runner Lambda transitions queued → running → complete (or
273
+ -- failed), writes the multipart upload to a non-Object-Lock S3 prefix,
274
+ -- and publishes a 15-minute presigned URL.
275
+ --
276
+ -- 90-day max date range and 10/hour/admin rate limit are enforced at
277
+ -- the resolver layer (U11), not in the schema. The schema permits any
278
+ -- filter shape but the GraphQL mutation rejects out-of-bounds requests
279
+ -- before the row lands.
280
+ -- ---------------------------------------------------------------------------
281
+
282
+ CREATE TABLE IF NOT EXISTS compliance.export_jobs (
283
+ job_id uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
284
+ tenant_id uuid NOT NULL,
285
+ requested_by_actor_id uuid NOT NULL,
286
+ filter jsonb NOT NULL,
287
+ format text NOT NULL,
288
+ status text NOT NULL DEFAULT 'queued',
289
+ s3_key text,
290
+ presigned_url text,
291
+ presigned_url_expires_at timestamp with time zone,
292
+ job_error text,
293
+ requested_at timestamp with time zone NOT NULL DEFAULT now(),
294
+ started_at timestamp with time zone,
295
+ completed_at timestamp with time zone,
296
+ CONSTRAINT export_jobs_format_allowed CHECK (
297
+ format IN ('csv', 'json')
298
+ ),
299
+ CONSTRAINT export_jobs_status_allowed CHECK (
300
+ status IN ('queued', 'running', 'complete', 'failed')
301
+ )
302
+ );
303
+
304
+ COMMENT ON TABLE compliance.export_jobs IS
305
+ 'Admin async-export job tracking. createAuditExport mutation enforces 90-day max range + 10/hour/admin rate limit before insert. export-runner Lambda streams CSV/NDJSON to S3 + publishes 15min presigned URL.';
306
+
307
+ CREATE INDEX IF NOT EXISTS idx_export_jobs_tenant_requested
308
+ ON compliance.export_jobs (tenant_id, requested_at DESC);
309
+
310
+ -- Rate-limit check at the resolver layer queries by (tenant, actor, recent window).
311
+ CREATE INDEX IF NOT EXISTS idx_export_jobs_actor_requested
312
+ ON compliance.export_jobs (tenant_id, requested_by_actor_id, requested_at DESC);
313
+
314
+ -- ---------------------------------------------------------------------------
315
+ -- Immutability trigger function + per-table triggers
316
+ --
317
+ -- Defense-in-depth on top of the role grants U2 ships. Even if the
318
+ -- writer role is mis-granted UPDATE/DELETE/TRUNCATE on audit_events
319
+ -- (config drift, mistaken break-glass grant, future privileged role),
320
+ -- the triggers RAISE EXCEPTION before the change applies.
321
+ --
322
+ -- TRUNCATE bypasses BEFORE DELETE triggers in Postgres — it has its own
323
+ -- BEFORE TRUNCATE trigger that fires FOR EACH STATEMENT. Without the
324
+ -- truncate trigger, an actor with TRUNCATE privilege could wipe the
325
+ -- entire audit log without producing the immutability error. The
326
+ -- trigger function below switches on TG_OP so a single function serves
327
+ -- ROW-level (UPDATE/DELETE) and STATEMENT-level (TRUNCATE) firings.
328
+ --
329
+ -- DROP TRIGGER IF EXISTS + CREATE TRIGGER is the portable idempotent
330
+ -- pattern; CREATE OR REPLACE TRIGGER is Postgres 14+ but DROP IF EXISTS
331
+ -- works identically on every supported version.
332
+ -- ---------------------------------------------------------------------------
333
+
334
+ CREATE OR REPLACE FUNCTION compliance.raise_immutable()
335
+ RETURNS TRIGGER
336
+ LANGUAGE plpgsql
337
+ AS $$
338
+ BEGIN
339
+ IF TG_OP = 'TRUNCATE' THEN
340
+ RAISE EXCEPTION 'compliance.audit_events rows are immutable (TRUNCATE blocked)';
341
+ END IF;
342
+ RAISE EXCEPTION 'compliance.audit_events rows are immutable (event_id=%)', COALESCE(OLD.event_id::text, NEW.event_id::text);
343
+ END;
344
+ $$;
345
+
346
+ DROP TRIGGER IF EXISTS audit_events_block_update ON compliance.audit_events;
347
+ CREATE TRIGGER audit_events_block_update
348
+ BEFORE UPDATE ON compliance.audit_events
349
+ FOR EACH ROW
350
+ EXECUTE FUNCTION compliance.raise_immutable();
351
+
352
+ DROP TRIGGER IF EXISTS audit_events_block_delete ON compliance.audit_events;
353
+ CREATE TRIGGER audit_events_block_delete
354
+ BEFORE DELETE ON compliance.audit_events
355
+ FOR EACH ROW
356
+ EXECUTE FUNCTION compliance.raise_immutable();
357
+
358
+ DROP TRIGGER IF EXISTS audit_events_block_truncate ON compliance.audit_events;
359
+ CREATE TRIGGER audit_events_block_truncate
360
+ BEFORE TRUNCATE ON compliance.audit_events
361
+ FOR EACH STATEMENT
362
+ EXECUTE FUNCTION compliance.raise_immutable();
363
+
364
+ COMMIT;
@@ -0,0 +1,171 @@
1
+ -- 0070_compliance_aurora_roles.sql
2
+ --
3
+ -- Phase 3 U2 of the System Workflows revert + Compliance reframe.
4
+ -- Provisions the three Aurora roles that scope per-tier access to the
5
+ -- compliance.* schema introduced in U1 (drizzle/0069_compliance_schema.sql,
6
+ -- merged via PR #880):
7
+ --
8
+ -- - compliance_writer: USAGE on schema + INSERT only on audit_outbox
9
+ -- and export_jobs. Used by Yoga resolvers and
10
+ -- Lambda handlers via the U3 emitAuditEvent
11
+ -- helper.
12
+ -- - compliance_drainer: USAGE + SELECT/UPDATE on audit_outbox + SELECT
13
+ -- on actor_pseudonym + INSERT only on
14
+ -- audit_events. Used exclusively by the U4
15
+ -- single-writer outbox drainer Lambda
16
+ -- (reserved-concurrency=1).
17
+ -- - compliance_reader: USAGE + SELECT-only on all four compliance.*
18
+ -- tables. Used by the graphql-http Lambda for
19
+ -- the U10 admin Compliance section read paths.
20
+ --
21
+ -- Plan reference:
22
+ -- docs/plans/2026-05-07-001-feat-compliance-u2-aurora-roles-plan.md
23
+ -- docs/plans/2026-05-06-011-feat-compliance-audit-event-log-plan.md
24
+ --
25
+ -- Apply manually:
26
+ -- The bootstrap helper script wraps this file with psql variable
27
+ -- substitution + Secrets Manager population:
28
+ -- STAGE=dev bash scripts/bootstrap-compliance-roles.sh
29
+ -- Direct apply (advanced — must supply all three passwords):
30
+ -- psql "$DATABASE_URL" \
31
+ -- -v writer_pass="$COMPLIANCE_WRITER_PASS" \
32
+ -- -v drainer_pass="$COMPLIANCE_DRAINER_PASS" \
33
+ -- -v reader_pass="$COMPLIANCE_READER_PASS" \
34
+ -- -f packages/database-pg/drizzle/0070_compliance_aurora_roles.sql
35
+ --
36
+ -- The DO $$ ... $$ blocks make role creation idempotent (re-running with
37
+ -- new passwords runs ALTER ROLE; re-running with same passwords is a
38
+ -- no-op). GRANT statements are inherently idempotent in PostgreSQL.
39
+ --
40
+ -- Markers (consumed by scripts/db-migrate-manual.sh as the post-deploy drift gate):
41
+ --
42
+ -- creates-role: compliance_writer
43
+ -- creates-role: compliance_drainer
44
+ -- creates-role: compliance_reader
45
+
46
+ \set ON_ERROR_STOP on
47
+
48
+ BEGIN;
49
+
50
+ SET LOCAL lock_timeout = '5s';
51
+ SET LOCAL statement_timeout = '60s';
52
+ SET LOCAL "thinkwork.writer_pass" = :'writer_pass';
53
+ SET LOCAL "thinkwork.drainer_pass" = :'drainer_pass';
54
+ SET LOCAL "thinkwork.reader_pass" = :'reader_pass';
55
+
56
+ -- Refuse to apply against an unexpected DB. Hand-rolled migrations are
57
+ -- applied by an operator and a stale DATABASE_URL pointing at a non-dev
58
+ -- target would create roles in the wrong cluster.
59
+ DO $$
60
+ BEGIN
61
+ IF current_database() != 'thinkwork' THEN
62
+ RAISE EXCEPTION 'wrong database: %, expected thinkwork', current_database();
63
+ END IF;
64
+ END $$;
65
+
66
+ -- Refuse to apply if the U1 compliance schema is missing. PostgreSQL
67
+ -- CREATE ROLE is NOT transactional — it commits to pg_authid before the
68
+ -- subsequent GRANT statements run, so a 0070 apply against a database
69
+ -- without 0069 would leave three roles with passwords but zero grants.
70
+ -- The drift gate then probes pg_roles and reports APPLIED, masking the
71
+ -- partial state. This guard converts the silent partial failure into a
72
+ -- hard stop before any role creation.
73
+ DO $$
74
+ BEGIN
75
+ IF NOT EXISTS (SELECT FROM information_schema.schemata WHERE schema_name = 'compliance') THEN
76
+ RAISE EXCEPTION 'compliance schema missing: apply drizzle/0069_compliance_schema.sql first (U1, PR #880)';
77
+ END IF;
78
+ END $$;
79
+
80
+ -- ---------------------------------------------------------------------------
81
+ -- Role creation (idempotent)
82
+ --
83
+ -- Each block creates the role if missing, otherwise rotates the password
84
+ -- via ALTER ROLE. CREATE ROLE has no IF NOT EXISTS form prior to PG 16,
85
+ -- so the DO $$ ... pg_roles existence check is the portable idempotent
86
+ -- pattern. ALTER ROLE on the same role with the same password is a
87
+ -- no-op, so re-running with stable passwords is safe.
88
+ -- ---------------------------------------------------------------------------
89
+
90
+ DO $$
91
+ BEGIN
92
+ IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'compliance_writer') THEN
93
+ EXECUTE format('CREATE ROLE compliance_writer WITH LOGIN PASSWORD %L', current_setting('thinkwork.writer_pass'));
94
+ ELSE
95
+ EXECUTE format('ALTER ROLE compliance_writer WITH LOGIN PASSWORD %L', current_setting('thinkwork.writer_pass'));
96
+ END IF;
97
+ END $$;
98
+
99
+ DO $$
100
+ BEGIN
101
+ IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'compliance_drainer') THEN
102
+ EXECUTE format('CREATE ROLE compliance_drainer WITH LOGIN PASSWORD %L', current_setting('thinkwork.drainer_pass'));
103
+ ELSE
104
+ EXECUTE format('ALTER ROLE compliance_drainer WITH LOGIN PASSWORD %L', current_setting('thinkwork.drainer_pass'));
105
+ END IF;
106
+ END $$;
107
+
108
+ DO $$
109
+ BEGIN
110
+ IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'compliance_reader') THEN
111
+ EXECUTE format('CREATE ROLE compliance_reader WITH LOGIN PASSWORD %L', current_setting('thinkwork.reader_pass'));
112
+ ELSE
113
+ EXECUTE format('ALTER ROLE compliance_reader WITH LOGIN PASSWORD %L', current_setting('thinkwork.reader_pass'));
114
+ END IF;
115
+ END $$;
116
+
117
+ -- ---------------------------------------------------------------------------
118
+ -- Schema-level USAGE for all three roles
119
+ -- ---------------------------------------------------------------------------
120
+
121
+ GRANT USAGE ON SCHEMA compliance TO compliance_writer, compliance_drainer, compliance_reader;
122
+
123
+ -- ---------------------------------------------------------------------------
124
+ -- compliance_writer — INSERT only on audit_outbox + export_jobs
125
+ --
126
+ -- The U3 emitAuditEvent helper inserts into audit_outbox in the caller's
127
+ -- transaction. The U11 createAuditExport mutation inserts into
128
+ -- export_jobs. Writer has no SELECT, no UPDATE, no DELETE, no access to
129
+ -- audit_events (only the drainer writes there) or actor_pseudonym (read
130
+ -- via the reader role for typeahead lookups in the admin UI).
131
+ -- ---------------------------------------------------------------------------
132
+
133
+ GRANT INSERT ON compliance.audit_outbox TO compliance_writer;
134
+ GRANT INSERT ON compliance.export_jobs TO compliance_writer;
135
+
136
+ -- ---------------------------------------------------------------------------
137
+ -- compliance_drainer — pulls outbox, computes hash chain, writes audit_events
138
+ --
139
+ -- SELECT + UPDATE on audit_outbox: poll FOR UPDATE SKIP LOCKED, mark
140
+ -- drained_at on success, write drainer_error on failure.
141
+ -- SELECT on actor_pseudonym: needed if the drainer canonicalizes the
142
+ -- payload with actor_id resolution (Decision #5 in the master plan
143
+ -- keeps actor_id opaque in audit rows; pseudonym lookup is read-only).
144
+ -- INSERT on audit_events: append-only chained log.
145
+ --
146
+ -- No DELETE/TRUNCATE on any table — drainer never removes outbox rows
147
+ -- (drained_at marker is the soft-tombstone) and never modifies historical
148
+ -- audit events. The U1 immutability triggers are belt-and-suspenders on
149
+ -- top of this grant boundary.
150
+ -- ---------------------------------------------------------------------------
151
+
152
+ GRANT SELECT, UPDATE ON compliance.audit_outbox TO compliance_drainer;
153
+ GRANT SELECT ON compliance.actor_pseudonym TO compliance_drainer;
154
+ GRANT INSERT ON compliance.audit_events TO compliance_drainer;
155
+
156
+ -- ---------------------------------------------------------------------------
157
+ -- compliance_reader — SELECT-only on all four tables
158
+ --
159
+ -- The U10 admin Compliance section reads via this role: events list,
160
+ -- detail drawer, exports filter resolution, verification status panel,
161
+ -- pseudonym typeahead resolution. No write surface; the U11 export
162
+ -- mutation flow uses compliance_writer for the export_jobs INSERT and
163
+ -- only reads via this role.
164
+ -- ---------------------------------------------------------------------------
165
+
166
+ GRANT SELECT ON compliance.audit_outbox TO compliance_reader;
167
+ GRANT SELECT ON compliance.audit_events TO compliance_reader;
168
+ GRANT SELECT ON compliance.actor_pseudonym TO compliance_reader;
169
+ GRANT SELECT ON compliance.export_jobs TO compliance_reader;
170
+
171
+ COMMIT;
@@ -0,0 +1,24 @@
1
+ -- Add nullable Computer ownership to Threads for the feature-flagged
2
+ -- ThinkWork Computer cutover.
3
+ --
4
+ -- Apply manually:
5
+ -- psql "$DATABASE_URL" -f packages/database-pg/drizzle/0072_threads_computer_ownership.sql
6
+ --
7
+ -- creates-column: public.threads.computer_id
8
+ -- creates: public.idx_threads_computer
9
+
10
+ \set ON_ERROR_STOP on
11
+
12
+ BEGIN;
13
+
14
+ SET LOCAL lock_timeout = '5s';
15
+ SET LOCAL statement_timeout = '120s';
16
+
17
+ ALTER TABLE public.threads
18
+ ADD COLUMN IF NOT EXISTS computer_id uuid REFERENCES public.computers(id);
19
+
20
+ CREATE INDEX IF NOT EXISTS idx_threads_computer
21
+ ON public.threads (tenant_id, computer_id)
22
+ WHERE computer_id IS NOT NULL;
23
+
24
+ COMMIT;
@@ -0,0 +1,105 @@
1
+ -- 0073_compliance_tenant_anchor_state.sql
2
+ --
3
+ -- Phase 3 U8a of the System Workflows revert + Compliance reframe.
4
+ -- (Originally drafted as 0071 but renumbered to 0073 to avoid collision
5
+ -- with the unrelated 0071_connector_computer_dispatch_target.sql that
6
+ -- shipped on origin/main concurrently with this work.)
7
+ -- Adds the per-tenant high-water-mark table the anchor Lambda uses to
8
+ -- track which audit_events have been included in a Merkle anchor.
9
+ --
10
+ -- Schema:
11
+ -- compliance.tenant_anchor_state(
12
+ -- tenant_id uuid PRIMARY KEY,
13
+ -- last_anchored_recorded_at timestamptz,
14
+ -- last_anchored_event_id uuid,
15
+ -- last_cadence_id uuid,
16
+ -- updated_at timestamptz NOT NULL DEFAULT now()
17
+ -- )
18
+ --
19
+ -- Anchor advance: at each cadence, the Lambda updates each tenant's row
20
+ -- with the maximum recorded_at across the events included in the cadence
21
+ -- (and the corresponding event_id for tie-breaking on equal timestamps).
22
+ -- The next cadence's chain-head SELECT uses
23
+ -- `recorded_at > last_anchored_recorded_at` (or > '-infinity' for
24
+ -- never-anchored tenants).
25
+ --
26
+ -- Role grants:
27
+ -- compliance_drainer gets SELECT/INSERT/UPDATE (not DELETE) on the new
28
+ -- table. Per master plan U8a Decision #5, the anchor Lambda reuses the
29
+ -- compliance_drainer role for tenant_anchor_state writes rather than
30
+ -- provisioning a 4th Aurora role. The anchor Lambda runs reserved-
31
+ -- concurrency=1 (matching the drainer pattern), so the role widening
32
+ -- is bounded to one concurrent writer.
33
+ --
34
+ -- Plan reference:
35
+ -- docs/plans/2026-05-07-010-feat-compliance-u8a-anchor-lambda-inert-plan.md
36
+ -- docs/plans/2026-05-06-011-feat-compliance-audit-event-log-plan.md
37
+ --
38
+ -- Apply manually (operator step before merging — drift gate currently
39
+ -- disabled per #905):
40
+ -- psql "$DATABASE_URL" -f packages/database-pg/drizzle/0073_compliance_tenant_anchor_state.sql
41
+ --
42
+ -- Markers (consumed by scripts/db-migrate-manual.sh as the post-deploy drift gate):
43
+ --
44
+ -- creates: compliance.tenant_anchor_state
45
+ -- creates: compliance.idx_tenant_anchor_state_updated_at
46
+
47
+ \set ON_ERROR_STOP on
48
+
49
+ BEGIN;
50
+
51
+ SET LOCAL lock_timeout = '5s';
52
+ SET LOCAL statement_timeout = '60s';
53
+
54
+ -- Database guard — ensures we never accidentally apply to a wrong-named
55
+ -- database. Mirrors 0069 + 0070 prologue.
56
+ DO $$
57
+ BEGIN
58
+ IF current_database() NOT IN ('thinkwork', 'postgres') THEN
59
+ RAISE EXCEPTION 'Wrong database: %, expected thinkwork or postgres', current_database();
60
+ END IF;
61
+ END $$;
62
+
63
+ -- ---------------------------------------------------------------------------
64
+ -- compliance.tenant_anchor_state
65
+ --
66
+ -- One row per tenant. The anchor Lambda updates last_anchored_recorded_at
67
+ -- with the maximum recorded_at across the events included in the latest
68
+ -- Merkle cadence's tenant slice (tie-broken by event_id for equal
69
+ -- timestamps). Seeded lazily on first anchor — never-anchored tenants
70
+ -- have no row, and the chain-head SELECT uses
71
+ -- COALESCE(last_anchored_recorded_at, '-infinity'::timestamptz) so they
72
+ -- still surface every event.
73
+ -- ---------------------------------------------------------------------------
74
+
75
+ CREATE TABLE IF NOT EXISTS compliance.tenant_anchor_state (
76
+ tenant_id uuid PRIMARY KEY,
77
+ last_anchored_recorded_at timestamptz,
78
+ last_anchored_event_id uuid,
79
+ last_cadence_id uuid,
80
+ updated_at timestamptz NOT NULL DEFAULT now()
81
+ );
82
+
83
+ -- Index for the watchdog's eventual "oldest un-anchored tenant" query
84
+ -- (U8b consumes this; U8a creates it inert).
85
+ CREATE INDEX IF NOT EXISTS idx_tenant_anchor_state_updated_at
86
+ ON compliance.tenant_anchor_state (updated_at);
87
+
88
+ -- ---------------------------------------------------------------------------
89
+ -- Grants
90
+ -- ---------------------------------------------------------------------------
91
+
92
+ -- USAGE on schema is already granted to compliance_drainer in 0070; this
93
+ -- restatement is for clarity (idempotent in PostgreSQL).
94
+ GRANT USAGE ON SCHEMA compliance TO compliance_drainer;
95
+
96
+ -- The anchor Lambda needs SELECT (read latest seq), INSERT (seed first
97
+ -- row per tenant), UPDATE (advance high-water-mark). No DELETE — this
98
+ -- table is append-or-update-only.
99
+ GRANT SELECT, INSERT, UPDATE ON compliance.tenant_anchor_state TO compliance_drainer;
100
+
101
+ -- compliance_reader gets SELECT for the eventual U10 admin Compliance UI
102
+ -- (showing per-tenant anchor lag). Read-only by intent.
103
+ GRANT SELECT ON compliance.tenant_anchor_state TO compliance_reader;
104
+
105
+ COMMIT;