create-safest-tools 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +71 -0
  3. package/bin/create-safest-tools.mjs +12 -0
  4. package/package.json +34 -0
  5. package/src/cli.mjs +128 -0
  6. package/src/config.mjs +151 -0
  7. package/src/scaffold.mjs +30 -0
  8. package/template/LICENSE +201 -0
  9. package/template/README.md +35 -0
  10. package/template/console/README.md +28 -0
  11. package/template/console/analytics/AnalyticsWorkspace.tsx +55 -0
  12. package/template/console/analytics/controller.ts +133 -0
  13. package/template/console/analytics/events.ts +33 -0
  14. package/template/console/analytics/store.ts +33 -0
  15. package/template/console/analytics/types.ts +38 -0
  16. package/template/console/appeals/AppealWorkspace.tsx +73 -0
  17. package/template/console/appeals/controller.ts +125 -0
  18. package/template/console/appeals/events.ts +33 -0
  19. package/template/console/appeals/store.ts +35 -0
  20. package/template/console/appeals/types.ts +37 -0
  21. package/template/console/assistant/AssistantWorkspace.tsx +133 -0
  22. package/template/console/assistant/types.ts +34 -0
  23. package/template/console/auth/AccountJourney.tsx +69 -0
  24. package/template/console/auth/InvitationAcceptance.tsx +145 -0
  25. package/template/console/auth/OAuthButtons.tsx +78 -0
  26. package/template/console/auth/OperatorLogin.tsx +110 -0
  27. package/template/console/auth/PasswordRecovery.tsx +132 -0
  28. package/template/console/auth/account-route.ts +15 -0
  29. package/template/console/auth/browser-navigation.ts +7 -0
  30. package/template/console/auth/events.ts +15 -0
  31. package/template/console/command/CommandCentre.tsx +119 -0
  32. package/template/console/command/controller.ts +267 -0
  33. package/template/console/command/events.ts +19 -0
  34. package/template/console/command/store.ts +36 -0
  35. package/template/console/command/types.ts +61 -0
  36. package/template/console/components/AnalystIdentity.tsx +25 -0
  37. package/template/console/components/PageHeader.tsx +27 -0
  38. package/template/console/configuration/ConfigurationDialog.tsx +200 -0
  39. package/template/console/configuration/ConfigurationWorkspace.tsx +66 -0
  40. package/template/console/configuration/FormBuilder.tsx +143 -0
  41. package/template/console/configuration/api.ts +40 -0
  42. package/template/console/configuration/events.ts +14 -0
  43. package/template/console/configuration/types.ts +79 -0
  44. package/template/console/lib/format.ts +49 -0
  45. package/template/console/lib/http.ts +96 -0
  46. package/template/console/main.tsx +225 -0
  47. package/template/console/operations/OperationsWorkspace.tsx +126 -0
  48. package/template/console/operations/controller.ts +237 -0
  49. package/template/console/operations/events.ts +37 -0
  50. package/template/console/operations/store.ts +32 -0
  51. package/template/console/operations/types.ts +107 -0
  52. package/template/console/people/PeopleWorkspace.tsx +81 -0
  53. package/template/console/people/types.ts +25 -0
  54. package/template/console/profile/ProfileWorkspace.tsx +136 -0
  55. package/template/console/profile/events.ts +9 -0
  56. package/template/console/profile/types.ts +6 -0
  57. package/template/console/quality/QualityWorkspace.tsx +87 -0
  58. package/template/console/quality/controller.ts +154 -0
  59. package/template/console/quality/events.ts +34 -0
  60. package/template/console/quality/store.ts +36 -0
  61. package/template/console/quality/types.ts +70 -0
  62. package/template/console/queues/QueueEditor.tsx +173 -0
  63. package/template/console/queues/QueueWorkspace.tsx +109 -0
  64. package/template/console/queues/controller.ts +194 -0
  65. package/template/console/queues/events.ts +34 -0
  66. package/template/console/queues/store.ts +37 -0
  67. package/template/console/queues/types.ts +102 -0
  68. package/template/console/registry/RegistryDialog.tsx +198 -0
  69. package/template/console/registry/RegistryWorkspace.tsx +108 -0
  70. package/template/console/registry/events.ts +14 -0
  71. package/template/console/registry/types.ts +52 -0
  72. package/template/console/reports/ReportDrawer.tsx +159 -0
  73. package/template/console/reports/ReportWorkspace.tsx +90 -0
  74. package/template/console/reports/controller.ts +517 -0
  75. package/template/console/reports/events.ts +42 -0
  76. package/template/console/reports/store.ts +39 -0
  77. package/template/console/reports/types.ts +230 -0
  78. package/template/console/settings/BrandEditor.tsx +126 -0
  79. package/template/console/settings/ChannelDialog.tsx +241 -0
  80. package/template/console/settings/SettingsWorkspace.tsx +155 -0
  81. package/template/console/settings/events.ts +19 -0
  82. package/template/console/settings/handoff.ts +22 -0
  83. package/template/console/settings/types.ts +93 -0
  84. package/template/console/shell/WorkspaceShell.tsx +160 -0
  85. package/template/console/shell/controller.ts +182 -0
  86. package/template/console/shell/events.ts +25 -0
  87. package/template/console/shell/navigation.ts +61 -0
  88. package/template/console/shell/store.ts +56 -0
  89. package/template/console/shell/types.ts +86 -0
  90. package/template/console/workflows/CreateWorkflowDialog.tsx +87 -0
  91. package/template/console/workflows/WorkflowCanvas.tsx +42 -0
  92. package/template/console/workflows/WorkflowDialogs.tsx +6 -0
  93. package/template/console/workflows/WorkflowStudio.tsx +331 -0
  94. package/template/console/workflows/WorkflowWorkspace.tsx +82 -0
  95. package/template/console/workflows/events.ts +17 -0
  96. package/template/console/workflows/graph.ts +156 -0
  97. package/template/console/workflows/templates.ts +70 -0
  98. package/template/console/workflows/types.ts +201 -0
  99. package/template/gitignore.template +18 -0
  100. package/template/migrations/0001_reports_foundation.sql +444 -0
  101. package/template/migrations/0002_human_report_loop.sql +65 -0
  102. package/template/migrations/0003_delivery_reliability.sql +18 -0
  103. package/template/migrations/0004_public_intake.sql +14 -0
  104. package/template/migrations/0005_operations_visibility.sql +24 -0
  105. package/template/migrations/0006_ai_governance.sql +198 -0
  106. package/template/migrations/0007_ai_release_gates.sql +6 -0
  107. package/template/migrations/0008_retention_analytics_exports.sql +52 -0
  108. package/template/migrations/0009_retention_derived_copies.sql +14 -0
  109. package/template/migrations/0010_ai_quality_controls.sql +26 -0
  110. package/template/migrations/0011_analyst_presence.sql +28 -0
  111. package/template/migrations/0012_queue_policies.sql +87 -0
  112. package/template/migrations/0013_routing_agents.sql +63 -0
  113. package/template/migrations/0014_webhook_enrichments.sql +122 -0
  114. package/template/migrations/0015_queue_owned_ai.sql +55 -0
  115. package/template/migrations/0016_operator_accounts.sql +67 -0
  116. package/template/migrations/0017_operator_profiles_and_recovery.sql +33 -0
  117. package/template/migrations/0018_platform_configuration.sql +382 -0
  118. package/template/migrations/0019_workflow_authoring_runtime.sql +372 -0
  119. package/template/migrations/0020_tasks_findings_assistant_budgets.sql +427 -0
  120. package/template/migrations/0021_abuse_evidence_operations.sql +324 -0
  121. package/template/migrations/0022_workflow_dispatch_operations.sql +42 -0
  122. package/template/migrations/0023_component_connection_execution.sql +75 -0
  123. package/template/migrations/0024_access_runtime_integrity.sql +72 -0
  124. package/template/migrations/0025_installation_timezone.sql +11 -0
  125. package/template/migrations/0026_ai_and_egress_execution_controls.sql +49 -0
  126. package/template/migrations/0027_prompt_and_ai_registry.sql +37 -0
  127. package/template/migrations/0028_step_attempt_ai_provenance.sql +13 -0
  128. package/template/migrations/0029_evidence_fetch_transport.sql +5 -0
  129. package/template/migrations/0030_evidence_dlq_incidents.sql +45 -0
  130. package/template/migrations/0031_shadow_quality_integrity.sql +7 -0
  131. package/template/migrations/0032_action_delivery_outbox.sql +55 -0
  132. package/template/migrations/0033_configuration_and_assistant_drafts.sql +43 -0
  133. package/template/migrations/0034_installation_integrations.sql +31 -0
  134. package/template/migrations/0035_workspace_governance.sql +21 -0
  135. package/template/migrations/0036_published_routing_baseline.sql +15 -0
  136. package/template/migrations/0037_builtin_phishing_specialist.sql +71 -0
  137. package/template/migrations/0038_remove_deprecated_enrichment_runtime.sql +228 -0
  138. package/template/migrations/0039_secure_reporting_channels.sql +45 -0
  139. package/template/migrations/0040_notification_only_reporting.sql +24 -0
  140. package/template/migrations/0041_better_auth_credentials.sql +17 -0
  141. package/template/package.json +47 -0
  142. package/template/public/_headers +27 -0
  143. package/template/public/app-icon-192.png +0 -0
  144. package/template/public/app-icon-512.png +0 -0
  145. package/template/public/brand-icon.svg +7 -0
  146. package/template/public/brand-tokens.css +80 -0
  147. package/template/public/console/auth-shell.js +19340 -0
  148. package/template/public/customer-brand.js +58 -0
  149. package/template/public/embed/embed.css +139 -0
  150. package/template/public/embed/embed.js +408 -0
  151. package/template/public/embed/index.html +91 -0
  152. package/template/public/favicon.svg +7 -0
  153. package/template/public/fonts/Manrope-Variable.ttf +0 -0
  154. package/template/public/fonts/Newsreader-Italic-Variable.ttf +0 -0
  155. package/template/public/fonts/Newsreader-Variable.ttf +0 -0
  156. package/template/public/index.html +123 -0
  157. package/template/public/logo-primary.svg +7 -0
  158. package/template/public/logo-reversed.svg +7 -0
  159. package/template/public/manifest.webmanifest +21 -0
  160. package/template/public/public-report.js +184 -0
  161. package/template/public/report/index.html +39 -0
  162. package/template/public/report/public-report.css +20 -0
  163. package/template/public/social-card.png +0 -0
  164. package/template/public/styles.css +1521 -0
  165. package/template/public/widget.css +80 -0
  166. package/template/public/widget.js +220 -0
  167. package/template/reports.config.example.json +38 -0
  168. package/template/reports.schema.json +89 -0
  169. package/template/scripts/reports-auth-onboarding.mjs +180 -0
  170. package/template/scripts/reports-backup.mjs +276 -0
  171. package/template/scripts/reports-cloudflare-preflight.mjs +152 -0
  172. package/template/scripts/reports-deploy.mjs +173 -0
  173. package/template/scripts/reports-plan.mjs +226 -0
  174. package/template/scripts/reports-restore.mjs +180 -0
  175. package/template/scripts/reports-secrets.mjs +73 -0
  176. package/template/scripts/reports-uninstall-plan.mjs +32 -0
  177. package/template/src/agent-executor.ts +330 -0
  178. package/template/src/ai-observability.ts +163 -0
  179. package/template/src/ai-registry-validation.ts +244 -0
  180. package/template/src/ai-registry.ts +292 -0
  181. package/template/src/api-cursor.ts +84 -0
  182. package/template/src/assistant.ts +554 -0
  183. package/template/src/audit.ts +39 -0
  184. package/template/src/backup-service.ts +269 -0
  185. package/template/src/budget-control.ts +127 -0
  186. package/template/src/component-executor.ts +845 -0
  187. package/template/src/configuration-registry.ts +550 -0
  188. package/template/src/connection-egress.ts +430 -0
  189. package/template/src/connection-oauth.ts +368 -0
  190. package/template/src/evidence-service.ts +335 -0
  191. package/template/src/human-tasks.ts +316 -0
  192. package/template/src/index.ts +4114 -0
  193. package/template/src/installation-admin.ts +301 -0
  194. package/template/src/intake-abuse.ts +156 -0
  195. package/template/src/platform-registry-validation.ts +367 -0
  196. package/template/src/platform-registry.ts +753 -0
  197. package/template/src/report-admin.ts +342 -0
  198. package/template/src/report-ai-quality.ts +390 -0
  199. package/template/src/report-ai-validation.ts +80 -0
  200. package/template/src/report-ai.ts +857 -0
  201. package/template/src/report-auth.ts +292 -0
  202. package/template/src/report-better-auth.ts +583 -0
  203. package/template/src/report-context-schema.ts +132 -0
  204. package/template/src/report-context.ts +126 -0
  205. package/template/src/report-crypto.ts +142 -0
  206. package/template/src/report-delivery.ts +579 -0
  207. package/template/src/report-form-validation.ts +51 -0
  208. package/template/src/report-governance.ts +510 -0
  209. package/template/src/report-http.ts +90 -0
  210. package/template/src/report-operations.ts +685 -0
  211. package/template/src/report-operator-accounts.ts +810 -0
  212. package/template/src/report-presence.ts +398 -0
  213. package/template/src/report-queue-validation.ts +309 -0
  214. package/template/src/report-queues.ts +478 -0
  215. package/template/src/report-repository.ts +972 -0
  216. package/template/src/report-router-agent.ts +328 -0
  217. package/template/src/report-router-validation.ts +81 -0
  218. package/template/src/report-routing.ts +178 -0
  219. package/template/src/report-turnstile.ts +142 -0
  220. package/template/src/report-types.ts +224 -0
  221. package/template/src/report-validation.ts +279 -0
  222. package/template/src/report-workflow-validation.ts +119 -0
  223. package/template/src/report-workflow.ts +886 -0
  224. package/template/src/shadow-quality.ts +278 -0
  225. package/template/src/workflow-actions.ts +782 -0
  226. package/template/src/workflow-compiler.ts +230 -0
  227. package/template/src/workflow-dynamic-runtime.ts +613 -0
  228. package/template/src/workflow-effects.ts +316 -0
  229. package/template/src/workflow-expressions.ts +191 -0
  230. package/template/src/workflow-platform-types.ts +121 -0
  231. package/template/src/workflow-platform-validation.ts +540 -0
  232. package/template/src/workflow-repository.ts +916 -0
  233. package/template/src/workflow-runs.ts +686 -0
  234. package/template/src/workflow-simulator.ts +211 -0
  235. package/template/src/workspace-branding.ts +128 -0
  236. package/template/src/workspace-governance.ts +279 -0
  237. package/template/tsconfig.console.json +23 -0
  238. package/template/tsconfig.json +22 -0
  239. package/template/vite.console.config.ts +20 -0
  240. package/template/worker-configuration.d.ts +65 -0
@@ -0,0 +1,324 @@
1
+ PRAGMA foreign_keys = ON;
2
+
3
+ CREATE TABLE intake_decisions (
4
+ id TEXT PRIMARY KEY,
5
+ installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
6
+ integration_id TEXT REFERENCES application_integrations(id),
7
+ report_id TEXT REFERENCES reports(id) ON DELETE SET NULL,
8
+ request_id TEXT NOT NULL,
9
+ decision TEXT NOT NULL CHECK (decision IN ('accept', 'challenge', 'reject', 'quarantine', 'group')),
10
+ reason_codes_json TEXT NOT NULL,
11
+ source TEXT NOT NULL CHECK (source IN ('signed_widget', 'public_page', 'server_api', 'connector')),
12
+ network_key_hash TEXT,
13
+ contact_key_hash TEXT,
14
+ target_key_hash TEXT,
15
+ campaign_fingerprint TEXT,
16
+ turnstile_outcome TEXT,
17
+ rate_summary_json TEXT NOT NULL DEFAULT '{}',
18
+ created_at TEXT NOT NULL,
19
+ UNIQUE (installation_id, request_id)
20
+ );
21
+
22
+ CREATE INDEX intake_decisions_installation_idx
23
+ ON intake_decisions(installation_id, created_at DESC, id DESC);
24
+ CREATE INDEX intake_decisions_campaign_idx
25
+ ON intake_decisions(installation_id, campaign_fingerprint, created_at DESC, id);
26
+
27
+ CREATE TABLE duplicate_groups (
28
+ id TEXT PRIMARY KEY,
29
+ installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
30
+ fingerprint_version TEXT NOT NULL,
31
+ fingerprint TEXT NOT NULL,
32
+ target_type TEXT NOT NULL,
33
+ target_reference_hash TEXT NOT NULL,
34
+ reason_family TEXT,
35
+ state TEXT NOT NULL CHECK (state IN ('active', 'split', 'merged', 'closed')),
36
+ canonical_report_id TEXT REFERENCES reports(id) ON DELETE SET NULL,
37
+ member_count INTEGER NOT NULL DEFAULT 0 CHECK (member_count >= 0),
38
+ first_seen_at TEXT NOT NULL,
39
+ last_seen_at TEXT NOT NULL,
40
+ created_at TEXT NOT NULL,
41
+ updated_at TEXT NOT NULL,
42
+ UNIQUE (installation_id, fingerprint_version, fingerprint)
43
+ );
44
+
45
+ CREATE INDEX duplicate_groups_activity_idx
46
+ ON duplicate_groups(installation_id, state, last_seen_at DESC, id DESC);
47
+
48
+ CREATE TABLE duplicate_group_members (
49
+ group_id TEXT NOT NULL REFERENCES duplicate_groups(id) ON DELETE CASCADE,
50
+ report_id TEXT NOT NULL UNIQUE REFERENCES reports(id) ON DELETE CASCADE,
51
+ similarity_basis_json TEXT NOT NULL,
52
+ added_at TEXT NOT NULL,
53
+ removed_at TEXT,
54
+ PRIMARY KEY (group_id, report_id)
55
+ );
56
+
57
+ CREATE INDEX duplicate_group_members_report_idx
58
+ ON duplicate_group_members(report_id, group_id);
59
+
60
+ ALTER TABLE reports ADD COLUMN intake_decision_id TEXT REFERENCES intake_decisions(id);
61
+ ALTER TABLE reports ADD COLUMN duplicate_group_id TEXT REFERENCES duplicate_groups(id);
62
+ ALTER TABLE reports ADD COLUMN quarantine_reason_code TEXT;
63
+
64
+ CREATE INDEX reports_duplicate_group_idx
65
+ ON reports(duplicate_group_id, received_at DESC, id DESC);
66
+
67
+ CREATE TABLE abuse_surge_states (
68
+ installation_id TEXT PRIMARY KEY REFERENCES installations(id) ON DELETE CASCADE,
69
+ state TEXT NOT NULL CHECK (state IN ('normal', 'elevated', 'surge', 'recovery')),
70
+ reason_codes_json TEXT NOT NULL,
71
+ activated_at TEXT,
72
+ recover_after TEXT,
73
+ revision INTEGER NOT NULL DEFAULT 1 CHECK (revision >= 1),
74
+ updated_by TEXT NOT NULL,
75
+ updated_at TEXT NOT NULL
76
+ );
77
+
78
+ CREATE TABLE report_queue_assignments (
79
+ id TEXT PRIMARY KEY,
80
+ report_id TEXT NOT NULL REFERENCES reports(id) ON DELETE CASCADE,
81
+ queue_id TEXT NOT NULL REFERENCES queue_definitions(id),
82
+ queue_version_id TEXT REFERENCES queue_versions(id),
83
+ routing_rule_version_id TEXT REFERENCES routing_rule_versions(id),
84
+ routing_agent_version_id TEXT,
85
+ assignment_kind TEXT NOT NULL CHECK (assignment_kind IN ('initial', 'reassignment', 'handoff', 'fallback')),
86
+ reason_json TEXT NOT NULL,
87
+ assigned_by_type TEXT NOT NULL,
88
+ assigned_by_id TEXT NOT NULL,
89
+ assigned_at TEXT NOT NULL,
90
+ ended_at TEXT
91
+ );
92
+
93
+ CREATE UNIQUE INDEX report_queue_assignments_one_active_idx
94
+ ON report_queue_assignments(report_id) WHERE ended_at IS NULL;
95
+ CREATE INDEX report_queue_assignments_queue_idx
96
+ ON report_queue_assignments(queue_id, ended_at, assigned_at, report_id);
97
+
98
+ INSERT INTO report_queue_assignments (
99
+ id, report_id, queue_id, queue_version_id, routing_rule_version_id,
100
+ routing_agent_version_id, assignment_kind, reason_json,
101
+ assigned_by_type, assigned_by_id, assigned_at
102
+ )
103
+ SELECT
104
+ 'migration-assignment:' || r.id,
105
+ r.id,
106
+ r.queue_id,
107
+ r.queue_version_id,
108
+ r.routing_rule_version_id,
109
+ r.routing_agent_version_id,
110
+ 'initial',
111
+ COALESCE(r.routing_reason_json, '{"reason_code":"legacy_assignment"}'),
112
+ 'system',
113
+ 'system:migration',
114
+ r.received_at
115
+ FROM reports r
116
+ WHERE r.queue_id IS NOT NULL;
117
+
118
+ CREATE TABLE evidence_fetch_requests (
119
+ id TEXT PRIMARY KEY,
120
+ installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
121
+ report_id TEXT NOT NULL REFERENCES reports(id) ON DELETE CASCADE,
122
+ evidence_reference_id TEXT NOT NULL REFERENCES evidence_references(id) ON DELETE CASCADE,
123
+ run_id TEXT REFERENCES workflow_runs(id) ON DELETE SET NULL,
124
+ step_run_id TEXT REFERENCES workflow_step_runs(id) ON DELETE SET NULL,
125
+ connection_version_id TEXT NOT NULL REFERENCES connection_versions(id),
126
+ requested_fields_json TEXT NOT NULL,
127
+ freshness_requirement_seconds INTEGER CHECK (freshness_requirement_seconds IS NULL OR freshness_requirement_seconds >= 0),
128
+ state TEXT NOT NULL CHECK (state IN ('pending', 'fetching', 'available', 'unavailable', 'failed')),
129
+ operation_id TEXT NOT NULL UNIQUE,
130
+ next_attempt_at TEXT NOT NULL,
131
+ attempt_count INTEGER NOT NULL DEFAULT 0 CHECK (attempt_count >= 0),
132
+ last_error_code TEXT,
133
+ created_at TEXT NOT NULL,
134
+ updated_at TEXT NOT NULL
135
+ );
136
+
137
+ CREATE INDEX evidence_fetch_requests_due_idx
138
+ ON evidence_fetch_requests(state, next_attempt_at, id);
139
+ CREATE INDEX evidence_fetch_requests_report_idx
140
+ ON evidence_fetch_requests(report_id, created_at DESC, id DESC);
141
+
142
+ CREATE TABLE evidence_fetch_attempts (
143
+ id TEXT PRIMARY KEY,
144
+ request_id TEXT NOT NULL REFERENCES evidence_fetch_requests(id) ON DELETE CASCADE,
145
+ attempt_number INTEGER NOT NULL CHECK (attempt_number >= 1),
146
+ request_digest TEXT NOT NULL,
147
+ response_status INTEGER,
148
+ response_digest TEXT,
149
+ response_class TEXT NOT NULL,
150
+ error_code TEXT,
151
+ started_at TEXT NOT NULL,
152
+ finished_at TEXT NOT NULL,
153
+ UNIQUE (request_id, attempt_number)
154
+ );
155
+
156
+ CREATE TABLE evidence_snapshots (
157
+ id TEXT PRIMARY KEY,
158
+ installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
159
+ report_id TEXT NOT NULL REFERENCES reports(id) ON DELETE CASCADE,
160
+ evidence_reference_id TEXT REFERENCES evidence_references(id) ON DELETE SET NULL,
161
+ fetch_request_id TEXT REFERENCES evidence_fetch_requests(id) ON DELETE SET NULL,
162
+ r2_object_key TEXT NOT NULL UNIQUE,
163
+ content_type TEXT NOT NULL,
164
+ size_bytes INTEGER NOT NULL CHECK (size_bytes >= 0),
165
+ digest TEXT NOT NULL,
166
+ encryption_key_reference TEXT,
167
+ retention_class TEXT NOT NULL,
168
+ access_policy_json TEXT NOT NULL,
169
+ source_observed_at TEXT,
170
+ created_by_type TEXT NOT NULL,
171
+ created_by_id TEXT NOT NULL,
172
+ created_at TEXT NOT NULL,
173
+ deleted_at TEXT
174
+ );
175
+
176
+ CREATE INDEX evidence_snapshots_report_idx
177
+ ON evidence_snapshots(report_id, created_at DESC, id DESC);
178
+
179
+ CREATE TABLE evidence_access_events (
180
+ id TEXT PRIMARY KEY,
181
+ installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
182
+ report_id TEXT NOT NULL REFERENCES reports(id) ON DELETE CASCADE,
183
+ evidence_reference_id TEXT REFERENCES evidence_references(id) ON DELETE SET NULL,
184
+ snapshot_id TEXT REFERENCES evidence_snapshots(id) ON DELETE SET NULL,
185
+ actor_type TEXT NOT NULL,
186
+ actor_id TEXT NOT NULL,
187
+ purpose_code TEXT NOT NULL,
188
+ request_id TEXT NOT NULL,
189
+ accessed_at TEXT NOT NULL
190
+ );
191
+
192
+ CREATE INDEX evidence_access_events_report_idx
193
+ ON evidence_access_events(report_id, accessed_at DESC, id DESC);
194
+ CREATE INDEX evidence_access_events_actor_idx
195
+ ON evidence_access_events(actor_id, accessed_at DESC, id DESC);
196
+
197
+ CREATE TABLE operations_incidents (
198
+ id TEXT PRIMARY KEY,
199
+ installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
200
+ incident_key TEXT NOT NULL,
201
+ severity TEXT NOT NULL CHECK (severity IN ('info', 'low', 'medium', 'high', 'critical')),
202
+ category TEXT NOT NULL,
203
+ state TEXT NOT NULL CHECK (state IN ('open', 'acknowledged', 'mitigated', 'resolved')),
204
+ title TEXT NOT NULL,
205
+ summary TEXT NOT NULL,
206
+ entity_type TEXT,
207
+ entity_id TEXT,
208
+ occurrence_count INTEGER NOT NULL DEFAULT 1 CHECK (occurrence_count >= 1),
209
+ first_seen_at TEXT NOT NULL,
210
+ last_seen_at TEXT NOT NULL,
211
+ acknowledged_by TEXT,
212
+ acknowledged_at TEXT,
213
+ resolved_by TEXT,
214
+ resolved_at TEXT,
215
+ resolution_summary TEXT,
216
+ metadata_json TEXT NOT NULL DEFAULT '{}',
217
+ UNIQUE (installation_id, incident_key)
218
+ );
219
+
220
+ CREATE INDEX operations_incidents_state_idx
221
+ ON operations_incidents(installation_id, state, severity, last_seen_at DESC, id DESC);
222
+
223
+ CREATE TABLE reconciliation_runs (
224
+ id TEXT PRIMARY KEY,
225
+ installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
226
+ reconciliation_kind TEXT NOT NULL,
227
+ state TEXT NOT NULL CHECK (state IN ('running', 'succeeded', 'failed', 'partial')),
228
+ cursor_json TEXT NOT NULL DEFAULT '{}',
229
+ scanned_count INTEGER NOT NULL DEFAULT 0 CHECK (scanned_count >= 0),
230
+ repaired_count INTEGER NOT NULL DEFAULT 0 CHECK (repaired_count >= 0),
231
+ incident_count INTEGER NOT NULL DEFAULT 0 CHECK (incident_count >= 0),
232
+ error_code TEXT,
233
+ started_at TEXT NOT NULL,
234
+ finished_at TEXT
235
+ );
236
+
237
+ CREATE INDEX reconciliation_runs_kind_idx
238
+ ON reconciliation_runs(installation_id, reconciliation_kind, started_at DESC, id DESC);
239
+
240
+ CREATE TABLE repair_actions (
241
+ id TEXT PRIMARY KEY,
242
+ reconciliation_run_id TEXT REFERENCES reconciliation_runs(id) ON DELETE SET NULL,
243
+ incident_id TEXT REFERENCES operations_incidents(id) ON DELETE SET NULL,
244
+ action_type TEXT NOT NULL,
245
+ entity_type TEXT NOT NULL,
246
+ entity_id TEXT NOT NULL,
247
+ operation_key TEXT NOT NULL UNIQUE,
248
+ state TEXT NOT NULL CHECK (state IN ('planned', 'applied', 'failed', 'skipped')),
249
+ before_json TEXT,
250
+ after_json TEXT,
251
+ error_code TEXT,
252
+ planned_by TEXT NOT NULL,
253
+ planned_at TEXT NOT NULL,
254
+ applied_at TEXT
255
+ );
256
+
257
+ CREATE TABLE security_events (
258
+ id TEXT PRIMARY KEY,
259
+ installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
260
+ event_type TEXT NOT NULL,
261
+ severity TEXT NOT NULL CHECK (severity IN ('info', 'low', 'medium', 'high', 'critical')),
262
+ actor_type TEXT,
263
+ actor_id TEXT,
264
+ source_key_hash TEXT,
265
+ entity_type TEXT,
266
+ entity_id TEXT,
267
+ details_json TEXT NOT NULL,
268
+ created_at TEXT NOT NULL
269
+ );
270
+
271
+ CREATE INDEX security_events_type_idx
272
+ ON security_events(installation_id, event_type, created_at DESC, id DESC);
273
+
274
+ CREATE TABLE export_jobs (
275
+ id TEXT PRIMARY KEY,
276
+ installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
277
+ requested_by TEXT NOT NULL,
278
+ export_type TEXT NOT NULL CHECK (export_type IN ('reports_csv', 'reports_ndjson', 'audit_ndjson', 'configuration')),
279
+ filters_json TEXT NOT NULL,
280
+ redaction_policy_json TEXT NOT NULL,
281
+ state TEXT NOT NULL CHECK (state IN ('pending', 'running', 'ready', 'failed', 'expired', 'deleted')),
282
+ r2_object_key TEXT,
283
+ digest TEXT,
284
+ size_bytes INTEGER CHECK (size_bytes IS NULL OR size_bytes >= 0),
285
+ row_count INTEGER CHECK (row_count IS NULL OR row_count >= 0),
286
+ error_code TEXT,
287
+ requested_at TEXT NOT NULL,
288
+ ready_at TEXT,
289
+ expires_at TEXT,
290
+ deleted_at TEXT
291
+ );
292
+
293
+ CREATE INDEX export_jobs_state_idx
294
+ ON export_jobs(installation_id, state, requested_at DESC, id DESC);
295
+
296
+ CREATE TABLE backup_manifests (
297
+ id TEXT PRIMARY KEY,
298
+ installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
299
+ environment TEXT NOT NULL,
300
+ schema_version INTEGER NOT NULL CHECK (schema_version >= 1),
301
+ application_version TEXT NOT NULL,
302
+ d1_export_reference TEXT NOT NULL,
303
+ d1_digest TEXT NOT NULL,
304
+ r2_manifest_reference TEXT,
305
+ r2_manifest_digest TEXT,
306
+ encryption_metadata_json TEXT NOT NULL DEFAULT '{}',
307
+ state TEXT NOT NULL CHECK (state IN ('planned', 'running', 'complete', 'failed', 'verified')),
308
+ created_by TEXT NOT NULL,
309
+ created_at TEXT NOT NULL,
310
+ completed_at TEXT,
311
+ verified_at TEXT
312
+ );
313
+
314
+ CREATE INDEX backup_manifests_created_idx
315
+ ON backup_manifests(installation_id, created_at DESC, id DESC);
316
+
317
+ CREATE TABLE deletion_proofs (
318
+ id TEXT PRIMARY KEY,
319
+ deletion_request_id TEXT NOT NULL REFERENCES deletion_requests(id) ON DELETE CASCADE,
320
+ proof_json TEXT NOT NULL,
321
+ proof_digest TEXT NOT NULL,
322
+ created_at TEXT NOT NULL,
323
+ UNIQUE (deletion_request_id)
324
+ );
@@ -0,0 +1,42 @@
1
+ PRAGMA foreign_keys = ON;
2
+
3
+ ALTER TABLE dead_letter_items RENAME TO dead_letter_items_v3;
4
+
5
+ CREATE TABLE dead_letter_items (
6
+ id TEXT PRIMARY KEY,
7
+ fingerprint TEXT NOT NULL UNIQUE,
8
+ source_queue TEXT NOT NULL,
9
+ source_message_id TEXT NOT NULL,
10
+ job_type TEXT NOT NULL CHECK (job_type IN (
11
+ 'route_report', 'process_ai_report', 'run_enrichment', 'dispatch_workflow',
12
+ 'deliver_action', 'deliver_notification', 'invalid'
13
+ )),
14
+ job_id TEXT,
15
+ report_id TEXT REFERENCES reports(id),
16
+ outbox_id TEXT,
17
+ state TEXT NOT NULL DEFAULT 'open' CHECK (state IN ('open', 'acknowledged', 'resolved')),
18
+ error_code TEXT NOT NULL,
19
+ occurrence_count INTEGER NOT NULL DEFAULT 1 CHECK (occurrence_count > 0),
20
+ details_json TEXT NOT NULL DEFAULT '{}',
21
+ first_seen_at TEXT NOT NULL,
22
+ last_seen_at TEXT NOT NULL,
23
+ resolved_at TEXT
24
+ );
25
+
26
+ INSERT INTO dead_letter_items (
27
+ id, fingerprint, source_queue, source_message_id, job_type, job_id,
28
+ report_id, outbox_id, state, error_code, occurrence_count,
29
+ details_json, first_seen_at, last_seen_at, resolved_at
30
+ )
31
+ SELECT
32
+ id, fingerprint, source_queue, source_message_id, job_type, job_id,
33
+ report_id, outbox_id, state, error_code, occurrence_count,
34
+ details_json, first_seen_at, last_seen_at, resolved_at
35
+ FROM dead_letter_items_v3;
36
+
37
+ DROP TABLE dead_letter_items_v3;
38
+
39
+ CREATE INDEX dead_letter_items_state_idx
40
+ ON dead_letter_items(state, last_seen_at DESC, id DESC);
41
+ CREATE INDEX dead_letter_items_report_idx
42
+ ON dead_letter_items(report_id, last_seen_at DESC, id DESC);
@@ -0,0 +1,75 @@
1
+ PRAGMA foreign_keys = ON;
2
+
3
+ CREATE TABLE component_runs (
4
+ id TEXT PRIMARY KEY,
5
+ installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
6
+ report_id TEXT NOT NULL REFERENCES reports(id) ON DELETE CASCADE,
7
+ workflow_run_id TEXT REFERENCES workflow_runs(id) ON DELETE SET NULL,
8
+ step_run_id TEXT REFERENCES workflow_step_runs(id) ON DELETE SET NULL,
9
+ component_version_id TEXT NOT NULL REFERENCES component_versions(id),
10
+ operation_id TEXT NOT NULL UNIQUE,
11
+ effect_class TEXT NOT NULL CHECK (effect_class IN ('read_only', 'side_effecting')),
12
+ state TEXT NOT NULL CHECK (state IN ('pending', 'running', 'succeeded', 'failed', 'timed_out', 'cancelled')),
13
+ input_json TEXT NOT NULL,
14
+ input_digest TEXT NOT NULL,
15
+ output_json TEXT,
16
+ output_digest TEXT,
17
+ finding_id TEXT REFERENCES findings(id),
18
+ cache_hit INTEGER NOT NULL DEFAULT 0 CHECK (cache_hit IN (0, 1)),
19
+ attempt_count INTEGER NOT NULL DEFAULT 0 CHECK (attempt_count >= 0),
20
+ error_code TEXT,
21
+ started_at TEXT,
22
+ finished_at TEXT,
23
+ created_at TEXT NOT NULL,
24
+ updated_at TEXT NOT NULL
25
+ );
26
+
27
+ CREATE INDEX component_runs_report_idx
28
+ ON component_runs(report_id, created_at DESC, id DESC);
29
+ CREATE INDEX component_runs_due_idx
30
+ ON component_runs(state, updated_at, id);
31
+
32
+ CREATE TABLE connection_attempts (
33
+ id TEXT PRIMARY KEY,
34
+ installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
35
+ connection_version_id TEXT NOT NULL REFERENCES connection_versions(id),
36
+ component_run_id TEXT REFERENCES component_runs(id) ON DELETE SET NULL,
37
+ action_proposal_id TEXT REFERENCES action_proposals(id) ON DELETE SET NULL,
38
+ operation_id TEXT NOT NULL,
39
+ attempt_number INTEGER NOT NULL CHECK (attempt_number >= 1),
40
+ method TEXT NOT NULL,
41
+ destination_origin TEXT NOT NULL,
42
+ destination_path_hash TEXT NOT NULL,
43
+ request_digest TEXT NOT NULL,
44
+ response_status INTEGER,
45
+ response_digest TEXT,
46
+ response_class TEXT NOT NULL CHECK (response_class IN (
47
+ 'success', 'retryable', 'permanent', 'timeout', 'circuit_open', 'denied', 'invalid'
48
+ )),
49
+ error_code TEXT,
50
+ started_at TEXT NOT NULL,
51
+ finished_at TEXT NOT NULL,
52
+ UNIQUE (operation_id, attempt_number)
53
+ );
54
+
55
+ CREATE INDEX connection_attempts_connection_idx
56
+ ON connection_attempts(connection_version_id, started_at DESC, id DESC);
57
+ CREATE INDEX connection_attempts_component_idx
58
+ ON connection_attempts(component_run_id, attempt_number, id);
59
+
60
+ CREATE TABLE component_cache_entries (
61
+ id TEXT PRIMARY KEY,
62
+ installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
63
+ component_version_id TEXT NOT NULL REFERENCES component_versions(id) ON DELETE CASCADE,
64
+ subject_key_hash TEXT NOT NULL,
65
+ input_digest TEXT NOT NULL,
66
+ finding_id TEXT NOT NULL REFERENCES findings(id) ON DELETE CASCADE,
67
+ output_json TEXT NOT NULL,
68
+ output_digest TEXT NOT NULL,
69
+ created_at TEXT NOT NULL,
70
+ expires_at TEXT NOT NULL,
71
+ UNIQUE (component_version_id, subject_key_hash, input_digest)
72
+ );
73
+
74
+ CREATE INDEX component_cache_entries_expiry_idx
75
+ ON component_cache_entries(expires_at, id);
@@ -0,0 +1,72 @@
1
+ PRAGMA foreign_keys = ON;
2
+
3
+ ALTER TABLE operator_invitations RENAME TO operator_invitations_v1;
4
+
5
+ CREATE TABLE operator_invitations (
6
+ id TEXT PRIMARY KEY,
7
+ email TEXT NOT NULL COLLATE NOCASE,
8
+ role TEXT NOT NULL CHECK (role IN (
9
+ 'administrator', 'workflow_author', 'reviewer', 'appeal_reviewer',
10
+ 'quality_reviewer', 'operations', 'auditor'
11
+ )),
12
+ token_hash TEXT NOT NULL UNIQUE,
13
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'accepted', 'revoked')),
14
+ invited_by TEXT NOT NULL,
15
+ accepted_by TEXT REFERENCES operator_users(id),
16
+ expires_at TEXT NOT NULL,
17
+ accepted_at TEXT,
18
+ revoked_at TEXT,
19
+ created_at TEXT NOT NULL
20
+ );
21
+
22
+ INSERT INTO operator_invitations (
23
+ id, email, role, token_hash, status, invited_by, accepted_by,
24
+ expires_at, accepted_at, revoked_at, created_at
25
+ )
26
+ SELECT
27
+ id, email, role, token_hash, status, invited_by, accepted_by,
28
+ expires_at, accepted_at, revoked_at, created_at
29
+ FROM operator_invitations_v1;
30
+
31
+ DROP TABLE operator_invitations_v1;
32
+
33
+ CREATE UNIQUE INDEX operator_invitations_pending_email_idx
34
+ ON operator_invitations(email) WHERE status = 'pending';
35
+ CREATE INDEX operator_invitations_status_idx
36
+ ON operator_invitations(status, expires_at, created_at DESC, id);
37
+
38
+ ALTER TABLE role_bindings RENAME TO role_bindings_v1;
39
+
40
+ CREATE TABLE role_bindings (
41
+ id TEXT PRIMARY KEY,
42
+ installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
43
+ principal_type TEXT NOT NULL CHECK (principal_type IN ('operator_user', 'access_group', 'service_identity')),
44
+ principal_id TEXT NOT NULL,
45
+ role TEXT NOT NULL CHECK (role IN (
46
+ 'owner', 'administrator', 'workflow_author', 'reviewer', 'appeal_reviewer',
47
+ 'quality_reviewer', 'operations', 'auditor'
48
+ )),
49
+ queue_scope_json TEXT NOT NULL DEFAULT '[]',
50
+ field_scope_json TEXT NOT NULL DEFAULT '{}',
51
+ created_by TEXT NOT NULL,
52
+ created_at TEXT NOT NULL,
53
+ revoked_at TEXT
54
+ );
55
+
56
+ INSERT INTO role_bindings (
57
+ id, installation_id, principal_type, principal_id, role,
58
+ queue_scope_json, field_scope_json, created_by, created_at, revoked_at
59
+ )
60
+ SELECT
61
+ id, installation_id, principal_type, principal_id,
62
+ CASE role WHEN 'operations_owner' THEN 'operations' ELSE role END,
63
+ queue_scope_json, field_scope_json, created_by, created_at, revoked_at
64
+ FROM role_bindings_v1;
65
+
66
+ DROP TABLE role_bindings_v1;
67
+
68
+ CREATE INDEX role_bindings_principal_idx
69
+ ON role_bindings(installation_id, principal_type, principal_id, revoked_at, role);
70
+
71
+ CREATE UNIQUE INDEX workflow_run_links_parent_node_idx
72
+ ON workflow_run_links(parent_run_id, node_id) WHERE node_id IS NOT NULL;
@@ -0,0 +1,11 @@
1
+ PRAGMA foreign_keys = ON;
2
+
3
+ ALTER TABLE installations ADD COLUMN timezone TEXT NOT NULL DEFAULT 'UTC';
4
+
5
+ ALTER TABLE assistant_sessions ADD COLUMN idempotency_key TEXT;
6
+ CREATE UNIQUE INDEX assistant_sessions_idempotency_idx
7
+ ON assistant_sessions(principal_id, idempotency_key) WHERE idempotency_key IS NOT NULL;
8
+
9
+ ALTER TABLE assistant_messages ADD COLUMN idempotency_key TEXT;
10
+ CREATE UNIQUE INDEX assistant_messages_idempotency_idx
11
+ ON assistant_messages(session_id, idempotency_key) WHERE idempotency_key IS NOT NULL;
@@ -0,0 +1,49 @@
1
+ PRAGMA foreign_keys = ON;
2
+
3
+ CREATE UNIQUE INDEX workflow_runs_report_pin_once_idx
4
+ ON workflow_runs(report_id, pin_id) WHERE report_id IS NOT NULL AND pin_id IS NOT NULL;
5
+
6
+ CREATE TABLE workflow_ai_runs (
7
+ id TEXT PRIMARY KEY,
8
+ installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
9
+ report_id TEXT NOT NULL REFERENCES reports(id) ON DELETE CASCADE,
10
+ workflow_run_id TEXT NOT NULL REFERENCES workflow_runs(id) ON DELETE CASCADE,
11
+ step_run_id TEXT NOT NULL REFERENCES workflow_step_runs(id) ON DELETE CASCADE,
12
+ component_version_id TEXT REFERENCES component_versions(id),
13
+ agent_version_id TEXT REFERENCES agent_versions(id),
14
+ authority_mode TEXT NOT NULL CHECK (authority_mode IN ('shadow', 'assist', 'bounded_auto')),
15
+ provider TEXT NOT NULL,
16
+ model TEXT NOT NULL,
17
+ prompt_version_id TEXT,
18
+ output_schema_digest TEXT NOT NULL,
19
+ status TEXT NOT NULL CHECK (status IN ('running', 'succeeded', 'invalid', 'failed', 'timed_out')),
20
+ input_digest TEXT NOT NULL,
21
+ output_json TEXT,
22
+ output_digest TEXT,
23
+ validation_errors_json TEXT NOT NULL DEFAULT '[]',
24
+ input_units INTEGER CHECK (input_units IS NULL OR input_units >= 0),
25
+ output_units INTEGER CHECK (output_units IS NULL OR output_units >= 0),
26
+ estimated_cost_microusd INTEGER NOT NULL DEFAULT 0 CHECK (estimated_cost_microusd >= 0),
27
+ provider_request_id TEXT,
28
+ started_at TEXT NOT NULL,
29
+ finished_at TEXT,
30
+ UNIQUE (step_run_id, component_version_id, agent_version_id)
31
+ );
32
+
33
+ CREATE INDEX workflow_ai_runs_report_idx
34
+ ON workflow_ai_runs(report_id, started_at DESC, id DESC);
35
+ CREATE INDEX workflow_ai_runs_run_idx
36
+ ON workflow_ai_runs(workflow_run_id, step_run_id, id);
37
+
38
+ CREATE TABLE connection_execution_leases (
39
+ id TEXT PRIMARY KEY,
40
+ connection_version_id TEXT NOT NULL REFERENCES connection_versions(id) ON DELETE CASCADE,
41
+ operation_id TEXT NOT NULL,
42
+ attempt_number INTEGER NOT NULL CHECK (attempt_number >= 1),
43
+ expires_at TEXT NOT NULL,
44
+ created_at TEXT NOT NULL,
45
+ UNIQUE (operation_id, attempt_number)
46
+ );
47
+
48
+ CREATE INDEX connection_execution_leases_expiry_idx
49
+ ON connection_execution_leases(connection_version_id, expires_at, id);
@@ -0,0 +1,37 @@
1
+ PRAGMA foreign_keys = ON;
2
+
3
+ CREATE TABLE prompt_versions (
4
+ id TEXT PRIMARY KEY,
5
+ installation_id TEXT NOT NULL REFERENCES installations(id) ON DELETE CASCADE,
6
+ prompt_key TEXT NOT NULL,
7
+ version INTEGER NOT NULL CHECK (version >= 1),
8
+ name TEXT NOT NULL,
9
+ template_text TEXT NOT NULL,
10
+ variable_schema_json TEXT NOT NULL DEFAULT '{"type":"object"}',
11
+ prompt_digest TEXT NOT NULL,
12
+ status TEXT NOT NULL DEFAULT 'published' CHECK (status IN ('published', 'retired')),
13
+ published_at TEXT NOT NULL,
14
+ retired_at TEXT,
15
+ created_by TEXT NOT NULL,
16
+ created_at TEXT NOT NULL,
17
+ UNIQUE (installation_id, prompt_key, version),
18
+ UNIQUE (installation_id, prompt_digest)
19
+ );
20
+
21
+ CREATE INDEX prompt_versions_lookup_idx
22
+ ON prompt_versions(installation_id, prompt_key, status, version DESC, id);
23
+
24
+ CREATE TABLE ai_registry_mutations (
25
+ idempotency_key TEXT PRIMARY KEY,
26
+ action TEXT NOT NULL,
27
+ actor_id TEXT NOT NULL,
28
+ target_id TEXT NOT NULL,
29
+ response_json TEXT NOT NULL,
30
+ created_at TEXT NOT NULL
31
+ );
32
+
33
+ CREATE INDEX model_aliases_admin_idx
34
+ ON model_aliases(installation_id, status, name, id);
35
+ CREATE INDEX agent_definitions_admin_idx
36
+ ON agent_definitions(installation_id, status, agent_kind, name, id);
37
+
@@ -0,0 +1,13 @@
1
+ PRAGMA foreign_keys = ON;
2
+
3
+ ALTER TABLE workflow_step_attempts ADD COLUMN external_request_id TEXT;
4
+ ALTER TABLE workflow_step_attempts ADD COLUMN cost_microusd INTEGER NOT NULL DEFAULT 0 CHECK (cost_microusd >= 0);
5
+
6
+ CREATE UNIQUE INDEX workflow_ai_runs_component_once_idx
7
+ ON workflow_ai_runs(step_run_id, component_version_id)
8
+ WHERE component_version_id IS NOT NULL;
9
+
10
+ CREATE UNIQUE INDEX workflow_ai_runs_agent_once_idx
11
+ ON workflow_ai_runs(step_run_id, agent_version_id)
12
+ WHERE agent_version_id IS NOT NULL;
13
+
@@ -0,0 +1,5 @@
1
+ PRAGMA foreign_keys = ON;
2
+
3
+ ALTER TABLE evidence_fetch_requests ADD COLUMN request_method TEXT NOT NULL DEFAULT 'POST';
4
+ ALTER TABLE evidence_fetch_requests ADD COLUMN request_path TEXT NOT NULL DEFAULT '/v1/evidence/fetch';
5
+
@@ -0,0 +1,45 @@
1
+ PRAGMA foreign_keys = ON;
2
+
3
+ ALTER TABLE dead_letter_items RENAME TO dead_letter_items_v4;
4
+
5
+ CREATE TABLE dead_letter_items (
6
+ id TEXT PRIMARY KEY,
7
+ fingerprint TEXT NOT NULL UNIQUE,
8
+ source_queue TEXT NOT NULL,
9
+ source_message_id TEXT NOT NULL,
10
+ job_type TEXT NOT NULL CHECK (job_type IN (
11
+ 'route_report', 'process_ai_report', 'run_enrichment', 'fetch_evidence',
12
+ 'dispatch_workflow', 'deliver_action', 'deliver_notification', 'deliver_email', 'invalid'
13
+ )),
14
+ job_id TEXT,
15
+ report_id TEXT REFERENCES reports(id),
16
+ outbox_id TEXT,
17
+ incident_id TEXT REFERENCES operations_incidents(id) ON DELETE SET NULL,
18
+ state TEXT NOT NULL DEFAULT 'open' CHECK (state IN ('open', 'acknowledged', 'resolved')),
19
+ error_code TEXT NOT NULL,
20
+ occurrence_count INTEGER NOT NULL DEFAULT 1 CHECK (occurrence_count > 0),
21
+ details_json TEXT NOT NULL DEFAULT '{}',
22
+ first_seen_at TEXT NOT NULL,
23
+ last_seen_at TEXT NOT NULL,
24
+ resolved_at TEXT
25
+ );
26
+
27
+ INSERT INTO dead_letter_items (
28
+ id, fingerprint, source_queue, source_message_id, job_type, job_id,
29
+ report_id, outbox_id, state, error_code, occurrence_count,
30
+ details_json, first_seen_at, last_seen_at, resolved_at
31
+ )
32
+ SELECT
33
+ id, fingerprint, source_queue, source_message_id, job_type, job_id,
34
+ report_id, outbox_id, state, error_code, occurrence_count,
35
+ details_json, first_seen_at, last_seen_at, resolved_at
36
+ FROM dead_letter_items_v4;
37
+
38
+ DROP TABLE dead_letter_items_v4;
39
+
40
+ CREATE INDEX dead_letter_items_state_idx
41
+ ON dead_letter_items(state, last_seen_at DESC, id DESC);
42
+ CREATE INDEX dead_letter_items_report_idx
43
+ ON dead_letter_items(report_id, last_seen_at DESC, id DESC);
44
+ CREATE INDEX dead_letter_items_incident_idx
45
+ ON dead_letter_items(incident_id, state, last_seen_at DESC, id DESC);