Docs

Decisions

Architecture and product decisions recorded as they were made, rendered from docs/decisions.

Shared recorder runtime

Source
docs/decisions/0002-shared-recorder-runtime.md
Last updated
2026-10-31, commit 3560401
Owner
runtime lane
Read it

Shared recorder ownership and local IPC

Decision: retain app.cassette.desktop, the existing SQLite library and native recorder. An explicit CASSETTE_HOME overrides the complete runtime root for isolated qualification; default Tauri app_data_dir is unchanged. Canonicalize before taking one OS file lock per root.

The GUI either owns the runtime or attaches to its existing owner. cassette-cli starts the same desktop executable in broker-only mode when needed. Broker-only processes exit after an idle timeout; capture/jobs keep them alive. There is no MCP service. File IPC stays beneath the canonical root, accepts a strict command enum and UUID filenames, limits payload size, atomically publishes files, checks schema/owner/request IDs and bounded deadlines, and never launches arbitrary commands. Windows payloads use per-user DPAPI; request-only provider keys never enter plaintext spool files. Request status never restarts or recovers a capture implicitly.

Capture remains in a crash-safe temporary WAV. Pending name/title/tags/folder metadata is stored separately and updated without reopening the microphone. Stop publishes an immutable destination WAV without replacing an existing file, imports it into the existing library, and only then cleans capture recovery material. Failed saves retain original temporary audio and plain errors. CLI, GUI, tray and shortcuts share these operations.

Frontend additions are backward compatible: CaptureMetadata filename?/destination?; CaptureStatus optional nullable pendingFilename/destination/fullPath/freeBytes/remainingSeconds/savedPath; saving/saved states; updateCaptureMetadata(patch), recordingSettings(), saveRecordingSettings({defaultDestination}), chooseDestination(), openFolder(path). Browser preview remains a separate synthetic capability surface and makes no native-folder claim.

Qualification requires synthetic collision/failure/owner-lock/correlated-response tests, copied-library tests, frontend builds and a Windows installer. Actual microphone/long capture/upgrade acceptance remains separate from code-test success.

Transcript review: domain model

Source
docs/decisions/2026-10-31-transcript-review-domain.md
Last updated
2026-10-31, commit d5309f4
Owner
transcript lane
Read it

Transcript review domain and string exports

Status: implementation foundation pending independent acceptance; not integrated into UI/CLI/storage or counted as a shipped Phase 2 feature.

  • Preserve the existing Transcript and SQLite schema. ReviewedTranscript owns an unchanged raw envelope plus separate text/speaker overlays, schemaVersion 1, timeBasis edited_audio_seconds, a pinned source duration/recipe, reviewRevision and derived reviewedText. No provider evidence, raw cue time, assetId or recipeRevision is overwritten. No word clocks or cross-file profiles are added.
  • Provider cue times address the concatenated normal-speed audio submitted for transcription. They are not source times, even for discontinuous source recipes. Creation validates asset identity, revision, finite in-bounds times and edit duration. Every public edit/render method requires the current asset; changed revisions reject, and exact recipe range/order/source-duration comparison also catches changes made under a reused revision. No silent retiming is performed.
  • Operations return new snapshots. Text is exact, including "" and whitespace; empty never means fallback to provider text. Aggregate reviewedText joins nonempty segment strings in provider order with one separating space, without trimming their contents. Raw aggregate text remains independent and unchanged. A changed operation increments reviewRevision with checked arithmetic; no-ops keep it. Failed operations leave their inputs untouched.
  • Targeted speaker assignment changes one segment; rename changes all exact matches; merge requires both labels and does not join cues. Assigned labels are trimmed and must be nonblank without interior controls/line separators. Original provider labels remain raw evidence. No speaker identity inference occurs.
  • JSON emits the versioned review wrapper, not a replacement legacy Transcript wire format. Checked restoration requires authoritative raw evidence and a current asset, rejects raw JSON field/semantic-value differences (including unknown raw fields), invalid schema/time basis, inconsistent overlays/aggregate and changed pins. There is no unchecked Deserialize implementation for the review wrapper. This verifies structure/evidence consistency, not authorship. The caller owns authoritative evidence, edit revision correctness and future concurrency control.
  • TXT/SRT/VTT render strings only. They flatten controls/newlines/whitespace for cue text and labels; SRT/VTT escape &, <, > including payload timing arrows. VTT uses escaped plain speaker prefixes rather than interpolated voice markup. Subtitle cues are stably ordered by start time; TXT/raw/review order is retained. Overlapping speakers are allowed. Explicitly empty/whitespace-only cues are omitted and SRT numbering remains contiguous. Empty VTT is WEBVTT\n\n.
  • Seconds are rounded to nearest milliseconds, with overflow rejected before a u64 cast. Hours do not wrap. Zero/sub-ms legacy cues remain valid evidence for review/TXT/JSON but nonempty subtitle cues that collapse to zero milliseconds fail explicitly; no end time is fabricated. JSON preserves exact reviewed strings and provider text without human-format whitespace normalization.
  • Reuse PR #2's pure clone/relabel and timestamp-formatting approach, adapted to the recovered envelope and pinned clock; do not import its incompatible model, missing-revision behavior or unescaped cue/voice interpolation. No dependency, manifest, lockfile, version ledger or existing contract changes are needed.

Future integration must add independently reviewed storage/CAS and typed shared runtime/UI/CLI adapters. Any filesystem save must use no-clobber publication; cloud consent/key handling remains in existing provider boundaries. This layer performs no filesystem, process, network, provider or credential operations and cannot qualify Windows, playback synchronization, persistence or packaging.

Transcript review: application

Source
docs/decisions/2026-10-31-transcript-review-application.md
Last updated
2026-10-31, commit 3d8c7ac
Owner
transcript lane
Read it

Local transcript review application slice

Status: connected implementation pending independent application review, not a release/Pondo acceptance or Windows qualification.

  • Keep legacy assets/transcripts, raw transcript JSON and audio unchanged. Add transcript_reviews(transcript_id TEXT PRIMARY KEY, review_revision TEXT, json TEXT) containing the approved schema-1 reviewed envelope. TEXT stores the full u64 review revision without SQLite signed-integer truncation. Existing DB user_version remains 1; this is additive, not a reinterpretation of legacy rows. An AFTER DELETE trigger removes review rows when their raw transcript is deleted by the existing library-removal path, avoiding orphaned duplicate transcript evidence.
  • No review row means a validated raw review at revision 0, without inserting a row on read. Corrupt/incompatible rows or changed raw evidence fail explicitly; never overwrite them, reset them silently or fabricate new legacy data. Exact raw JSON text remains in the old table, including whitespace; the review duplicates typed raw evidence only in its own envelope.
  • Reads use one SQLite snapshot. Mutations use BEGIN IMMEDIATE, then validate raw evidence/current asset, compare expected review and recipe revisions, apply a 1–1000-operation batch in memory and persist once with an SQL revision condition. Any failed operation rolls back the entire batch. Revision counts changed domain operations, not RPCs. No-ops retain the revision and do not create a default row.
  • Restore resets only reviewed text/speaker overlays to raw values through the core domain; review revision stays monotonic. Original raw display remains available even when a transcript is stale or cannot be reviewed. No provider retranscription, credential lookup, automatic repair or audio mutation accompanies restoration.
  • Export carries the caller's expected review and recipe revisions and holds a DB write reservation through string rendering/publication. The publisher writes a same-directory named temporary file, syncs it and uses persist_noclobber. Existing destinations, source/sidecar files and files appearing during publication are never replaced. Existing library source paths are also refused before publishing, including missing literal source paths. Managed captures/assets/jobs/IPC folders are not transcript destinations. Explicit CASSETTE_HOME isolation is respected. No new folders, audio encoders or capture paths are involved.
  • Extend the shared typed Command enum with get_transcript_review, update_transcript_review and export_transcript_review. Desktop and CLI use the existing dispatcher/transport; recorder commands, broker/GUI lifetime and IPC limits/ownership remain untouched. Legacy transcript show ASSET_ID is unchanged. New CLI mutations/exports require --expect-review and --expect-recipe; conflicts and stale edits exit 3, usage exits 2, other failures exit 4. No cloud/key flags.
  • TranscriptReview is confined to the existing editor panel. Changes remain drafts until one atomic Save batch. Unsaved drafts survive conflicts; reload/discard is explicit. Save and export are serialized in the component; dirty or stale content cannot export. Raw disclosure and restore are explicit controls. JSON exports include raw evidence, which the format selector discloses. Plain formats contain reviewed text only. Browser preview remains read-only for this native-store slice.
  • Phase 1 identity stays 2.2.6. There is no speaker solo/matching/live/local-provider feature, CLI provider transcription, recording UI change or Windows acceptance in this slice. Raw envelope, recipe and provider consent boundaries are unchanged.

Exports hold the library DB/store guard while syncing the destination, so slow media/very large transcripts need latency qualification. DB review updates and filesystem publication each have their own atomic boundary; no distributed DB/filesystem transaction is claimed. Reverting app integration can leave the additive table intact; old readers continue to use raw rows. Preserve review rows for a later forward-compatible migration rather than deleting accepted edits.

Library search and metadata

Source
docs/decisions/2026-10-31-library-search-metadata.md
Last updated
2026-10-31, commit 10b1591
Owner
library lane
Read it

Local recording context and bounded evidence search

LIBRARY-01/02/06 and the local/manual portion of SPEAKER-06 are implemented as an independent slice above the approved transcript application. The linked S1/S3 stories already captured in NEXT-VERSION/spec inform the design. This does not implement identity matching, voice profiles, live speakers or provider inference.

Keep legacy asset/transcript JSON and audio immutable. Store notes and a user-selected kind in a separate schema-1 recording_details envelope with a TEXT-backed unsigned revision, additive table and asset-delete cleanup trigger. Absent rows mean recording/empty notes/revision 0; reads insert nothing. Saving uses an IMMEDIATE transaction and optimistic revision check. Never infer kind, reset corrupt metadata or update title/tags through this command. Existing user_version=1 and readers remain compatible.

Use a bounded SQLite snapshot scan rather than a persistent derived index for this first slice. It avoids stale index migration and additional dependencies, with explicit corpus/evidence bounds and omission warnings. Search reviewed segments when a valid review exists; explicit empty edits suppress that raw transcript's text. Raw fallback applies only when no review row exists. Reject invalid reviews without revealing their raw text as a fallback. Older recipe revisions require opt-in and remain visibly historical; never retime or seek from these search results. Multiple transcript records are separate evidence.

Index user-assigned labels only when the reviewed label differs from its raw segment label. Never treat a provider label as a confirmed participant. Terms are ANDed across the recording's searchable fields; this is not proof that one person spoke all queried words. Exact substrings precede conservative one-typo word matching; label matching has no fuzzy inference. Return provenance and plain snippets, not identity/confidence scores. Keep the legacy CLI search asset-array shape; --details opts into snippets, warnings and pagination.

Keep existing dense sage/ivory library rows and editor context. Notes/kind have explicit saving, a separate revision from transcript review, retained conflict drafts and a reload/discard action. Larger libraries/indexing, Unicode canonical normalization, participant-specific phrase queries and transcript navigation remain later work. No manifest, lockfile, CI or Pondo changes are required.

Local review repair: the combined Recording details modal remains mounted after independent title/tag saves. LibraryDetails keeps draft ownership and synchronously reports pending requests to parent guards; an idle explicit Close or Reload can still discard the ephemeral draft. This leaves native CAS/storage contracts intact.

Transport feedback (reels and sounds)

Source
docs/decisions/2026-10-31-transport-feedback.md
Last updated
2026-10-31, commit ab72559
Owner
design lane
Read it

Optional transport feedback

Date: 2026-10-31

Keep cassette feedback inside the React UI. Reuse the recovered illustration and synthesize short mechanical cues through one lazily created Web Audio context. This introduces no media download or provider dependency. Feedback errors are swallowed at the feedback boundary; capture, playback, seeking, saving, and removal never depend on a cue completing.

Persist only non-sensitive animation/sound/volume/cue preferences under a versioned localStorage key. Sound defaults off. Animation and sound are independent; system reduced motion overrides reel animation. A cue fires after a successful transport action, never from capture-status polling. Eject pauses and unloads the editor by returning to the library; it cannot call the asset removal API.

The native recorder/storage contract remains authoritative. Display real native destinations and filenames when available; do not infer a pending UUID path in the UI. Native settings, capture ownership, and filename finalization are owned by the recovery/runtime lane.

CLI stream detachment

Source
docs/decisions/broker-stdio-detachment.md
Last updated
2026-10-31, commit 8e48776
Owner
runtime lane
Read it

Broker standard-handle inheritance

Decision: before a Windows CLI spawns the on-demand broker, clear HANDLE_FLAG_INHERIT on its three existing standard handles. Keep those parent handles open for the CLI result/error and keep the broker's requested stdin/stdout/stderr attached to NUL. This uses stable SetHandleInformation through the existing windows-sys dependency; no new process-creation or environment builder is introduced.

Reason: a real subprocess capture test showed CLI exit in under 0.3 seconds but pipe EOF delayed until broker idle exit at about 30 seconds. The regression now requires complete captured stdout/error output while the broker is still alive, and verifies/cleans only its own synthetic child process.

The adjustment applies only to the broker-launching CLI process. It does not change audio capture, GUI lifetime, local IPC consent, or retry ambiguous mutations. Keep physical recording qualification separate.

Application evolution

Source
docs/design/application-evolution.md
Last updated
2026-10-31, commit 0bd0575
Owner
design lane
Read it

References reviewed

  • Awwwards UI design collection: reviewed the collection and its UI hierarchy/navigation. Borrow clear grouping and a restrained visual hierarchy. Marketing-sized type, animation-heavy introductions, and large showcase spacing do not serve a recorder.
  • Godly and its LocalCan reference: reviewed the gallery and desktop-utility reference. Borrow the direct product focus and visible working state. Gallery pages are inspiration, not proof of accessibility or native behavior.
  • Installed Cassette and recovered desktop source: preserve sage/ivory surfaces, restrained green primary actions, serif page headings, and the existing illustrated cassette. Preserve all recording, import, edit, export, and provider flows.

Decisions

  • Reduce page heading to 28px, toolbar to 56px, sidebar to 208px, and work-area padding to 24px. Use 4/8/12/16/24px spacing; preserve at least 36px action targets and 44px recorder controls.
  • Remove the library marketing banner and sidebar promo card. One global Record action and Import sit in the persistent toolbar. Keep the cassette illustration in recorder/playback; a compact empty-library row introduces recording/import. Library rows, search, filters, and sort carry the workspace.
  • Provide a sidebar collapse control and a compact recorder presentation. Compact is a UI mode in the same window, not a claim of native floating/tray behavior.
  • Start capture from the global Record action without a preliminary metadata form. Keep input and title accessible during capture; native errors stay visible, and saving remains an explicit state.
  • Keep filename/location claims tied to actual native data. The recovered baseline lacked pending filename and folder controls. The shared recorder contract now supplies those fields; the UI applies changes through updateCaptureMetadata and labels a path saved only after native state is Saved.
  • Reuse the original cassette illustration. Reel rotation conveys active recording/playback and pauses with the transport. Respect OS reduced motion and a separate persisted animation preference.
  • Add five short, local synthesized mechanical cues: start, stop, eject, rewind, fast forward. Sound is independently opt-in with its own volume. Cue failure must never prevent capture, playback, saving, or seeking. Eject only unloads playback; it never invokes removal.
  • Keep cloud processing behind the existing explicit provider consent. No media or library is uploaded for design testing.