fix(core): race-safe matching, visibility-checked ID matches, child-preserving merges

- migration 0002: unique index on works (parent_id, position)
- matchWork retries the whole transaction on a unique violation (3 attempts)
- addMissingExternalIds uses ON CONFLICT DO NOTHING
- ID matches only return shared works or the user's own; private works cannot carry external ids
- adminMergeWorks requires shared works and repoints non-colliding children
- draft is computed for the aliased work id

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-10-30 19:29:48 +01:00
1 parent a46fb60eee
commit 437ca143e5
9 files changed
+270 -18

No files matched your search

@@ -0,0 +1,16 @@
import type { Kysely } from "kysely";
import type { Migration } from "kysely/migration";
/**
* One child per (parent, position), so concurrent matches cannot duplicate seasons or episodes.
* NULLs are distinct on both dialects: top-level works and children without a position are unaffected.
*/
export const worksPositionUnique: Migration = {
async up(db: Kysely<any>) {
await db.schema.createIndex("works_parent_id_position_uq").unique().on("works").columns(["parent_id", "position"]).execute();
},
async down(db: Kysely<any>) {
await db.schema.dropIndex("works_parent_id_position_uq").execute();
},
};
+2
View File
@@ -1,7 +1,9 @@
import type { Migration } from "kysely/migration";
import { initial } from "./0001_initial.ts";
import { worksPositionUnique } from "./0002_works_position_unique.ts";
/** All migrations, keyed by sortable name. Imported statically so bundles need no filesystem. */
export const migrations: Record<string, Migration> = {
"0001_initial": initial,
"0002_works_position_unique": worksPositionUnique,
};