Family · @miadi/capture

Capture

A capture is any take a device makes in one unrepeatable instant — audio, video, MIDI, or other. The core was abstracted out of gmtermux, where the lifecycle, the naming rule and the provenance sidecar had each been written more than once. Four packages now carry it: the lifecycle, the HTTP envelope, the deployable, and the record button written once.

packages/capture/README.md · rispecs/capture/capture-vocabulary.spec.md
Core@miadi/capture 0.1.1 (npm)
Service@miadi/capture-service 0.1.0 · port 8770
UI@miadi/capture-ui 0.1.0 · headless-first
Kindsaudio · video · midi · other
How the four fit

One seam per layer

▲UI panels · MCP tools · forgewright · scripts
Consumers. They construct their own client.
│@miadi/capture-client
The envelope + fetch, and register().
│@miadi/capture-service
HTTP over the core — the deployable.
│@miadi/capture
Recorder + TakeStore + the CaptureDriver seam.
│drivers
file-import (reference) · termux (built, device-untested).
The strangler condition
“gmtermux capture keeps running until the capture-service records its first real take on ilex. Nobody loses the ability to record during the transition.”
No cutover date, no flag day. Until that first real take, the pixel prototype stays up and untouched — and this family makes no claim to have replaced it.
What the core owns

The lifecycle, the name, the provenance

Recorder
start / pause / resume / stop with lossless segment join. Context freezes at start() — the Songbird rule — and every failure leaves more than nothing: a join that cannot happen retains its segments and the recorder stays paused.
TLID
YYMMDDHHMMSS, the take's local-instant name. Implemented three times in gmtermux; this is the one copy.
TakeStore
The flat take library plus a <take>.take.json provenance sidecar — closing the defect where provenance lived only in process memory and degraded to file mtime after a restart.
CaptureDriver / SegmentJoiner
The device seam. Termux, ALSA, or any host that owns a capture device implements these; the lifecycle never learns which.
The origin law
A take is captured — a device, an instant, one chance, irreplaceable. Its transcription is derived, regenerable for exactly as long as the voice is kept. A sidecar whose origin fails the guard is refused rather than defaulted, because defaulting to derived marks an unrepeatable take safe to delete.
What it refuses to own
Association
A take is durable before it belongs to anything. Binding to a musical-composition is the studio domain's act; binding to a chronicle episode belongs to inquiry-weave / musical-composition-to-episode.
Transport
The HTTP envelope lives in @miadi/capture-client.
Bytes at rest beyond the library directory
Registries hold records and URIs, never media.
packages/capture/README.md
The deployable

Nine routes, and a port law

Routes are born under /api/captures/* — this service never carries the old /api/recordings name. It runs on the gaia server and, one day, on the Android device: same service, same routes, different drivers.

VerbRouteAnswers
startPOST /api/captures/start{success, filename, filepath, startedAt, context}
pausePOST /api/captures/pause{success, segments}
resumePOST /api/captures/resume{success, segment}
stopPOST /api/captures/stop{success, filename, tlid, segments, durationSeconds, capture, belonging, registration}
transcribePOST /api/captures/transcribe{success, filename, cached, transcription}
statusGET /api/captures/statusRecorderStatus + {driver, takesDir, device, pendingRegistrations}
listGET /api/captures{takes: [{filename, kind, sizeBytes, modifiedAt, hasProvenance, hasTranscription}]}
audioGET /api/captures/audio/<filename>the bytes; honors Range: (206/416) for a seeking player
describeGET /service identity and the route table
MIADI_CAPTURE_PORT defaults to 8770 and the service refuses to boot on 8768 — that port belongs to the pixel prototype and dies with it. Registration is queued durably: a take is durable before the registry hears about it.
packages/capture-service/README.md · src/config.ts:20
The record button, written once

Client in, callbacks out

Every surface — forgewright, the studio app, this one — renders the same lifecycle and restyles it freely: minimal markup with capture-* classnames and data-* attributes as styling hooks. Two rules make that possible: no component fetches episode lists (episodes arrive as props) and no component holds a service URL (the host constructs the client and passes the instance in).

ExportWhat it is
useCaptureLifecycleThe lifecycle as a hook: idle → recording ⇄ paused → idle, elapsed time, error surface, belonging carried per the timing ruling.
RecordButtonOne <button>: Record when idle, Stop when rolling.
RecorderPanelState, elapsed, take in flight, pause/resume/stop, error alert.
TakeListFilename, kind badge, origin, duration, belonging or INBOX marker, transcription marker.
InboxViewTakeList filtered to takes with no declared belonging, each row offering assignment.
AssignToEpisodePickerAssigns one take to a host-provided episode via client.register(), always carrying the take's ORIGINAL id.
Belonging, and the inbox
A take carries 0, 1, or 2 belongings — episode_path and composition. A take with neither lives in the inbox: whole and complete, awaiting the human's later choice. Nothing is lost by not deciding at the moment of recording.
packages/capture-ui/README.md
Three roles, once entangled

Capture is not composition

One package used to carry all three, and they were ruled fully separate — otherwise they “will always be confused”. A voice memo, a video take, a MIDI capture are takes in the capture domain; what a studio composes of them is another domain entirely; and carrying the old world across is a third, transitional one.

@miadi/capture · @miadi/capture-client
The device act of capturing
A take: one device, one unrepeatable instant.
@miadi/musical-composition
The studio artifact domain
What a studio makes OF captures — key, capo, tempo, sections, chords, and the takes gathered around them.
@miadi/musical-composition-to-episode
The migration weaver (transitional)
Reads a legacy gmtermux composition folder and weaves its provenance into a chronicle episode. A strangler-fig instrument, not a domain home.
packages/musical-composition/README.md · packages/musical-composition-to-episode/README.md
Where to look next

Two doors

Related issues
@miadi/capture 0.1.1 · capture-client 0.1.0 · capture-service 0.1.0 · capture-ui 0.1.0 · musical-composition 0.1.0 · musical-composition-to-episode 0.3.0 · versions read from the npm registry 2026-08-16