docs(xtream): record why category names stay scoped to matched rows

Codex flagged that the FTS predicate runs before the aggregate, so a
sibling row under a localized title contributes no category. True, and
deliberate: the field is a guess feeding a chip and a browse filter, and
completing it costs measured latency — 0.74s to 2.0s for a correlated
subquery on a 3.9GB catalog, 19.7s for a second bounded lookup — to
correct a cosmetic guess in a shape that occurs 0 times in 2.7M rows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Fable 5 committed 2026-08-12 01:49:34 +02:00
1 parent 5063e53f71
commit a5c818b5a4
2 files changed
+30 -8

No files matched your search

@@ -345,13 +345,24 @@ export async function findTitleSources(
// One playlist can list the same film in several categories; the user
// thinks of that as one source.
const seen = new Set<string>();
// Every category the stream sits in, merged across the rows that carry
// it. The FTS tier already joined them into one row, so this is a no-op
// there; the scan tier returns a row per category and must not group (see
// `scanCandidateQuery`), so this is where its names come together. Rows
// the confirmation later rejects still contribute — a differently titled
// sibling row is the same stream, and its category says the same thing
// about the language.
// Every category the stream sits in AMONG THE ROWS THE QUERY MATCHED,
// merged. The FTS tier already joined those into one row, so this is a
// no-op there; the scan tier returns a row per category and must not
// group (see `scanCandidateQuery`), so this is where its names come
// together. Rows the confirmation later rejects still contribute — a
// differently titled sibling row is the same stream, and its category
// says the same thing about the language.
//
// A sibling row whose title did NOT match is invisible here, so a stream
// listed under a localized title in another category can look
// unanimous when it is not. That is deliberate. The field is a guess
// feeding a chip and a browse filter — ranking and failover cannot reach
// it — and completing it costs real latency: measured on a 3.9 GB
// catalog, resolving every category through a correlated subquery takes
// the discovery query from 0.74 s to 2.0 s, and a second bounded lookup
// for the same 60 streams takes 19.7 s. A detail page pays that on every
// open, to correct a cosmetic guess in a shape that occurs 0 times in
// 2.7M rows.
const categoryNames = new Map<string, string[]>();
for (const row of rows) {
const key = `${row.playlist_id}:${row.xtream_id}`;
+12 -1
View File
@@ -227,7 +227,18 @@ the scan tier must NOT group. `content` is unique per
rows whose titles need not agree; grouping would let SQLite keep an arbitrary
one and the normalized confirmation would then reject a stream that a sibling
row would have matched. The scan therefore returns a row per category and the
names are merged per stream in TypeScript. Either way
names are merged per stream in TypeScript.
Either tier only ever sees the categories of rows the query MATCHED, so a
stream also listed under a localized title in another category can look
unanimous when it is not. Deliberate: the field is a guess feeding a chip and
a browse filter, and completing it is expensive — on a 3.9 GB catalog,
resolving every category through a correlated subquery takes discovery from
0.74 s to 2.0 s (a cost every detail-page open pays), and a second bounded
lookup for the same 60 streams takes 19.7 s, to correct a cosmetic guess in a
shape that occurs 0 times in 2.7M rows.
Either way
`unambiguousCategoryLanguage` reduces them: categories without a recognized
language prefix abstain, all prefixed ones must agree, and a conflict yields
nothing. Category prefixes must additionally pass `isKnownLanguageTag`.