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

Open
opened 2026-08-08 20:03:45 -04:00 by agent · 0 comments
Member

Write the transcript in roughly one-minute parts while the recording runs.

Why parts exist

Two reasons, both from the 8/6 scope: the vault agent can read transcripts as they arrive rather
than waiting for a walk to end, and a crash costs a minute rather than a memo.

This is the most portable issue in the milestone — port it, do not reinvent it

stash-ios spent two rounds of review getting WebVTT right, and the findings are all format-level
rather than platform-level. Every one of them applies here, and arriving at them independently
would be repeating work that is already written down:

  • Assembly is not concatenation. A part must end on a blank line, or joining two parts puts the
    next part's WEBVTT signature inside the previous cue. The first part keeps its signature and the
    rest give theirs up — every part still carries one on disk, because being openable on its own
    mid-walk is the entire reason parts exist.
  • Cue text is escaped&, <, >. Unescaped, a --> in what somebody said truncates the
    line. Interior blank lines collapse, because a blank line inside a cue ends the cue and takes the
    rest of its words with it.
  • Timestamps round to milliseconds before hours, minutes and seconds are split off, or 59.9996
    renders as 00:00:60.000, which WebVTT does not allow. A cue's end is always at least a
    millisecond after its own start.
  • A part number must round-trip through the writer's own padding. .000.vtt and .0001.vtt
    both parse loosely and one of them makes the gap check iterate an invalid range and trap.
  • A part that fails to write is retried at every later boundary and again at stop, reported
    while the recording runs rather than only at stop — storage is what fails here and it is the one
    failure a person can act on, but only while there is still a recording for the freed space to take.

Read stash-ios .agent/m1-09-vtt-assembly/SUMMARY.md before planning this. It is the record of
what was wrong and why.

Cross-client parity is the point

Both clients write files the same sync engine and the same vault consume. The stash-docs format
spec governs, and if this issue finds something the spec does not cover, the spec changes.

Tests

The iOS repo wrote a WebVTT conformance reader from the grammar, with no knowledge of the
renderer, plus a test proving the reader rejects all four pre-fix behaviours — because the
byte-comparison tests that existed before were written from the same mental model as the code and
could only pin whatever it happened to emit. Do the same here. Pure logic, so it is JVM unit tests
with no device and no emulator.

Done when

Parts land once a minute, each opens on its own, and assembling them gives one transcript with a
single WEBVTT line that a conformance reader accepts.


Milestone 1 of Stash for Android, mirroring stash-ios M1. M1 is a functioning app on the test
device with no sync at all — record voice memos reliably and get them off the device by hand.
Sync arrives in M3.

Test device is a Pixel 6 (Tensor G1) borrowed from a friend, until something better turns up.
Stock Android, so no OEM battery-killer behaviour to fight — worth remembering that a phone from
another manufacturer may not be as forgiving about background work.

Translation of Stash/stash-ios#9. Where the platform forces a different answer this issue says so; where it
does not, the iOS issue is the fuller statement of intent and should be read alongside this one.

Working agreement for every issue in this repo: feature branch, tested before the PR opens
(on-device where the label says so), then Wes reviews the PR and we walk the code together. Docs
update in the same commit. A PR that takes more than 20 minutes to review is too big — say so and
split it.

Write the transcript in roughly one-minute parts while the recording runs. ## Why parts exist Two reasons, both from the 8/6 scope: the vault agent can read transcripts as they arrive rather than waiting for a walk to end, and a crash costs a minute rather than a memo. ## This is the most portable issue in the milestone — port it, do not reinvent it `stash-ios` spent two rounds of review getting WebVTT right, and the findings are all format-level rather than platform-level. Every one of them applies here, and arriving at them independently would be repeating work that is already written down: - **Assembly is not concatenation.** A part must end on a blank line, or joining two parts puts the next part's `WEBVTT` signature inside the previous cue. The first part keeps its signature and the rest give theirs up — every part still carries one on disk, because being openable on its own mid-walk is the entire reason parts exist. - **Cue text is escaped** — `&`, `<`, `>`. Unescaped, a `-->` in what somebody said truncates the line. Interior blank lines collapse, because a blank line inside a cue ends the cue and takes the rest of its words with it. - **Timestamps round to milliseconds before hours, minutes and seconds are split off**, or `59.9996` renders as `00:00:60.000`, which WebVTT does not allow. A cue's end is always at least a millisecond after its own start. - **A part number must round-trip through the writer's own padding.** `.000.vtt` and `.0001.vtt` both parse loosely and one of them makes the gap check iterate an invalid range and trap. - **A part that fails to write is retried** at every later boundary and again at stop, reported *while the recording runs* rather than only at stop — storage is what fails here and it is the one failure a person can act on, but only while there is still a recording for the freed space to take. Read `stash-ios` `.agent/m1-09-vtt-assembly/SUMMARY.md` before planning this. It is the record of what was wrong and why. ## Cross-client parity is the point Both clients write files the same sync engine and the same vault consume. The `stash-docs` format spec governs, and if this issue finds something the spec does not cover, the spec changes. ## Tests The iOS repo wrote a **WebVTT conformance reader from the grammar**, with no knowledge of the renderer, plus a test proving the reader rejects all four pre-fix behaviours — because the byte-comparison tests that existed before were written from the same mental model as the code and could only pin whatever it happened to emit. Do the same here. Pure logic, so it is JVM unit tests with no device and no emulator. ## Done when Parts land once a minute, each opens on its own, and assembling them gives one transcript with a single `WEBVTT` line that a conformance reader accepts. --- *Milestone 1 of Stash for Android, mirroring `stash-ios` M1. M1 is a functioning app on the test device with **no sync at all** — record voice memos reliably and get them off the device by hand. Sync arrives in M3.* *Test device is a **Pixel 6** (Tensor G1) borrowed from a friend, until something better turns up. Stock Android, so no OEM battery-killer behaviour to fight — worth remembering that a phone from another manufacturer may not be as forgiving about background work.* *Translation of `Stash/stash-ios#9`. Where the platform forces a different answer this issue says so; where it does not, the iOS issue is the fuller statement of intent and should be read alongside this one.* *Working agreement for every issue in this repo: feature branch, tested before the PR opens (on-device where the label says so), then Wes reviews the PR and we walk the code together. Docs update in the same commit. A PR that takes more than 20 minutes to review is too big — say so and split it.*
Sign in to join this conversation.
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-android#9
No description provided.