feat(sources): MP-aligned TMDB recall, alias registration guard, re-resolve on retitle (ID-2)

recallTmdbCandidates searches cn then en names in the preferred language by
type and fetches details for a bounded set of candidates (MP alias fallback);
translations become aliases; bindAuto no longer registers generic or
alias-only site titles; snapshot/feed title changes reparse and force a
re-resolve that unbinds when it can no longer confirm.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-29 17:41:22 +08:00
1 parent f6ec993b5f
commit 387f7931e0
9 files changed
+269 -39

No files matched your search

@@ -1,5 +1,6 @@
import { CapabilityRegistry } from "@umt/agent";
import { sql } from "@umt/db";
import { PARSER_VERSION } from "@umt/domain";
import { createMockTmdb, type MockTmdb, TMDB_FIXTURES, type TmdbFixture } from "@umt/testkit";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { refreshMetadata } from "../../src/modules/sources-identity/service/metadata.ts";
@@ -82,7 +83,11 @@ describe.skipIf(!up)("resolveObservation / 身份与元数据", () => {
const created = await h.db.selectFrom("media_entity").selectAll().where("id", "=", oppId).executeTakeFirstOrThrow();
expect((created.metadata as Record<string, { source: string }>).overview?.source).toBe("tmdb");
const rel = await h.db.selectFrom("source_release").selectAll().where("id", "=", r.id).executeTakeFirstOrThrow();
expect(rel).toMatchObject({ resolution_status: "resolved", media_entity_id: oppId, parser_version: "1" });
expect(rel).toMatchObject({
resolution_status: "resolved",
media_entity_id: oppId,
parser_version: String(PARSER_VERSION),
});
expect((rel.parsed as { year: number }).year).toBe(2023);
const ev = (await h.outbox("release.resolved")).find((x) => x.payload.sourceReleaseId === r.id);
expect(ev?.payload).toMatchObject({ mediaEntityId: oppId, evidenceLevel: "rule_confirmed", contentUnitIds: [] });