Skip to content

Fix the video Voice choice; tighten the VideoSpec after review - #209

Merged
davior merged 3 commits into
mainfrom
claude/admiring-tesla-breov5
Oct 15, 2026
Merged

davior merged 3 commits into
mainfrom
claude/admiring-tesla-breov5

Conversation

@davior

@davior davior commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

This PR has two parts, in separate commits: the Voice dropdown fix, then the fixes from the VideoSpec review.

1. The video dialog's Voice choice now works (731595f)

The Voice dropdown in the video dialog saved RenderOptions.voice, but nothing read it. Every render used the account's voice. The dropdown also only ever listed fal.ai voices. On an account with a Deepgram key, "auto" reads with Deepgram, so no choice in that list could have mattered.

  • The dialog lists the voices of the engine that will read the video. A new helper, utils/renderVoices.ts, applies the same provider rule as the worker. "Account default" now names the voice it stands for, e.g. Account default (Haley — female, US (Flux)).
  • The worker passes the choice on (video/worker.py:_render_voice) when it is a voice of the engine doing the reading. Otherwise the account's voice is used, so a choice saved under the other engine no longer turns into the model's first voice.
  • The speech core honours a Flux voice id for Deepgram (routers/settings.py:synthesize_tts_bytes). Any other name still means the account's Deepgram voice. That includes read-aloud's fal.ai voice name, so read-aloud is unchanged.
  • Re-rendering can't reuse old narration. The shot cache keys on the narration audio itself, so a new voice is never served cached shots from the old one.

2. VideoSpec review fixes (60c9d51, 0058942)

These fix what the adversarial review of the VideoSpec path confirmed. A separate skeptic reproduced every finding before I acted on it. Nothing that renders changes: the parity tests against the original walk still pass.

  • Provenance:
    • When a quote or code block takes over a picture's section, the segment now records the picture's block, as does a dropped repeat of the same picture.
    • A heading that isn't read aloud keeps its block in its section.
    • The silent beat that holds a picture names that picture.
    • The spec's source records imported_at, and selection when only part of the note was imported.
    • Blocks are listed in document order.
  • Schema:
    • The from / type tags are required, and compact dumps load back.
    • Dumps always use the spec's own spelling (from, in).
    • Transition, card-text and code-style settings refuse misspelt keys instead of silently using defaults.
  • Compiler:
    • A clip whose file has gone missing is skipped, as its warning says, instead of becoming a shot that fails the render.
    • Now refused up front:
      • an intro that isn't first, or an outro that isn't last
      • an audio file used as a picture
      • a watermark or background that isn't an image
    • An orphaned anchor is named in the refusal.
  • Doc:
    • §5.8 states the rule Notes renders by: a scene is cut into units at its overlaid beats.
    • The sounded-clip mapping, heading_attaches (marked as planned), stale line citations and the Background type are corrected.
    • New open decisions 12–14 for GVC.
  • Confirmed but left alone, each with its reason given in the commit:
    • the pre-existing 500 on malformed blocks
    • the importer's internal labels
    • option URLs that aren't checked
    • overlay text kept as its own copy
    • unused title / shot_segments

Test plan

  • Voice:
    • backend tests/test_video_voice.py (14 tests): the worker's choice per engine, Deepgram honouring a Flux id, and a render pinned to each engine
    • frontend renderVoices.test.ts (4 tests)
  • VideoSpec: 15 new tests. Every one fails on main and passes here.
  • pytest -q from backend/: 1,314 passed, 1 skipped (that skip was already there).
  • npm test (208 passed) and npm run build from frontend/.
  • CI green.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PwaujUokXQAxnebBArpZuj

When a quote or code block arrived in a section with nothing to say, it
took the section over (as the walk always has) and the section's own
blocks went with it: the segment showed that picture, but no segment's
source.block_ids named its block, so a re-sync would have offered it as
new. The same happened to a repeated picture's section dropped next to
a quote. Both now pass their blocks on, and a segment lists its blocks
in document order. No shot changes; the spec's provenance only.

Found by the adversarial parity review, which found no rendering
difference.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PwaujUokXQAxnebBArpZuj
Spec: the `from` and `type` tags are required (the schema said optional
and compact dumps would not load back); dumps always use the spec's own
spelling ("from", "in"); transition, card-text and code-style settings
refuse unknown keys like the rest instead of dropping a typo; source
records when it was imported and whether it was a selection.

Importer: a heading that isn't read aloud keeps its block in its
section's provenance, and the silent beat that holds a picture before a
quote names that picture.

Compiler: a clip whose file has gone is skipped as its warning says,
instead of leaving a shot with no input that fails the render; an intro
or outro out of place, an audio file used as a picture and a non-image
watermark or background are refused; orphaned anchors are named.

Doc: §5.8 now states the rule Notes renders by (a scene is cut into
units at its overlaid beats), a wrong mapping line and stale citations
are fixed, and the review's design questions for GVC are open decisions.

No shot changes: the parity tests against the original walk still pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PwaujUokXQAxnebBArpZuj
@davior davior changed the title Keep a picture's block in the spec when a quote takes its section Tighten the VideoSpec after review: provenance, strict schema, safer compile Oct 15, 2026
The video dialog's Voice choice was saved in RenderOptions.voice and
never read: the render worker always used the account's voice. It also
only ever listed fal.ai voices, so on an account reading with Deepgram
(the default once a Deepgram key is set) no choice could have mattered.

- The dialog lists the voices of the engine that will read the video,
  mirroring the worker's provider resolution, and labels "Account
  default" with the voice it stands for.
- The worker passes the choice on when it is a voice of that engine,
  and otherwise falls back to the account's voice (a choice saved under
  the other engine no longer becomes the model's first voice).
- The speech core honours a Flux voice id for Deepgram; any other name,
  such as read-aloud's fal.ai voice, still means the account's.

The shot cache keys on the narration audio itself, so a new voice is
never served old shots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PwaujUokXQAxnebBArpZuj
@davior davior changed the title Tighten the VideoSpec after review: provenance, strict schema, safer compile Fix the video Voice choice; tighten the VideoSpec after review Oct 15, 2026
@davior
davior marked this pull request as ready for review October 15, 2026 06:51
@davior
davior merged commit 302e3cb into main Oct 15, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants