Files
UmbraAudio/specs
B.D. Murphy 714b11367a Duplicate-speaker scan that refuses to eat an approved voice (spec 23)
Last Dwarf has accumulated duplicate speakers from successive discovery and
designation scans: The Girl / The Girl helping / One Girl Helping, Guard 1 / The
first guard, Walking oldest / The Oldest, plus three lowercase spoken names that
differ from their profiles by one letter. The operator wants them eliminated and
was explicit that created and approved voices must not be lost.

umbraaudio/analysis/speaker_dedup.py
  Read-only fact gathering (profiles, ledger, voice refs, chapter spread) and a
  matcher that explains itself: determiner and ordinal normalization, superlative
  stemming, and three VETO rules, because string similarity alone would merge
  people who are not the same person:
    - distinguishing modifiers: The Oldest is not The Tallest; Guard 1 is not Guard 2
    - role nouns: Soldier (Rider) is not One Rider
    - proper names need ratio >= 0.88: Anfisa (0.77 from Tanisha) is a different
      character; Rielan/Rieland at 0.93 is a misspelling
  Verdicts auto / review / blocked. A group with two different voice references is
  blocked, because merging would discard a generated voice. Survivor selection
  prefers an approval, then a voice reference, then utterance volume.

Two rules were written and then removed, recorded in the spec so nobody re-adds
them: a "shared token plus role noun" rule proposed The Oldest = The Old Woman,
and a "single shared token" rule proposed Scum leader = The Leader. A missed
duplicate costs one manual merge; a wrong one silently rewrites a speaker.

Two real bugs, both found by running against a COPY of Last Dwarf rather than by
reading code:

1. orphan_resolution.merge_speakers never touches voice_status.json, so merging
   an approved speaker into an unapproved target would have deleted the approval
   record silently. merge_group() now carries the approval to the survivor,
   drops the loser's entry, and reports approvals_carried.
2. The first re-point was unscoped and moved 5,981 rows instead of 3: `narrator`
   is the labelling convention for ~5,978 NARRATION rows too, and renaming them
   would invalidate thousands of clips because every marker records the speaker
   exactly. The re-point is now dialogue-only. Narration casing is a separate,
   deliberate decision and is flagged as such, not taken by a duplicate scan.

Verified on a copy: 93 -> 84 profiles, 23 utterances re-pointed, narration
untouched, 72 -> 72 voice ref files with zero removed, 13 -> 13 approvals with
zero lost.

Last Dwarf scan: 10 auto, 2 review, 4 blocked. Blocked groups are exactly the
ones the operator must decide - The Girl (two voices for one person), Officer,
the three-way Narrator family, and Border Soldier vs Soldier (Rider).

Nothing was applied to the live project. tests/test_speaker_dedup.py: 37 tests,
vetoes weighted as heavily as positives. Full suite 575 passed / 8 failed, the
same 8 pre-existing failures. ruff clean.
2026-10-04 18:36:37 -07:00
..

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

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