M1-09: VTT part writer — one-minute transcript chunks #23

Merged
wk merged 1 commit from m1-09-vtt into main 2026-08-07 09:51:39 -04:00
Member

Closes #9 — transcript parts written as a recording runs, validated on wes-iphone.

A 44-minute walk produced 40 parts, 320 cues, continuous numbering across 39 boundaries, with no
duplicated or dropped text at any seam. The issue asked for ten.

What is here

Parts land in a sibling <base>.parts/ folder so the vault agent can read a transcript before the
memo is finished, and so a crash costs a minute rather than everything.

The boundary is a cue boundary, never the clock. A part closes when a cue arrives that starts
a minute or more after that part's first cue, and that cue opens the next part whole. Cutting at
exactly sixty seconds would cut through a sentence the recogniser has not finished with, leaving a
truncated cue in one part and a duplicated clause in the next.

Stash gets the hard half free: the transcriber only emits finalised cues, so there is no
in-progress cue to carry across a boundary. The one still being decided has not arrived yet and
lands in whichever part is open when it does.

Every part is complete on its own — its own header, timestamps absolute to the start of the
recording, cue numbers continuing across parts. Assembly is concatenation, not arithmetic. Writes
go through .atomic, so a part is never visible half-written.

The first test target

21 tests, make test, and a CI step. This is the repository's first, and the design bent to allow
it: TranscriptParts is a pure struct holding the boundary rule and nothing else, so the part
most easily got subtly wrong is exercised without a device, a microphone or a filesystem.

Fixtures are synthetic. This repository is public.

Reviewing it

One commit. TranscriptParts is the substance; VTT renders; TranscriptPartWriter is an actor
that keeps the writes off the main actor. CaptureNaming gained the parts naming.

Still open

Cue times have not been checked by ear against a recording — carried from #8.

A recording killed before stop() leaves parts that nothing assembles. Filed as #22, which also
covers writing the assembled <base>_transcription.vtt beside the audio, so a capture reaches the
vault as the pair it expects.

🤖 Generated with Claude Code

Closes #9 — transcript parts written as a recording runs, validated on `wes-iphone`. A 44-minute walk produced 40 parts, 320 cues, continuous numbering across 39 boundaries, with no duplicated or dropped text at any seam. The issue asked for ten. ## What is here Parts land in a sibling `<base>.parts/` folder so the vault agent can read a transcript before the memo is finished, and so a crash costs a minute rather than everything. **The boundary is a cue boundary, never the clock.** A part closes when a cue arrives that starts a minute or more after that part's first cue, and that cue opens the next part whole. Cutting at exactly sixty seconds would cut through a sentence the recogniser has not finished with, leaving a truncated cue in one part and a duplicated clause in the next. Stash gets the hard half free: the transcriber only emits finalised cues, so there is no in-progress cue to carry across a boundary. The one still being decided has not arrived yet and lands in whichever part is open when it does. Every part is complete on its own — its own header, timestamps absolute to the start of the recording, cue numbers continuing across parts. Assembly is concatenation, not arithmetic. Writes go through `.atomic`, so a part is never visible half-written. ## The first test target 21 tests, `make test`, and a CI step. This is the repository's first, and the design bent to allow it: `TranscriptParts` is a pure struct holding the boundary rule and nothing else, so the part most easily got subtly wrong is exercised without a device, a microphone or a filesystem. Fixtures are synthetic. This repository is public. ## Reviewing it One commit. `TranscriptParts` is the substance; `VTT` renders; `TranscriptPartWriter` is an actor that keeps the writes off the main actor. `CaptureNaming` gained the parts naming. ## Still open Cue times have not been checked by ear against a recording — carried from #8. A recording killed before `stop()` leaves parts that nothing assembles. Filed as #22, which also covers writing the assembled `<base>_transcription.vtt` beside the audio, so a capture reaches the vault as the pair it expects. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Transcript chunks land in a sibling `<base>.parts/` folder as a recording runs,
so the vault agent can read a transcript before the memo is finished and a crash
costs a minute rather than everything.

    2026-08-06-090139-tjgcaz.parts/
      2026-08-06-090139-tjgcaz_transcription.001.vtt
      2026-08-06-090139-tjgcaz_transcription.002.vtt

The boundary is a cue boundary, never the clock. A part closes when a cue
arrives that starts a minute or more after the part's first cue, and that cue
opens the next part whole. Cutting at exactly sixty seconds would cut through a
sentence the recogniser has not finished with, leaving a truncated cue in one
part and a duplicated or dropped clause in the next. Parts come out roughly a
minute, which matters to nothing.

Stash gets the hard half of that for free: the transcriber only ever emits
finalised cues, so there is no in-progress cue to carry across a boundary. The
one still being decided has not arrived yet and lands in whichever part is open
when it does.

Every part is complete on its own — its own WEBVTT header, timestamps absolute
to the start of the recording, cue numbers continuing across parts. Assembly is
concatenation, not arithmetic. Writes go through `.atomic` so a part is never
visible half-written; a consumer reading a truncated .vtt has no way to tell.

`TranscriptParts` is a pure struct holding nothing but the boundary rule, which
is what makes the interesting part testable without a device.

Adds the repository's first test target. 21 tests, `make test`, and a CI step
that will run with the rest once a macOS agent exists. They cover the boundary
rule against ten simulated minutes, cue numbering across parts, a cue spanning a
boundary, a ten-minute silence, WebVTT rendering byte for byte, reassembly by
concatenation, and the filesystem layout including a write that cannot happen.
Fixtures are synthetic — this repository is public.
wk merged commit b7c56988a5 into main 2026-08-07 09:51:39 -04:00
wk deleted branch m1-09-vtt 2026-08-07 09:51:39 -04:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Stash/stash-ios!23
No description provided.