feat(issues): the honest processing-issues ledger — row-less import failures, self-healing resolution, family lens (faceted plan Phase 11)
processing_issues only recorded cross-library move rejections; files that failed to import vanished into an error counter and stdout. The ledger now records every failure class and heals itself: Schema (in place, pre-release boundary): - media_item_id NULLABLE + file_path TEXT (deliberately unbounded — an over-long path is itself a failure mode; a VARCHAR(500) ledger column died on the exact 22001 it existed to report — caught by the new test). Row-less failures key on (library, file_path); item-backed on (item, type). Widened dedup = COALESCE-expression unique index; the old table UNIQUE stays (NULLs pairwise-distinct for row-less). - Conflict-target upsert: repeat failures UPDATE the same row and REOPEN resolved ones — never duplicates. Wirings: - CreateMediaItem failure (single-file + multifile createAudioBookItem) → row-less import_failed (error). - Extraction hard-fail with fallback import → item-backed metadata_extraction_failed (warning). - Change-path re-extraction failure (the ev/sidecar guard) → item-backed metadata_extraction_failed; LogProcessingIssue keeps its signature, a row-less helper joins it. Auto-resolution: - Successful import → ResolveOpenProcessingIssuesForPath (library, file_path). - Successful (re)extraction → ResolveOpenProcessingIssuesForItem (single-file change path, multifile editorial apply). - Unchanged-skip scans do NOT resolve: extraction never re-ran, so the degraded metadata is still degraded — the page heals exactly when the book does. Family lens on ALL issues surfaces (list, stats, resolve, delete, SSR page): explicit out-of-set library id → 403, never silent. This tightens one pre-lens behavior — a nonexistent library id returned 200-empty, now 403 (pinned, changelog entry in docs/contract_pins.md). List is LEFT-JOINed so row-less rows appear (media_item_id "", title falls back to the path). Tests: full lifecycle state machine through real scans (fail → appears, repeat → no dups, success-at-path → resolved, LEFT-JOIN list shape); family lens 403s with a fresh non-shared admin; all 7 pre-existing issue suites green (two expectations widened to the lens, changelogged); adjacent suites (CrossLibraryMove, Audiobook, Calibre, ExtractorVersion, SchemaIdempotency) green; unit suites green.
This commit is contained in:
1 parent
19b9c7c9b8
commit
9b5defcb31
50 files changed
+1244
-681
No files matched your search
@@ -621,8 +621,33 @@ CREATE INDEX IF NOT EXISTS idx_processing_issues_library
|
||||
CREATE INDEX IF NOT EXISTS idx_processing_issues_severity
|
||||
ON processing_issues(severity, resolved);
|
||||
|
||||
-- Phase 11 (faceted plan): the ledger records ROW-LESS import failures
|
||||
-- too — a book whose media_items row was never created has no item to
|
||||
-- reference. media_item_id becomes nullable; file_path carries the
|
||||
-- failing file's library-relative path for those rows. The table-level
|
||||
-- UNIQUE stays (item-backed safety; row-less rows have NULL item ids,
|
||||
-- which unique constraints treat as pairwise-distinct). The widened
|
||||
-- dedup key is a COALESCE expression index covering BOTH classes in one
|
||||
-- shape: item-backed = (item, library, type, ''), row-less =
|
||||
-- (zero-uuid, library, type, path). An expression index (not NULLS NOT
|
||||
-- DISTINCT) because the two classes key on different columns.
|
||||
ALTER TABLE processing_issues ALTER COLUMN media_item_id DROP NOT NULL;
|
||||
-- TEXT, deliberately NOT VARCHAR(500): the ledger must be able to
|
||||
-- record paths LONGER than media_items' limit — an over-long path is
|
||||
-- itself a failure mode, and a 500-char cap would make the issue row
|
||||
-- die on the exact error it exists to report.
|
||||
ALTER TABLE processing_issues ADD COLUMN IF NOT EXISTS file_path TEXT;
|
||||
ALTER TABLE processing_issues ALTER COLUMN file_path TYPE TEXT;
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_processing_issues_dedup
|
||||
ON processing_issues (
|
||||
COALESCE(media_item_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
||||
library_id,
|
||||
issue_type,
|
||||
COALESCE(file_path, '')
|
||||
);
|
||||
|
||||
-- Add comment for documentation
|
||||
COMMENT ON TABLE processing_issues IS 'Tracks media items that cannot be properly processed in their assigned library due to format mismatches or other issues';
|
||||
COMMENT ON TABLE processing_issues IS 'Tracks media that failed processing in a library: item-backed issues (library-type mismatches, metadata extraction failures) and row-less import failures (no media_items row exists; keyed by library + file_path). Auto-resolved by later successful scans.';
|
||||
|
||||
|
||||
-- ============================================
|
||||
|
||||
Reference in new issue
Block a user