fix(library): first-pass enrich gate skips post-resolve skeletons

isActive() for book/game/comic only checked for a resolved id, so a
note that had run resolve() but never a real sync() (no read_status/
play_status/status field yet) was treated as static and never
enriched on subsequent runs. Now also requires the status-analog
field canonical render always writes; missing it means sync() hasn't
actually produced output yet.
This commit is contained in:
afiqzudinhadi 2026-08-03 22:08:09 +08:00
parent 6225231d67
commit 1d5537742f
6 changed files with 41 additions and 11 deletions

View file

@ -126,9 +126,12 @@ export const bookSpec: MediaTypeSpec = {
},
isActive(fm: Record<string, string>): boolean {
// static once enriched; no chapter/issue sources, no flips -- only a missing
// olid ever makes a book note active again (a full sync bypasses this check)
return !stripQuotes(fm['olid']);
// active until the first real enrich has actually run: missing olid (never
// resolved) OR missing read_status (post-resolve skeleton -- canonical render
// always writes read_status, so its absence means sync() hasn't produced
// canonical output yet). Static once both are present; no chapter/issue sources,
// no flips -- a full sync bypasses this check regardless.
return !stripQuotes(fm['olid']) || !stripQuotes(fm['read_status']);
},
async resolve(ctx: LibraryNoteCtx, deps: SpecDeps): Promise<Record<string, string> | null> {

View file

@ -158,6 +158,9 @@ export const comicSpec: MediaTypeSpec = {
isActive(fm: Record<string, string>): boolean {
if (!stripQuotes(fm['comicvine_id'])) return true; // never enriched -> needs first pass
// post-resolve skeleton -- canonical render always writes status, so its absence
// means sync() hasn't produced canonical output yet
if (!stripQuotes(fm['status'])) return true;
if (stripQuotes(fm['status']) === 'Ongoing') return true;
if (stripQuotes(fm['read_status']) === 'Reading') return true;
return false;

View file

@ -192,9 +192,12 @@ export const gameSpec: MediaTypeSpec = {
},
isActive(fm: Record<string, string>): boolean {
// static once either id is set; no chapter/issue-style automation exists for games --
// only missing both ids ever makes a game note active again (full sync bypasses this)
return !stripQuotes(fm['steam_appid']) && !stripQuotes(fm['rawg_id']);
// active until the first real enrich has actually run: both ids empty (never
// resolved) OR missing play_status (post-resolve skeleton -- canonical render
// always writes play_status, so its absence means sync() hasn't produced canonical
// output yet). Static once an id + play_status are present; no chapter/issue-style
// automation exists for games -- a full sync bypasses this check regardless.
return (!stripQuotes(fm['steam_appid']) && !stripQuotes(fm['rawg_id'])) || !stripQuotes(fm['play_status']);
},
async resolve(ctx: LibraryNoteCtx, deps: SpecDeps): Promise<Record<string, string> | null> {