Real-library audit (270 records) traced the data damage to:
- identity matching on ANY shared token ("Zwerg", "der Zwerg", "Mann") with
"longer text wins" merging (Arthag b02 = Kolon's species/relationships), and
- csRosterCanonicalName renaming a sheet to the first cast name among its
aliases, which saved 16 whole-sheet copies (Zwerg/Kolon = Arthag b03,
Fremder = Mann, Alchimist = Promos ...).
Fixes:
- static/js/character-identity.js + core/character_records.py: generic labels
are never identity evidence; csFindMergeKey, csConsolidateAliasDuplicates,
csRosterCanonicalName, clSameIdentity use it; own name always wins; clUpsert
writes to the exact-name record before any alias scan; no seed rename onto a
name that has its own record.
- _charsheets_parse: in cast-target mode drop other cast members' names from
aliases / first / last name (Kolon: "Sharraz Garthai"), strip reasoning
leakage ("Warte:", "Der Text sagt ...") from every text field.
- _character_scene_evidence: only a quote naming the character is used,
otherwise its own mannerisms (16 panels showed another character's scene).
- _character_age_category: Junge/Mädchen/boy/girl, number words, Sommer/
Winter ages, evidence clause, age-tagged quotes; "15 Jahre älter" is no age.
- scripts/audit_character_records.py (read-only) and
scripts/clean_character_records.py (dry run by default, --apply backs up).
Tests: tests/test_character_record_hygiene.py, tests/character-identity.test.mjs.
Docs: architecture/character-extraction.md, Manual, CHANGELOG.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Index rows reuse the playlist state helper (magListenState): round 44px play/resume/replay button,
smaller muted 'by Author', not played / partly played / played indicator with accessible text, live updates.
Also fix a stale word highlight in the lyrics view that made a driving-mode test fail.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Replace the three-icon view switch with one flyout menu (views, show text/pictures, minimize), add an Index sheet listing all articles with durations, and move playback speed into the slider row.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Controls can no longer fall off screen (320x568 up to tablets and landscape), share no longer overlaps the title.
Picture view with zoom/pan, Spotify-style whole-text lyrics with follow/back-to-current, minimize to current line,
persisted view choice, 44px segmented control.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Previous/current/next sentence in a fixed-height box with a fade, tap to jump. The
highlight is a binary search over sentence starts on audio.currentTime every animation
frame, with forced resync on seeking, seeked, ratechange, play, pause and visibility; the
marked word uses the sentence's own span. Playwright covers seeks, 1.5x/2x, rapid seeks.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Share buttons (toolbar, playlist, driving mode) build #s-magazine?doc=..&article=..&mode=drive; opening it routes to the article and starts driving mode with a Tap-to-play prompt if autoplay is blocked. Ids are validated strictly; access is unchanged.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Adds GET .../articles/{id}/timings (exact per-sentence times via plan_sentences); the player uses them, falls back to the length estimate, and gives captions no time slot.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Also reuses the existing magFmtClock for the contents list (the duplicate
tripped the frontend collision gate).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A fresh import used to stop once laid out, leaving missed text, unverified
OCR and provable misspellings for a separate manual click. _ingest_sync now
runs _quality_run_sync itself, under the same job, so the ingest progress
bar carries through it and the reviewer opens an already-worked magazine.
An Auto-fix failure is reported as an ingest failure without losing the
ingest's own result.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- fuse_paragraph_blocks joins a paragraph split by a column or page break into one
narration block (reader, podcast source and MP3), keeping source regions for highlighting
- language decided once per article; per-article acronym glossary (LLM classifies unknown
acronyms from context), letter-spelled acronym lists, extended DE/EN database
- core/spelling: dictionary findings (compounds, inflections, line-break hyphens, other-language
words), marked in review and listed per page/article; guarded LLM agent fixes; quality run
now includes the spelling pass
- keep suspended compound hyphens (Programmier- und) when merging regions / building layouts
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Export the region-type picker handlers and magRunPageQuality to the page (they threw
"not defined", so the picker never opened and Find missed text / Verify OCR did nothing)
- Add a unit test that fails when an inline mag* handler is not exported
- Quality audit: pixel-level clipping check on all four box edges, duplicate-article
detection, detected full-page advertisements skipped and shown green (no manual button)
- Driving mode text is karaoke-style (whole article, current sentence and word highlighted)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Quality run: per-page missed-text recovery, guarded OCR re-read and a pixel audit
(uncovered ink, overlapping boxes, clipped box edges); report saved as quality_report.json
- Thumbnail grid: quality-stage colours, flag badge, live per-page indicator for every
batch step (OCR, detection, figure fixing, quality run, audio)
- Pages can be approved as advertisements (not narrated, skipped by quality run)
- Driving mode: +/-15 s, seek bar, playback speed, text and image toggles
- Plus the previously uncommitted 1.53.5 magazine/OCR work in the same tree
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>