@miadi/episode-capture
v0.1.1
Published
The association ceremony for a capture: bind a take to a chronicle episode as a durable bundle. Copies the audio into captures/<tlid>/ with a verified digest, writes each transcription output beside it, and emits capture.json (miadi.episode-capture.v1) pl
Readme
@miadi/episode-capture
The association ceremony for a capture: bind a take to a chronicle episode as a durable bundle.
captures/260825114010/
capture.json miadi.episode-capture.v1
260825114010.m4a copied, digest measured as it landed
transcription.json the sidecar, verbatim
transcription_260825114010_FR.txt
transcription_260825114010_EN.txtWhy this package exists
@miadi/capture opens by stating what it will not do:
A take is durable before it belongs to anything… Association is owned elsewhere.
Elsewhere had no address. The only implementation of the ceremony lived in gmtermux, which is why every capture bundle in the chronicle was written by the lineage the @miadi/* packages were meant to replace — 33 of them, against one miadi.take.v1 record. This package is that address (jgwill/Miadi#644).
Use
import { storeCaptureBundle, verifyBundle, readBundle } from "@miadi/episode-capture"
const stored = await storeCaptureBundle(
{ dir: episodeDir, path: "2026-08-25-episode-339-…", number: 339, title: "…" },
takePath,
"260825114010.m4a",
{
inbox: "Recordings-episodes",
sidecar, // parsed by @miadi/transcription
outputs: [
{ language: "fr", label: "French transcription", content: fr },
{ language: "en", label: "English translation", content: en },
],
},
)
stored.artifacts // ArtifactReference[] — hand these to a registry, or don'tIt is requireable as well as importable, so a phone, an HTTP service and a hermetic test perform one ceremony rather than three.
The order is the guarantee
- the audio is copied into the bundle and measured as it is written, so the digest describes what landed rather than what the source claimed;
- the sidecar and each output are written atomically beside it;
- only then is
capture.jsonwritten — a descriptor never names a file that is not already on disk; - registration is a later call by someone else, and the descriptor reads
pendinguntil it happens.
A phone in a forest with no signal writes a complete, valid bundle. This library has no network and no registry client.
What it holds to
- Copied, never moved. The take stays in its library, so
@miadi/capture's promise that deleting a take never reaches the copies an association made stays true. - Every recorded path is relative to the episode. Three episodes already carry
/data/data/com.termux/files/home/…as provenance — true on one device, resolvable on no other host (jgwill/Miadi#622). An absolute or traversing filename is refused, and containment is checked against the resolved episode root. - Re-storing updates, never duplicates.
storedAtis preserved and any registration already earned is carried forward. Re-binding the same take is a correction, not a second capture. - An id the wheel minted is permanent (
capture-vocabulary.spec.md§6).priorRecordIdreturns what a re-registration must offer back rather than minting a second identity for the same bytes.
What gmtermux does not have
| | |
|---|---|
| verifyBundle | re-hash and report ok / missing / modified / unhashed per file. A hash written and never checked is decoration. |
| unbindBundle | a take bound to the wrong episode is otherwise unrecoverable except by hand. Refuses a registered bundle unless forced. |
| unhashed as a distinct verdict | a file whose digest was never recorded has not been verified; reporting it ok would be a claim nobody measured. |
Parity with the ancestor
test/gmtermux-parity.test.mjs reproduces real bundles from the chronicle and asserts the descriptor matches key for key — including each transcript's sha256, which catches any change to newline policy, encoding or the filename rule. 8 real bundles reproduce exactly.
What did not come across from web/pixel/episodes/captures.js: HTTP status codes thrown out of a storage layer (EpisodeCaptureError(…, 409)), a global recordings directory, and readFileSync hashing that blocks the recorder's event loop on a phone.
Scope
One episode. Walking the chronicle root to find where a take was assigned is enumeration, and that belongs to @miadi/inquiry-weave.
