@taodigital/database 0.0.0-stage → 1.2.1

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 (173) hide show
  1. package/README.md +21 -2
  2. package/dist/index.d.ts +58 -0
  3. package/dist/index.js +209 -0
  4. package/package.json +29 -4
  5. package/prisma/migrations/0000_init/migration.sql +220 -0
  6. package/prisma/migrations/0001_tenant_isolation/migration.sql +43 -0
  7. package/prisma/migrations/0002_app_runtime_role/migration.sql +14 -0
  8. package/prisma/migrations/0003_skill_taxonomy/migration.sql +30 -0
  9. package/prisma/migrations/0004_extended_models/migration.sql +215 -0
  10. package/prisma/migrations/0005_enhanced_test_artifacts/migration.sql +24 -0
  11. package/prisma/migrations/0006_tenant_settings/migration.sql +28 -0
  12. package/prisma/migrations/0007_project_slug/migration.sql +11 -0
  13. package/prisma/migrations/0008_project_crawl_config/migration.sql +3 -0
  14. package/prisma/migrations/0009_tenant_slug/migration.sql +11 -0
  15. package/prisma/migrations/0010_user_password/migration.sql +3 -0
  16. package/prisma/migrations/0011_oauth_connections/migration.sql +78 -0
  17. package/prisma/migrations/0012_agent_webhooks/migration.sql +46 -0
  18. package/prisma/migrations/0013_project_artifacts/migration.sql +27 -0
  19. package/prisma/migrations/0014_knowledge_chunks/migration.sql +37 -0
  20. package/prisma/migrations/20260626022056_platform_job_migration/migration.sql +44 -0
  21. package/prisma/migrations/20260626053339_kag_schema_update/migration.sql +27 -0
  22. package/prisma/migrations/20260626053340_restore_bm25_index/migration.sql +13 -0
  23. package/prisma/migrations/20260626063704_token_optimisation/migration.sql +86 -0
  24. package/prisma/migrations/20260626080000_job_name/migration.sql +2 -0
  25. package/prisma/migrations/20260626090000_rls_missing_tables/migration.sql +39 -0
  26. package/prisma/migrations/20260626100000_token_budget/migration.sql +21 -0
  27. package/prisma/migrations/20260626110000_screen_groups/migration.sql +42 -0
  28. package/prisma/migrations/20260626120000_artifact_intelligence/migration.sql +5 -0
  29. package/prisma/migrations/20260626130000_notifications/migration.sql +34 -0
  30. package/prisma/migrations/20260627100000_element_viewport/migration.sql +2 -0
  31. package/prisma/migrations/20260627100000_screenshot_cloud_url/migration.sql +2 -0
  32. package/prisma/migrations/20260627110000_screenshot_index/migration.sql +2 -0
  33. package/prisma/migrations/20260627140000_artifact_storage_key/migration.sql +3 -0
  34. package/prisma/migrations/20260627150000_screen_lifecycle/migration.sql +5 -0
  35. package/prisma/migrations/20260627160000_native_app_support/migration.sql +41 -0
  36. package/prisma/migrations/20260627200000_rls_missing_tables/migration.sql +57 -0
  37. package/prisma/migrations/20260627210000_llmcalllog_tasktype/migration.sql +1 -0
  38. package/prisma/migrations/20260628100000_element_enrichment/migration.sql +7 -0
  39. package/prisma/migrations/20260628110000_element_hash/migration.sql +6 -0
  40. package/prisma/migrations/20260628120000_screen_diff/migration.sql +20 -0
  41. package/prisma/migrations/20260628130000_probe_observations/migration.sql +19 -0
  42. package/prisma/migrations/20260629100000_visual_baseline_masking/migration.sql +1 -0
  43. package/prisma/migrations/20260629110000_testcase_viewports/migration.sql +1 -0
  44. package/prisma/migrations/20260630100000_visual_baseline_element_snapshot/migration.sql +1 -0
  45. package/prisma/migrations/20260630110000_git_branched_baselines/migration.sql +11 -0
  46. package/prisma/migrations/20260630120000_testcase_viewport_constraint/migration.sql +1 -0
  47. package/prisma/migrations/20260630130000_test_case_runs/migration.sql +22 -0
  48. package/prisma/migrations/20260630140000_perf_env_comments/migration.sql +68 -0
  49. package/prisma/migrations/20260630150000_testcase_gen_at/migration.sql +2 -0
  50. package/prisma/migrations/20260630160000_rls_catchup_late_tables/migration.sql +41 -0
  51. package/prisma/migrations/20260701100000_skill_file_diff_approval/migration.sql +9 -0
  52. package/prisma/migrations/20260701200000_workspace_connector/migration.sql +20 -0
  53. package/prisma/migrations/20260701220000_screen_a11y_security/migration.sql +2 -0
  54. package/prisma/migrations/20260702100000_ci_threshold_root_cause/migration.sql +2 -0
  55. package/prisma/migrations/20260702120000_medium_priority_gaps/migration.sql +28 -0
  56. package/prisma/migrations/20260702140000_testcase_v2_technique/migration.sql +7 -0
  57. package/prisma/migrations/20260702150000_add_app_field/migration.sql +40 -0
  58. package/prisma/migrations/20260702160000_add_requirement_model/migration.sql +62 -0
  59. package/prisma/migrations/20260702180000_add_invitation_model/migration.sql +53 -0
  60. package/prisma/migrations/20260702190000_suite_hierarchy_artifact_trace/migration.sql +28 -0
  61. package/prisma/migrations/20260702200000_project_member_access/migration.sql +31 -0
  62. package/prisma/migrations/20260703000000_add_system_config/migration.sql +11 -0
  63. package/prisma/migrations/20260703000000_custom_roles/migration.sql +29 -0
  64. package/prisma/migrations/20260703100000_platform_admin/migration.sql +11 -0
  65. package/prisma/migrations/20260703110000_add_extension_models/migration.sql +106 -0
  66. package/prisma/migrations/20260703120000_project_restricted_column/migration.sql +1 -0
  67. package/prisma/migrations/20260703200000_system_config_category_index/migration.sql +1 -0
  68. package/prisma/migrations/20260703210000_workspace_layer/migration.sql +24 -0
  69. package/prisma/migrations/20260703230000_workspace_agent_tokens/migration.sql +2 -0
  70. package/prisma/migrations/20260704000000_requirement_rls_policy/migration.sql +35 -0
  71. package/prisma/migrations/20260704100000_fix_oauth_auditlog_cascade/migration.sql +9 -0
  72. package/prisma/migrations/20260704110000_skillfile_approval_status/migration.sql +2 -0
  73. package/prisma/migrations/20260704120000_scenario_suspect_workspace/migration.sql +35 -0
  74. package/prisma/migrations/20260704130000_testcase_traceto_suspect_indexes/migration.sql +5 -0
  75. package/prisma/migrations/20260704140000_workspace_rls/migration.sql +19 -0
  76. package/prisma/migrations/20260704150000_project_cascade_deletes/migration.sql +125 -0
  77. package/prisma/migrations/20260705100000_platform_job_worker_id/migration.sql +12 -0
  78. package/prisma/migrations/20260705110000_screen_discovery_method/migration.sql +7 -0
  79. package/prisma/migrations/20260705120000_webhook_delivery/migration.sql +32 -0
  80. package/prisma/migrations/20260705130000_tenant_feature_flags/migration.sql +23 -0
  81. package/prisma/migrations/20260705140000_auth_sessions/migration.sql +30 -0
  82. package/prisma/migrations/20260705150000_fix_rls_wrong_session_var/migration.sql +51 -0
  83. package/prisma/migrations/20260705160000_testcase_flaky_quarantine/migration.sql +14 -0
  84. package/prisma/migrations/20260705170000_testcaserun_browser/migration.sql +6 -0
  85. package/prisma/migrations/20260705180000_allow_custom_roles/migration.sql +22 -0
  86. package/prisma/migrations/20260706050000_screen_markdown_extraction/migration.sql +8 -0
  87. package/prisma/migrations/20260706090000_add_lighthouse_to_performance_snapshot/migration.sql +4 -0
  88. package/prisma/migrations/20260706100000_add_lighthouse_bp_and_job_index/migration.sql +5 -0
  89. package/prisma/migrations/20260706110000_add_visual_diff_ai_summary/migration.sql +2 -0
  90. package/prisma/migrations/20260707120000_script_bundles/migration.sql +25 -0
  91. package/prisma/migrations/20260707130000_requirement_source_section/migration.sql +4 -0
  92. package/prisma/migrations/20260707140000_structured_skill_metadata/migration.sql +8 -0
  93. package/prisma/migrations/20260707150000_crawler_platform_primitives/migration.sql +112 -0
  94. package/prisma/migrations/20260707160000_screen_group_versioning/migration.sql +9 -0
  95. package/prisma/migrations/20260707170000_add_mfa_fields/migration.sql +24 -0
  96. package/prisma/migrations/20260707180000_add_missing_tables/migration.sql +158 -0
  97. package/prisma/migrations/20260707190000_add_missing_columns/migration.sql +15 -0
  98. package/prisma/migrations/20260707200000_add_plan_sessions/migration.sql +1 -0
  99. package/prisma/migrations/20260708100000_missing_columns_comprehensive/migration.sql +15 -0
  100. package/prisma/migrations/20260708200000_testcase_dataset_column/migration.sql +16 -0
  101. package/prisma/migrations/20260709100000_add_filename_to_native_app_build/migration.sql +1 -0
  102. package/prisma/migrations/20260709200000_cloud_url_visual_artifacts/migration.sql +6 -0
  103. package/prisma/migrations/20260714200000_add_cached_scenarios_to_artifact/migration.sql +4 -0
  104. package/prisma/migrations/20260715200000_screen_markdown_content/migration.sql +2 -0
  105. package/prisma/migrations/20260716100000_github_repo_coverage_tables/migration.sql +114 -0
  106. package/prisma/migrations/20260716200000_github_pr_script_validation/migration.sql +48 -0
  107. package/prisma/migrations/20260716210000_s3_content_keys/migration.sql +20 -0
  108. package/prisma/migrations/20260717220000_ide_push_token/migration.sql +21 -0
  109. package/prisma/migrations/20260717230000_ide_push_script_ids/migration.sql +2 -0
  110. package/prisma/migrations/20260730040000_add_step_evidence_and_validation/migration.sql +93 -0
  111. package/prisma/migrations/20260803000000_add_tenantId_to_test_data_row/migration.sql +40 -0
  112. package/prisma/migrations/20260804000000_add_project_restricted/migration.sql +6 -0
  113. package/prisma/migrations/20260804000000_add_user_avatar_url/migration.sql +4 -0
  114. package/prisma/migrations/20260805000000_add_auditlog_ipaddress/migration.sql +2 -0
  115. package/prisma/migrations/20260807000000_add_test_data_classification/migration.sql +18 -0
  116. package/prisma/migrations/20260807000001_add_test_case_notes/migration.sql +5 -0
  117. package/prisma/migrations/20260807000002_add_visual_baseline_browser_structured_steps/migration.sql +11 -0
  118. package/prisma/migrations/20260807100000_add_tenant_plan_config/migration.sql +14 -0
  119. package/prisma/migrations/20260808000000_agent_token_missing_columns/migration.sql +22 -0
  120. package/prisma/migrations/20260808000001_visual_diff_image_and_threshold/migration.sql +8 -0
  121. package/prisma/migrations/20260808000002_ssoconfig_email_domains/migration.sql +7 -0
  122. package/prisma/migrations/20260808000003_project_status_description_updatedat/migration.sql +7 -0
  123. package/prisma/migrations/20260814000000_workspace_require_mfa/migration.sql +2 -0
  124. package/prisma/migrations/20260814000001_add_screenshot_dpr/migration.sql +2 -0
  125. package/prisma/migrations/20260818000000_add_content_hash_to_test_case/migration.sql +6 -0
  126. package/prisma/migrations/20260818000001_add_grounding_hash_to_test_case/migration.sql +3 -0
  127. package/prisma/migrations/20260819000000_coverage_mapping_cross_encoder_fields/migration.sql +3 -0
  128. package/prisma/migrations/20260820000000_add_locators_json_to_test_case/migration.sql +6 -0
  129. package/prisma/migrations/20260820000001_add_test_case_version/migration.sql +27 -0
  130. package/prisma/migrations/20260826000000_add_capture_step_screenshots/migration.sql +1 -0
  131. package/prisma/migrations/20260903000000_add_platform_job_dlq/migration.sql +10 -0
  132. package/prisma/migrations/20260903000002_add_versioning_tables/migration.sql +38 -0
  133. package/prisma/migrations/20260903000003_add_learning_models/migration.sql +71 -0
  134. package/prisma/migrations/20260903000004_add_workspace_agent_prompts/migration.sql +33 -0
  135. package/prisma/migrations/20260903000005_add_config_history/migration.sql +70 -0
  136. package/prisma/migrations/20260903000006_add_skill_file_config/migration.sql +24 -0
  137. package/prisma/migrations/20260904000000_add_agent_prompt_platform_table/migration.sql +23 -0
  138. package/prisma/migrations/20260904180424_rbac_role_configs_admin_roles/migration.sql +606 -0
  139. package/prisma/migrations/20260907000000_fix_testcase_timestamps/migration.sql +13 -0
  140. package/prisma/migrations/20260907000001_add_artifact_imported_suite_id/migration.sql +1 -0
  141. package/prisma/migrations/20260907000002_add_user_login_tracking/migration.sql +9 -0
  142. package/prisma/migrations/20260907000003_add_artifact_import_tracking/migration.sql +9 -0
  143. package/prisma/migrations/20260907000004_add_artifact_version_chain/migration.sql +18 -0
  144. package/prisma/migrations/20260908000000_add_project_vsa_enabled/migration.sql +2 -0
  145. package/prisma/migrations/20260908000001_rbac_migration_catchup/migration.sql +314 -0
  146. package/prisma/migrations/20260908000002_fix_role_configs_empty_permissions/migration.sql +22 -0
  147. package/prisma/migrations/20260908000003_sync_builtin_role_permissions/migration.sql +38 -0
  148. package/prisma/migrations/20260909000000_add_filepath_to_testscript/migration.sql +5 -0
  149. package/prisma/migrations/20260909000001_add_alignment_result_table/migration.sql +31 -0
  150. package/prisma/migrations/20260910000000_add_admin_mfa/migration.sql +35 -0
  151. package/prisma/migrations/20260910000000_add_dom_snapshot_cloud_url/migration.sql +2 -0
  152. package/prisma/migrations/20260910000001_add_requires_auth_to_screen/migration.sql +1 -0
  153. package/prisma/migrations/20260910000002_add_screen_requires_auth/migration.sql +2 -0
  154. package/prisma/migrations/20260910000003_add_admin_auth_sessions/migration.sql +17 -0
  155. package/prisma/migrations/20260916000001_add_admin_last_login/migration.sql +1 -0
  156. package/prisma/migrations/20260916000002_add_admin_failed_login/migration.sql +2 -0
  157. package/prisma/migrations/20260916000003_seed_system_tenant/migration.sql +6 -0
  158. package/prisma/migrations/20260918000000_fix_permissions_comprehensive/migration.sql +63 -0
  159. package/prisma/migrations/20260924000000_add_screen_role_access/migration.sql +2 -0
  160. package/prisma/migrations/20260928000000_add_navigation_graph/migration.sql +41 -0
  161. package/prisma/migrations/20260929000000_artifact_content_hash_suite_unique/migration.sql +25 -0
  162. package/prisma/migrations/20260929000000_navigation_graph_unique_constraint/migration.sql +5 -0
  163. package/prisma/migrations/20260929000001_add_membership_status/migration.sql +7 -0
  164. package/prisma/migrations/20260929000002_add_sso_config_scim_token_hash/migration.sql +8 -0
  165. package/prisma/migrations/20261005000001_jobs_workers_hardening/migration.sql +63 -0
  166. package/prisma/migrations/20261009000001_add_suite_category/migration.sql +2 -0
  167. package/prisma/migrations/20261009000002_add_testcase_approved_fields/migration.sql +3 -0
  168. package/prisma/migrations/20261012_auto_version_history/migration.sql +111 -0
  169. package/prisma/migrations/20261015000001_traceability_alignment_gaps/migration.sql +11 -0
  170. package/prisma/migrations/20261028000001_context_intelligence_p1_p7/migration.sql +76 -0
  171. package/prisma/migrations/migration_lock.toml +3 -0
  172. package/prisma/schema.prisma +2901 -0
  173. package/prisma/seed.ts +1262 -0
@@ -0,0 +1,62 @@
1
+ -- Migration: add_requirement_model
2
+ -- Additive only — no existing tables modified.
3
+ -- Every TestCase.traceTo references a reqId from this table.
4
+
5
+ CREATE TABLE IF NOT EXISTS "Requirement" (
6
+ "id" TEXT NOT NULL,
7
+ "tenantId" TEXT NOT NULL,
8
+ "projectId" TEXT NOT NULL,
9
+ "reqId" TEXT NOT NULL,
10
+ "title" TEXT NOT NULL,
11
+ "actor" TEXT,
12
+ "type" TEXT NOT NULL DEFAULT 'functional',
13
+ "priority" TEXT NOT NULL DEFAULT 'HIGH',
14
+ "acceptanceCriteria" JSONB NOT NULL DEFAULT '[]',
15
+ "businessRules" JSONB NOT NULL DEFAULT '[]',
16
+ "relatedScreenHints" JSONB NOT NULL DEFAULT '[]',
17
+ "derived" BOOLEAN NOT NULL DEFAULT false,
18
+ "status" TEXT NOT NULL DEFAULT 'pending',
19
+ "source" TEXT NOT NULL DEFAULT 'AI_GENERATED',
20
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
21
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
22
+
23
+ CONSTRAINT "Requirement_pkey" PRIMARY KEY ("id")
24
+ );
25
+
26
+ -- Unique constraint: one reqId per project per tenant
27
+ CREATE UNIQUE INDEX IF NOT EXISTS "Requirement_tenantId_projectId_reqId_key"
28
+ ON "Requirement"("tenantId", "projectId", "reqId");
29
+
30
+ -- General project lookup
31
+ CREATE INDEX IF NOT EXISTS "Requirement_tenantId_projectId_idx"
32
+ ON "Requirement"("tenantId", "projectId");
33
+
34
+ -- Priority-based filtering
35
+ CREATE INDEX IF NOT EXISTS "Requirement_tenantId_projectId_priority_idx"
36
+ ON "Requirement"("tenantId", "projectId", "priority");
37
+
38
+ -- Status-based filtering (covered/untested/unbuilt)
39
+ CREATE INDEX IF NOT EXISTS "Requirement_tenantId_projectId_status_idx"
40
+ ON "Requirement"("tenantId", "projectId", "status");
41
+
42
+ -- FK to Project with cascade delete
43
+ ALTER TABLE "Requirement"
44
+ ADD CONSTRAINT "Requirement_projectId_fkey"
45
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
46
+
47
+ -- Enable RLS (consistent with all other tenant tables)
48
+ ALTER TABLE "Requirement" ENABLE ROW LEVEL SECURITY;
49
+
50
+ -- Updated-at trigger (reuse existing function if present)
51
+ CREATE OR REPLACE FUNCTION update_updated_at_column()
52
+ RETURNS TRIGGER AS $$
53
+ BEGIN
54
+ NEW."updatedAt" = CURRENT_TIMESTAMP;
55
+ RETURN NEW;
56
+ END;
57
+ $$ language 'plpgsql';
58
+
59
+ DROP TRIGGER IF EXISTS "Requirement_updatedAt" ON "Requirement";
60
+ CREATE TRIGGER "Requirement_updatedAt"
61
+ BEFORE UPDATE ON "Requirement"
62
+ FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
@@ -0,0 +1,53 @@
1
+ -- Migration: add_invitation_model
2
+ -- Pending workspace invitations for users who have not registered yet.
3
+ -- Additive only — no existing tables modified except a NOT VALID role check on Membership.
4
+
5
+ CREATE TABLE IF NOT EXISTS "Invitation" (
6
+ "id" TEXT NOT NULL,
7
+ "tenantId" TEXT NOT NULL,
8
+ "email" TEXT NOT NULL,
9
+ "role" TEXT NOT NULL,
10
+ "tokenHash" TEXT NOT NULL,
11
+ "invitedBy" TEXT,
12
+ "status" TEXT NOT NULL DEFAULT 'PENDING',
13
+ "expiresAt" TIMESTAMP(3) NOT NULL,
14
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
15
+ "acceptedAt" TIMESTAMP(3),
16
+
17
+ CONSTRAINT "Invitation_pkey" PRIMARY KEY ("id"),
18
+ CONSTRAINT "Invitation_role_check" CHECK ("role" IN ('OWNER','ADMIN','QA_LEAD','TESTER','DEVELOPER','VIEWER')),
19
+ CONSTRAINT "Invitation_status_check" CHECK ("status" IN ('PENDING','ACCEPTED','REVOKED','EXPIRED'))
20
+ );
21
+
22
+ CREATE UNIQUE INDEX IF NOT EXISTS "Invitation_tokenHash_key" ON "Invitation"("tokenHash");
23
+ CREATE UNIQUE INDEX IF NOT EXISTS "Invitation_tenantId_email_key" ON "Invitation"("tenantId", "email");
24
+ CREATE INDEX IF NOT EXISTS "Invitation_tenantId_status_idx" ON "Invitation"("tenantId", "status");
25
+
26
+ ALTER TABLE "Invitation"
27
+ ADD CONSTRAINT "Invitation_tenantId_fkey"
28
+ FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
29
+
30
+ -- RLS (consistent with all other tenant tables)
31
+ ALTER TABLE "Invitation" ENABLE ROW LEVEL SECURITY;
32
+
33
+ DO $$ BEGIN
34
+ IF NOT EXISTS (
35
+ SELECT 1 FROM pg_policies WHERE tablename = 'Invitation' AND policyname = 'tenant_isolation'
36
+ ) THEN
37
+ EXECUTE format(
38
+ 'CREATE POLICY tenant_isolation ON "Invitation" USING ("tenantId" = current_setting(''app.current_tenant'', true))'
39
+ );
40
+ END IF;
41
+ END $$;
42
+
43
+ -- Role integrity on Membership — NOT VALID so legacy rows are untouched,
44
+ -- but every new write must use a known role.
45
+ DO $$ BEGIN
46
+ IF NOT EXISTS (
47
+ SELECT 1 FROM pg_constraint WHERE conname = 'Membership_role_check'
48
+ ) THEN
49
+ ALTER TABLE "Membership"
50
+ ADD CONSTRAINT "Membership_role_check"
51
+ CHECK ("role" IN ('OWNER','ADMIN','QA_LEAD','TESTER','DEVELOPER','VIEWER')) NOT VALID;
52
+ END IF;
53
+ END $$;
@@ -0,0 +1,28 @@
1
+ -- Migration: suite_hierarchy_artifact_trace
2
+ -- Additive only.
3
+ -- 1. TestSuite.parentId — child suites created per Section column on import.
4
+ -- 2. TestCase.artifactId — traceability from imported test cases back to the source artifact.
5
+
6
+ ALTER TABLE "TestSuite" ADD COLUMN IF NOT EXISTS "parentId" TEXT;
7
+
8
+ DO $$ BEGIN
9
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'TestSuite_parentId_fkey') THEN
10
+ ALTER TABLE "TestSuite"
11
+ ADD CONSTRAINT "TestSuite_parentId_fkey"
12
+ FOREIGN KEY ("parentId") REFERENCES "TestSuite"("id") ON DELETE CASCADE ON UPDATE CASCADE;
13
+ END IF;
14
+ END $$;
15
+
16
+ CREATE INDEX IF NOT EXISTS "TestSuite_parentId_idx" ON "TestSuite"("parentId");
17
+
18
+ ALTER TABLE "TestCase" ADD COLUMN IF NOT EXISTS "artifactId" TEXT;
19
+
20
+ DO $$ BEGIN
21
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'TestCase_artifactId_fkey') THEN
22
+ ALTER TABLE "TestCase"
23
+ ADD CONSTRAINT "TestCase_artifactId_fkey"
24
+ FOREIGN KEY ("artifactId") REFERENCES "ProjectArtifact"("id") ON DELETE SET NULL ON UPDATE CASCADE;
25
+ END IF;
26
+ END $$;
27
+
28
+ CREATE INDEX IF NOT EXISTS "TestCase_artifactId_idx" ON "TestCase"("artifactId");
@@ -0,0 +1,31 @@
1
+ -- Migration: project_member_access
2
+ -- Optional per-project access list. No rows = project open to all tenant members.
3
+
4
+ CREATE TABLE IF NOT EXISTS "ProjectMember" (
5
+ "id" TEXT NOT NULL,
6
+ "tenantId" TEXT NOT NULL,
7
+ "projectId" TEXT NOT NULL,
8
+ "userId" TEXT NOT NULL,
9
+
10
+ CONSTRAINT "ProjectMember_pkey" PRIMARY KEY ("id")
11
+ );
12
+
13
+ CREATE UNIQUE INDEX IF NOT EXISTS "ProjectMember_projectId_userId_key" ON "ProjectMember"("projectId", "userId");
14
+ CREATE INDEX IF NOT EXISTS "ProjectMember_tenantId_userId_idx" ON "ProjectMember"("tenantId", "userId");
15
+
16
+ ALTER TABLE "ProjectMember"
17
+ ADD CONSTRAINT "ProjectMember_projectId_fkey"
18
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
19
+
20
+ -- RLS (consistent with all other tenant tables)
21
+ ALTER TABLE "ProjectMember" ENABLE ROW LEVEL SECURITY;
22
+
23
+ DO $$ BEGIN
24
+ IF NOT EXISTS (
25
+ SELECT 1 FROM pg_policies WHERE tablename = 'ProjectMember' AND policyname = 'tenant_isolation'
26
+ ) THEN
27
+ EXECUTE format(
28
+ 'CREATE POLICY tenant_isolation ON "ProjectMember" USING ("tenantId" = current_setting(''app.current_tenant'', true))'
29
+ );
30
+ END IF;
31
+ END $$;
@@ -0,0 +1,11 @@
1
+ -- CreateTable
2
+ CREATE TABLE "SystemConfig" (
3
+ "key" TEXT NOT NULL,
4
+ "value" TEXT NOT NULL,
5
+ "category" TEXT NOT NULL,
6
+ "sensitive" BOOLEAN NOT NULL DEFAULT false,
7
+ "updatedAt" TIMESTAMP(3) NOT NULL,
8
+ "updatedByAdminId" TEXT,
9
+
10
+ CONSTRAINT "SystemConfig_pkey" PRIMARY KEY ("key")
11
+ );
@@ -0,0 +1,29 @@
1
+ -- CreateTable: CustomRole — per-tenant custom roles with explicit permission arrays
2
+ CREATE TABLE "CustomRole" (
3
+ "id" TEXT NOT NULL,
4
+ "tenantId" TEXT NOT NULL,
5
+ "name" TEXT NOT NULL,
6
+ "description" TEXT,
7
+ "permissions" JSONB NOT NULL DEFAULT '[]',
8
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
9
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
10
+
11
+ CONSTRAINT "CustomRole_pkey" PRIMARY KEY ("id")
12
+ );
13
+
14
+ -- UniqueIndex: one name per tenant
15
+ CREATE UNIQUE INDEX "CustomRole_tenantId_name_key" ON "CustomRole"("tenantId", "name");
16
+
17
+ -- FK to Tenant
18
+ ALTER TABLE "CustomRole"
19
+ ADD CONSTRAINT "CustomRole_tenantId_fkey"
20
+ FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id")
21
+ ON DELETE CASCADE ON UPDATE CASCADE;
22
+
23
+ -- RLS: users only see their own tenant's custom roles
24
+ ALTER TABLE "CustomRole" ENABLE ROW LEVEL SECURITY;
25
+ CREATE POLICY tenant_isolation ON "CustomRole"
26
+ USING (
27
+ "tenantId" = current_setting('app.current_tenant', true)
28
+ OR current_setting('app.current_tenant', true) = ''
29
+ );
@@ -0,0 +1,11 @@
1
+ -- Platform Admin: separate from tenant users, no RLS, cross-tenant authority
2
+ CREATE TABLE "AdminUser" (
3
+ "id" TEXT NOT NULL,
4
+ "email" TEXT NOT NULL,
5
+ "passwordHash" TEXT NOT NULL,
6
+ "name" TEXT,
7
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
8
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
9
+ CONSTRAINT "AdminUser_pkey" PRIMARY KEY ("id")
10
+ );
11
+ CREATE UNIQUE INDEX "AdminUser_email_key" ON "AdminUser"("email");
@@ -0,0 +1,106 @@
1
+ -- Migration: add_extension_models
2
+ -- Adds ExtensionCapture, ExtensionInteraction, and ExtensionError models.
3
+ -- Also updates the Element model to support extension-captured elements.
4
+
5
+ -- ── Element model updates ───────────────────────────────────────────────────
6
+ -- Make previously required fields nullable (to support extension captures
7
+ -- which don't have a crawled Screen, meaning, role, or isInteractive value).
8
+ ALTER TABLE "Element"
9
+ ALTER COLUMN "screenId" DROP NOT NULL,
10
+ ALTER COLUMN "meaning" DROP NOT NULL,
11
+ ALTER COLUMN "role" DROP NOT NULL,
12
+ ALTER COLUMN "isInteractive" DROP NOT NULL;
13
+
14
+ -- Add extension-specific fields
15
+ ALTER TABLE "Element"
16
+ ADD COLUMN IF NOT EXISTS "selector" TEXT,
17
+ ADD COLUMN IF NOT EXISTS "pageUrl" TEXT,
18
+ ADD COLUMN IF NOT EXISTS "screenshotUrl" TEXT,
19
+ ADD COLUMN IF NOT EXISTS "source" TEXT NOT NULL DEFAULT 'crawl',
20
+ ADD COLUMN IF NOT EXISTS "metadata" JSONB;
21
+
22
+ -- Unique index for upsert from the extension
23
+ CREATE UNIQUE INDEX IF NOT EXISTS "Element_ext_upsert_key"
24
+ ON "Element" ("tenantId", "projectId", "selector", "pageUrl");
25
+
26
+ -- ── ExtensionCapture ────────────────────────────────────────────────────────
27
+ CREATE TABLE IF NOT EXISTS "ExtensionCapture" (
28
+ "id" TEXT NOT NULL PRIMARY KEY,
29
+ "tenantId" TEXT NOT NULL,
30
+ "projectId" TEXT NOT NULL,
31
+ "kind" TEXT NOT NULL,
32
+ "s3Url" TEXT,
33
+ "entryCount" INTEGER NOT NULL DEFAULT 0,
34
+ "capturedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
35
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
36
+ "metadata" JSONB,
37
+ CONSTRAINT "ExtensionCapture_projectId_fkey"
38
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE
39
+ );
40
+
41
+ CREATE INDEX IF NOT EXISTS "ExtensionCapture_tenantId_projectId_idx"
42
+ ON "ExtensionCapture" ("tenantId", "projectId");
43
+
44
+ CREATE INDEX IF NOT EXISTS "ExtensionCapture_tenantId_projectId_kind_idx"
45
+ ON "ExtensionCapture" ("tenantId", "projectId", "kind");
46
+
47
+ CREATE INDEX IF NOT EXISTS "ExtensionCapture_tenantId_projectId_capturedAt_idx"
48
+ ON "ExtensionCapture" ("tenantId", "projectId", "capturedAt");
49
+
50
+ -- ── ExtensionInteraction ────────────────────────────────────────────────────
51
+ CREATE TABLE IF NOT EXISTS "ExtensionInteraction" (
52
+ "id" TEXT NOT NULL PRIMARY KEY,
53
+ "tenantId" TEXT NOT NULL,
54
+ "captureId" TEXT NOT NULL,
55
+ "type" TEXT NOT NULL,
56
+ "selector" TEXT,
57
+ "xpath" TEXT,
58
+ "tagName" TEXT,
59
+ "value" TEXT,
60
+ "url" TEXT,
61
+ "timestamp" BIGINT,
62
+ "metadata" JSONB,
63
+ CONSTRAINT "ExtensionInteraction_captureId_fkey"
64
+ FOREIGN KEY ("captureId") REFERENCES "ExtensionCapture"("id") ON DELETE CASCADE ON UPDATE CASCADE
65
+ );
66
+
67
+ CREATE INDEX IF NOT EXISTS "ExtensionInteraction_tenantId_captureId_idx"
68
+ ON "ExtensionInteraction" ("tenantId", "captureId");
69
+
70
+ CREATE INDEX IF NOT EXISTS "ExtensionInteraction_captureId_idx"
71
+ ON "ExtensionInteraction" ("captureId");
72
+
73
+ -- ── ExtensionError ──────────────────────────────────────────────────────────
74
+ CREATE TABLE IF NOT EXISTS "ExtensionError" (
75
+ "id" TEXT NOT NULL PRIMARY KEY,
76
+ "tenantId" TEXT NOT NULL,
77
+ "projectId" TEXT NOT NULL,
78
+ "pageUrl" TEXT,
79
+ "kind" TEXT NOT NULL,
80
+ "level" TEXT NOT NULL DEFAULT 'error',
81
+ "text" TEXT NOT NULL,
82
+ "url" TEXT,
83
+ "lineNumber" INTEGER,
84
+ "columnNumber" INTEGER,
85
+ "stackTrace" JSONB,
86
+ "source" TEXT,
87
+ "capturedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
88
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
89
+ CONSTRAINT "ExtensionError_projectId_fkey"
90
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE
91
+ );
92
+
93
+ CREATE INDEX IF NOT EXISTS "ExtensionError_tenantId_projectId_idx"
94
+ ON "ExtensionError" ("tenantId", "projectId");
95
+
96
+ CREATE INDEX IF NOT EXISTS "ExtensionError_tenantId_projectId_pageUrl_idx"
97
+ ON "ExtensionError" ("tenantId", "projectId", "pageUrl");
98
+
99
+ CREATE INDEX IF NOT EXISTS "ExtensionError_tenantId_projectId_kind_idx"
100
+ ON "ExtensionError" ("tenantId", "projectId", "kind");
101
+
102
+ CREATE INDEX IF NOT EXISTS "ExtensionError_tenantId_projectId_level_idx"
103
+ ON "ExtensionError" ("tenantId", "projectId", "level");
104
+
105
+ CREATE INDEX IF NOT EXISTS "ExtensionError_tenantId_projectId_capturedAt_idx"
106
+ ON "ExtensionError" ("tenantId", "projectId", "capturedAt");
@@ -0,0 +1 @@
1
+ ALTER TABLE "Project" ADD COLUMN IF NOT EXISTS "restricted" BOOLEAN NOT NULL DEFAULT false;
@@ -0,0 +1 @@
1
+ CREATE INDEX "SystemConfig_category_idx" ON "SystemConfig"("category");
@@ -0,0 +1,24 @@
1
+ -- Drop old workspace foreign keys and columns
2
+ ALTER TABLE "Tenant" DROP COLUMN IF EXISTS "workspaceId";
3
+ ALTER TABLE "User" DROP COLUMN IF EXISTS "workspaceId";
4
+ DROP TABLE IF EXISTS "Workspace" CASCADE;
5
+
6
+ -- Create new Workspace table (team/group within a Tenant)
7
+ CREATE TABLE "Workspace" (
8
+ "id" TEXT NOT NULL,
9
+ "tenantId" TEXT NOT NULL,
10
+ "name" TEXT NOT NULL,
11
+ "slug" TEXT NOT NULL,
12
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
13
+ CONSTRAINT "Workspace_pkey" PRIMARY KEY ("id")
14
+ );
15
+ CREATE UNIQUE INDEX "Workspace_tenantId_slug_key" ON "Workspace"("tenantId", "slug");
16
+ CREATE INDEX "Workspace_tenantId_idx" ON "Workspace"("tenantId");
17
+ ALTER TABLE "Workspace" ADD CONSTRAINT "Workspace_tenantId_fkey"
18
+ FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
19
+
20
+ -- Add workspaceId to Project (nullable — projects may not be in a workspace yet)
21
+ ALTER TABLE "Project" ADD COLUMN "workspaceId" TEXT;
22
+ ALTER TABLE "Project" ADD CONSTRAINT "Project_workspaceId_fkey"
23
+ FOREIGN KEY ("workspaceId") REFERENCES "Workspace"("id") ON DELETE SET NULL ON UPDATE CASCADE;
24
+ CREATE INDEX "Project_workspaceId_idx" ON "Project"("workspaceId");
@@ -0,0 +1,2 @@
1
+ -- Make AgentToken.projectId optional so workspace-scoped tokens (not tied to a project) can exist.
2
+ ALTER TABLE "AgentToken" ALTER COLUMN "projectId" DROP NOT NULL;
@@ -0,0 +1,35 @@
1
+ -- ============================================================================
2
+ -- Add RLS policies to Requirement and AppField tables
3
+ --
4
+ -- Both tables were created with ENABLE ROW LEVEL SECURITY but without any
5
+ -- policy. PostgreSQL defaults to deny-all when RLS is enabled with no
6
+ -- policies, causing error 42501 on any write from qip_app.
7
+ --
8
+ -- This migration adds the standard tenant_isolation policy consistent with
9
+ -- all other tenant-scoped tables in this project.
10
+ -- ============================================================================
11
+
12
+ DO $$
13
+ DECLARE
14
+ t text;
15
+ tables text[] := ARRAY['Requirement', 'AppField'];
16
+ BEGIN
17
+ FOREACH t IN ARRAY tables LOOP
18
+ -- Force RLS even for table owner (consistent with other tables)
19
+ EXECUTE format('ALTER TABLE "%s" FORCE ROW LEVEL SECURITY;', t);
20
+
21
+ -- Add tenant isolation policy
22
+ BEGIN
23
+ EXECUTE format($f$
24
+ CREATE POLICY tenant_isolation ON "%s"
25
+ USING ("tenantId" = current_setting('app.current_tenant', true))
26
+ WITH CHECK ("tenantId" = current_setting('app.current_tenant', true));
27
+ $f$, t);
28
+ EXCEPTION WHEN duplicate_object THEN
29
+ NULL; -- already exists, skip
30
+ END;
31
+
32
+ -- Ensure qip_app has table-level privileges
33
+ EXECUTE format('GRANT SELECT, INSERT, UPDATE, DELETE ON "%s" TO qip_app;', t);
34
+ END LOOP;
35
+ END $$;
@@ -0,0 +1,9 @@
1
+ -- OAuthConnection: ON DELETE CASCADE so deleting a project removes its OAuth connections
2
+ ALTER TABLE "OAuthConnection" DROP CONSTRAINT IF EXISTS "OAuthConnection_projectId_fkey";
3
+ ALTER TABLE "OAuthConnection" ADD CONSTRAINT "OAuthConnection_projectId_fkey"
4
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
5
+
6
+ -- AuditLog: ON DELETE SET NULL so deleting a project nullifies audit log project references
7
+ ALTER TABLE "AuditLog" DROP CONSTRAINT IF EXISTS "AuditLog_projectId_fkey";
8
+ ALTER TABLE "AuditLog" ADD CONSTRAINT "AuditLog_projectId_fkey"
9
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE SET NULL ON UPDATE CASCADE;
@@ -0,0 +1,2 @@
1
+ -- Add approvalStatus to SkillFile (was being set via `as any` hack, causing 500 on approve)
2
+ ALTER TABLE "SkillFile" ADD COLUMN IF NOT EXISTS "approvalStatus" TEXT NOT NULL DEFAULT 'PENDING';
@@ -0,0 +1,35 @@
1
+ -- Add settings to Workspace
2
+ ALTER TABLE "Workspace" ADD COLUMN IF NOT EXISTS "settings" JSONB;
3
+
4
+ -- Create Scenario table
5
+ CREATE TABLE IF NOT EXISTS "Scenario" (
6
+ "id" TEXT NOT NULL,
7
+ "tenantId" TEXT NOT NULL,
8
+ "projectId" TEXT NOT NULL,
9
+ "requirementId" TEXT NOT NULL,
10
+ "title" TEXT NOT NULL,
11
+ "description" TEXT,
12
+ "gherkin" TEXT NOT NULL DEFAULT '',
13
+ "status" TEXT NOT NULL DEFAULT 'draft',
14
+ "version" INTEGER NOT NULL DEFAULT 1,
15
+ "source" TEXT NOT NULL DEFAULT 'AI_GENERATED',
16
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
17
+ "updatedAt" TIMESTAMP(3) NOT NULL,
18
+ CONSTRAINT "Scenario_pkey" PRIMARY KEY ("id")
19
+ );
20
+
21
+ CREATE INDEX IF NOT EXISTS "Scenario_tenantId_projectId_idx" ON "Scenario"("tenantId", "projectId");
22
+ CREATE INDEX IF NOT EXISTS "Scenario_requirementId_idx" ON "Scenario"("requirementId");
23
+
24
+ ALTER TABLE "Scenario" ADD CONSTRAINT "Scenario_requirementId_fkey"
25
+ FOREIGN KEY ("requirementId") REFERENCES "Requirement"("id") ON DELETE CASCADE ON UPDATE CASCADE;
26
+
27
+ -- Add scenario + suspect fields to TestCase
28
+ ALTER TABLE "TestCase" ADD COLUMN IF NOT EXISTS "scenarioId" TEXT;
29
+ ALTER TABLE "TestCase" ADD COLUMN IF NOT EXISTS "suspect" BOOLEAN NOT NULL DEFAULT false;
30
+ ALTER TABLE "TestCase" ADD COLUMN IF NOT EXISTS "suspectReason" TEXT;
31
+
32
+ CREATE INDEX IF NOT EXISTS "TestCase_scenarioId_idx" ON "TestCase"("scenarioId");
33
+
34
+ ALTER TABLE "TestCase" ADD CONSTRAINT "TestCase_scenarioId_fkey"
35
+ FOREIGN KEY ("scenarioId") REFERENCES "Scenario"("id") ON DELETE SET NULL ON UPDATE CASCADE;
@@ -0,0 +1,5 @@
1
+ -- CreateIndex
2
+ CREATE INDEX IF NOT EXISTS "TestCase_traceTo_idx" ON "TestCase"("traceTo");
3
+
4
+ -- CreateIndex
5
+ CREATE INDEX IF NOT EXISTS "TestCase_tenantId_projectId_suspect_idx" ON "TestCase"("tenantId", "projectId", "suspect");
@@ -0,0 +1,19 @@
1
+ -- ============================================================================
2
+ -- RLS FOR Workspace TABLE
3
+ --
4
+ -- Workspace was missing from the initial tenant isolation migration.
5
+ -- Enforces the same pattern: rows only visible when tenantId matches
6
+ -- the app.current_tenant session variable set by withTenant().
7
+ -- ============================================================================
8
+
9
+ ALTER TABLE "Workspace" ENABLE ROW LEVEL SECURITY;
10
+ ALTER TABLE "Workspace" FORCE ROW LEVEL SECURITY;
11
+
12
+ DO $$
13
+ BEGIN
14
+ CREATE POLICY tenant_isolation ON "Workspace"
15
+ USING ("tenantId" = current_setting('app.current_tenant', true))
16
+ WITH CHECK ("tenantId" = current_setting('app.current_tenant', true));
17
+ EXCEPTION WHEN duplicate_object THEN
18
+ NULL;
19
+ END $$;
@@ -0,0 +1,125 @@
1
+ -- Add ON DELETE CASCADE to Project relations that were missing it.
2
+ -- Without these, deleting a Project fails with FK constraint violation.
3
+
4
+ -- Clean up orphaned rows (projectId references non-existent Project) before adding FK constraints.
5
+ DELETE FROM "TestData" WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
6
+ DELETE FROM "TestScript" WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
7
+ DELETE FROM "PlatformJob" WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
8
+ DELETE FROM "SkillFile" WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
9
+ DELETE FROM "ProjectArtifact" WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
10
+ DELETE FROM "Flow" WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
11
+ DELETE FROM "ScreenGroup" WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
12
+ DELETE FROM "ElementFingerprint" WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
13
+ DELETE FROM "UserSession" WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
14
+ DELETE FROM "VisualBaseline" WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
15
+ DELETE FROM "HumanReviewItem" WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
16
+ DELETE FROM "TestRun" WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
17
+ DELETE FROM "Webhook" WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
18
+ DELETE FROM "AgentToken" WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
19
+ DELETE FROM "ProbeObservation" WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
20
+ DELETE FROM "ScreenDiff" WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
21
+ DELETE FROM "ChangeLog" WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
22
+ DELETE FROM "Connector" WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
23
+ -- Tables with nullable projectId → SET NULL orphaned refs instead of DELETE
24
+ UPDATE "LLMCallLog" SET "projectId" = NULL WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
25
+ UPDATE "KnowledgeChunk" SET "projectId" = NULL WHERE "projectId" IS NOT NULL AND "projectId" NOT IN (SELECT "id" FROM "Project");
26
+
27
+ -- ScreenGroup
28
+ ALTER TABLE "ScreenGroup" DROP CONSTRAINT IF EXISTS "ScreenGroup_projectId_fkey";
29
+ ALTER TABLE "ScreenGroup" ADD CONSTRAINT "ScreenGroup_projectId_fkey"
30
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
31
+
32
+ -- Flow
33
+ ALTER TABLE "Flow" DROP CONSTRAINT IF EXISTS "Flow_projectId_fkey";
34
+ ALTER TABLE "Flow" ADD CONSTRAINT "Flow_projectId_fkey"
35
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
36
+
37
+ -- SkillFile
38
+ ALTER TABLE "SkillFile" DROP CONSTRAINT IF EXISTS "SkillFile_projectId_fkey";
39
+ ALTER TABLE "SkillFile" ADD CONSTRAINT "SkillFile_projectId_fkey"
40
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
41
+
42
+ -- ProjectArtifact
43
+ ALTER TABLE "ProjectArtifact" DROP CONSTRAINT IF EXISTS "ProjectArtifact_projectId_fkey";
44
+ ALTER TABLE "ProjectArtifact" ADD CONSTRAINT "ProjectArtifact_projectId_fkey"
45
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
46
+
47
+ -- TestData
48
+ ALTER TABLE "TestData" DROP CONSTRAINT IF EXISTS "TestData_projectId_fkey";
49
+ ALTER TABLE "TestData" ADD CONSTRAINT "TestData_projectId_fkey"
50
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
51
+
52
+ -- TestScript
53
+ ALTER TABLE "TestScript" DROP CONSTRAINT IF EXISTS "TestScript_projectId_fkey";
54
+ ALTER TABLE "TestScript" ADD CONSTRAINT "TestScript_projectId_fkey"
55
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
56
+
57
+ -- PlatformJob
58
+ ALTER TABLE "PlatformJob" DROP CONSTRAINT IF EXISTS "PlatformJob_projectId_fkey";
59
+ ALTER TABLE "PlatformJob" ADD CONSTRAINT "PlatformJob_projectId_fkey"
60
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
61
+
62
+ -- ElementFingerprint
63
+ ALTER TABLE "ElementFingerprint" DROP CONSTRAINT IF EXISTS "ElementFingerprint_projectId_fkey";
64
+ ALTER TABLE "ElementFingerprint" ADD CONSTRAINT "ElementFingerprint_projectId_fkey"
65
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
66
+
67
+ -- UserSession
68
+ ALTER TABLE "UserSession" DROP CONSTRAINT IF EXISTS "UserSession_projectId_fkey";
69
+ ALTER TABLE "UserSession" ADD CONSTRAINT "UserSession_projectId_fkey"
70
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
71
+
72
+ -- VisualBaseline
73
+ ALTER TABLE "VisualBaseline" DROP CONSTRAINT IF EXISTS "VisualBaseline_projectId_fkey";
74
+ ALTER TABLE "VisualBaseline" ADD CONSTRAINT "VisualBaseline_projectId_fkey"
75
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
76
+
77
+ -- HumanReviewItem
78
+ ALTER TABLE "HumanReviewItem" DROP CONSTRAINT IF EXISTS "HumanReviewItem_projectId_fkey";
79
+ ALTER TABLE "HumanReviewItem" ADD CONSTRAINT "HumanReviewItem_projectId_fkey"
80
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
81
+
82
+ -- TestRun
83
+ ALTER TABLE "TestRun" DROP CONSTRAINT IF EXISTS "TestRun_projectId_fkey";
84
+ ALTER TABLE "TestRun" ADD CONSTRAINT "TestRun_projectId_fkey"
85
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
86
+
87
+ -- Webhook
88
+ ALTER TABLE "Webhook" DROP CONSTRAINT IF EXISTS "Webhook_projectId_fkey";
89
+ ALTER TABLE "Webhook" ADD CONSTRAINT "Webhook_projectId_fkey"
90
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
91
+
92
+ -- AgentToken
93
+ ALTER TABLE "AgentToken" DROP CONSTRAINT IF EXISTS "AgentToken_projectId_fkey";
94
+ ALTER TABLE "AgentToken" ADD CONSTRAINT "AgentToken_projectId_fkey"
95
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
96
+
97
+ -- ProbeObservation
98
+ ALTER TABLE "ProbeObservation" DROP CONSTRAINT IF EXISTS "ProbeObservation_projectId_fkey";
99
+ ALTER TABLE "ProbeObservation" ADD CONSTRAINT "ProbeObservation_projectId_fkey"
100
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
101
+
102
+ -- ScreenDiff (has screenId FK, but also projectId)
103
+ ALTER TABLE "ScreenDiff" DROP CONSTRAINT IF EXISTS "ScreenDiff_projectId_fkey";
104
+ ALTER TABLE "ScreenDiff" ADD CONSTRAINT "ScreenDiff_projectId_fkey"
105
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
106
+
107
+ -- ChangeLog
108
+ ALTER TABLE "ChangeLog" DROP CONSTRAINT IF EXISTS "ChangeLog_projectId_fkey";
109
+ ALTER TABLE "ChangeLog" ADD CONSTRAINT "ChangeLog_projectId_fkey"
110
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
111
+
112
+ -- KnowledgeChunk (nullable projectId → SetNull)
113
+ ALTER TABLE "KnowledgeChunk" DROP CONSTRAINT IF EXISTS "KnowledgeChunk_projectId_fkey";
114
+ ALTER TABLE "KnowledgeChunk" ADD CONSTRAINT "KnowledgeChunk_projectId_fkey"
115
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE SET NULL ON UPDATE CASCADE;
116
+
117
+ -- LLMCallLog (nullable projectId → SetNull)
118
+ ALTER TABLE "LLMCallLog" DROP CONSTRAINT IF EXISTS "LLMCallLog_projectId_fkey";
119
+ ALTER TABLE "LLMCallLog" ADD CONSTRAINT "LLMCallLog_projectId_fkey"
120
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE SET NULL ON UPDATE CASCADE;
121
+
122
+ -- Connector
123
+ ALTER TABLE "Connector" DROP CONSTRAINT IF EXISTS "Connector_projectId_fkey";
124
+ ALTER TABLE "Connector" ADD CONSTRAINT "Connector_projectId_fkey"
125
+ FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1,12 @@
1
+ -- Adds workerId + heartbeatAt to PlatformJob: identifies which worker process
2
+ -- (host:pid) claimed a job, and lets the stale-job recovery sweep compare a
3
+ -- liveness heartbeat (bumped on every progress update) instead of startedAt,
4
+ -- so a slow-but-alive job isn't requeued and double-processed by another
5
+ -- worker just for taking a long time.
6
+ ALTER TABLE "PlatformJob" ADD COLUMN IF NOT EXISTS "workerId" TEXT;
7
+ ALTER TABLE "PlatformJob" ADD COLUMN IF NOT EXISTS "heartbeatAt" TIMESTAMP(3);
8
+
9
+ -- Speeds up the atomic claim query's `ORDER BY "queuedAt" WHERE status = 'QUEUED'`
10
+ -- scan and the stale-job sweep's `WHERE status = 'RUNNING' AND "heartbeatAt" < ...`.
11
+ CREATE INDEX IF NOT EXISTS "PlatformJob_status_queuedAt_idx" ON "PlatformJob"("status", "queuedAt");
12
+ CREATE INDEX IF NOT EXISTS "PlatformJob_status_heartbeatAt_idx" ON "PlatformJob"("status", "heartbeatAt");
@@ -0,0 +1,7 @@
1
+ -- Adds discoveryMethod to Screen: how the screen was first found during a crawl
2
+ -- (ENTRY_POINT | LINK_FOLLOW | SEED_URL | SKYVERN_AI). Set once at creation, never
3
+ -- changed on re-crawl. Powers orphan-page detection — see repository.ts's
4
+ -- getOrphanScreens().
5
+ ALTER TABLE "Screen" ADD COLUMN IF NOT EXISTS "discoveryMethod" TEXT NOT NULL DEFAULT 'LINK_FOLLOW';
6
+
7
+ CREATE INDEX IF NOT EXISTS "Screen_tenantId_projectId_discoveryMethod_idx" ON "Screen"("tenantId", "projectId", "discoveryMethod");