Skip to content

feat(core): --phase record runs without a prior tts phase (#50)#61

Merged
jbrecht merged 1 commit into
mainfrom
fix-50-record-without-tts
Jun 16, 2026
Merged

feat(core): --phase record runs without a prior tts phase (#50)#61
jbrecht merged 1 commit into
mainfrom
fix-50-record-without-tts

Conversation

@jbrecht

@jbrecht jbrecht commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Closes #50.

Problem

render --phase record --contact-sheet is the documented TTS-free way to verify selectors + framing across a whole tutorial before paying for narration — but on a fresh work dir it threw No tts.json … run the tts phase first (tts.ts loadTTSResult, reached from render.ts). So the "cheap check" required first running --phase tts, which with a real provider costs money/time — exactly what it was meant to avoid. Surfaced by umami's #133 dogfood.

Fix

For phase === 'record' only, a missing tts.json now falls back to silent placeholder timings (silentTTSResult — the same audioFile: null, audioDurationMs: 0 shape runTTSPhase already emits for silent steps). Steps pace as silent, so the record drives the browser through each step's state and screenshots it without synthesizing any narration.

  • --phase post stays strict (it genuinely needs real timings).
  • A corrupt tts.json still errors — only a missing file is the clean fallback, via the new loadTTSResultIfPresent (returns null on ENOENT, rethrows otherwise). loadTTSResult now delegates to it.
  • New public exports: silentTTSResult, loadTTSResultIfPresent.

Tests

  • Unit (tts.test.ts): silentTTSResult shape; loadTTSResultIfPresent returns null when absent, parses when present, throws on corrupt; loadTTSResult keeps its clear "run the tts phase first" error.
  • e2e: a --phase record render on a fresh work dir asserts a contact sheet is produced and no tts.json was written (proves it was truly TTS-free).

174 unit tests pass; build/typecheck clean. (I didn't run e2e locally to avoid skewing the performance-engineer's in-flight profiling; CI runs it.)

Docs

writing-tutorials.md now documents the TTS-free whole-tutorial check alongside the contact sheet.

🤖 Generated with Claude Code

The TTS-free framing check (`--phase record --contact-sheet`, used to
verify selectors + framing across a whole tutorial before paying for
narration) threw "run the tts phase first" on a fresh work dir, because
the record phase loaded tts.json unconditionally.

Now, for `phase === 'record'` only, a missing tts.json falls back to
silent placeholder timings (silentTTSResult) — the same null-audio/0-ms
shape runTTSPhase emits for silent steps — so steps pace as silent and
the record still reaches each step's state and screenshots it. `--phase
post` stays strict (it needs real timings). A *corrupt* tts.json still
errors (distinct from absent) via the new loadTTSResultIfPresent.

- New exports: silentTTSResult, loadTTSResultIfPresent.
- Unit tests for both helpers + the loadTTSResult error path.
- e2e scenario: --phase record on a fresh work dir emits a contact sheet
  and writes no tts.json.
- Docs: the TTS-free whole-tutorial check in writing-tutorials.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jbrecht jbrecht merged commit 475acda into main Jun 16, 2026
1 check 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.

render --phase record can't run without a prior tts phase, breaking the cheap TTS-free authoring check

1 participant