spine-framework 0.3.47 → 0.3.49

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.
@@ -90,10 +90,39 @@ async function migrateCommand(options: MigrateOptions): Promise<void> {
90
90
  process.exit(1)
91
91
  }
92
92
 
93
+ // Create migration tracking table if it doesn't exist
94
+ try {
95
+ await client.query(`
96
+ CREATE TABLE IF NOT EXISTS _spine_migrations (
97
+ id SERIAL PRIMARY KEY,
98
+ filename TEXT UNIQUE NOT NULL,
99
+ applied_at TIMESTAMPTZ DEFAULT NOW()
100
+ )
101
+ `)
102
+ } catch {
103
+ // Non-fatal: proceed without tracking
104
+ }
105
+
106
+ // Load already-applied migrations
107
+ let appliedMigrations = new Set<string>()
108
+ try {
109
+ const { rows } = await client.query('SELECT filename FROM _spine_migrations')
110
+ appliedMigrations = new Set(rows.map((r: { filename: string }) => r.filename))
111
+ } catch {
112
+ // Non-fatal: proceed without tracking
113
+ }
114
+
93
115
  let applied = 0
116
+ let skipped = 0
94
117
  let failed = 0
95
118
 
96
119
  for (const { name, path } of migrationFiles) {
120
+ if (appliedMigrations.has(name)) {
121
+ console.log(` Skipping ${name} (already applied)`)
122
+ skipped++
123
+ continue
124
+ }
125
+
97
126
  const sql = readFileSync(path, 'utf8')
98
127
  process.stdout.write(` Applying ${name}... `)
99
128
 
@@ -103,6 +132,11 @@ async function migrateCommand(options: MigrateOptions): Promise<void> {
103
132
  for (const stmt of statements) {
104
133
  await client.query(stmt)
105
134
  }
135
+ // Record as applied
136
+ await client.query(
137
+ `INSERT INTO _spine_migrations (filename) VALUES ($1) ON CONFLICT (filename) DO NOTHING`,
138
+ [name]
139
+ )
106
140
  console.log('✓')
107
141
  applied++
108
142
  } catch (err: any) {
@@ -114,7 +148,7 @@ async function migrateCommand(options: MigrateOptions): Promise<void> {
114
148
 
115
149
  await client.end()
116
150
 
117
- console.log(`\n ${applied} applied, ${failed} failed`)
151
+ console.log(`\n ${applied} applied, ${skipped} skipped, ${failed} failed`)
118
152
 
119
153
  if (failed > 0) {
120
154
  console.error('\nSome migrations failed. Check errors above.')
@@ -14,7 +14,7 @@ CREATE EXTENSION IF NOT EXISTS vector;
14
14
  -- IDENTITY TABLES (accounts, people)
15
15
  -- ============================================
16
16
 
17
- CREATE TABLE public.accounts (
17
+ CREATE TABLE IF NOT EXISTS public.accounts (
18
18
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
19
19
  parent_id uuid, type_id uuid NOT NULL, slug text NOT NULL UNIQUE,
20
20
  display_name text NOT NULL, description text,
@@ -24,7 +24,7 @@ CREATE TABLE public.accounts (
24
24
  app_id uuid, created_by uuid, updated_by uuid
25
25
  );
26
26
 
27
- CREATE TABLE public.people (
27
+ CREATE TABLE IF NOT EXISTS public.people (
28
28
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
29
29
  auth_uid uuid UNIQUE, email text UNIQUE NOT NULL, full_name text NOT NULL,
30
30
  avatar_url text, phone text, status text DEFAULT 'active',
@@ -39,7 +39,7 @@ CREATE TABLE public.people (
39
39
  -- TYPES, APPS, ROLES
40
40
  -- ============================================
41
41
 
42
- CREATE TABLE public.types (
42
+ CREATE TABLE IF NOT EXISTS public.types (
43
43
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
44
44
  app_id uuid, kind text NOT NULL, slug text NOT NULL, name text NOT NULL,
45
45
  description text, icon text, color text,
@@ -49,7 +49,7 @@ CREATE TABLE public.types (
49
49
  UNIQUE(app_id, kind, slug)
50
50
  );
51
51
 
52
- CREATE TABLE public.apps (
52
+ CREATE TABLE IF NOT EXISTS public.apps (
53
53
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
54
54
  slug text NOT NULL UNIQUE, name text NOT NULL, description text,
55
55
  icon text, color text, version text DEFAULT '1.0.0',
@@ -64,7 +64,7 @@ CREATE TABLE public.apps (
64
64
  CONSTRAINT chk_apps_renderer CHECK (renderer IN ('generic', 'custom', 'none'))
65
65
  );
66
66
 
67
- CREATE TABLE public.roles (
67
+ CREATE TABLE IF NOT EXISTS public.roles (
68
68
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
69
69
  slug text NOT NULL, name text NOT NULL, description text,
70
70
  permissions jsonb DEFAULT '[]', is_system boolean DEFAULT false,
@@ -78,7 +78,7 @@ CREATE TABLE public.roles (
78
78
  -- RUNTIME ENTITIES
79
79
  -- ============================================
80
80
 
81
- CREATE TABLE public.items (
81
+ CREATE TABLE IF NOT EXISTS public.items (
82
82
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
83
83
  type_id uuid NOT NULL, account_id uuid NOT NULL, app_id uuid,
84
84
  title text, description text, status text DEFAULT 'active',
@@ -88,7 +88,7 @@ CREATE TABLE public.items (
88
88
  created_at timestamptz DEFAULT now(), updated_at timestamptz DEFAULT now()
89
89
  );
90
90
 
91
- CREATE TABLE public.threads (
91
+ CREATE TABLE IF NOT EXISTS public.threads (
92
92
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
93
93
  type_id uuid NOT NULL, account_id uuid NOT NULL, app_id uuid,
94
94
  title text, target_type text NOT NULL, target_id uuid NOT NULL,
@@ -100,7 +100,7 @@ CREATE TABLE public.threads (
100
100
  CONSTRAINT threads_unique_target_visibility UNIQUE (target_type, target_id, visibility)
101
101
  );
102
102
 
103
- CREATE TABLE public.messages (
103
+ CREATE TABLE IF NOT EXISTS public.messages (
104
104
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
105
105
  type_id uuid NOT NULL, thread_id uuid NOT NULL,
106
106
  content text NOT NULL, direction text DEFAULT 'outbound',
@@ -112,7 +112,7 @@ CREATE TABLE public.messages (
112
112
  UNIQUE(thread_id, sequence)
113
113
  );
114
114
 
115
- CREATE TABLE public.links (
115
+ CREATE TABLE IF NOT EXISTS public.links (
116
116
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
117
117
  type_id uuid NOT NULL, account_id uuid NOT NULL, link_type_id uuid,
118
118
  source_type text NOT NULL, source_id uuid NOT NULL,
@@ -124,7 +124,7 @@ CREATE TABLE public.links (
124
124
  UNIQUE(source_type, source_id, target_type, target_id, link_type_id)
125
125
  );
126
126
 
127
- CREATE TABLE public.attachments (
127
+ CREATE TABLE IF NOT EXISTS public.attachments (
128
128
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
129
129
  type_id uuid NOT NULL, account_id uuid NOT NULL,
130
130
  filename text NOT NULL, file_size integer, mime_type text,
@@ -135,7 +135,7 @@ CREATE TABLE public.attachments (
135
135
  created_at timestamptz DEFAULT now(), updated_at timestamptz DEFAULT now()
136
136
  );
137
137
 
138
- CREATE TABLE public.watchers (
138
+ CREATE TABLE IF NOT EXISTS public.watchers (
139
139
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
140
140
  type_id uuid NOT NULL, account_id uuid,
141
141
  target_type text NOT NULL, target_id uuid NOT NULL, person_id uuid NOT NULL,
@@ -150,7 +150,7 @@ CREATE TABLE public.watchers (
150
150
  -- ADMIN/AUTOMATION TABLES
151
151
  -- ============================================
152
152
 
153
- CREATE TABLE public.link_types (
153
+ CREATE TABLE IF NOT EXISTS public.link_types (
154
154
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
155
155
  app_id uuid, slug text NOT NULL, name text NOT NULL,
156
156
  description text, icon text, color text,
@@ -159,7 +159,7 @@ CREATE TABLE public.link_types (
159
159
  UNIQUE(app_id, slug)
160
160
  );
161
161
 
162
- CREATE TABLE public.pipelines (
162
+ CREATE TABLE IF NOT EXISTS public.pipelines (
163
163
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
164
164
  app_id uuid, account_id uuid, name text NOT NULL,
165
165
  description text, trigger_type text, config jsonb DEFAULT '{}',
@@ -169,7 +169,7 @@ CREATE TABLE public.pipelines (
169
169
  created_at timestamptz DEFAULT now(), updated_at timestamptz DEFAULT now()
170
170
  );
171
171
 
172
- CREATE TABLE public.pipeline_executions (
172
+ CREATE TABLE IF NOT EXISTS public.pipeline_executions (
173
173
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
174
174
  pipeline_id uuid NOT NULL, account_id uuid NOT NULL,
175
175
  status text DEFAULT 'pending', trigger_data jsonb DEFAULT '{}',
@@ -178,7 +178,7 @@ CREATE TABLE public.pipeline_executions (
178
178
  created_by uuid, created_at timestamptz DEFAULT now()
179
179
  );
180
180
 
181
- CREATE TABLE public.triggers (
181
+ CREATE TABLE IF NOT EXISTS public.triggers (
182
182
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
183
183
  app_id uuid, account_id uuid, name text NOT NULL,
184
184
  description text, trigger_type text NOT NULL, event_type text,
@@ -189,7 +189,7 @@ CREATE TABLE public.triggers (
189
189
  created_at timestamptz DEFAULT now(), updated_at timestamptz DEFAULT now()
190
190
  );
191
191
 
192
- CREATE TABLE public.trigger_executions (
192
+ CREATE TABLE IF NOT EXISTS public.trigger_executions (
193
193
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
194
194
  trigger_id uuid NOT NULL, status text DEFAULT 'pending',
195
195
  triggered_at timestamptz DEFAULT now(), started_at timestamptz,
@@ -198,7 +198,7 @@ CREATE TABLE public.trigger_executions (
198
198
  created_at timestamptz DEFAULT now()
199
199
  );
200
200
 
201
- CREATE TABLE public.timers (
201
+ CREATE TABLE IF NOT EXISTS public.timers (
202
202
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
203
203
  app_id uuid, account_id uuid, name text NOT NULL,
204
204
  description text, timer_type text NOT NULL, config jsonb DEFAULT '{}',
@@ -213,7 +213,7 @@ CREATE TABLE public.timers (
213
213
  -- SYSTEM TABLES (AI, Integrations, Logs, API Keys)
214
214
  -- ============================================
215
215
 
216
- CREATE TABLE public.ai_agents (
216
+ CREATE TABLE IF NOT EXISTS public.ai_agents (
217
217
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
218
218
  app_id uuid, account_id uuid, name text NOT NULL,
219
219
  description text, agent_type text NOT NULL, model_config jsonb DEFAULT '{}',
@@ -224,7 +224,7 @@ CREATE TABLE public.ai_agents (
224
224
  created_at timestamptz DEFAULT now(), updated_at timestamptz DEFAULT now()
225
225
  );
226
226
 
227
- CREATE TABLE public.embeddings (
227
+ CREATE TABLE IF NOT EXISTS public.embeddings (
228
228
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
229
229
  account_id uuid NOT NULL, model_id text NOT NULL, document_id text NOT NULL,
230
230
  chunk_index integer NOT NULL, content text NOT NULL, embedding vector(1536),
@@ -232,7 +232,7 @@ CREATE TABLE public.embeddings (
232
232
  UNIQUE(model_id, document_id, chunk_index)
233
233
  );
234
234
 
235
- CREATE TABLE public.integrations (
235
+ CREATE TABLE IF NOT EXISTS public.integrations (
236
236
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
237
237
  app_id uuid, account_id uuid, name text NOT NULL,
238
238
  description text, integration_type text NOT NULL, provider text,
@@ -243,7 +243,7 @@ CREATE TABLE public.integrations (
243
243
  created_at timestamptz DEFAULT now(), updated_at timestamptz DEFAULT now()
244
244
  );
245
245
 
246
- CREATE TABLE public.prompt_configs (
246
+ CREATE TABLE IF NOT EXISTS public.prompt_configs (
247
247
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
248
248
  app_id uuid, account_id uuid, name text NOT NULL, slug text NOT NULL,
249
249
  system_prompt text, context_template text, model text, temperature numeric,
@@ -257,7 +257,7 @@ CREATE TABLE public.prompt_configs (
257
257
  created_at timestamptz DEFAULT now(), updated_at timestamptz DEFAULT now()
258
258
  );
259
259
 
260
- CREATE TABLE public.api_keys (
260
+ CREATE TABLE IF NOT EXISTS public.api_keys (
261
261
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
262
262
  integration_id uuid, account_id uuid, name text NOT NULL,
263
263
  key_value text UNIQUE NOT NULL, key_prefix text NOT NULL,
@@ -269,7 +269,7 @@ CREATE TABLE public.api_keys (
269
269
  created_by uuid, created_at timestamptz DEFAULT now(), updated_at timestamptz DEFAULT now()
270
270
  );
271
271
 
272
- CREATE TABLE public.api_key_usage_logs (
272
+ CREATE TABLE IF NOT EXISTS public.api_key_usage_logs (
273
273
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
274
274
  api_key_id uuid NOT NULL, account_id uuid NOT NULL,
275
275
  request_method text, request_path text, request_ip text, user_agent text,
@@ -278,14 +278,14 @@ CREATE TABLE public.api_key_usage_logs (
278
278
  created_at timestamptz DEFAULT now()
279
279
  );
280
280
 
281
- CREATE TABLE public.logs (
281
+ CREATE TABLE IF NOT EXISTS public.logs (
282
282
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
283
283
  level text NOT NULL, message text NOT NULL, context jsonb DEFAULT '{}',
284
284
  source text, source_type text, source_id uuid, person_id uuid, account_id uuid,
285
285
  metadata jsonb DEFAULT '{}', created_at timestamptz DEFAULT now()
286
286
  );
287
287
 
288
- CREATE TABLE public.actions (
288
+ CREATE TABLE IF NOT EXISTS public.actions (
289
289
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
290
290
  account_id uuid, name text NOT NULL, slug text NOT NULL,
291
291
  description text, handler text NOT NULL, handler_module text DEFAULT 'functions',
@@ -296,7 +296,7 @@ CREATE TABLE public.actions (
296
296
  retry_count integer DEFAULT 3, created_by uuid, created_at timestamptz DEFAULT now()
297
297
  );
298
298
 
299
- CREATE TABLE public.schedules (
299
+ CREATE TABLE IF NOT EXISTS public.schedules (
300
300
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
301
301
  account_id uuid, action_id uuid NOT NULL, name text NOT NULL,
302
302
  schedule_type text NOT NULL, cron_expression text,
@@ -306,7 +306,7 @@ CREATE TABLE public.schedules (
306
306
  created_by uuid, created_at timestamptz DEFAULT now(), updated_at timestamptz DEFAULT now()
307
307
  );
308
308
 
309
- CREATE TABLE public.schedule_executions (
309
+ CREATE TABLE IF NOT EXISTS public.schedule_executions (
310
310
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
311
311
  schedule_id uuid NOT NULL, account_id uuid NOT NULL, machine_principal_id uuid,
312
312
  status text DEFAULT 'pending', input_params jsonb DEFAULT '{}',
@@ -318,7 +318,7 @@ CREATE TABLE public.schedule_executions (
318
318
  -- TEST TABLES
319
319
  -- ============================================
320
320
 
321
- CREATE TABLE public.test_runs (
321
+ CREATE TABLE IF NOT EXISTS public.test_runs (
322
322
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
323
323
  suite text NOT NULL,
324
324
  status text NOT NULL,
@@ -333,7 +333,7 @@ CREATE TABLE public.test_runs (
333
333
  account_id uuid REFERENCES public.accounts(id)
334
334
  );
335
335
 
336
- CREATE TABLE public.test_results (
336
+ CREATE TABLE IF NOT EXISTS public.test_results (
337
337
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
338
338
  run_id uuid NOT NULL REFERENCES public.test_runs(id) ON DELETE CASCADE,
339
339
  suite text NOT NULL,
@@ -350,7 +350,7 @@ CREATE TABLE public.test_results (
350
350
  -- ITEM PROGRESS
351
351
  -- ============================================
352
352
 
353
- CREATE TABLE public.item_progress (
353
+ CREATE TABLE IF NOT EXISTS public.item_progress (
354
354
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
355
355
  type_id uuid NOT NULL,
356
356
  account_id uuid NOT NULL REFERENCES public.accounts(id) ON DELETE CASCADE,
@@ -376,7 +376,7 @@ CREATE TABLE public.item_progress (
376
376
  -- WEBHOOK HANDLERS
377
377
  -- ============================================
378
378
 
379
- CREATE TABLE public.webhook_handlers (
379
+ CREATE TABLE IF NOT EXISTS public.webhook_handlers (
380
380
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
381
381
  name VARCHAR(255) NOT NULL UNIQUE,
382
382
  function_name VARCHAR(255) NOT NULL,
@@ -394,7 +394,7 @@ CREATE TABLE public.webhook_handlers (
394
394
  -- ACCOUNT HIERARCHY (from 005_functions.sql)
395
395
  -- ============================================
396
396
 
397
- CREATE TABLE public.account_paths (
397
+ CREATE TABLE IF NOT EXISTS public.account_paths (
398
398
  ancestor_id uuid NOT NULL REFERENCES public.accounts(id) ON DELETE CASCADE,
399
399
  descendant_id uuid NOT NULL REFERENCES public.accounts(id) ON DELETE CASCADE,
400
400
  depth integer NOT NULL,
@@ -90,6 +90,7 @@ END;
90
90
  $$ LANGUAGE plpgsql;
91
91
 
92
92
  -- Trigger for account paths
93
+ DROP TRIGGER IF EXISTS account_paths_trigger ON public.accounts;
93
94
  CREATE TRIGGER account_paths_trigger
94
95
  AFTER INSERT OR UPDATE OR DELETE ON public.accounts
95
96
  FOR EACH ROW EXECUTE FUNCTION public.update_account_paths();
@@ -334,155 +335,155 @@ ALTER TABLE public.schedule_executions ADD CONSTRAINT fk_se_machine
334
335
  -- ============================================
335
336
 
336
337
  -- account_paths indexes
337
- CREATE INDEX idx_account_paths_ancestor ON public.account_paths(ancestor_id);
338
- CREATE INDEX idx_account_paths_descendant ON public.account_paths(descendant_id);
338
+ CREATE INDEX IF NOT EXISTS idx_account_paths_ancestor ON public.account_paths(ancestor_id);
339
+ CREATE INDEX IF NOT EXISTS idx_account_paths_descendant ON public.account_paths(descendant_id);
339
340
 
340
341
  -- accounts
341
- CREATE INDEX idx_accounts_parent_id ON public.accounts(parent_id);
342
- CREATE INDEX idx_accounts_type_id ON public.accounts(type_id);
343
- CREATE INDEX idx_accounts_active ON public.accounts(is_active);
342
+ CREATE INDEX IF NOT EXISTS idx_accounts_parent_id ON public.accounts(parent_id);
343
+ CREATE INDEX IF NOT EXISTS idx_accounts_type_id ON public.accounts(type_id);
344
+ CREATE INDEX IF NOT EXISTS idx_accounts_active ON public.accounts(is_active);
344
345
 
345
346
  -- people
346
- CREATE INDEX idx_people_account ON public.people(account_id);
347
- CREATE INDEX idx_people_auth_uid ON public.people(auth_uid);
348
- CREATE INDEX idx_people_email ON public.people(email);
349
- CREATE INDEX idx_people_role ON public.people(role_id);
350
- CREATE INDEX idx_people_active ON public.people(is_active);
347
+ CREATE INDEX IF NOT EXISTS idx_people_account ON public.people(account_id);
348
+ CREATE INDEX IF NOT EXISTS idx_people_auth_uid ON public.people(auth_uid);
349
+ CREATE INDEX IF NOT EXISTS idx_people_email ON public.people(email);
350
+ CREATE INDEX IF NOT EXISTS idx_people_role ON public.people(role_id);
351
+ CREATE INDEX IF NOT EXISTS idx_people_active ON public.people(is_active);
351
352
 
352
353
  -- types
353
- CREATE INDEX idx_types_slug ON public.types(slug);
354
- CREATE INDEX idx_types_kind ON public.types(kind);
354
+ CREATE INDEX IF NOT EXISTS idx_types_slug ON public.types(slug);
355
+ CREATE INDEX IF NOT EXISTS idx_types_kind ON public.types(kind);
355
356
 
356
357
  -- apps
357
- CREATE INDEX idx_apps_slug ON public.apps(slug);
358
- CREATE INDEX idx_apps_owner ON public.apps(owner_account_id);
359
- CREATE UNIQUE INDEX idx_apps_route_prefix ON public.apps(route_prefix)
358
+ CREATE INDEX IF NOT EXISTS idx_apps_slug ON public.apps(slug);
359
+ CREATE INDEX IF NOT EXISTS idx_apps_owner ON public.apps(owner_account_id);
360
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_apps_route_prefix ON public.apps(route_prefix)
360
361
  WHERE is_active = true AND route_prefix IS NOT NULL;
361
362
 
362
363
  -- roles
363
- CREATE INDEX idx_roles_slug ON public.roles(slug);
364
+ CREATE INDEX IF NOT EXISTS idx_roles_slug ON public.roles(slug);
364
365
 
365
366
  -- items
366
- CREATE INDEX idx_items_type ON public.items(type_id);
367
- CREATE INDEX idx_items_account ON public.items(account_id);
368
- CREATE INDEX idx_items_active ON public.items(is_active);
369
- CREATE INDEX idx_items_created_at ON public.items(created_at);
367
+ CREATE INDEX IF NOT EXISTS idx_items_type ON public.items(type_id);
368
+ CREATE INDEX IF NOT EXISTS idx_items_account ON public.items(account_id);
369
+ CREATE INDEX IF NOT EXISTS idx_items_active ON public.items(is_active);
370
+ CREATE INDEX IF NOT EXISTS idx_items_created_at ON public.items(created_at);
370
371
 
371
372
  -- threads
372
- CREATE INDEX idx_threads_type ON public.threads(type_id);
373
- CREATE INDEX idx_threads_account ON public.threads(account_id);
374
- CREATE INDEX idx_threads_target ON public.threads(target_type, target_id);
375
- CREATE INDEX idx_threads_active ON public.threads(is_active);
373
+ CREATE INDEX IF NOT EXISTS idx_threads_type ON public.threads(type_id);
374
+ CREATE INDEX IF NOT EXISTS idx_threads_account ON public.threads(account_id);
375
+ CREATE INDEX IF NOT EXISTS idx_threads_target ON public.threads(target_type, target_id);
376
+ CREATE INDEX IF NOT EXISTS idx_threads_active ON public.threads(is_active);
376
377
 
377
378
  -- messages
378
- CREATE INDEX idx_messages_thread ON public.messages(thread_id);
379
- CREATE INDEX idx_messages_type ON public.messages(type_id);
380
- CREATE INDEX idx_messages_person ON public.messages(person_id);
379
+ CREATE INDEX IF NOT EXISTS idx_messages_thread ON public.messages(thread_id);
380
+ CREATE INDEX IF NOT EXISTS idx_messages_type ON public.messages(type_id);
381
+ CREATE INDEX IF NOT EXISTS idx_messages_person ON public.messages(person_id);
381
382
 
382
383
  -- links
383
- CREATE INDEX idx_links_type ON public.links(type_id);
384
- CREATE INDEX idx_links_account ON public.links(account_id);
385
- CREATE INDEX idx_links_source ON public.links(source_type, source_id);
386
- CREATE INDEX idx_links_target ON public.links(target_type, target_id);
384
+ CREATE INDEX IF NOT EXISTS idx_links_type ON public.links(type_id);
385
+ CREATE INDEX IF NOT EXISTS idx_links_account ON public.links(account_id);
386
+ CREATE INDEX IF NOT EXISTS idx_links_source ON public.links(source_type, source_id);
387
+ CREATE INDEX IF NOT EXISTS idx_links_target ON public.links(target_type, target_id);
387
388
 
388
389
  -- attachments
389
- CREATE INDEX idx_attachments_type ON public.attachments(type_id);
390
- CREATE INDEX idx_attachments_account ON public.attachments(account_id);
390
+ CREATE INDEX IF NOT EXISTS idx_attachments_type ON public.attachments(type_id);
391
+ CREATE INDEX IF NOT EXISTS idx_attachments_account ON public.attachments(account_id);
391
392
 
392
393
  -- watchers
393
- CREATE INDEX idx_watchers_type ON public.watchers(type_id);
394
- CREATE INDEX idx_watchers_target ON public.watchers(target_type, target_id);
395
- CREATE INDEX idx_watchers_person ON public.watchers(person_id);
394
+ CREATE INDEX IF NOT EXISTS idx_watchers_type ON public.watchers(type_id);
395
+ CREATE INDEX IF NOT EXISTS idx_watchers_target ON public.watchers(target_type, target_id);
396
+ CREATE INDEX IF NOT EXISTS idx_watchers_person ON public.watchers(person_id);
396
397
 
397
398
  -- link_types
398
- CREATE INDEX idx_link_types_slug ON public.link_types(slug);
399
+ CREATE INDEX IF NOT EXISTS idx_link_types_slug ON public.link_types(slug);
399
400
 
400
401
  -- pipelines
401
- CREATE INDEX idx_pipelines_app ON public.pipelines(app_id);
402
- CREATE INDEX idx_pipelines_account ON public.pipelines(account_id);
402
+ CREATE INDEX IF NOT EXISTS idx_pipelines_app ON public.pipelines(app_id);
403
+ CREATE INDEX IF NOT EXISTS idx_pipelines_account ON public.pipelines(account_id);
403
404
 
404
405
  -- pipeline_executions
405
- CREATE INDEX idx_pe_pipeline ON public.pipeline_executions(pipeline_id);
406
- CREATE INDEX idx_pe_account ON public.pipeline_executions(account_id);
407
- CREATE INDEX idx_pe_status ON public.pipeline_executions(status);
406
+ CREATE INDEX IF NOT EXISTS idx_pe_pipeline ON public.pipeline_executions(pipeline_id);
407
+ CREATE INDEX IF NOT EXISTS idx_pe_account ON public.pipeline_executions(account_id);
408
+ CREATE INDEX IF NOT EXISTS idx_pe_status ON public.pipeline_executions(status);
408
409
 
409
410
  -- triggers
410
- CREATE INDEX idx_triggers_app ON public.triggers(app_id);
411
- CREATE INDEX idx_triggers_account ON public.triggers(account_id);
412
- CREATE INDEX idx_triggers_pipeline ON public.triggers(pipeline_id);
411
+ CREATE INDEX IF NOT EXISTS idx_triggers_app ON public.triggers(app_id);
412
+ CREATE INDEX IF NOT EXISTS idx_triggers_account ON public.triggers(account_id);
413
+ CREATE INDEX IF NOT EXISTS idx_triggers_pipeline ON public.triggers(pipeline_id);
413
414
 
414
415
  -- trigger_executions
415
- CREATE INDEX idx_te_trigger ON public.trigger_executions(trigger_id);
416
+ CREATE INDEX IF NOT EXISTS idx_te_trigger ON public.trigger_executions(trigger_id);
416
417
 
417
418
  -- timers
418
- CREATE INDEX idx_timers_app ON public.timers(app_id);
419
- CREATE INDEX idx_timers_account ON public.timers(account_id);
420
- CREATE INDEX idx_timers_pipeline ON public.timers(pipeline_id);
419
+ CREATE INDEX IF NOT EXISTS idx_timers_app ON public.timers(app_id);
420
+ CREATE INDEX IF NOT EXISTS idx_timers_account ON public.timers(account_id);
421
+ CREATE INDEX IF NOT EXISTS idx_timers_pipeline ON public.timers(pipeline_id);
421
422
 
422
423
  -- ai_agents
423
- CREATE INDEX idx_ai_agents_app ON public.ai_agents(app_id);
424
- CREATE INDEX idx_ai_agents_account ON public.ai_agents(account_id);
424
+ CREATE INDEX IF NOT EXISTS idx_ai_agents_app ON public.ai_agents(app_id);
425
+ CREATE INDEX IF NOT EXISTS idx_ai_agents_account ON public.ai_agents(account_id);
425
426
 
426
427
  -- embeddings
427
- CREATE INDEX idx_embeddings_account ON public.embeddings(account_id);
428
- CREATE INDEX idx_embeddings_model_doc ON public.embeddings(model_id, document_id);
428
+ CREATE INDEX IF NOT EXISTS idx_embeddings_account ON public.embeddings(account_id);
429
+ CREATE INDEX IF NOT EXISTS idx_embeddings_model_doc ON public.embeddings(model_id, document_id);
429
430
 
430
431
  -- integrations
431
- CREATE INDEX idx_integrations_app ON public.integrations(app_id);
432
- CREATE INDEX idx_integrations_account ON public.integrations(account_id);
432
+ CREATE INDEX IF NOT EXISTS idx_integrations_app ON public.integrations(app_id);
433
+ CREATE INDEX IF NOT EXISTS idx_integrations_account ON public.integrations(account_id);
433
434
 
434
435
  -- prompt_configs
435
- CREATE INDEX idx_prompt_configs_slug ON public.prompt_configs(slug);
436
- CREATE INDEX idx_prompt_configs_app ON public.prompt_configs(app_id);
437
- CREATE INDEX idx_prompt_configs_account ON public.prompt_configs(account_id);
436
+ CREATE INDEX IF NOT EXISTS idx_prompt_configs_slug ON public.prompt_configs(slug);
437
+ CREATE INDEX IF NOT EXISTS idx_prompt_configs_app ON public.prompt_configs(app_id);
438
+ CREATE INDEX IF NOT EXISTS idx_prompt_configs_account ON public.prompt_configs(account_id);
438
439
 
439
440
  -- api_keys
440
- CREATE INDEX idx_api_keys_integration ON public.api_keys(integration_id);
441
- CREATE INDEX idx_api_keys_account ON public.api_keys(account_id);
442
- CREATE INDEX idx_api_keys_key_value ON public.api_keys(key_value);
441
+ CREATE INDEX IF NOT EXISTS idx_api_keys_integration ON public.api_keys(integration_id);
442
+ CREATE INDEX IF NOT EXISTS idx_api_keys_account ON public.api_keys(account_id);
443
+ CREATE INDEX IF NOT EXISTS idx_api_keys_key_value ON public.api_keys(key_value);
443
444
 
444
445
  -- api_key_usage_logs
445
- CREATE INDEX idx_akul_api_key ON public.api_key_usage_logs(api_key_id);
446
- CREATE INDEX idx_akul_account ON public.api_key_usage_logs(account_id);
447
- CREATE INDEX idx_akul_created ON public.api_key_usage_logs(created_at);
446
+ CREATE INDEX IF NOT EXISTS idx_akul_api_key ON public.api_key_usage_logs(api_key_id);
447
+ CREATE INDEX IF NOT EXISTS idx_akul_account ON public.api_key_usage_logs(account_id);
448
+ CREATE INDEX IF NOT EXISTS idx_akul_created ON public.api_key_usage_logs(created_at);
448
449
 
449
450
  -- logs
450
- CREATE INDEX idx_logs_account ON public.logs(account_id);
451
- CREATE INDEX idx_logs_level ON public.logs(level);
452
- CREATE INDEX idx_logs_created ON public.logs(created_at);
453
- CREATE INDEX idx_logs_source ON public.logs(source_type, source_id);
451
+ CREATE INDEX IF NOT EXISTS idx_logs_account ON public.logs(account_id);
452
+ CREATE INDEX IF NOT EXISTS idx_logs_level ON public.logs(level);
453
+ CREATE INDEX IF NOT EXISTS idx_logs_created ON public.logs(created_at);
454
+ CREATE INDEX IF NOT EXISTS idx_logs_source ON public.logs(source_type, source_id);
454
455
 
455
456
  -- actions
456
- CREATE INDEX idx_actions_account ON public.actions(account_id);
457
- CREATE INDEX idx_actions_slug ON public.actions(slug);
457
+ CREATE INDEX IF NOT EXISTS idx_actions_account ON public.actions(account_id);
458
+ CREATE INDEX IF NOT EXISTS idx_actions_slug ON public.actions(slug);
458
459
 
459
460
  -- schedules
460
- CREATE INDEX idx_schedules_account ON public.schedules(account_id);
461
- CREATE INDEX idx_schedules_action ON public.schedules(action_id);
462
- CREATE INDEX idx_schedules_next_run ON public.schedules(next_run_at);
461
+ CREATE INDEX IF NOT EXISTS idx_schedules_account ON public.schedules(account_id);
462
+ CREATE INDEX IF NOT EXISTS idx_schedules_action ON public.schedules(action_id);
463
+ CREATE INDEX IF NOT EXISTS idx_schedules_next_run ON public.schedules(next_run_at);
463
464
 
464
465
  -- schedule_executions
465
- CREATE INDEX idx_se_schedule ON public.schedule_executions(schedule_id);
466
- CREATE INDEX idx_se_account ON public.schedule_executions(account_id);
466
+ CREATE INDEX IF NOT EXISTS idx_se_schedule ON public.schedule_executions(schedule_id);
467
+ CREATE INDEX IF NOT EXISTS idx_se_account ON public.schedule_executions(account_id);
467
468
 
468
469
  -- test tables
469
- CREATE INDEX idx_test_results_run_id ON public.test_results(run_id);
470
- CREATE INDEX idx_test_runs_suite ON public.test_runs(suite);
471
- CREATE INDEX idx_test_runs_started_at ON public.test_runs(started_at DESC);
470
+ CREATE INDEX IF NOT EXISTS idx_test_results_run_id ON public.test_results(run_id);
471
+ CREATE INDEX IF NOT EXISTS idx_test_runs_suite ON public.test_runs(suite);
472
+ CREATE INDEX IF NOT EXISTS idx_test_runs_started_at ON public.test_runs(started_at DESC);
472
473
 
473
474
  -- item_progress
474
- CREATE INDEX idx_item_progress_person ON public.item_progress(person_id);
475
- CREATE INDEX idx_item_progress_item ON public.item_progress(item_id);
476
- CREATE INDEX idx_item_progress_account ON public.item_progress(account_id);
477
- CREATE INDEX idx_item_progress_type ON public.item_progress(type_id);
478
- CREATE INDEX idx_item_progress_status ON public.item_progress(status);
479
- CREATE INDEX idx_item_progress_data_gin ON public.item_progress USING gin(data);
480
- CREATE INDEX idx_item_progress_active ON public.item_progress(account_id, is_active) WHERE is_active = true;
475
+ CREATE INDEX IF NOT EXISTS idx_item_progress_person ON public.item_progress(person_id);
476
+ CREATE INDEX IF NOT EXISTS idx_item_progress_item ON public.item_progress(item_id);
477
+ CREATE INDEX IF NOT EXISTS idx_item_progress_account ON public.item_progress(account_id);
478
+ CREATE INDEX IF NOT EXISTS idx_item_progress_type ON public.item_progress(type_id);
479
+ CREATE INDEX IF NOT EXISTS idx_item_progress_status ON public.item_progress(status);
480
+ CREATE INDEX IF NOT EXISTS idx_item_progress_data_gin ON public.item_progress USING gin(data);
481
+ CREATE INDEX IF NOT EXISTS idx_item_progress_active ON public.item_progress(account_id, is_active) WHERE is_active = true;
481
482
 
482
483
  -- webhook_handlers
483
- CREATE INDEX idx_webhook_handlers_name ON public.webhook_handlers(name) WHERE is_active = true AND is_deleted = false;
484
- CREATE INDEX idx_webhook_handlers_account ON public.webhook_handlers(account_id) WHERE is_deleted = false;
485
- CREATE INDEX idx_webhook_handlers_events ON public.webhook_handlers USING GIN(events) WHERE is_active = true;
484
+ CREATE INDEX IF NOT EXISTS idx_webhook_handlers_name ON public.webhook_handlers(name) WHERE is_active = true AND is_deleted = false;
485
+ CREATE INDEX IF NOT EXISTS idx_webhook_handlers_account ON public.webhook_handlers(account_id) WHERE is_deleted = false;
486
+ CREATE INDEX IF NOT EXISTS idx_webhook_handlers_events ON public.webhook_handlers USING GIN(events) WHERE is_active = true;
486
487
 
487
488
  -- ============================================
488
489
  -- ENABLE RLS ON ALL TABLES
@@ -523,31 +524,38 @@ ALTER TABLE public.webhook_handlers ENABLE ROW LEVEL SECURITY;
523
524
  -- ============================================
524
525
 
525
526
  -- accounts
527
+ DROP POLICY IF EXISTS accounts_access ON public.accounts;
526
528
  CREATE POLICY accounts_access ON public.accounts FOR ALL
527
529
  USING (id IN (SELECT public.get_accessible_accounts(public.current_actor_id()))
528
530
  OR public.current_actor_id() IS NULL);
529
531
 
530
532
  -- people
533
+ DROP POLICY IF EXISTS people_access ON public.people;
531
534
  CREATE POLICY people_access ON public.people FOR ALL
532
535
  USING (account_id IN (SELECT public.get_accessible_accounts(public.current_actor_id()))
533
536
  OR public.current_actor_id() IS NULL);
534
537
 
535
538
  -- types
539
+ DROP POLICY IF EXISTS types_access ON public.types;
536
540
  CREATE POLICY types_access ON public.types FOR ALL
537
541
  USING (public.current_actor_id() IS NOT NULL OR true);
538
542
 
539
543
  -- apps
544
+ DROP POLICY IF EXISTS apps_access ON public.apps;
540
545
  CREATE POLICY apps_access ON public.apps FOR ALL
541
546
  USING (is_system = true OR owner_account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
542
547
  OR auth.uid() IS NULL);
543
548
 
544
549
  -- roles
550
+ DROP POLICY IF EXISTS roles_read ON public.roles;
545
551
  CREATE POLICY roles_read ON public.roles FOR SELECT USING (true);
552
+ DROP POLICY IF EXISTS roles_modify ON public.roles;
546
553
  CREATE POLICY roles_modify ON public.roles FOR ALL
547
554
  USING ((is_system = true AND public.person_is_system_admin(public.current_actor_id()))
548
555
  OR is_system = false OR public.current_actor_id() IS NULL);
549
556
 
550
557
  -- items (with platform scope visibility from 012_rls_scope.sql)
558
+ DROP POLICY IF EXISTS items_access ON public.items;
551
559
  CREATE POLICY items_access ON public.items FOR ALL
552
560
  USING (
553
561
  account_id IN (SELECT public.get_accessible_accounts(public.current_actor_id()))
@@ -556,6 +564,7 @@ CREATE POLICY items_access ON public.items FOR ALL
556
564
  );
557
565
 
558
566
  -- threads (with platform scope visibility from 012_rls_scope.sql)
567
+ DROP POLICY IF EXISTS threads_access ON public.threads;
559
568
  CREATE POLICY threads_access ON public.threads FOR ALL
560
569
  USING (
561
570
  account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
@@ -564,6 +573,7 @@ CREATE POLICY threads_access ON public.threads FOR ALL
564
573
  );
565
574
 
566
575
  -- messages
576
+ DROP POLICY IF EXISTS messages_access ON public.messages;
567
577
  CREATE POLICY messages_access ON public.messages FOR ALL
568
578
  USING (EXISTS (SELECT 1 FROM public.threads t
569
579
  WHERE t.id = messages.thread_id
@@ -571,6 +581,7 @@ CREATE POLICY messages_access ON public.messages FOR ALL
571
581
  OR auth.uid() IS NULL);
572
582
 
573
583
  -- links (with platform scope visibility from 012_rls_scope.sql)
584
+ DROP POLICY IF EXISTS links_access ON public.links;
574
585
  CREATE POLICY links_access ON public.links FOR ALL
575
586
  USING (
576
587
  account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
@@ -579,6 +590,7 @@ CREATE POLICY links_access ON public.links FOR ALL
579
590
  );
580
591
 
581
592
  -- attachments (with platform scope visibility from 012_rls_scope.sql)
593
+ DROP POLICY IF EXISTS attachments_access ON public.attachments;
582
594
  CREATE POLICY attachments_access ON public.attachments FOR ALL
583
595
  USING (
584
596
  account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
@@ -587,10 +599,12 @@ CREATE POLICY attachments_access ON public.attachments FOR ALL
587
599
  );
588
600
 
589
601
  -- watchers
602
+ DROP POLICY IF EXISTS watchers_access ON public.watchers;
590
603
  CREATE POLICY watchers_access ON public.watchers FOR ALL
591
604
  USING (auth.uid() IS NOT NULL OR true);
592
605
 
593
606
  -- link_types
607
+ DROP POLICY IF EXISTS link_types_access ON public.link_types;
594
608
  CREATE POLICY link_types_access ON public.link_types FOR ALL
595
609
  USING (app_id IS NULL
596
610
  OR EXISTS (SELECT 1 FROM public.apps a WHERE a.id = link_types.app_id
@@ -598,21 +612,25 @@ CREATE POLICY link_types_access ON public.link_types FOR ALL
598
612
  OR auth.uid() IS NULL);
599
613
 
600
614
  -- pipelines
615
+ DROP POLICY IF EXISTS pipelines_access ON public.pipelines;
601
616
  CREATE POLICY pipelines_access ON public.pipelines FOR ALL
602
617
  USING (account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
603
618
  OR auth.uid() IS NULL);
604
619
 
605
620
  -- pipeline_executions
621
+ DROP POLICY IF EXISTS pe_access ON public.pipeline_executions;
606
622
  CREATE POLICY pe_access ON public.pipeline_executions FOR ALL
607
623
  USING (account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
608
624
  OR auth.uid() IS NULL);
609
625
 
610
626
  -- triggers
627
+ DROP POLICY IF EXISTS triggers_access ON public.triggers;
611
628
  CREATE POLICY triggers_access ON public.triggers FOR ALL
612
629
  USING (account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
613
630
  OR auth.uid() IS NULL);
614
631
 
615
632
  -- trigger_executions
633
+ DROP POLICY IF EXISTS te_access ON public.trigger_executions;
616
634
  CREATE POLICY te_access ON public.trigger_executions FOR ALL
617
635
  USING (EXISTS (SELECT 1 FROM public.triggers t
618
636
  WHERE t.id = trigger_executions.trigger_id
@@ -620,26 +638,31 @@ CREATE POLICY te_access ON public.trigger_executions FOR ALL
620
638
  OR auth.uid() IS NULL);
621
639
 
622
640
  -- timers
641
+ DROP POLICY IF EXISTS timers_access ON public.timers;
623
642
  CREATE POLICY timers_access ON public.timers FOR ALL
624
643
  USING (account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
625
644
  OR auth.uid() IS NULL);
626
645
 
627
646
  -- ai_agents
647
+ DROP POLICY IF EXISTS ai_agents_access ON public.ai_agents;
628
648
  CREATE POLICY ai_agents_access ON public.ai_agents FOR ALL
629
649
  USING (account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
630
650
  OR auth.uid() IS NULL);
631
651
 
632
652
  -- embeddings
653
+ DROP POLICY IF EXISTS embeddings_access ON public.embeddings;
633
654
  CREATE POLICY embeddings_access ON public.embeddings FOR ALL
634
655
  USING (account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
635
656
  OR auth.uid() IS NULL);
636
657
 
637
658
  -- integrations
659
+ DROP POLICY IF EXISTS integrations_access ON public.integrations;
638
660
  CREATE POLICY integrations_access ON public.integrations FOR ALL
639
661
  USING (account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
640
662
  OR auth.uid() IS NULL);
641
663
 
642
664
  -- prompt_configs
665
+ DROP POLICY IF EXISTS prompt_configs_access ON public.prompt_configs;
643
666
  CREATE POLICY prompt_configs_access ON public.prompt_configs FOR ALL
644
667
  USING ((app_id IS NULL OR EXISTS (SELECT 1 FROM public.apps a
645
668
  WHERE a.id = prompt_configs.app_id
@@ -647,36 +670,43 @@ CREATE POLICY prompt_configs_access ON public.prompt_configs FOR ALL
647
670
  OR auth.uid() IS NULL);
648
671
 
649
672
  -- api_keys
673
+ DROP POLICY IF EXISTS api_keys_access ON public.api_keys;
650
674
  CREATE POLICY api_keys_access ON public.api_keys FOR ALL
651
675
  USING (account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
652
676
  OR id = auth.uid() OR auth.uid() IS NULL);
653
677
 
654
678
  -- api_key_usage_logs
679
+ DROP POLICY IF EXISTS akul_access ON public.api_key_usage_logs;
655
680
  CREATE POLICY akul_access ON public.api_key_usage_logs FOR ALL
656
681
  USING (account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
657
682
  OR auth.uid() IS NULL);
658
683
 
659
684
  -- logs
685
+ DROP POLICY IF EXISTS logs_access ON public.logs;
660
686
  CREATE POLICY logs_access ON public.logs FOR ALL
661
687
  USING (account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
662
688
  OR auth.uid() IS NULL);
663
689
 
664
690
  -- actions
691
+ DROP POLICY IF EXISTS actions_access ON public.actions;
665
692
  CREATE POLICY actions_access ON public.actions FOR ALL
666
693
  USING (account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
667
694
  OR auth.uid() IS NULL);
668
695
 
669
696
  -- schedules
697
+ DROP POLICY IF EXISTS schedules_access ON public.schedules;
670
698
  CREATE POLICY schedules_access ON public.schedules FOR ALL
671
699
  USING (account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
672
700
  OR auth.uid() IS NULL);
673
701
 
674
702
  -- schedule_executions
703
+ DROP POLICY IF EXISTS se_access ON public.schedule_executions;
675
704
  CREATE POLICY se_access ON public.schedule_executions FOR ALL
676
705
  USING (account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
677
706
  OR auth.uid() IS NULL);
678
707
 
679
708
  -- item_progress
709
+ DROP POLICY IF EXISTS item_progress_access ON public.item_progress;
680
710
  CREATE POLICY item_progress_access ON public.item_progress FOR ALL
681
711
  USING (
682
712
  account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
@@ -684,6 +714,7 @@ CREATE POLICY item_progress_access ON public.item_progress FOR ALL
684
714
  );
685
715
 
686
716
  -- webhook_handlers
717
+ DROP POLICY IF EXISTS webhook_handlers_system_admin ON public.webhook_handlers;
687
718
  CREATE POLICY webhook_handlers_system_admin ON public.webhook_handlers
688
719
  FOR ALL
689
720
  TO authenticated
@@ -694,6 +725,7 @@ CREATE POLICY webhook_handlers_system_admin ON public.webhook_handlers
694
725
  (SELECT raw_user_meta_data->>'role' FROM auth.users WHERE id = auth.uid()) = 'system_admin'
695
726
  );
696
727
 
728
+ DROP POLICY IF EXISTS webhook_handlers_service_role ON public.webhook_handlers;
697
729
  CREATE POLICY webhook_handlers_service_role ON public.webhook_handlers
698
730
  FOR ALL
699
731
  TO service_role
@@ -18,7 +18,8 @@ VALUES (
18
18
  true,
19
19
  NULL,
20
20
  NULL
21
- );
21
+ )
22
+ ON CONFLICT (app_id, slug) DO NOTHING;
22
23
 
23
24
  -- NOTE: System account inserted after types are seeded (section 5) to satisfy FK constraint.
24
25
 
@@ -39,7 +40,8 @@ VALUES (
39
40
  true,
40
41
  'none',
41
42
  NULL
42
- );
43
+ )
44
+ ON CONFLICT (slug) DO NOTHING;
43
45
 
44
46
  -- ============================================
45
47
  -- 4. ADMIN APP
@@ -127,6 +129,7 @@ BEGIN
127
129
  'pack',
128
130
  true
129
131
  );
132
+ ON CONFLICT (app_id, kind, slug) DO NOTHING;
130
133
 
131
134
  -- 2. MASTER SYSTEM ACCOUNT (inserted here to satisfy FK on type_id)
132
135
  INSERT INTO public.accounts (id, slug, display_name, description, data, is_active, design_schema, validation_schema, type_id)
@@ -187,6 +190,7 @@ BEGIN
187
190
  'pack',
188
191
  true
189
192
  );
193
+ ON CONFLICT (app_id, kind, slug) DO NOTHING;
190
194
 
191
195
  -- 5.3 items type
192
196
  INSERT INTO public.types (id, app_id, kind, slug, name, description, design_schema, validation_schema, ownership, is_active)
@@ -229,6 +233,7 @@ BEGIN
229
233
  'pack',
230
234
  true
231
235
  );
236
+ ON CONFLICT (app_id, kind, slug) DO NOTHING;
232
237
 
233
238
  -- 5.4 threads type
234
239
  INSERT INTO public.types (id, app_id, kind, slug, name, description, design_schema, validation_schema, ownership, is_active)
@@ -273,6 +278,7 @@ BEGIN
273
278
  'pack',
274
279
  true
275
280
  );
281
+ ON CONFLICT (app_id, kind, slug) DO NOTHING;
276
282
 
277
283
  -- 5.5 messages type
278
284
  INSERT INTO public.types (id, app_id, kind, slug, name, description, design_schema, validation_schema, ownership, is_active)
@@ -316,6 +322,7 @@ BEGIN
316
322
  'pack',
317
323
  true
318
324
  );
325
+ ON CONFLICT (app_id, kind, slug) DO NOTHING;
319
326
 
320
327
  -- 5.6 links type
321
328
  INSERT INTO public.types (id, app_id, kind, slug, name, description, design_schema, validation_schema, ownership, is_active)
@@ -359,6 +366,7 @@ BEGIN
359
366
  'pack',
360
367
  true
361
368
  );
369
+ ON CONFLICT (app_id, kind, slug) DO NOTHING;
362
370
 
363
371
  -- 5.7 attachments type
364
372
  INSERT INTO public.types (id, app_id, kind, slug, name, description, design_schema, validation_schema, ownership, is_active)
@@ -403,6 +411,7 @@ BEGIN
403
411
  'pack',
404
412
  true
405
413
  );
414
+ ON CONFLICT (app_id, kind, slug) DO NOTHING;
406
415
 
407
416
  -- 5.8 watchers type
408
417
  INSERT INTO public.types (id, app_id, kind, slug, name, description, design_schema, validation_schema, ownership, is_active)
@@ -447,6 +456,7 @@ BEGIN
447
456
  'pack',
448
457
  true
449
458
  );
459
+ ON CONFLICT (app_id, kind, slug) DO NOTHING;
450
460
 
451
461
  -- Update the spine-system account with the actual accounts type_id
452
462
  UPDATE public.accounts
@@ -0,0 +1,21 @@
1
+ -- Patch: fix uuid = text operator error in current_actor_id()
2
+ -- The previous version cast v_auth_id to text before comparing with auth_uid (uuid),
3
+ -- which causes PostgreSQL error 42883. This re-creates the function with matching types.
4
+
5
+ CREATE OR REPLACE FUNCTION public.current_actor_id()
6
+ RETURNS uuid AS $$
7
+ DECLARE
8
+ v_auth_id UUID;
9
+ v_person_id UUID;
10
+ BEGIN
11
+ v_auth_id := auth.uid();
12
+
13
+ -- Look up internal person ID from auth_uid
14
+ SELECT p.id INTO v_person_id
15
+ FROM public.people p
16
+ WHERE p.auth_uid = v_auth_id AND p.is_active = true
17
+ LIMIT 1;
18
+
19
+ RETURN v_person_id;
20
+ END;
21
+ $$ LANGUAGE plpgsql SECURITY DEFINER STABLE;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spine-framework",
3
- "version": "0.3.47",
3
+ "version": "0.3.49",
4
4
  "description": "Multi-tenant, modular application platform for modern SaaS systems",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,6 +20,7 @@
20
20
  ".framework/migrations/003_system_data.sql",
21
21
  ".framework/migrations/004_enhancements.sql",
22
22
  ".framework/migrations/005_auth_user_trigger.sql",
23
+ ".framework/migrations/006_fix_current_actor_id.sql",
23
24
  ".framework/public/",
24
25
  ".framework/scripts/",
25
26
  ".framework/seeds/",