The operator reviewed ch6 with author knowledge and found real defects the
duplicate scan could not see. Two corrections and one structural diagnosis.
I was wrong about two pairs. I wrote "probably one person" about The Officer /
Officer, and "probably two different riders" about Border Soldier / Soldier
(Rider). Both are two different characters. Measured, The Officer (113.3 Hz) and
Officer (104.5 Hz) are 1.4 SEMITONES apart - two distinct characters with
near-identical voices, which is a genuine cast-clarity risk that a merge would
have hidden. Border Soldier and Soldier (Rider) are 12.2 semitones apart, so
their risk is narrative proximity, not vocal.
The real lesson is the principle behind it: because the author wrote the book he
knows these facts, and on a book he did not write nobody does. So every merge
decision must be resolvable from evidence in the text, and the default when
evidence is absent must be SEPARATION. Keeping two speakers separate costs one
redundant voice; merging two people costs a wrong character for the rest of the
book. Reworded the scan so a blocked group is reported as "same name-space,
different characters unproven" and never as a probable merge.
Why the scan missed ch6: it can only compare names that already exist. It cannot
see a line attributed to the wrong EXISTING speaker. ch6 utt29 - "The leader on
the right for Hallr says," / "We want to be the ones to claim we killed the last
dwarf" - is a scum boy attributed to Hallr. No duplicate existed to find. The
scum group was also missed on identity: only the case variants were caught, and
nothing compares a designation against the text that produced it.
Diagnosis, measured book-wide: a role speech-tag must not resolve to a distinct
named character. 20 dialogue utterances violate it. True positives include ch11
utt191-193 where ONE tag ("The young man replies,") was split across Kaleb,
Tobias and Hallr, and ch13 utt45 where "The girl replies," became The King. But
40% are false positives because ROLE_WORDS is incomplete - royal, king, witch,
doctor and others are missing - and a gate that noisy would be ignored. The
vocabulary must come from definitions.py, not a hand-written list.
Plan, model-independent first: (A) mechanical evidence-coverage gate, which
catches this class with certainty; (B) constrained assignment - choose from the
evidence-supported candidates only, not all 93 names, which is the actual cause
of utt29; (C) role-phrase preservation so "the leader ON THE RIGHT" stops
acquiring a new name per mention; (D) tag-run consistency, never split one
introduction across three characters; (E) a segmentation gate for narration typed
as dialogue (ch6 utt26); (F) confusability signals, a casting concern distinct
from identity; (G) an evaluation harness with a ch6 gold set and five metrics,
because without a number "use a better model" is a guess.
On a better model, honestly: 12b cannot fit in 16 GB, and the GPU box at
192.168.123.14:1234 refused a connection today (curl exit 7), so it is blocked.
And B removes the search space first, which is where the error actually was.
Full suite unchanged at 575 passed / 8 failed, the same 8 pre-existing failures.
UmbraAudio Specifications
Index of all specification documents for the UmbraAudio audiobook generation system.
Specifications
| File | Topic | Description |
|---|---|---|
| 00-project-overview.md | Architecture | Pipeline overview, tech stack, data layout |
| 01-parsing.md | Pass 1a | EPUB, PDF, TXT, MD manuscript parsing |
| 02-analysis.md | Pass 1b | BookNLP, character profiling, pronunciation |
| 03-voice-design.md | Pass 2 | Per-character voice reference generation |
| 04-mapping.md | Pass 3 | Text segmentation, speaker attribution, manifests |
| 05-generation.md | Pass 5 | Audio generation, caching, retry, CUDA management |
| 06-normalization-watermarking.md | Post-gen | Audio normalization, ACX compliance, Perth watermark |
| 07-assembly-export.md | Export | M4B, MP3, WAV export via FFmpeg |
| 08-database.md | Database | SQLite schema, entity relationships, CRUD API |
| 09-web-review-ui.md | Web UI | FastAPI + HTMX review interface |
| 10-cli-orchestrator.md | CLI | Click commands, full pipeline orchestration |
| 11-configuration-dependencies.md | Config | Pydantic settings, dependencies, build system |
| 12-quality.md | Quality | MOS, WER, consistency, ACX compliance |
| 18-UmbraTrans.md | Multilingual | Meaning-preserving translation, target voices, and multilingual audiobook production |
| 19-enterprise-control-plane.md | Enterprise | Multi-tenant projects, access control, artifact lineage, and commercial voice library |
| 20-enterprise-database-integration.md | Enterprise DB | Shared-PostgreSQL contract, blockers, cutover phases P1-P4 |
| 21-umbra-system-nodes-tasks-and-changes.md | System | Worker nodes, task assignment, cross-product change management, UI topology |
| 22-releases-and-the-publish-flag.md | Release | The publish flag, pinned artifact sets, post-publication change, and the Deal Hunter lesson |
| 23-speaker-duplicate-detection.md | Speakers | Duplicate-speaker scan, approval-safe merging, and the crowd veto |
| 24-analysis-quality-plan.md | Analysis | Why the ch6 speakers were missed, and a ranked plan to improve attribution |
Quick Reference
Pipeline Passes
1a: Parse → chapters table
1b: Analyze → characters, pronunciation_guide
2: Voices → voice_ref WAVs
3: Map → manifests/*.json
4: Test → sample audio (optional)
5: Generate → audio/*.wav, audio_normalized/*.wav
Export → export/*.m4b, *.mp3
Key Entry Points
| Component | File | Class/Function |
|---|---|---|
| CLI | cli.py |
main Click group |
| Pipeline | orchestrator.py |
run_pipeline() |
| TTS | voices/chatterbox.py |
ChatterboxEngine |
| Segmenter | mapping/segmenter.py |
Segmenter |
| Generator | generation/chapter.py |
ChapterGenerator |
| Normalizer | assembly/normalize.py |
AudioNormalizer |
| Web UI | web/__init__.py |
create_app() |
| Database | database.py |
Database |