@marinoscar/kvox-eval 0.0.0-bootstrap → 0.1.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/LICENSE +21 -0
  3. package/README.md +166 -0
  4. package/dist/ask/index.d.ts +6 -0
  5. package/dist/ask/index.js +28 -0
  6. package/dist/ask/question-schema.d.ts +121 -0
  7. package/dist/ask/question-schema.js +167 -0
  8. package/dist/ask/report.d.ts +88 -0
  9. package/dist/ask/report.js +170 -0
  10. package/dist/ask/scorer.d.ts +104 -0
  11. package/dist/ask/scorer.js +164 -0
  12. package/dist/ask-run/ask-command.d.ts +62 -0
  13. package/dist/ask-run/ask-command.js +254 -0
  14. package/dist/ask-run/host/app.d.ts +65 -0
  15. package/dist/ask-run/host/app.js +146 -0
  16. package/dist/ask-run/host/db.d.ts +38 -0
  17. package/dist/ask-run/host/db.js +126 -0
  18. package/dist/ask-run/host/ports.d.ts +35 -0
  19. package/dist/ask-run/host/ports.js +113 -0
  20. package/dist/ask-run/index.d.ts +13 -0
  21. package/dist/ask-run/index.js +35 -0
  22. package/dist/ask-run/replay.d.ts +128 -0
  23. package/dist/ask-run/replay.js +212 -0
  24. package/dist/ask-run/run-ask-eval.d.ts +125 -0
  25. package/dist/ask-run/run-ask-eval.js +193 -0
  26. package/dist/ask-run/seed-golden-graph.d.ts +64 -0
  27. package/dist/ask-run/seed-golden-graph.js +495 -0
  28. package/dist/cli/index.d.ts +3 -0
  29. package/dist/cli/index.js +12 -0
  30. package/dist/cli/kg-command.d.ts +66 -0
  31. package/dist/cli/kg-command.js +238 -0
  32. package/dist/cli/main.d.ts +18 -0
  33. package/dist/cli/main.js +69 -0
  34. package/dist/fixtures/domains.d.ts +16 -0
  35. package/dist/fixtures/domains.js +33 -0
  36. package/dist/fixtures/fixture-schema.d.ts +323 -0
  37. package/dist/fixtures/fixture-schema.js +199 -0
  38. package/dist/fixtures/index.d.ts +16 -0
  39. package/dist/fixtures/index.js +40 -0
  40. package/dist/fixtures/load.d.ts +73 -0
  41. package/dist/fixtures/load.js +137 -0
  42. package/dist/fixtures/questions.d.ts +15 -0
  43. package/dist/fixtures/questions.js +26 -0
  44. package/dist/index.d.ts +20 -0
  45. package/dist/index.js +36 -0
  46. package/dist/kg/gold-runner.d.ts +13 -0
  47. package/dist/kg/gold-runner.js +57 -0
  48. package/dist/kg/index.d.ts +17 -0
  49. package/dist/kg/index.js +65 -0
  50. package/dist/kg/internals.d.ts +7 -0
  51. package/dist/kg/internals.js +17 -0
  52. package/dist/kg/prediction-schema.d.ts +245 -0
  53. package/dist/kg/prediction-schema.js +102 -0
  54. package/dist/kg/predictions.d.ts +11 -0
  55. package/dist/kg/predictions.js +49 -0
  56. package/dist/kg/report.d.ts +84 -0
  57. package/dist/kg/report.js +125 -0
  58. package/dist/kg/run-kg-eval.d.ts +77 -0
  59. package/dist/kg/run-kg-eval.js +137 -0
  60. package/dist/kg/runner.d.ts +72 -0
  61. package/dist/kg/runner.js +51 -0
  62. package/dist/kg/runners/extract-resolve-runner.d.ts +40 -0
  63. package/dist/kg/runners/extract-resolve-runner.js +199 -0
  64. package/dist/kg/runners/extract-runner.d.ts +65 -0
  65. package/dist/kg/runners/extract-runner.js +243 -0
  66. package/dist/kg/scorer.d.ts +226 -0
  67. package/dist/kg/scorer.js +463 -0
  68. package/dist/kg/targets.d.ts +17 -0
  69. package/dist/kg/targets.js +16 -0
  70. package/dist/kg/text.d.ts +45 -0
  71. package/dist/kg/text.js +100 -0
  72. package/dist/live/adapters.d.ts +16 -0
  73. package/dist/live/adapters.js +58 -0
  74. package/dist/live/env.d.ts +39 -0
  75. package/dist/live/env.js +56 -0
  76. package/dist/live/index.d.ts +6 -0
  77. package/dist/live/index.js +16 -0
  78. package/dist/live/live-ai.d.ts +104 -0
  79. package/dist/live/live-ai.js +157 -0
  80. package/dist/parity/constants.d.ts +79 -0
  81. package/dist/parity/constants.js +104 -0
  82. package/dist/parity/index.d.ts +20 -0
  83. package/dist/parity/index.js +38 -0
  84. package/dist/parity/internals.d.ts +8 -0
  85. package/dist/parity/internals.js +16 -0
  86. package/dist/parity/scenario.d.ts +45 -0
  87. package/dist/parity/scenario.js +245 -0
  88. package/dist/parity/script.d.ts +44 -0
  89. package/dist/parity/script.js +136 -0
  90. package/dist/parity/snapshot.d.ts +43 -0
  91. package/dist/parity/snapshot.js +71 -0
  92. package/dist/real-data/index.d.ts +2 -0
  93. package/dist/real-data/index.js +12 -0
  94. package/dist/real-data/real-data.d.ts +76 -0
  95. package/dist/real-data/real-data.js +156 -0
  96. package/fixtures/kg-golden/README.md +136 -0
  97. package/fixtures/kg-golden/ask-questions.json +458 -0
  98. package/fixtures/kg-golden/meetings/m01-northwind-pilot-kickoff.json +792 -0
  99. package/fixtures/kg-golden/meetings/m02-halden-migration-planning.json +814 -0
  100. package/fixtures/kg-golden/meetings/m03-larkspur-weekly-sync.json +601 -0
  101. package/fixtures/kg-golden/meetings/m04-corvid-intake-discovery.json +690 -0
  102. package/fixtures/kg-golden/meetings/m05-hosting-vendor-selection.json +631 -0
  103. package/fixtures/kg-golden/meetings/m06-northwind-floor-data-review.json +649 -0
  104. package/fixtures/kg-golden/meetings/m07-gullwing-intro-notes.json +428 -0
  105. package/fixtures/kg-golden/meetings/m08-halden-migration-checkpoint.json +672 -0
  106. package/fixtures/kg-golden/meetings/m09-northwind-platform-org-update.json +711 -0
  107. package/fixtures/kg-golden/meetings/m10-corvid-prototype-review.json +623 -0
  108. package/fixtures/kg-golden/meetings/m11-solvane-grid-dashboard-scoping.json +689 -0
  109. package/fixtures/kg-golden/meetings/m12-northwind-pilot-replan.json +681 -0
  110. package/fixtures/kg-golden/meetings/m13-halden-cutover-go-no-go.json +721 -0
  111. package/fixtures/kg-golden/meetings/m14-gullwing-discovery-readout.json +719 -0
  112. package/fixtures/kg-golden/meetings/m15-halden-handover-ravi-leaving.json +716 -0
  113. package/fixtures/kg-golden/meetings/m16-varden-claims-portal-intro.json +704 -0
  114. package/fixtures/kg-golden/meetings/m17-corvid-usability-debrief.json +572 -0
  115. package/fixtures/kg-golden/meetings/m18-hosting-decision-revisited.json +655 -0
  116. package/fixtures/kg-golden/meetings/m19-solvane-integration-kickoff.json +726 -0
  117. package/fixtures/kg-golden/meetings/m20-northwind-go-live-retro.json +673 -0
  118. package/fixtures/kg-golden/meetings/m21-quarterly-account-review.json +449 -0
  119. package/fixtures/kg-golden/meetings/m22-halden-intro-call-2020.json +558 -0
  120. package/fixtures/kg-golden/meetings/m23-gullwing-model-review.json +702 -0
  121. package/fixtures/kg-golden/meetings/m24-varden-discovery-midpoint.json +643 -0
  122. package/fixtures/kg-golden/meetings/m25-corvid-rollout-planning.json +642 -0
  123. package/fixtures/kg-golden/meetings/m26-larkspur-engineering-prep.json +695 -0
  124. package/fixtures/kg-golden/meetings/m27-solvane-phase-one-demo.json +649 -0
  125. package/fixtures/kg-golden/meetings/m28-northwind-building-5-scoping.json +715 -0
  126. package/fixtures/kg-golden/meetings/m29-halden-southern-cutover-review.json +752 -0
  127. package/fixtures/kg-golden/meetings/m30-varden-portal-build-kickoff.json +697 -0
  128. package/fixtures/kg-golden/meetings/m31-gullwing-rollout-renewal.json +681 -0
  129. package/fixtures/kg-golden/meetings/m32-brambleway-weekly-sync.json +472 -0
  130. package/fixtures/kg-golden/meetings/m33-nina-oliver-one-on-one.json +342 -0
  131. package/fixtures/kg-golden/meetings/m34-coffee-with-dana.json +249 -0
  132. package/fixtures/kg-golden/meetings/m35-brambleway-june-sync.json +553 -0
  133. package/fixtures/kg-golden/meetings/m36-copenhagen-conference-planning.json +296 -0
  134. package/host/generated/client.d.ts +1 -0
  135. package/host/generated/client.js +5 -0
  136. package/host/generated/default.d.ts +1 -0
  137. package/host/generated/default.js +5 -0
  138. package/host/generated/edge.d.ts +1 -0
  139. package/host/generated/edge.js +1658 -0
  140. package/host/generated/index-browser.js +1684 -0
  141. package/host/generated/index.d.ts +192335 -0
  142. package/host/generated/index.js +1658 -0
  143. package/host/generated/package.json +139 -0
  144. package/host/generated/query_compiler_fast_bg.js +2 -0
  145. package/host/generated/query_compiler_fast_bg.wasm +0 -0
  146. package/host/generated/query_compiler_fast_bg.wasm-base64.js +2 -0
  147. package/host/generated/runtime/client.d.ts +3433 -0
  148. package/host/generated/runtime/client.js +110 -0
  149. package/host/generated/runtime/index-browser.d.ts +90 -0
  150. package/host/generated/runtime/index-browser.js +6 -0
  151. package/host/generated/runtime/wasm-compiler-edge.js +100 -0
  152. package/host/generated/schema.prisma +6312 -0
  153. package/host/generated/wasm-edge-light-loader.mjs +5 -0
  154. package/host/generated/wasm-worker-loader.mjs +5 -0
  155. package/host/prisma/fragments/kvox.ask.prisma +126 -0
  156. package/host/prisma/fragments/kvox.graph.prisma +1073 -0
  157. package/host/prisma/fragments/kvox.notes.prisma +787 -0
  158. package/host/prisma/fragments/kvox.search.prisma +211 -0
  159. package/host/prisma/fragments/kvox.transcripts.prisma +790 -0
  160. package/host/prisma/kvox.lock +101 -0
  161. package/host/prisma/migrations/20240101000000_initial/migration.sql +297 -0
  162. package/host/prisma/migrations/20240101000001_add_personal_access_tokens/migration.sql +31 -0
  163. package/host/prisma/migrations/20240101000002_add_credentials/migration.sql +20 -0
  164. package/host/prisma/migrations/20240101000003_add_notification_deliveries/migration.sql +27 -0
  165. package/host/prisma/migrations/20240101000004_drop_stale_uuid_defaults/migration.sql +35 -0
  166. package/host/prisma/migrations/20240101000005_add_notifications/migration.sql +22 -0
  167. package/host/prisma/migrations/20240101000006_add_push_subscriptions/migration.sql +25 -0
  168. package/host/prisma/migrations/20240101000007_add_jobs/migration.sql +110 -0
  169. package/host/prisma/migrations/20240101000008_add_worker_nodes/migration.sql +90 -0
  170. package/host/prisma/migrations/20240101000009_add_database_backup_runs/migration.sql +104 -0
  171. package/host/prisma/migrations/20240101000010_add_notification_broadcasts/migration.sql +57 -0
  172. package/host/prisma/migrations/20240101000011_add_backup_run_job_link/migration.sql +87 -0
  173. package/host/prisma/migrations/20240101000012_add_job_node_secrets/migration.sql +61 -0
  174. package/host/prisma/migrations/20240101000013_add_backup_run_pg_dump_version/migration.sql +26 -0
  175. package/host/prisma/migrations/20240101000014_add_job_claim_token/migration.sql +92 -0
  176. package/host/prisma/migrations/20240101000015_add_ai_platform/migration.sql +108 -0
  177. package/host/prisma/migrations/20240101000016_revoke_viewer_ai_use/migration.sql +32 -0
  178. package/host/prisma/migrations/20240101000017_add_user_credentials/migration.sql +29 -0
  179. package/host/prisma/migrations/20240101000018_add_device_session_credential_link/migration.sql +52 -0
  180. package/host/prisma/migrations/20240101000019_add_worker_node_vitals/migration.sql +3 -0
  181. package/host/prisma/migrations/20240101000020_add_job_trace_context/migration.sql +6 -0
  182. package/host/prisma/migrations/20240101000021_add_retention_created_at_indexes/migration.sql +13 -0
  183. package/host/prisma/migrations/20240101000022_add_organizations/migration.sql +142 -0
  184. package/host/prisma/migrations/20240101000023_split_system_org_roles/migration.sql +131 -0
  185. package/host/prisma/migrations/20240101000024_org_scoped_rls/migration.sql +215 -0
  186. package/host/prisma/migrations/20240101000025_add_groups/migration.sql +165 -0
  187. package/host/prisma/migrations/20240101000026_add_grants/migration.sql +117 -0
  188. package/host/prisma/migrations/20240101000027_add_org_settings/migration.sql +49 -0
  189. package/host/prisma/migrations/20240101000028_add_org_credentials/migration.sql +54 -0
  190. package/host/prisma/migrations/20240101000029_add_jobs_org_id/migration.sql +18 -0
  191. package/host/prisma/migrations/20240101000030_add_jobs_org_id_status_index/migration.sql +6 -0
  192. package/host/prisma/migrations/20240101000031_add_broadcast_target_org/migration.sql +21 -0
  193. package/host/prisma/migrations/20240101000032_add_android_app/migration.sql +60 -0
  194. package/host/prisma/migrations/20240101000033_open_job_reason/migration.sql +16 -0
  195. package/host/prisma/migrations/20240101000034_ai_usage_feature/migration.sql +37 -0
  196. package/host/prisma/migrations/20240101000035_add_app_metadata/migration.sql +49 -0
  197. package/host/prisma/migrations/20240101000036_open_sharing_vocabularies/migration.sql +97 -0
  198. package/host/prisma/migrations/20260914130000_kvox_add_transcripts/migration.sql +322 -0
  199. package/host/prisma/migrations/20260914140000_kvox_add_notes/migration.sql +311 -0
  200. package/host/prisma/migrations/20260915120000_kvox_add_search_vectors/migration.sql +150 -0
  201. package/host/prisma/migrations/20260915130000_kvox_add_search_embeddings/migration.sql +362 -0
  202. package/host/prisma/migrations/20260915210000_kvox_add_note_title_source/migration.sql +30 -0
  203. package/host/prisma/migrations/20260923120000_kvox_add_note_generation_context_snapshot/migration.sql +24 -0
  204. package/host/prisma/migrations/20260923130000_kvox_add_user_hidden_note_templates/migration.sql +35 -0
  205. package/host/prisma/migrations/20260923140000_kvox_add_transcript_speaker_identities/migration.sql +6 -0
  206. package/host/prisma/migrations/20260923150000_kvox_transcript_name_checks/migration.sql +95 -0
  207. package/host/prisma/migrations/20260923160000_kvox_add_note_body_format/migration.sql +27 -0
  208. package/host/prisma/migrations/20260923170000_kvox_add_note_version_body_format/migration.sql +23 -0
  209. package/host/prisma/migrations/20260926012548_kvox_add_knowledge_graph/migration.sql +680 -0
  210. package/host/prisma/migrations/20260926030000_kvox_fix_kg_relations_valid_precision_chk/migration.sql +29 -0
  211. package/host/prisma/migrations/20260926030100_kvox_add_transcript_recorded_at/migration.sql +9 -0
  212. package/host/prisma/migrations/20260926030200_kvox_kg_evidence_invariant/migration.sql +60 -0
  213. package/host/prisma/migrations/20260926040000_kvox_add_kg_graph_layouts/migration.sql +30 -0
  214. package/host/prisma/migrations/20260926050000_kvox_add_ask_conversations/migration.sql +99 -0
  215. package/host/prisma/migrations/20260926231512_kvox_add_kg_exports/migration.sql +52 -0
  216. package/host/prisma/migrations/20261016120000_kvox_transcripts_org_id/migration.sql +49 -0
  217. package/host/prisma/platform.lock +319 -0
  218. package/host/prisma/schema/app.kvox.ask.prisma +110 -0
  219. package/host/prisma/schema/app.kvox.graph.prisma +1030 -0
  220. package/host/prisma/schema/app.kvox.notes.prisma +746 -0
  221. package/host/prisma/schema/app.kvox.search.prisma +208 -0
  222. package/host/prisma/schema/app.kvox.transcripts.prisma +726 -0
  223. package/host/prisma/schema/platform.ai.prisma +419 -0
  224. package/host/prisma/schema/platform.android-app.prisma +46 -0
  225. package/host/prisma/schema/platform.base.prisma +15 -0
  226. package/host/prisma/schema/platform.credentials.prisma +156 -0
  227. package/host/prisma/schema/platform.db-backup.prisma +359 -0
  228. package/host/prisma/schema/platform.identity.prisma +638 -0
  229. package/host/prisma/schema/platform.jobs.prisma +824 -0
  230. package/host/prisma/schema/platform.notifications.prisma +641 -0
  231. package/host/prisma/schema/platform.settings.prisma +53 -0
  232. package/host/prisma/schema/platform.sharing.prisma +208 -0
  233. package/host/prisma/schema/platform.storage.prisma +118 -0
  234. package/package.json +112 -1
  235. package/parity/requests.ask.json +2681 -0
  236. package/parity/requests.m01.json +1554 -0
  237. package/parity/requests.m02.json +1554 -0
  238. package/parity/requests.m03.json +1554 -0
  239. package/parity/requests.m04.json +1554 -0
  240. package/parity/requests.m05.json +1554 -0
  241. package/parity/requests.m06.json +1554 -0
  242. package/parity/requests.m07.json +1554 -0
  243. package/parity/requests.m08.json +1554 -0
  244. package/parity/requests.m09.json +1554 -0
  245. package/parity/requests.m10.json +1554 -0
@@ -0,0 +1,97 @@
1
+ -- =============================================================================
2
+ -- Open the sharing vocabularies (PP-14.15b, issue #967)
3
+ -- =============================================================================
4
+ --
5
+ -- Two closed Prisma enums become plain TEXT, so an app can add a group role
6
+ -- (`registerGroupRole`) and a persisted grantee kind (`registerGranteeKind`)
7
+ -- at zero further migrations:
8
+ --
9
+ -- group_members.role, group_invites.role "GroupRole" admin | editor | viewer
10
+ -- grants.grantee_kind "GrantGranteeKind" user | group | link
11
+ --
12
+ -- Every stored value is kept as it is (the cast is the enum's own label) and
13
+ -- both enums are dropped. No CHECK on the role columns on purpose: a row must
14
+ -- outlive the role that wrote it, and the allowed set is the API's registry.
15
+ --
16
+ -- grants gains the nullable `grantee_ref` TEXT: the reference of a grantee of an
17
+ -- app-registered kind (a domain, a team id), as the kind normalised it. The
18
+ -- built-in kinds keep their own columns (grantee_user_id, grantee_group_id,
19
+ -- link_token_hash) and leave it NULL.
20
+ --
21
+ -- RAW SQL (intentional schema drift, as in 0027_add_grants):
22
+ -- grants_grantee_consistency_check relaxed: the three built-in row shapes
23
+ -- (now also with grantee_ref NULL) OR a row
24
+ -- of any OTHER kind that carries a
25
+ -- grantee_ref and none of the built-in
26
+ -- grantee columns. This CHECK is the second
27
+ -- line of defence behind the API.
28
+ -- grants_active_user_uniq_idx,
29
+ -- grants_active_group_uniq_idx rebuilt unchanged apart from the predicate
30
+ -- literal, which was an enum cast and is
31
+ -- now text (an index predicate over a column
32
+ -- whose type changes cannot be re-parsed).
33
+ -- grants_active_ref_uniq_idx NEW: at most one ACTIVE grant per
34
+ -- (resource, app kind, reference); the grants
35
+ -- API upserts on it.
36
+ -- All three indexes are listed in raw-sql-indexes.json.
37
+ --
38
+ -- group_members, group_invites and grants are organization tables under forced
39
+ -- row-level security, so the migration runs with the bypass on for its
40
+ -- transaction (set_config with is_local = true: transaction-local, never
41
+ -- session-level).
42
+ --
43
+ -- LOCKING. Each ALTER TABLE takes an ACCESS EXCLUSIVE lock held until COMMIT, so
44
+ -- no concurrent write can slip a duplicate in while an index is dropped and
45
+ -- rebuilt. The three column changes rewrite their tables (the sharing tables
46
+ -- are small: one row per membership, invite and grant).
47
+ -- =============================================================================
48
+
49
+ BEGIN;
50
+ SELECT set_config('app.rls_bypass', 'on', true);
51
+
52
+ -- Dependents of grants.grantee_kind: the two partial unique indexes and the CHECK.
53
+ DROP INDEX "grants_active_user_uniq_idx";
54
+ DROP INDEX "grants_active_group_uniq_idx";
55
+ ALTER TABLE "grants" DROP CONSTRAINT "grants_grantee_consistency_check";
56
+
57
+ -- AlterTable (the default is an enum literal: drop it first, set the text one after)
58
+ ALTER TABLE "group_members" ALTER COLUMN "role" DROP DEFAULT;
59
+ ALTER TABLE "group_members" ALTER COLUMN "role" TYPE TEXT USING "role"::text;
60
+ ALTER TABLE "group_members" ALTER COLUMN "role" SET DEFAULT 'viewer';
61
+
62
+ -- AlterTable
63
+ ALTER TABLE "group_invites" ALTER COLUMN "role" DROP DEFAULT;
64
+ ALTER TABLE "group_invites" ALTER COLUMN "role" TYPE TEXT USING "role"::text;
65
+ ALTER TABLE "group_invites" ALTER COLUMN "role" SET DEFAULT 'viewer';
66
+
67
+ -- AlterTable
68
+ ALTER TABLE "grants" ALTER COLUMN "grantee_kind" TYPE TEXT USING "grantee_kind"::text;
69
+ ALTER TABLE "grants" ADD COLUMN "grantee_ref" TEXT;
70
+
71
+ -- DropEnum
72
+ DROP TYPE "GroupRole";
73
+
74
+ -- DropEnum
75
+ DROP TYPE "GrantGranteeKind";
76
+
77
+ -- CreateIndex (partial: one active grant per resource and user)
78
+ CREATE UNIQUE INDEX "grants_active_user_uniq_idx" ON "grants"("resource_type", "resource_id", "grantee_user_id") WHERE "grantee_kind" = 'user' AND "revoked_at" IS NULL;
79
+
80
+ -- CreateIndex (partial: one active grant per resource and group)
81
+ CREATE UNIQUE INDEX "grants_active_group_uniq_idx" ON "grants"("resource_type", "resource_id", "grantee_group_id") WHERE "grantee_kind" = 'group' AND "revoked_at" IS NULL;
82
+
83
+ -- CreateIndex (partial: one active grant per resource, app-registered kind and reference)
84
+ CREATE UNIQUE INDEX "grants_active_ref_uniq_idx" ON "grants"("resource_type", "resource_id", "grantee_kind", "grantee_ref") WHERE "grantee_kind" NOT IN ('user', 'group', 'link') AND "revoked_at" IS NULL;
85
+
86
+ -- AddCheckConstraint (the grantee columns match the grantee kind)
87
+ ALTER TABLE "grants" ADD CONSTRAINT "grants_grantee_consistency_check" CHECK (
88
+ ("grantee_kind" = 'user' AND "grantee_user_id" IS NOT NULL AND "grantee_group_id" IS NULL AND "link_token_hash" IS NULL AND "grantee_ref" IS NULL)
89
+ OR
90
+ ("grantee_kind" = 'group' AND "grantee_user_id" IS NULL AND "grantee_group_id" IS NOT NULL AND "link_token_hash" IS NULL AND "grantee_ref" IS NULL)
91
+ OR
92
+ ("grantee_kind" = 'link' AND "grantee_user_id" IS NULL AND "grantee_group_id" IS NULL AND "link_token_hash" IS NOT NULL AND "grantee_ref" IS NULL)
93
+ OR
94
+ ("grantee_kind" NOT IN ('user', 'group', 'link') AND "grantee_ref" IS NOT NULL AND "grantee_user_id" IS NULL AND "grantee_group_id" IS NULL AND "link_token_hash" IS NULL)
95
+ );
96
+
97
+ COMMIT;
@@ -0,0 +1,322 @@
1
+ -- =============================================================================
2
+ -- Transcripts (issue #24, epic #19)
3
+ -- =============================================================================
4
+ -- Six tables: `transcripts`, `transcript_speakers`, `transcript_segments`,
5
+ -- `transcript_versions`, `transcript_shares`, `transcript_exports`. See the
6
+ -- block comment above the `Transcript` model in prisma/schema.prisma for the
7
+ -- full reasoning behind every `onDelete` choice below (in particular why
8
+ -- `owner_id` cascades while every `storage_objects` FK on these tables
9
+ -- restricts, and why `speaker_id` on `transcript_segments` also restricts)
10
+ -- and docs/specs/transcription.md §3–§6 for the product reasoning this file
11
+ -- intentionally does not restate at length.
12
+ --
13
+ -- ONE HAND-WRITTEN, PRISMA-INEXPRESSIBLE CONSTRAINT, flagged where it
14
+ -- appears below: `transcript_speakers_transcript_id_label_key`, a PARTIAL
15
+ -- unique index (`WHERE "label" IS NOT NULL`) — the same
16
+ -- Prisma-DSL-has-no-WHERE-clause pattern `jobs_active_dedup_uniq_idx` and
17
+ -- `database_backup_runs_active_uniq_idx` already establish in this
18
+ -- repository. `prisma migrate dev`/`diff` will never regenerate it and will
19
+ -- want to drop it on a naive reconcile — do not let it. Every OTHER unique
20
+ -- constraint in this migration (`transcript_versions` on `(transcript_id,
21
+ -- version)` and `(transcript_id, client_batch_id)`, `transcript_shares` on
22
+ -- `(transcript_id, user_id)`) is a plain, fully Prisma-expressible unique
23
+ -- index and needed no such treatment.
24
+ -- =============================================================================
25
+
26
+ -- CreateEnum
27
+ CREATE TYPE "TranscriptStatus" AS ENUM ('uploading', 'processing', 'ready', 'failed', 'deleting');
28
+
29
+ -- CreateEnum
30
+ CREATE TYPE "TranscriptionStatus" AS ENUM ('waiting_input', 'queued', 'submitting', 'submitted', 'processing', 'completed', 'failed', 'cancelled');
31
+
32
+ -- CreateEnum
33
+ CREATE TYPE "PlaybackStatus" AS ENUM ('pending', 'processing', 'ready', 'failed', 'not_needed');
34
+
35
+ -- CreateEnum
36
+ CREATE TYPE "WordsAlignment" AS ENUM ('exact', 'interpolated', 'none');
37
+
38
+ -- CreateEnum
39
+ CREATE TYPE "SegmentOrigin" AS ENUM ('ai', 'user');
40
+
41
+ -- CreateEnum
42
+ CREATE TYPE "TranscriptVersionKind" AS ENUM ('ai_original', 'edit', 'restore');
43
+
44
+ -- CreateEnum
45
+ CREATE TYPE "TranscriptShareRole" AS ENUM ('viewer', 'editor');
46
+
47
+ -- CreateEnum
48
+ CREATE TYPE "TranscriptExportStatus" AS ENUM ('pending', 'ready', 'failed');
49
+
50
+ -- CreateTable
51
+ CREATE TABLE "transcripts" (
52
+ "id" UUID NOT NULL,
53
+ "owner_id" UUID NOT NULL,
54
+ "title" TEXT NOT NULL,
55
+ "language" TEXT,
56
+ "status" "TranscriptStatus" NOT NULL DEFAULT 'uploading',
57
+ "transcription_status" "TranscriptionStatus" NOT NULL DEFAULT 'waiting_input',
58
+ "playback_status" "PlaybackStatus" NOT NULL DEFAULT 'pending',
59
+ "source_object_id" UUID NOT NULL,
60
+ "playback_object_id" UUID,
61
+ "raw_result_object_id" UUID,
62
+ "provider" TEXT NOT NULL,
63
+ "provider_job_id" TEXT,
64
+ "provider_options" JSONB,
65
+ "submitted_at" TIMESTAMPTZ,
66
+ "last_polled_at" TIMESTAMPTZ,
67
+ "completed_at" TIMESTAMPTZ,
68
+ "remote_deleted_at" TIMESTAMPTZ,
69
+ "deleted_at" TIMESTAMPTZ,
70
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
71
+ "updated_at" TIMESTAMPTZ NOT NULL,
72
+ "duration_ms" INTEGER,
73
+ "failure_reason" TEXT,
74
+ "current_version" INTEGER NOT NULL DEFAULT 0,
75
+ "speaker_count" INTEGER NOT NULL DEFAULT 0,
76
+ "word_count" INTEGER NOT NULL DEFAULT 0,
77
+
78
+ CONSTRAINT "transcripts_pkey" PRIMARY KEY ("id")
79
+ );
80
+
81
+ -- CreateTable
82
+ CREATE TABLE "transcript_speakers" (
83
+ "id" UUID NOT NULL,
84
+ "transcript_id" UUID NOT NULL,
85
+ "label" TEXT,
86
+ "display_name" TEXT NOT NULL,
87
+ "color_index" INTEGER NOT NULL,
88
+ "rev" INTEGER NOT NULL DEFAULT 1,
89
+
90
+ CONSTRAINT "transcript_speakers_pkey" PRIMARY KEY ("id")
91
+ );
92
+
93
+ -- CreateTable
94
+ CREATE TABLE "transcript_segments" (
95
+ "id" UUID NOT NULL,
96
+ "transcript_id" UUID NOT NULL,
97
+ "speaker_id" UUID NOT NULL,
98
+ "start_ms" INTEGER NOT NULL,
99
+ "end_ms" INTEGER NOT NULL,
100
+ "ordinal" DOUBLE PRECISION NOT NULL,
101
+ "text" TEXT NOT NULL,
102
+ "words" JSONB NOT NULL,
103
+ "words_alignment" "WordsAlignment" NOT NULL DEFAULT 'exact',
104
+ "confidence" DOUBLE PRECISION,
105
+ "origin" "SegmentOrigin" NOT NULL DEFAULT 'ai',
106
+ "rev" INTEGER NOT NULL DEFAULT 1,
107
+ "edited_by_id" UUID,
108
+ "edited_at" TIMESTAMPTZ,
109
+
110
+ CONSTRAINT "transcript_segments_pkey" PRIMARY KEY ("id")
111
+ );
112
+
113
+ -- CreateTable
114
+ CREATE TABLE "transcript_versions" (
115
+ "id" UUID NOT NULL,
116
+ "transcript_id" UUID NOT NULL,
117
+ "version" INTEGER NOT NULL,
118
+ "kind" "TranscriptVersionKind" NOT NULL,
119
+ "author_id" UUID,
120
+ "summary" TEXT,
121
+ "ops" JSONB NOT NULL,
122
+ "restored_from_version" INTEGER,
123
+ "snapshot_object_id" UUID,
124
+ "client_batch_id" TEXT,
125
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
126
+
127
+ CONSTRAINT "transcript_versions_pkey" PRIMARY KEY ("id")
128
+ );
129
+
130
+ -- CreateTable
131
+ CREATE TABLE "transcript_shares" (
132
+ "id" UUID NOT NULL,
133
+ "transcript_id" UUID NOT NULL,
134
+ "user_id" UUID NOT NULL,
135
+ "role" "TranscriptShareRole" NOT NULL,
136
+ "granted_by_id" UUID NOT NULL,
137
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
138
+
139
+ CONSTRAINT "transcript_shares_pkey" PRIMARY KEY ("id")
140
+ );
141
+
142
+ -- CreateTable
143
+ CREATE TABLE "transcript_exports" (
144
+ "id" UUID NOT NULL,
145
+ "transcript_id" UUID NOT NULL,
146
+ "version" INTEGER NOT NULL,
147
+ "format" TEXT NOT NULL,
148
+ "options" JSONB NOT NULL,
149
+ "options_hash" TEXT NOT NULL,
150
+ "status" "TranscriptExportStatus" NOT NULL DEFAULT 'pending',
151
+ "object_id" UUID,
152
+ "job_id" UUID,
153
+ "requested_by_id" UUID NOT NULL,
154
+ "error" TEXT,
155
+ "expires_at" TIMESTAMPTZ NOT NULL,
156
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
157
+
158
+ CONSTRAINT "transcript_exports_pkey" PRIMARY KEY ("id")
159
+ );
160
+
161
+ -- CreateIndex
162
+ -- "This owner's transcripts, newest first" — the transcript list view.
163
+ CREATE INDEX "transcripts_owner_id_updated_at_idx" ON "transcripts"("owner_id", "updated_at" DESC);
164
+
165
+ -- CreateIndex
166
+ -- "Every transcript in state X" — transcripts.housekeeping's scans (spec
167
+ -- §1.5.8) and any future admin-adjacent health check.
168
+ CREATE INDEX "transcripts_status_idx" ON "transcripts"("status");
169
+
170
+ -- =============================================================================
171
+ -- HAND-WRITTEN INDEX — INTENTIONAL SCHEMA DRIFT (issue #24, epic #19)
172
+ -- =============================================================================
173
+ -- NOT emitted by `prisma migrate dev`/`diff` and never will be: it needs a
174
+ -- partial-index `WHERE` clause, which the Prisma schema language has no
175
+ -- syntax for. See the block comment above the `Transcript` model in
176
+ -- prisma/schema.prisma for the same fact recorded where anyone reading the
177
+ -- schema without this file open will see it, and
178
+ -- 20260907120000_add_database_backup_runs/migration.sql's own header for the
179
+ -- precedent this follows to the letter.
180
+ --
181
+ -- ⚠ `prisma migrate dev` WILL WANT TO DROP THIS on the next diff. Do not
182
+ -- "reconcile" that away, and do not expect `prisma migrate diff` against a
183
+ -- live database to come back clean — this drift is deliberate and permanent
184
+ -- for as long as Prisma's DSL lacks partial indexes.
185
+ --
186
+ -- A provider-labelled speaker's `label` (e.g. "A") must be unique per
187
+ -- transcript; a user-created speaker (`speaker.create`, spec §4.1)
188
+ -- legitimately has none, so the constraint is scoped to labelled rows only.
189
+ -- =============================================================================
190
+ CREATE UNIQUE INDEX "transcript_speakers_transcript_id_label_key"
191
+ ON "transcript_speakers" ("transcript_id", "label") WHERE "label" IS NOT NULL;
192
+
193
+ -- CreateIndex
194
+ -- The compound order a segment list and a time-window query
195
+ -- (`GET /:id/words?fromMs&toMs`) both need (spec §3.3).
196
+ CREATE INDEX "transcript_segments_transcript_id_start_ms_ordinal_idx" ON "transcript_segments"("transcript_id", "start_ms", "ordinal");
197
+
198
+ -- CreateIndex
199
+ -- The version sequence itself, and what `materialize()` (spec §4.4) looks
200
+ -- up by. A plain UNIQUE index — Postgres needs no partial clause here.
201
+ CREATE UNIQUE INDEX "transcript_versions_transcript_id_version_key" ON "transcript_versions"("transcript_id", "version");
202
+
203
+ -- CreateIndex
204
+ -- The idempotent-retry key for a batch save (spec §5). Also a plain UNIQUE
205
+ -- index, deliberately NOT hand-written like the speaker label above:
206
+ -- PostgreSQL's standard NULLS-DISTINCT behaviour already gives exactly the
207
+ -- wanted semantics for this nullable column (any number of
208
+ -- null-`client_batch_id` versions, e.g. ingest/restore, may coexist; at most
209
+ -- one per transcript for any given non-null value).
210
+ CREATE UNIQUE INDEX "transcript_versions_transcript_id_client_batch_id_key" ON "transcript_versions"("transcript_id", "client_batch_id");
211
+
212
+ -- CreateIndex
213
+ -- `POST /api/transcripts/:id/shares` (spec §6.3) upserts on this pair — one
214
+ -- role per (transcript, user), never two competing rows.
215
+ CREATE UNIQUE INDEX "transcript_shares_transcript_id_user_id_key" ON "transcript_shares"("transcript_id", "user_id");
216
+
217
+ -- CreateIndex
218
+ -- One `transcript_exports` row per rendering job — mirrors
219
+ -- `database_backup_runs_job_id_key` exactly (see that model's own `jobId`
220
+ -- comment): a second render for the SAME job is unrepresentable.
221
+ CREATE UNIQUE INDEX "transcript_exports_job_id_key" ON "transcript_exports"("job_id");
222
+
223
+ -- CreateIndex
224
+ -- The reuse-by-content-hash lookup spec §8.5 names explicitly ("the index
225
+ -- issue #24 declares for exactly this lookup"). Not unique: an expired or
226
+ -- failed row may briefly coexist with a fresh one sharing the same key
227
+ -- before housekeeping removes the stale one. Named explicitly
228
+ -- (`map: "transcript_exports_lookup_idx"` in schema.prisma) because the
229
+ -- default four-column name is 64 bytes, one over PostgreSQL's 63-byte
230
+ -- identifier limit.
231
+ CREATE INDEX "transcript_exports_lookup_idx" ON "transcript_exports"("transcript_id", "version", "format", "options_hash");
232
+
233
+ -- AddForeignKey
234
+ -- Cascade: a transcript's only reason to exist is that exactly one user
235
+ -- owns it, and this application's access model has no path to it once its
236
+ -- owner is gone (no `transcripts:read_any`, spec §6.2). See the block
237
+ -- comment above the `Transcript` model for the full reasoning, including
238
+ -- the honestly-stated consequence for storage cleanup on a raw user delete.
239
+ ALTER TABLE "transcripts" ADD CONSTRAINT "transcripts_owner_id_fkey" FOREIGN KEY ("owner_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
240
+
241
+ -- AddForeignKey
242
+ -- Restrict, not Cascade: a storage_objects row a transcript still
243
+ -- references may never be deleted out from under it by an unrelated
244
+ -- storage cleanup. The only path that may remove it is `transcript.purge`
245
+ -- deleting the referencing transcripts row first (spec §1.5.7, §10), which
246
+ -- Restrict permits freely.
247
+ ALTER TABLE "transcripts" ADD CONSTRAINT "transcripts_source_object_id_fkey" FOREIGN KEY ("source_object_id") REFERENCES "storage_objects"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
248
+
249
+ -- AddForeignKey
250
+ ALTER TABLE "transcripts" ADD CONSTRAINT "transcripts_playback_object_id_fkey" FOREIGN KEY ("playback_object_id") REFERENCES "storage_objects"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
251
+
252
+ -- AddForeignKey
253
+ ALTER TABLE "transcripts" ADD CONSTRAINT "transcripts_raw_result_object_id_fkey" FOREIGN KEY ("raw_result_object_id") REFERENCES "storage_objects"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
254
+
255
+ -- AddForeignKey
256
+ ALTER TABLE "transcript_speakers" ADD CONSTRAINT "transcript_speakers_transcript_id_fkey" FOREIGN KEY ("transcript_id") REFERENCES "transcripts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
257
+
258
+ -- AddForeignKey
259
+ ALTER TABLE "transcript_segments" ADD CONSTRAINT "transcript_segments_transcript_id_fkey" FOREIGN KEY ("transcript_id") REFERENCES "transcripts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
260
+
261
+ -- AddForeignKey
262
+ -- Restrict: a speaker may not be deleted while a segment still names it.
263
+ -- `speaker.merge` (spec §4.1) re-points every segment away first, in the
264
+ -- same transaction, before deleting the source speaker rows.
265
+ ALTER TABLE "transcript_segments" ADD CONSTRAINT "transcript_segments_speaker_id_fkey" FOREIGN KEY ("speaker_id") REFERENCES "transcript_speakers"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
266
+
267
+ -- AddForeignKey
268
+ -- SetNull, matching AuditEvent.actorUserId / Credential.updatedByUserId:
269
+ -- "who last hand-edited this line" is an audit fact, not something the
270
+ -- segment's own existence depends on.
271
+ ALTER TABLE "transcript_segments" ADD CONSTRAINT "transcript_segments_edited_by_id_fkey" FOREIGN KEY ("edited_by_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
272
+
273
+ -- AddForeignKey
274
+ ALTER TABLE "transcript_versions" ADD CONSTRAINT "transcript_versions_transcript_id_fkey" FOREIGN KEY ("transcript_id") REFERENCES "transcripts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
275
+
276
+ -- AddForeignKey
277
+ -- SetNull: a version is permanent history (spec §4.5 — nothing ever deletes
278
+ -- a transcript_versions row short of purging the whole transcript) and must
279
+ -- survive its author's own account being deleted, the same audit posture as
280
+ -- DatabaseBackupRun.createdById. NULL already means "the AI" for a
281
+ -- different reason (the ingest handler never sets an author at all) — this
282
+ -- FK action only ever fires for a human-authored version whose author was
283
+ -- later deleted.
284
+ ALTER TABLE "transcript_versions" ADD CONSTRAINT "transcript_versions_author_id_fkey" FOREIGN KEY ("author_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
285
+
286
+ -- AddForeignKey
287
+ -- Restrict, matching every other storage_objects FK on this feature above.
288
+ ALTER TABLE "transcript_versions" ADD CONSTRAINT "transcript_versions_snapshot_object_id_fkey" FOREIGN KEY ("snapshot_object_id") REFERENCES "storage_objects"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
289
+
290
+ -- AddForeignKey
291
+ ALTER TABLE "transcript_shares" ADD CONSTRAINT "transcript_shares_transcript_id_fkey" FOREIGN KEY ("transcript_id") REFERENCES "transcripts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
292
+
293
+ -- AddForeignKey
294
+ -- Cascade: a share row has no meaning once its recipient is gone.
295
+ ALTER TABLE "transcript_shares" ADD CONSTRAINT "transcript_shares_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
296
+
297
+ -- AddForeignKey
298
+ -- Cascade: today the granter is always the owner, whose deletion already
299
+ -- cascades the transcript (and this row via transcript_id) regardless; see
300
+ -- the schema.prisma block comment for the full reasoning.
301
+ ALTER TABLE "transcript_shares" ADD CONSTRAINT "transcript_shares_granted_by_id_fkey" FOREIGN KEY ("granted_by_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
302
+
303
+ -- AddForeignKey
304
+ ALTER TABLE "transcript_exports" ADD CONSTRAINT "transcript_exports_transcript_id_fkey" FOREIGN KEY ("transcript_id") REFERENCES "transcripts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
305
+
306
+ -- AddForeignKey
307
+ -- Restrict, matching every other storage_objects FK on this feature above.
308
+ ALTER TABLE "transcript_exports" ADD CONSTRAINT "transcript_exports_object_id_fkey" FOREIGN KEY ("object_id") REFERENCES "storage_objects"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
309
+
310
+ -- AddForeignKey
311
+ -- SetNull, mirroring DatabaseBackupRun.jobId exactly: job.history.purge
312
+ -- deletes jobs rows on a retention schedule independent of this row's own
313
+ -- 7-day export expiry, and losing the LINK must never mean losing the
314
+ -- EXPORT.
315
+ ALTER TABLE "transcript_exports" ADD CONSTRAINT "transcript_exports_job_id_fkey" FOREIGN KEY ("job_id") REFERENCES "jobs"("id") ON DELETE SET NULL ON UPDATE CASCADE;
316
+
317
+ -- AddForeignKey
318
+ -- Cascade: unlike every SetNull audit column elsewhere in this schema, an
319
+ -- export is a disposable, byte-for-byte reproducible artifact with a 7-day
320
+ -- expiry already built in — nothing is lost by deleting a pending/ready
321
+ -- export row alongside the account that requested it.
322
+ ALTER TABLE "transcript_exports" ADD CONSTRAINT "transcript_exports_requested_by_id_fkey" FOREIGN KEY ("requested_by_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;