Persist per-utterance attribution transparency so the system can auto-accept
high-confidence lines and surface only a small review queue:
- DB: utterances gains attribution_evidence_kind, attribution_tag_name,
attribution_uncertain, needs_review, review_reason; attribution_confidence
default -> NULL. Idempotent migrations in database._migrate, routes
_ensure_utterance_columns, and a new ensure in reattribute_all (raw-sqlite
driver must add columns itself).
- llm_attributor: accumulates scene-level mechanical evidence across batches and
stamps evidence kind + tag name on attributed utterances.
- New umbraaudio/analysis/clarity.py: deterministic evidence classification
(name/role/pronoun/address_only/none), in-scene pronoun resolvability
(gender-filtered candidate count), and composite needs_review rule:
QA issues (long_monologue excluded), non-unique pronoun referent, role tag
with no profile, none/address_only evidence < 0.75, unresolved uncertain
flag, narrator-left dialogue, confidence < 0.7. Addressee counts only for
known names/titles (guards 'But where...' false positives).
- Drivers persist all fields: reattribute_all (per-chapter clarity pass),
run_llm_attribution (via update_utterance_speaker), both web attribution
paths. _populate_exceptions keys off needs_review (was confidence<0.7 +
keyword sniff) and fixes a latent NameError (pid not in scope).
- tests/test_clarity.py: 25 tests. Eval on Last Dwarf: 3918 dialogue lines ->
173 needs_review (4.4%), within the 1-5% target.
Full suite 380 passed; 6 pre-existing voice_profile_manager accent failures.
ruff/mypy clean on new code; routes.py baseline unchanged (73 pre-existing).