thinkwork-cli 0.12.15 → 0.12.17

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 (152) hide show
  1. package/dist/{api-client-4VT3FWOH.js → api-client-JUBCQQDF.js} +1 -1
  2. package/dist/{chunk-H7AB42ES.js → chunk-STY44FQO.js} +13 -0
  3. package/dist/cli.js +1107 -10225
  4. package/dist/commands/enterprise/templates/deploy-repo/docs/runbook.md +3 -8
  5. package/dist/commands/enterprise/templates/deploy-repo/terraform/main.tf +1 -184
  6. package/dist/drizzle/0050_brain_v0_entity_subtype.sql +2 -2
  7. package/dist/drizzle/0051_brain_v0_tenant_entity_pages.sql +6 -6
  8. package/dist/drizzle/0052_brain_v0_external_refs.sql +2 -2
  9. package/dist/drizzle/0053_brain_v0_unresolved_mentions_subtype.sql +2 -2
  10. package/dist/drizzle/0145_knowledge_graph_thread_ingest.sql +14 -14
  11. package/dist/drizzle/0146_knowledge_graph_source_scope.sql +1 -1
  12. package/dist/drizzle/0157_knowledge_graph_observations_source.sql +2 -2
  13. package/dist/drizzle/0158_wiki_tenant_scope.sql +1 -1
  14. package/dist/drizzle/0165_plugin_premium_entitlements.sql +2 -2
  15. package/dist/drizzle/{0166_company_brain_substrate_contract.sql → 0166_brain_substrate_contract.sql} +9 -9
  16. package/dist/drizzle/{0167_company_brain_artifact_manifest_runtime.sql → 0167_brain_artifact_manifest_runtime.sql} +5 -5
  17. package/dist/drizzle/0168_eval_skill_gate.sql +1 -1
  18. package/dist/drizzle/0183_okf_artifact_manifests.sql +1 -1
  19. package/dist/drizzle/0197_rcm_context_columns.sql +28 -0
  20. package/dist/drizzle/0200_eval_results_execution_tier.sql +40 -0
  21. package/dist/drizzle/0201_brain_dream_runs.sql +72 -0
  22. package/dist/drizzle/0202_backfill_thread_participants_from_senders.sql +110 -0
  23. package/dist/drizzle/0203_threads_mode_override.sql +61 -0
  24. package/dist/drizzle/0204_thread_turns_triggering_message_id.sql +48 -0
  25. package/dist/drizzle/0205_routines_git_python.sql +185 -0
  26. package/dist/drizzle/0206_agent_loop_versions_routine_actions.sql +40 -0
  27. package/dist/drizzle/0207_tenant_credentials_github_repo_kind.sql +52 -0
  28. package/dist/drizzle/0208_knowledge_graph_neutral_identifiers.sql +253 -0
  29. package/dist/drizzle/0209_living_artifacts_schema.sql +109 -0
  30. package/dist/drizzle/0210_think137_automations_additive_schema.sql +113 -0
  31. package/dist/drizzle/0211_backfill_agent_loop_versions_target_spec.sql +120 -0
  32. package/dist/drizzle/0212_fold_webhooks_into_automations.sql +312 -0
  33. package/dist/drizzle/0213_agent_loop_versions_judge_evidence_nullable.sql +63 -0
  34. package/dist/drizzle/0214_think137_drop_judge_evidence_roi.sql +99 -0
  35. package/dist/drizzle/0215_agent_loop_versions_goal_worker_policy_nullable.sql +70 -0
  36. package/dist/drizzle/0216_drop_agent_loop_versions_goal_worker_policy.sql +81 -0
  37. package/dist/drizzle/0217_agents_capability_folder_dispatch.sql +12 -0
  38. package/dist/drizzle/0218_document_plates.sql +30 -0
  39. package/dist/drizzle/0219_document_section_waivers.sql +29 -0
  40. package/dist/drizzle/0220_artifacts_created_by_user.sql +21 -0
  41. package/dist/drizzle/0221_artifact_shares.sql +29 -0
  42. package/dist/drizzle/0221_workflow_interpreter_thin_slice.sql +71 -0
  43. package/dist/drizzle/0222_compliance_drainer_select_audit_events.sql +21 -0
  44. package/dist/drizzle/0223_document_conformance_reports.sql +38 -0
  45. package/dist/drizzle/0224_artifact_refresh_state.sql +11 -0
  46. package/dist/drizzle/0225_think216_agent_loop_convergence.sql +21 -0
  47. package/dist/drizzle/0226_drop_n8n_agent_step_runs.sql +6 -0
  48. package/dist/drizzle/0227_analyst_reader_role.sql +1055 -0
  49. package/dist/drizzle/0228_tenant_credentials_rds_iam_kind.sql +62 -0
  50. package/dist/drizzle/0229_analyst_reader_rds_iam_grant.sql +79 -0
  51. package/dist/drizzle/0230_analyst_rls.sql +1613 -0
  52. package/dist/drizzle/0231_cost_events_cache_write_enforcement_exempt.sql +11 -0
  53. package/dist/plugins/catalog/package.json +0 -1
  54. package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +0 -1
  55. package/dist/plugins/catalog/src/__tests__/catalog.test.ts +16 -8
  56. package/dist/plugins/catalog/src/__tests__/contracts.test.ts +56 -0
  57. package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +0 -2
  58. package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +0 -5
  59. package/dist/plugins/catalog/src/contracts.ts +112 -0
  60. package/dist/plugins/catalog/src/registry/generated-first-party.ts +0 -6
  61. package/dist/plugins/n8n/README.md +4 -36
  62. package/dist/plugins/n8n/src/deployment/managed-app.ts +0 -17
  63. package/dist/plugins/n8n/src/manifest.ts +0 -5
  64. package/dist/plugins/n8n/terraform/n8n/main.tf +0 -26
  65. package/dist/plugins/n8n/terraform/n8n/outputs.tf +0 -5
  66. package/dist/plugins/n8n/terraform/n8n/variables.tf +0 -6
  67. package/dist/plugins/n8n/test/manifest.test.ts +0 -1
  68. package/dist/plugins/twenty/package.json +5 -0
  69. package/dist/plugins/twenty/scripts/lib/__tests__/load-records.test.ts +232 -0
  70. package/dist/plugins/twenty/scripts/lib/__tests__/mappers.test.ts +489 -0
  71. package/dist/plugins/twenty/scripts/lib/__tests__/members-ensure.test.ts +199 -0
  72. package/dist/plugins/twenty/scripts/lib/__tests__/provision-members-db.test.ts +43 -0
  73. package/dist/plugins/twenty/scripts/lib/__tests__/schema-ensure.test.ts +230 -0
  74. package/dist/plugins/twenty/scripts/lib/__tests__/twenty-client.test.ts +207 -0
  75. package/dist/plugins/twenty/scripts/lib/lastmile-reader.ts +282 -0
  76. package/dist/plugins/twenty/scripts/lib/load-attachments.ts +220 -0
  77. package/dist/plugins/twenty/scripts/lib/load-records.ts +680 -0
  78. package/dist/plugins/twenty/scripts/lib/mappers.ts +559 -0
  79. package/dist/plugins/twenty/scripts/lib/members-ensure.ts +381 -0
  80. package/dist/plugins/twenty/scripts/lib/provision-members-db.ts +191 -0
  81. package/dist/plugins/twenty/scripts/lib/schema-ensure.ts +425 -0
  82. package/dist/plugins/twenty/scripts/lib/twenty-client.ts +246 -0
  83. package/dist/plugins/twenty/scripts/migrate-lastmile.ts +610 -0
  84. package/dist/plugins/twenty/scripts/provision-twenty-members.ts +214 -0
  85. package/dist/plugins/twenty/src/manifest.ts +99 -0
  86. package/dist/plugins/twenty/terraform/twenty/README.md +1 -1
  87. package/dist/plugins/twenty/test/manifest.test.ts +12 -2
  88. package/dist/terraform/examples/greenfield/main.tf +90 -380
  89. package/dist/terraform/examples/greenfield/terraform.tfvars.example +0 -1
  90. package/dist/terraform/modules/app/agentcore-pi/README.md +1 -3
  91. package/dist/terraform/modules/app/agentcore-pi/main.tf +36 -32
  92. package/dist/terraform/modules/app/agentcore-pi/variables.tf +23 -15
  93. package/dist/terraform/modules/app/agentcore-runtime/main.tf +3 -3
  94. package/dist/terraform/modules/app/customer-domain/main.tf +16 -2
  95. package/dist/terraform/modules/app/deployment-control-plane/main.tf +5 -0
  96. package/dist/terraform/modules/app/deployment-control-plane/runner.py +85 -207
  97. package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +56 -2
  98. package/dist/terraform/modules/app/hindsight-memory/main.tf +52 -4
  99. package/dist/terraform/modules/app/lambda-api/eval-fanout.tf +7 -0
  100. package/dist/terraform/modules/app/lambda-api/handlers.tf +493 -89
  101. package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +215 -96
  102. package/dist/terraform/modules/app/lambda-api/main.tf +24 -5
  103. package/dist/terraform/modules/app/lambda-api/outputs.tf +19 -11
  104. package/dist/terraform/modules/app/lambda-api/variables.tf +94 -67
  105. package/dist/terraform/modules/app/ses-email/main.tf +14 -2
  106. package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/main.tf +493 -0
  107. package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/outputs.tf +34 -0
  108. package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/variables.tf +32 -0
  109. package/dist/terraform/modules/data/aurora-postgres/main.tf +55 -0
  110. package/dist/terraform/modules/data/aurora-postgres/outputs.tf +15 -0
  111. package/dist/terraform/modules/data/s3-buckets/main.tf +26 -0
  112. package/dist/terraform/modules/thinkwork/README.md +0 -1
  113. package/dist/terraform/modules/thinkwork/main.tf +167 -446
  114. package/dist/terraform/modules/thinkwork/outputs.tf +2 -142
  115. package/dist/terraform/modules/thinkwork/variables.tf +55 -309
  116. package/dist/terraform/schema.graphql +4 -0
  117. package/dist/workspace-defaults/files/AGENTS.md +45 -31
  118. package/dist/workspace-defaults/files/CONTEXT.md +48 -4
  119. package/dist/workspace-defaults/files/GUARDRAILS.md +18 -0
  120. package/dist/workspace-defaults/files/MEMORY_GUIDE.md +32 -4
  121. package/dist/workspace-defaults/files/SPACE.md +34 -19
  122. package/dist/workspace-defaults/files/TOOLS.md +30 -0
  123. package/dist/workspace-defaults/files/skills/document-composer/SKILL.md +154 -0
  124. package/dist/workspace-defaults/files/skills/document-composer/references/authoring-rules.md +101 -0
  125. package/package.json +2 -1
  126. package/dist/plugins/company-brain/README.md +0 -37
  127. package/dist/plugins/company-brain/package.json +0 -33
  128. package/dist/plugins/company-brain/runtime/cognee/Dockerfile +0 -11
  129. package/dist/plugins/company-brain/smoke/cognee-managed-app-smoke.mjs +0 -458
  130. package/dist/plugins/company-brain/smoke/cognee-memory-cutover-smoke.mjs +0 -539
  131. package/dist/plugins/company-brain/smoke/company-brain-context-engine-smoke.mjs +0 -312
  132. package/dist/plugins/company-brain/smoke/company-brain-operations-smoke.mjs +0 -449
  133. package/dist/plugins/company-brain/smoke/company-brain-plugin-smoke.mjs +0 -655
  134. package/dist/plugins/company-brain/smoke/hindsight-memory-isolation-smoke.mjs +0 -474
  135. package/dist/plugins/company-brain/src/api/cognee-client.ts +0 -896
  136. package/dist/plugins/company-brain/src/api/cognee-cluster-identity.ts +0 -49
  137. package/dist/plugins/company-brain/src/api/cognee-memory-scope.ts +0 -84
  138. package/dist/plugins/company-brain/src/api/context-engine-provider.ts +0 -888
  139. package/dist/plugins/company-brain/src/api/migration.ts +0 -665
  140. package/dist/plugins/company-brain/src/deployment/cognee-managed-app.ts +0 -396
  141. package/dist/plugins/company-brain/src/index.ts +0 -62
  142. package/dist/plugins/company-brain/src/manifest.ts +0 -112
  143. package/dist/plugins/company-brain/terraform/cognee/README.md +0 -263
  144. package/dist/plugins/company-brain/terraform/cognee/main.tf +0 -766
  145. package/dist/plugins/company-brain/terraform/cognee/outputs.tf +0 -144
  146. package/dist/plugins/company-brain/terraform/cognee/variables.tf +0 -402
  147. package/dist/plugins/company-brain/test/api/cognee-client.test.ts +0 -562
  148. package/dist/plugins/company-brain/test/api/cognee-memory-scope.test.ts +0 -74
  149. package/dist/plugins/company-brain/test/api/context-engine-provider.test.ts +0 -447
  150. package/dist/plugins/company-brain/test/api/migration.test.ts +0 -465
  151. package/dist/plugins/company-brain/test/manifest.test.ts +0 -95
  152. package/dist/plugins/company-brain/tsconfig.json +0 -8
@@ -0,0 +1,312 @@
1
+ -- Purpose: fold standalone webhooks into Automations (THINK-137 U8, R8). The
2
+ -- Settings → Webhooks surface retires; every webhook that a user could
3
+ -- manage is henceforth an Automation with a `webhook` trigger. This migration
4
+ -- converts each convertible `webhooks` row (target_type agent|routine, NOT
5
+ -- connector-created) into an Automation (`agent_loops` + `agent_loop_versions`)
6
+ -- and re-points the webhook row at that loop with target_type='automation',
7
+ -- PRESERVING the token so inbound URLs keep working.
8
+ --
9
+ -- Convertible set (matches the plan's agent|routine target scope):
10
+ -- target_type IN ('agent','routine')
11
+ -- AND connect_provider_id IS NULL -- connector-created rows are NOT user-
12
+ -- managed webhooks; they stay untouched
13
+ -- AND agent_loop_id IS NULL -- not already an Automation
14
+ -- AND target_type <> 'automation' -- (redundant with the IN, kept explicit)
15
+ --
16
+ -- Left UNTOUCHED (deliberately):
17
+ -- * target_type='task' rows (external-task connector webhooks) — out of the
18
+ -- convertible set; their handler branch survives.
19
+ -- * any row with connect_provider_id set (connector-minted) — untouched.
20
+ -- * target_type='automation' rows — already new-model.
21
+ --
22
+ -- Per-target mapping (mirrors targetSpecFromLegacy + saveAgentLoop defaults):
23
+ -- * agent target →
24
+ -- target_spec {kind:'agent_thread', agentThread:{instructions:<prompt or
25
+ -- default>, workerId:<agent_id>, workerType:'agent',
26
+ -- threadMode:'new_per_run'}}
27
+ -- worker_spec {type:'agent', id:<agent_id>, toolHints:[], config:{}}
28
+ -- * routine target →
29
+ -- target_spec {kind:'routine', routine:{routineId:<routine_id>}}
30
+ -- worker_spec points at the tenant's platform-default agent (worker_spec
31
+ -- is NOT NULL but is never read for a routine-kind dispatch; the
32
+ -- placeholder keeps the column shape valid for later re-saves).
33
+ -- The NOT-NULL legacy spec columns (trigger/goal/worker/judge/loop) get the
34
+ -- SAME fixed defaults saveAgentLoop writes: judge self_check, loop_policy
35
+ -- {maxIterations:1, failBehavior:'return_blocker', escalateOnFailure:false},
36
+ -- trigger {family:'webhook', enabled:true, config:{}}. evidence_policy uses
37
+ -- its column default.
38
+ --
39
+ -- run_as_user_id CHOICE: left NULL (system identity). webhooks.created_by_id is
40
+ -- untyped `text` (system|user via created_by_type) and is NOT a users FK, so it
41
+ -- cannot be trusted as a run-as identity. A NULL run_as_user_id means the run
42
+ -- acts as the system identity — exactly the pre-migration behavior, where the
43
+ -- agent branch enqueued wakeups with requested_by_actor_type='system'.
44
+ --
45
+ -- NULL-space agent rows (none on dev; written for customer stages): an
46
+ -- agent_thread automation needs a home Space (R4). When the source webhook has
47
+ -- no space_id, the Automation is created DISABLED (agent_loops.enabled=false),
48
+ -- the webhook row stays disabled, and a deduplicated inbox item
49
+ -- (type='automation_needs_space', entity_type='agent_loop', entity_id=<loop>)
50
+ -- is filed so an operator assigns a Space and re-enables. A disabled loop makes
51
+ -- the inbound token resolve to a dispatcher skip (agent_loop_disabled →
52
+ -- HTTP 200 "ignored"), never an error. Routine targets are headless and never
53
+ -- need a Space, so they convert enabled regardless.
54
+ -- INBOX CHOICE: a new explicit `automation_needs_space` type (over reusing a
55
+ -- headless-failure shape) — it names the exact operator action and carries the
56
+ -- loop id as entity_id for the deep link.
57
+ --
58
+ -- Idempotent: only webhooks in the convertible set are processed, and
59
+ -- conversion sets webhooks.agent_loop_id + target_type='automation', which drops
60
+ -- the row out of the set — a re-run is a no-op. The inbox insert is guarded by
61
+ -- NOT EXISTS on (tenant_id, type, entity_id). The drift-marker VIEW is CREATE OR
62
+ -- REPLACE. Safe to re-run. dev converts exactly ONE row (the agent webhook with
63
+ -- a Space); customer stages convert 0.
64
+ --
65
+ -- Pure SQL (no psql vars). Hand-rolled per convention (0210/0211 precedent):
66
+ -- advisory lock, pre-flight guards, DO-block loop, drift-marker view.
67
+ --
68
+ -- Apply manually (verification pass before merge):
69
+ -- psql "$DATABASE_URL" -v ON_ERROR_STOP=1 --single-transaction \
70
+ -- -f packages/database-pg/drizzle/0212_fold_webhooks_into_automations.sql
71
+ --
72
+ -- creates: public.view_webhooks_folded_into_automations
73
+
74
+ \set ON_ERROR_STOP on
75
+
76
+ BEGIN;
77
+
78
+ SELECT pg_advisory_xact_lock(hashtext('think137_fold_webhooks_into_automations_0212'));
79
+
80
+ SET LOCAL lock_timeout = '5s';
81
+ SET LOCAL statement_timeout = '120s';
82
+
83
+ DO $$
84
+ BEGIN
85
+ IF to_regclass('public.webhooks') IS NULL THEN
86
+ RAISE EXCEPTION 'pre-flight: public.webhooks does not exist';
87
+ END IF;
88
+ IF to_regclass('public.agent_loops') IS NULL THEN
89
+ RAISE EXCEPTION 'pre-flight: public.agent_loops does not exist';
90
+ END IF;
91
+ IF to_regclass('public.agent_loop_versions') IS NULL THEN
92
+ RAISE EXCEPTION 'pre-flight: public.agent_loop_versions does not exist';
93
+ END IF;
94
+ IF to_regclass('public.inbox_items') IS NULL THEN
95
+ RAISE EXCEPTION 'pre-flight: public.inbox_items does not exist';
96
+ END IF;
97
+ IF NOT EXISTS (
98
+ SELECT 1 FROM information_schema.columns
99
+ WHERE table_schema = 'public' AND table_name = 'webhooks'
100
+ AND column_name = 'agent_loop_id'
101
+ ) THEN
102
+ RAISE EXCEPTION 'pre-flight: public.webhooks.agent_loop_id missing (apply 0210 first)';
103
+ END IF;
104
+ IF NOT EXISTS (
105
+ SELECT 1 FROM information_schema.columns
106
+ WHERE table_schema = 'public' AND table_name = 'agent_loop_versions'
107
+ AND column_name = 'target_spec'
108
+ ) THEN
109
+ RAISE EXCEPTION 'pre-flight: public.agent_loop_versions.target_spec missing (apply 0210 first)';
110
+ END IF;
111
+ END $$;
112
+
113
+ DO $$
114
+ DECLARE
115
+ w RECORD;
116
+ v_loop_id uuid;
117
+ v_version_id uuid;
118
+ v_slug text;
119
+ v_target_kind text;
120
+ v_instructions text;
121
+ v_has_space boolean;
122
+ v_loop_enabled boolean;
123
+ v_worker_id uuid;
124
+ v_target_spec jsonb;
125
+ v_goal_spec jsonb;
126
+ v_worker_spec jsonb;
127
+ BEGIN
128
+ FOR w IN
129
+ SELECT *
130
+ FROM public.webhooks
131
+ WHERE target_type IN ('agent', 'routine')
132
+ AND connect_provider_id IS NULL
133
+ AND agent_loop_id IS NULL
134
+ AND target_type <> 'automation'
135
+ ORDER BY created_at
136
+ LOOP
137
+ v_target_kind := CASE WHEN w.target_type = 'routine' THEN 'routine' ELSE 'agent_thread' END;
138
+ v_has_space := w.space_id IS NOT NULL;
139
+
140
+ -- agent_thread automations need a Space (R4). No Space ⇒ create disabled +
141
+ -- file an operator inbox item. Routine targets are headless; always enabled
142
+ -- to the webhook's own enabled flag.
143
+ IF v_target_kind = 'agent_thread' AND NOT v_has_space THEN
144
+ v_loop_enabled := false;
145
+ ELSE
146
+ v_loop_enabled := w.enabled;
147
+ END IF;
148
+
149
+ -- Unique slug: slugify(name) + short webhook-id suffix so the
150
+ -- (tenant_id, slug) unique index never collides across converted rows.
151
+ v_slug := regexp_replace(lower(coalesce(w.name, 'webhook')), '[^a-z0-9]+', '-', 'g');
152
+ v_slug := regexp_replace(v_slug, '(^-+|-+$)', '', 'g');
153
+ v_slug := left(nullif(v_slug, ''), 60);
154
+ v_slug := coalesce(v_slug, 'webhook') || '-' || left(replace(w.id::text, '-', ''), 8);
155
+
156
+ v_instructions := coalesce(
157
+ nullif(btrim(w.prompt), ''),
158
+ 'Handle the incoming webhook payload and complete the requested work.'
159
+ );
160
+
161
+ IF v_target_kind = 'agent_thread' THEN
162
+ v_worker_id := w.agent_id;
163
+ v_worker_spec := jsonb_build_object(
164
+ 'type', 'agent',
165
+ 'id', w.agent_id,
166
+ 'toolHints', '[]'::jsonb,
167
+ 'config', '{}'::jsonb
168
+ );
169
+ v_goal_spec := jsonb_build_object(
170
+ 'objective', v_instructions,
171
+ 'completionCriteria', '[]'::jsonb
172
+ );
173
+ v_target_spec := jsonb_build_object(
174
+ 'kind', 'agent_thread',
175
+ 'agentThread', jsonb_build_object(
176
+ 'instructions', v_instructions,
177
+ 'workerId', w.agent_id,
178
+ 'workerType', 'agent',
179
+ 'threadMode', 'new_per_run'
180
+ )
181
+ );
182
+ ELSE
183
+ -- routine target — worker_spec is unread for routine dispatch but the
184
+ -- column is NOT NULL; point it at a real platform-default agent when one
185
+ -- exists so a later re-save re-normalizes cleanly.
186
+ v_worker_id := (
187
+ SELECT a.id FROM public.agents a
188
+ WHERE a.tenant_id = w.tenant_id AND a.type = 'agent'
189
+ ORDER BY (a.is_platform_default) DESC NULLS LAST, a.created_at
190
+ LIMIT 1
191
+ );
192
+ v_worker_spec := jsonb_build_object(
193
+ 'type', 'agent',
194
+ 'id', coalesce(v_worker_id, '00000000-0000-0000-0000-000000000000'::uuid),
195
+ 'toolHints', '[]'::jsonb,
196
+ 'config', '{}'::jsonb
197
+ );
198
+ v_goal_spec := jsonb_build_object(
199
+ 'objective', coalesce(nullif(btrim(w.name), ''), 'Webhook routine'),
200
+ 'completionCriteria', '[]'::jsonb
201
+ );
202
+ v_target_spec := jsonb_build_object(
203
+ 'kind', 'routine',
204
+ 'routine', jsonb_build_object('routineId', w.routine_id)
205
+ );
206
+ END IF;
207
+
208
+ INSERT INTO public.agent_loops (
209
+ tenant_id, name, slug, description, lifecycle_status, enabled,
210
+ space_id, run_as_user_id, primary_trigger_family
211
+ )
212
+ VALUES (
213
+ w.tenant_id,
214
+ coalesce(nullif(btrim(w.name), ''), 'Webhook automation'),
215
+ v_slug,
216
+ w.description,
217
+ 'active',
218
+ v_loop_enabled,
219
+ w.space_id,
220
+ NULL, -- run_as_user_id: system identity (see header)
221
+ 'webhook'
222
+ )
223
+ RETURNING id INTO v_loop_id;
224
+
225
+ INSERT INTO public.agent_loop_versions (
226
+ tenant_id, agent_loop_id, version_number, version_status,
227
+ trigger_spec, goal_spec, worker_spec, judge_spec, loop_policy,
228
+ target_spec, created_by_actor_type, published_at
229
+ )
230
+ VALUES (
231
+ w.tenant_id,
232
+ v_loop_id,
233
+ 1,
234
+ 'active',
235
+ jsonb_build_object('family', 'webhook', 'enabled', true, 'config', '{}'::jsonb),
236
+ v_goal_spec,
237
+ v_worker_spec,
238
+ jsonb_build_object('mode', 'self_check', 'criteria', '[]'::jsonb, 'config', '{}'::jsonb),
239
+ jsonb_build_object('maxIterations', 1, 'failBehavior', 'return_blocker', 'escalateOnFailure', false),
240
+ v_target_spec,
241
+ 'system',
242
+ now()
243
+ )
244
+ RETURNING id INTO v_version_id;
245
+
246
+ UPDATE public.agent_loops
247
+ SET current_version_id = v_version_id,
248
+ current_version_number = 1,
249
+ updated_at = now()
250
+ WHERE id = v_loop_id;
251
+
252
+ -- Re-point the webhook at the new Automation. Token PRESERVED; enabled
253
+ -- mirrors the loop (disabled for the NULL-space agent case).
254
+ UPDATE public.webhooks
255
+ SET target_type = 'automation',
256
+ agent_loop_id = v_loop_id,
257
+ enabled = v_loop_enabled,
258
+ updated_at = now()
259
+ WHERE id = w.id;
260
+
261
+ -- NULL-space agent row: file a deduplicated operator inbox item.
262
+ IF v_target_kind = 'agent_thread' AND NOT v_has_space THEN
263
+ IF NOT EXISTS (
264
+ SELECT 1 FROM public.inbox_items i
265
+ WHERE i.tenant_id = w.tenant_id
266
+ AND i.type = 'automation_needs_space'
267
+ AND i.entity_type = 'agent_loop'
268
+ AND i.entity_id = v_loop_id
269
+ ) THEN
270
+ INSERT INTO public.inbox_items (
271
+ tenant_id, requester_type, type, status, title, description,
272
+ entity_type, entity_id, config
273
+ )
274
+ VALUES (
275
+ w.tenant_id,
276
+ 'system',
277
+ 'automation_needs_space',
278
+ 'pending',
279
+ 'Automation needs a Space',
280
+ format(
281
+ 'The webhook "%s" became an Automation but has no Space, so it is disabled. Assign a Space and re-enable it to accept inbound deliveries.',
282
+ coalesce(nullif(btrim(w.name), ''), 'webhook')
283
+ ),
284
+ 'agent_loop',
285
+ v_loop_id,
286
+ jsonb_build_object('webhookId', w.id, 'source', 'think137_u8_fold')
287
+ );
288
+ END IF;
289
+ END IF;
290
+ END LOOP;
291
+ END $$;
292
+
293
+ -- Drift marker + summary. Presence proves the fold transaction committed.
294
+ CREATE OR REPLACE VIEW public.view_webhooks_folded_into_automations AS
295
+ SELECT
296
+ COUNT(*) FILTER (
297
+ WHERE target_type = 'automation' AND agent_loop_id IS NOT NULL
298
+ )::int AS automation_webhooks,
299
+ COUNT(*) FILTER (
300
+ WHERE target_type IN ('agent', 'routine')
301
+ AND connect_provider_id IS NULL
302
+ AND agent_loop_id IS NULL
303
+ )::int AS unconverted_convertible_webhooks,
304
+ COUNT(*) FILTER (WHERE connect_provider_id IS NOT NULL)::int AS connector_webhooks,
305
+ COUNT(*) FILTER (WHERE target_type = 'task')::int AS task_webhooks,
306
+ now() AS checked_at
307
+ FROM public.webhooks;
308
+
309
+ COMMENT ON VIEW public.view_webhooks_folded_into_automations IS
310
+ 'Drift marker and summary for 0212_fold_webhooks_into_automations.sql (THINK-137 U8).';
311
+
312
+ COMMIT;
@@ -0,0 +1,63 @@
1
+ -- Purpose: drop the NOT NULL constraint on
2
+ -- agent_loop_versions.judge_spec and agent_loop_versions.evidence_policy
3
+ -- (THINK-137 U10, PR A — code removal + nullability).
4
+ --
5
+ -- The judge / evidence / ROI feature was removed in this PR: finalize no longer
6
+ -- records judgments/evidence, saveAgentLoop no longer writes judge_spec /
7
+ -- evidence_policy, and the GraphQL + web readers are gone. Both columns were
8
+ -- NOT NULL, so saveAgentLoop can only legally stop writing them once they are
9
+ -- nullable. This migration makes that write-stop legal. The COLUMNS themselves
10
+ -- (and the agent_loop_judgments / agent_loop_evidence tables and the ROI
11
+ -- counters) are DROPPED in the follow-up PR B, after this deploys and
12
+ -- saveAgentLoop is observed creating Automations live.
13
+ --
14
+ -- Schema-only migration on an existing column. Following the 0202/0211
15
+ -- precedent for hand-rolled DDL that Drizzle's generator does not track, it
16
+ -- publishes a drift-marker VIEW whose columns reflect the resulting nullability
17
+ -- so the drift reporter (scripts/db-migrate-manual.sh) can prove the ALTER
18
+ -- committed; the `-- creates:` marker below is what the reporter probes.
19
+ --
20
+ -- Idempotent: ALTER COLUMN ... DROP NOT NULL is a no-op when the constraint is
21
+ -- already gone; the marker view is CREATE OR REPLACE. Safe to re-run.
22
+ --
23
+ -- Apply manually (verification pass before merge):
24
+ -- psql "$DATABASE_URL" -v ON_ERROR_STOP=1 --single-transaction \
25
+ -- -f packages/database-pg/drizzle/0213_agent_loop_versions_judge_evidence_nullable.sql
26
+ --
27
+ -- creates: public.view_agent_loop_versions_judge_evidence_nullable
28
+
29
+ \set ON_ERROR_STOP on
30
+
31
+ BEGIN;
32
+
33
+ SELECT pg_advisory_xact_lock(hashtext('agent_loop_versions_judge_evidence_nullable_0213'));
34
+
35
+ SET LOCAL lock_timeout = '5s';
36
+ SET LOCAL statement_timeout = '60s';
37
+
38
+ DO $$
39
+ BEGIN
40
+ IF to_regclass('public.agent_loop_versions') IS NULL THEN
41
+ RAISE EXCEPTION 'pre-flight: public.agent_loop_versions does not exist';
42
+ END IF;
43
+ END $$;
44
+
45
+ ALTER TABLE public.agent_loop_versions ALTER COLUMN judge_spec DROP NOT NULL;
46
+ ALTER TABLE public.agent_loop_versions ALTER COLUMN evidence_policy DROP NOT NULL;
47
+
48
+ -- Drift marker. Its boolean columns reflect the resulting nullability so the
49
+ -- reporter can prove both constraints were dropped.
50
+ CREATE OR REPLACE VIEW public.view_agent_loop_versions_judge_evidence_nullable AS
51
+ SELECT
52
+ bool_or(column_name = 'judge_spec' AND is_nullable = 'YES') AS judge_spec_nullable,
53
+ bool_or(column_name = 'evidence_policy' AND is_nullable = 'YES') AS evidence_policy_nullable,
54
+ now() AS checked_at
55
+ FROM information_schema.columns
56
+ WHERE table_schema = 'public'
57
+ AND table_name = 'agent_loop_versions'
58
+ AND column_name IN ('judge_spec', 'evidence_policy');
59
+
60
+ COMMENT ON VIEW public.view_agent_loop_versions_judge_evidence_nullable IS
61
+ 'Drift marker for 0213_agent_loop_versions_judge_evidence_nullable.sql.';
62
+
63
+ COMMIT;
@@ -0,0 +1,99 @@
1
+ -- Purpose: DROP the judge / evidence / ROI schema for Automations
2
+ -- (THINK-137 U10, PR B — destructive schema removal).
3
+ --
4
+ -- Migration ordering: this is the SECOND and final U10 migration. PR A (0213)
5
+ -- removed every reader/writer of the judge / evidence / ROI feature from code
6
+ -- and made agent_loop_versions.judge_spec + evidence_policy nullable so
7
+ -- saveAgentLoop could legally stop writing them. PR A has deployed and
8
+ -- saveAgentLoop was observed creating Automations live WITHOUT these columns.
9
+ -- Only now — after the code-removal PR is live — is it safe to DROP the
10
+ -- columns and tables nothing reads or writes anymore. Running this before PR A
11
+ -- deployed would break live writers, hence the strict A→B ordering.
12
+ --
13
+ -- Drops (in FK-safe order):
14
+ -- 1. public.agent_loop_evidence (has FK agent_loop_judgment_id ->
15
+ -- agent_loop_judgments.id, so it goes
16
+ -- FIRST)
17
+ -- 2. public.agent_loop_judgments
18
+ -- 3. public.agent_loops.{accepted_run_count, rejected_run_count,
19
+ -- escalated_run_count, total_cost_usd_cents,
20
+ -- cost_per_accepted_run_usd_cents} (ROI counters)
21
+ -- 4. public.agent_loop_versions.{judge_spec, evidence_policy}
22
+ -- (already nullable from 0213)
23
+ --
24
+ -- Explicitly NOT touched: agent_loops' goal_spec/worker_spec/loop_policy/
25
+ -- target_spec/policy_snapshot are deferred to U11; agent_loop_runs and
26
+ -- agent_loop_iterations keep their own total_cost_usd_cents (U4 cost-cap gate);
27
+ -- workflow_runs.total_cost_usd_cents is unrelated.
28
+ --
29
+ -- This is a hand-rolled DDL migration that Drizzle's generator does not track.
30
+ -- Following the 0210-0213 precedent it uses drop markers (`-- drops:` /
31
+ -- `-- drops-column:`) so the drift reporter (scripts/db-migrate-manual.sh) can
32
+ -- prove each object is ABSENT (DROPPED) in the target DB after apply. It also
33
+ -- drops the now-meaningless 0213 drift-marker view, whose only reason to exist
34
+ -- was to prove the nullability step that this migration supersedes.
35
+ --
36
+ -- Idempotent: every statement uses IF EXISTS, so re-running is a no-op.
37
+ --
38
+ -- Apply manually (verification pass before merge):
39
+ -- psql "$DATABASE_URL" -v ON_ERROR_STOP=1 --single-transaction \
40
+ -- -f packages/database-pg/drizzle/0214_think137_drop_judge_evidence_roi.sql
41
+ --
42
+ -- drops: public.agent_loop_evidence
43
+ -- drops: public.agent_loop_judgments
44
+ -- drops-column: public.agent_loops.accepted_run_count
45
+ -- drops-column: public.agent_loops.rejected_run_count
46
+ -- drops-column: public.agent_loops.escalated_run_count
47
+ -- drops-column: public.agent_loops.total_cost_usd_cents
48
+ -- drops-column: public.agent_loops.cost_per_accepted_run_usd_cents
49
+ -- drops-column: public.agent_loop_versions.judge_spec
50
+ -- drops-column: public.agent_loop_versions.evidence_policy
51
+
52
+ \set ON_ERROR_STOP on
53
+
54
+ BEGIN;
55
+
56
+ SELECT pg_advisory_xact_lock(hashtext('think137_drop_judge_evidence_roi_0214'));
57
+
58
+ SET LOCAL lock_timeout = '5s';
59
+ SET LOCAL statement_timeout = '60s';
60
+
61
+ -- Pre-flight: the parent tables whose columns we alter must exist. (The
62
+ -- judge/evidence tables themselves are dropped with IF EXISTS, so their
63
+ -- absence is fine.)
64
+ DO $$
65
+ BEGIN
66
+ IF to_regclass('public.agent_loops') IS NULL THEN
67
+ RAISE EXCEPTION 'pre-flight: public.agent_loops does not exist';
68
+ END IF;
69
+ IF to_regclass('public.agent_loop_versions') IS NULL THEN
70
+ RAISE EXCEPTION 'pre-flight: public.agent_loop_versions does not exist';
71
+ END IF;
72
+ END $$;
73
+
74
+ -- Drop the now-obsolete 0213 drift-marker view. It probed judge_spec /
75
+ -- evidence_policy nullability via information_schema (a name reference, not a
76
+ -- real column dependency), so it does not block the column drops — but it is
77
+ -- meaningless once the columns are gone.
78
+ DROP VIEW IF EXISTS public.view_agent_loop_versions_judge_evidence_nullable;
79
+
80
+ -- 1. Evidence first (FK agent_loop_judgment_id -> agent_loop_judgments.id).
81
+ DROP TABLE IF EXISTS public.agent_loop_evidence;
82
+
83
+ -- 2. Then judgments.
84
+ DROP TABLE IF EXISTS public.agent_loop_judgments;
85
+
86
+ -- 3. ROI counters on agent_loops (NOT agent_loop_runs/iterations cost columns).
87
+ ALTER TABLE public.agent_loops
88
+ DROP COLUMN IF EXISTS accepted_run_count,
89
+ DROP COLUMN IF EXISTS rejected_run_count,
90
+ DROP COLUMN IF EXISTS escalated_run_count,
91
+ DROP COLUMN IF EXISTS total_cost_usd_cents,
92
+ DROP COLUMN IF EXISTS cost_per_accepted_run_usd_cents;
93
+
94
+ -- 4. judge_spec / evidence_policy on agent_loop_versions (nullable since 0213).
95
+ ALTER TABLE public.agent_loop_versions
96
+ DROP COLUMN IF EXISTS judge_spec,
97
+ DROP COLUMN IF EXISTS evidence_policy;
98
+
99
+ COMMIT;
@@ -0,0 +1,70 @@
1
+ -- Purpose: drop the NOT NULL constraint on
2
+ -- agent_loop_versions.goal_spec, .worker_spec, and .loop_policy
3
+ -- (THINK-159 U11, PR A — code removal + nullability).
4
+ --
5
+ -- target_spec is now the SOLE dispatch source: resolveDispatchableVersion,
6
+ -- finalize-projection, the schedule/manual/webhook dispatch SELECTs, and the
7
+ -- automations list all derive goal/worker/loop-policy from target_spec (or the
8
+ -- DEFAULT_LOOP_POLICY) and no longer read these three columns. saveAgentLoop
9
+ -- has stopped writing them, and the GraphQL + web readers are gone. All three
10
+ -- columns were NOT NULL, so the write-stop is only legal once they are
11
+ -- nullable. This migration makes that write-stop legal. The COLUMNS themselves
12
+ -- are DROPPED in the follow-up PR B, after this deploys and saveAgentLoop is
13
+ -- observed creating Automations live.
14
+ --
15
+ -- Behavior-preserving: every live version row already carries a backfilled
16
+ -- target_spec (migration 0211), and the form never set loop_policy, so every
17
+ -- stored loop_policy already equalled DEFAULT_LOOP_POLICY.
18
+ --
19
+ -- Schema-only migration on existing columns. Following the 0202/0211/0213
20
+ -- precedent for hand-rolled DDL that Drizzle's generator does not track, it
21
+ -- publishes a drift-marker VIEW whose columns reflect the resulting nullability
22
+ -- so the drift reporter (scripts/db-migrate-manual.sh) can prove the ALTERs
23
+ -- committed; the `-- creates:` marker below is what the reporter probes.
24
+ --
25
+ -- Idempotent: ALTER COLUMN ... DROP NOT NULL is a no-op when the constraint is
26
+ -- already gone; the marker view is CREATE OR REPLACE. Safe to re-run.
27
+ --
28
+ -- Apply manually (verification pass before merge):
29
+ -- psql "$DATABASE_URL" -v ON_ERROR_STOP=1 --single-transaction \
30
+ -- -f packages/database-pg/drizzle/0215_agent_loop_versions_goal_worker_policy_nullable.sql
31
+ --
32
+ -- creates: public.view_agent_loop_versions_goal_worker_policy_nullable
33
+
34
+ \set ON_ERROR_STOP on
35
+
36
+ BEGIN;
37
+
38
+ SELECT pg_advisory_xact_lock(hashtext('agent_loop_versions_goal_worker_policy_nullable_0215'));
39
+
40
+ SET LOCAL lock_timeout = '5s';
41
+ SET LOCAL statement_timeout = '60s';
42
+
43
+ DO $$
44
+ BEGIN
45
+ IF to_regclass('public.agent_loop_versions') IS NULL THEN
46
+ RAISE EXCEPTION 'pre-flight: public.agent_loop_versions does not exist';
47
+ END IF;
48
+ END $$;
49
+
50
+ ALTER TABLE public.agent_loop_versions ALTER COLUMN goal_spec DROP NOT NULL;
51
+ ALTER TABLE public.agent_loop_versions ALTER COLUMN worker_spec DROP NOT NULL;
52
+ ALTER TABLE public.agent_loop_versions ALTER COLUMN loop_policy DROP NOT NULL;
53
+
54
+ -- Drift marker. Its boolean columns reflect the resulting nullability so the
55
+ -- reporter can prove all three constraints were dropped.
56
+ CREATE OR REPLACE VIEW public.view_agent_loop_versions_goal_worker_policy_nullable AS
57
+ SELECT
58
+ bool_or(column_name = 'goal_spec' AND is_nullable = 'YES') AS goal_spec_nullable,
59
+ bool_or(column_name = 'worker_spec' AND is_nullable = 'YES') AS worker_spec_nullable,
60
+ bool_or(column_name = 'loop_policy' AND is_nullable = 'YES') AS loop_policy_nullable,
61
+ now() AS checked_at
62
+ FROM information_schema.columns
63
+ WHERE table_schema = 'public'
64
+ AND table_name = 'agent_loop_versions'
65
+ AND column_name IN ('goal_spec', 'worker_spec', 'loop_policy');
66
+
67
+ COMMENT ON VIEW public.view_agent_loop_versions_goal_worker_policy_nullable IS
68
+ 'Drift marker for 0215_agent_loop_versions_goal_worker_policy_nullable.sql.';
69
+
70
+ COMMIT;
@@ -0,0 +1,81 @@
1
+ -- Purpose: DROP the now-inert agent_loop_versions.goal_spec, .worker_spec,
2
+ -- .loop_policy columns and promote target_spec to NOT NULL (THINK-159 U11,
3
+ -- PR B — the destructive follow-up to PR A #3351).
4
+ --
5
+ -- PR A (deployed) made target_spec the sole dispatch source: resolveDispatchableVersion
6
+ -- derives goalSpec/workerSpec from target_spec.agentThread and synthesizes
7
+ -- DEFAULT_LOOP_POLICY; saveAgentLoop stopped writing the three columns;
8
+ -- migration 0215 made them nullable. Every version row has target_spec
9
+ -- (backfilled by 0211), so the columns are pure dead weight.
10
+ --
11
+ -- Ordering (expand/contract, per docs/solutions/workflow-issues/
12
+ -- dropping-orm-declared-columns-needs-def-removal-deploy-first.md): this file's
13
+ -- companion PR removes the Drizzle column DEFINITIONS from
14
+ -- src/schema/agent-loops.ts, so the deployed ORM stops emitting them in
15
+ -- `.select()` star reads. This DROP is applied to a stage ONLY AFTER that
16
+ -- def-removal code is live there — never pre-applied while a prior deploy's
17
+ -- ORM still declares the columns.
18
+ --
19
+ -- Pre-flight: aborts if any version row has NULL target_spec (would become
20
+ -- undispatchable once the legacy fallback is gone). Idempotent (IF EXISTS).
21
+ --
22
+ -- Apply manually (verification pass before merge, AFTER the def-removal deploy):
23
+ -- psql "$DATABASE_URL" -v ON_ERROR_STOP=1 --single-transaction \
24
+ -- -f packages/database-pg/drizzle/0216_drop_agent_loop_versions_goal_worker_policy.sql
25
+ --
26
+ -- drops-column: public.agent_loop_versions.goal_spec
27
+ -- drops-column: public.agent_loop_versions.worker_spec
28
+ -- drops-column: public.agent_loop_versions.loop_policy
29
+ -- creates: public.view_agent_loop_versions_target_spec_required
30
+
31
+ \set ON_ERROR_STOP on
32
+
33
+ BEGIN;
34
+
35
+ SELECT pg_advisory_xact_lock(hashtext('think159_drop_goal_worker_policy_0216'));
36
+
37
+ SET LOCAL lock_timeout = '5s';
38
+ SET LOCAL statement_timeout = '60s';
39
+
40
+ DO $$
41
+ DECLARE
42
+ null_target_spec_count bigint;
43
+ BEGIN
44
+ IF to_regclass('public.agent_loop_versions') IS NULL THEN
45
+ RAISE EXCEPTION 'pre-flight: public.agent_loop_versions does not exist';
46
+ END IF;
47
+ SELECT count(*) INTO null_target_spec_count
48
+ FROM public.agent_loop_versions
49
+ WHERE target_spec IS NULL;
50
+ IF null_target_spec_count > 0 THEN
51
+ RAISE EXCEPTION
52
+ 'pre-flight: % agent_loop_versions rows have NULL target_spec; re-run the 0211 backfill before dropping the legacy columns',
53
+ null_target_spec_count;
54
+ END IF;
55
+ END $$;
56
+
57
+ -- Obsolete nullability drift-marker view from 0215 (its columns are dropped
58
+ -- below); mirrors 0214 dropping the 0213 marker view.
59
+ DROP VIEW IF EXISTS public.view_agent_loop_versions_goal_worker_policy_nullable;
60
+
61
+ ALTER TABLE public.agent_loop_versions ALTER COLUMN target_spec SET NOT NULL;
62
+
63
+ ALTER TABLE public.agent_loop_versions
64
+ DROP COLUMN IF EXISTS goal_spec,
65
+ DROP COLUMN IF EXISTS worker_spec,
66
+ DROP COLUMN IF EXISTS loop_policy;
67
+
68
+ -- Drift marker: presence proves the drop transaction committed and target_spec
69
+ -- is now required.
70
+ CREATE OR REPLACE VIEW public.view_agent_loop_versions_target_spec_required AS
71
+ SELECT
72
+ bool_or(column_name = 'target_spec' AND is_nullable = 'NO') AS target_spec_not_null,
73
+ bool_and(column_name NOT IN ('goal_spec', 'worker_spec', 'loop_policy')) AS legacy_columns_dropped,
74
+ now() AS checked_at
75
+ FROM information_schema.columns
76
+ WHERE table_schema = 'public' AND table_name = 'agent_loop_versions';
77
+
78
+ COMMENT ON VIEW public.view_agent_loop_versions_target_spec_required IS
79
+ 'Drift marker for 0216_drop_agent_loop_versions_goal_worker_policy.sql (THINK-159 U11).';
80
+
81
+ COMMIT;
@@ -0,0 +1,12 @@
1
+ -- THINK-173 U5 (R20): per-agent capability-folder migration flag.
2
+ -- When true, dispatch reads the agent's capability surface from the
3
+ -- rendered folder manifest instead of the legacy MCP tables. Flipped
4
+ -- atomically per agent by the U11 backfill after its divergence check
5
+ -- passes. Additive + default false — safe to apply ahead of code deploy.
6
+ --
7
+ -- Hand-rolled (drizzle meta journal is not in use for this change);
8
+ -- apply with: psql "$DATABASE_URL" -f drizzle/0217_agents_capability_folder_dispatch.sql
9
+ -- creates-column: public.agents.capability_folder_dispatch
10
+
11
+ ALTER TABLE public.agents
12
+ ADD COLUMN IF NOT EXISTS capability_folder_dispatch boolean NOT NULL DEFAULT false;
@@ -0,0 +1,30 @@
1
+ -- THINK-153 U1: tenant-scoped document plate registry.
2
+ -- Platform plates are code-defined (packages/api plate-definitions.ts);
3
+ -- this table stores only tenant deltas (origin = 'platform_override') and
4
+ -- tenant-created plates (origin = 'tenant'). Additive — safe to apply ahead
5
+ -- of code deploy.
6
+ --
7
+ -- Hand-rolled (drizzle meta journal is not in use for this change);
8
+ -- apply with: psql "$DATABASE_URL" -f drizzle/0218_document_plates.sql
9
+ -- creates: public.document_plates
10
+
11
+ CREATE TABLE IF NOT EXISTS public.document_plates (
12
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
13
+ tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
14
+ slug text NOT NULL,
15
+ origin text NOT NULL,
16
+ config jsonb NOT NULL DEFAULT '{}'::jsonb,
17
+ hidden boolean NOT NULL DEFAULT false,
18
+ created_at timestamptz NOT NULL DEFAULT now(),
19
+ updated_at timestamptz NOT NULL DEFAULT now(),
20
+ CONSTRAINT document_plates_origin_check
21
+ CHECK (origin IN ('platform_override', 'tenant')),
22
+ CONSTRAINT document_plates_slug_check
23
+ CHECK (slug ~ '^[a-z0-9][a-z0-9-]{0,63}$')
24
+ );
25
+
26
+ CREATE UNIQUE INDEX IF NOT EXISTS document_plates_tenant_slug_uidx
27
+ ON public.document_plates (tenant_id, slug);
28
+
29
+ CREATE INDEX IF NOT EXISTS document_plates_tenant_idx
30
+ ON public.document_plates (tenant_id);