docs(tmdb): finish the sweep — a missed cache key and a last observed-fact claim

Two spots the first pass left behind:

- `tmdb-search-cache-cleanup.spec.ts` kept a catalog-derived lookup key
  verbatim. Only the TMDB id beside it had been swapped, because the sweep
  matched the title in its display casing and the key stores it lowercased —
  so one item of the inventory stayed in the repository, twice.
- `SearchTitleVariant`'s doc comment still asserted that one specific folded
  string finds nothing on TMDB. State the mechanism instead: folding rewrites
  the letters the search matches on, so the folded form finds nothing there.

`content-search.util.sqlite.spec.ts` gets a channel fixture that no longer
reads as a film title.

`search-text-fold.util.spec.ts` is deliberately left alone. Its "Ёлки" is a
common noun sitting in a Unicode corpus beside "Amélie", "İnşaat" and "Ά",
not an inventory entry — and its rows are precomposed/decomposed PAIRS
(U+0401 against U+0415 U+0308). A textual substitution rewrites only the
composed half and silently turns the pair into two different words; doing it
failed that spec, which is what a fixture encoding an invisible property is
for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5 committed 2026-09-21 20:46:36 +02:00
1 parent f0096c6437
commit 278079519f
3 files changed
+6 -6

No files matched your search

@@ -30,7 +30,7 @@ const TITLES = [
'Şan Ve Şeref',
'Первый канал HD',
'ПЕРВЫЙ КАНАЛ',
'Ёлки ТВ',
'Ёжик ТВ',
'Йога для всех',
'Матч ТВ',
'Ελλάδα Σήμερα',
@@ -179,7 +179,7 @@ describe('content-search.util against SQLite', () => {
it.each([
['Первый', 'первый', ['Первый канал HD', 'ПЕРВЫЙ КАНАЛ']],
['ПЕРВ', 'перв', ['Первый канал HD', 'ПЕРВЫЙ КАНАЛ']],
['Ёлки', 'ёлки', ['Ёлки ТВ']],
['Ёжик', 'ёжик', ['Ёжик ТВ']],
['Йога', 'йога', ['Йога для всех']],
['Ма', 'ма', ['Матч ТВ']],
['ΕΛΛ', 'ελλ', ['Ελλάδα Σήμερα']],
+2 -2
View File
@@ -40,8 +40,8 @@ function stripLeadingLanguageToken(raw: string): string | null {
/**
* One search candidate: what to SEND to TMDB and what to COMPARE its
* answers against. The two differ on purpose — see `cleanTitleForSearch`:
* a folded query ("леика") finds nothing on TMDB while the folded key is
* exactly what the confidence gate and the cache need.
* folding rewrites letters TMDB matches on, so the folded form finds nothing
* there, while it is exactly what the confidence gate and the cache need.
*/
export interface SearchTitleVariant {
/** Provider spelling with tags/brackets/season/year stripped */
@@ -63,7 +63,7 @@ it('drops only retired search rows across skipped, previous, pre-person, fresh a
hooks.runMigrations(skipped);
const skippedAfter = snapshot(skipped);
// Next startup: a row written meanwhile under the current key survives
skipped.prepare("INSERT INTO tmdb_metadata (media_type, lookup_key, language, tmdb_id) VALUES ('tv', 'title:гудовы|year:2026|v4', 'ru-RU', 101103)").run();
skipped.prepare("INSERT INTO tmdb_metadata (media_type, lookup_key, language, tmdb_id) VALUES ('tv', 'title:сосенка|year:2026|v4', 'ru-RU', 101103)").run();
hooks.runMigrations(skipped);
const repeated = snapshot(skipped);
// Previous release: the earlier cleanups already ran; only v3 rows go
@@ -124,7 +124,7 @@ it('drops only retired search rows across skipped, previous, pre-person, fresh a
markers: [V2_MARKER, V3_MARKER, V4_MARKER],
},
repeated: {
keys: [...survivors, 'title:гудовы|year:2026|v4'].sort(),
keys: [...survivors, 'title:сосенка|year:2026|v4'].sort(),
payloads: detailsPayloads,
markers: [V2_MARKER, V3_MARKER, V4_MARKER],
},