Resident transcription pipeline

Status: Implemented and validated on the maintained Linux/NVIDIA baseline

Primary target: Linux x86_64 with an NVIDIA GPU

Initial deployment: Official NVIDIA Parakeet TDT 0.6B v3, Transformers, PyTorch FP16, and CUDA

This document defines the target architecture. Current production code remains authoritative until the migration is implemented and verified.

Executive decision

VoicePad will use one resident, warmed transcription deployment and one bounded, disk-backed audio pipeline. The application will load the selected deployment once, keep it ready across recording sessions, transcribe VAD-planned chunks while recording continues, drain the final speech-bearing tail on stop, persist one honest result, copy complete text, and return to ready without restarting the TUI.

The first production deployment is deliberately optimized for the maintainer’s actual system:

Operating system: Linux x86_64
Device:           NVIDIA CUDA GPU
Tested memory:    4 GiB VRAM
Model:            NVIDIA Parakeet TDT 0.6B v3
Artifact:         official model.safetensors
Runtime:          Transformers + PyTorch
Precision:        FP16

The pipeline is not coupled to Parakeet or PyTorch. Applications select a curated deployment. A deployment binds a model, immutable artifacts, one adapter, capabilities, a resource profile, and a processing profile. New model families or runtimes add deployments and adapters without changing recording, VAD, planning, persistence, history, Markdown, clipboard, or TUI contracts.

VoicePad will not initially ship several speculative runtimes. Official Parakeet/PyTorch is the one production path. Other artifacts such as GGUF, ONNX, or TensorRT become separate deployments only when a measured user need and quality evaluation justify them.

Design principles

  • Prefer official model artifacts, repositories, released packages, and public APIs over conversions, forks, copied private internals, or compatibility wrappers.
  • Pin every external version and immutable artifact; verify downloaded bytes.
  • Keep one obvious production path until evidence justifies another deployment.
  • Keep model/runtime specifics behind the deployment adapter, not spread across application code.
  • Preserve user audio first; inference and derived text are replaceable.
  • Fail unsupported requests explicitly rather than ignore them.
  • Prefer bounded, deterministic state and visible recovery over silent fallback.
  • Add configuration only for behavior users can understand and verify.
  • Do not optimize already sub-second work through a more complex runtime without an observable product benefit.

Clean replacement policy

Backward compatibility is not a requirement. The migration does not preserve legacy core APIs, model IDs, backend names, configuration fields, cache layouts, Markdown schemas, keyword arguments, or synthetic result shapes. It introduces no compatibility adapters, deprecation shims, dual-write formats, or permanent legacy branches.

Obsolete source files, tests, dependencies, documentation, and configuration may be deleted or rewritten when their final consumer cuts over. Temporary coexistence is allowed only to keep main functional across focused pull requests; it is not part of the final architecture. Tests that assert obsolete behavior are removed with that behavior rather than weakening new contracts to keep them passing.

Data retention is separate from API compatibility. Existing WAV files remain immutable and legacy model caches remain on disk until the user deletes them, but new code ignores old cache layouts. Obsolete configuration fails with an actionable clean-break message and is never silently migrated or overwritten. Old Markdown compatibility is not guaranteed; the original WAV remains the source for a new transcription.

Goals

  • Make the official Parakeet model reliable on the target Linux/NVIDIA system.
  • Keep the model loaded and warmed between recordings.
  • Preserve microphone audio independently of inference.
  • Process bounded ranges while recording continues.
  • Prefer natural VAD boundaries and force a safe boundary during continuous speech.
  • Produce real token/word timing and conservative overlap assembly.
  • Use the same pipeline for live recording, files, history, CLI, TUI, and the global hotkey.
  • Represent unsupported model features honestly.
  • Permit a future model family or runtime without application-level model conditionals.
  • Preserve existing WAV files and leave obsolete caches untouched.

Non-goals for the first deployment

  • Supporting every operating system or GPU.
  • macOS support.
  • Claiming Windows support before a Windows/NVIDIA validation.
  • Shipping several runtime engines merely for theoretical flexibility.
  • Training or fine-tuning Parakeet.
  • Creating or hosting a converted model before a measured need exists.
  • Native model streaming.
  • Concurrent inference calls against one loaded model.
  • Cloud transcription.
  • Translation.
  • Claiming native hotword support that the model does not provide.
  • Publishing packages directly from the backend implementation work.

Verified research basis

All private audio remained local. Only aggregate measurements are recorded.

Official Transformers/PyTorch FP16

Test environment:

Python:       3.13.14
GPU:          NVIDIA GeForce RTX 3050 Laptop GPU, 4 GiB
Driver:       595.84
PyTorch:      2.13.0+cu130
Transformers: 5.14.1
Accelerate:   1.14.0
Precision:    FP16

Measured behavior:

Case Result
Cached model load 4.57 s in the first measured process; 0.76-1.31 s after unload/reload in-process
30 s zero-audio warm-up 1.41 s
First real 30 s after same-shape warm-up 0.193 s
Repeated warm 30 s 0.168-0.234 s, 0.181 s mean over 49 runs
Complete 141.792 s inference 1.43 s
Complete transcript size 1,705 characters, 529 timestamp records, 321 aggregated words
Resident 30 s GPU use about 1.46 GiB reported by nvidia-smi
Complete-file GPU peak about 1.90 GiB reported by nvidia-smi
Fifty repeated jobs one deterministic output hash; no allocator growth
Explicit unload GPU use fell to the CUDA-context floor, about 148 MiB
Offline reload succeeded and reproduced the same output
Generation cancellation returned after encoder completion and the model remained reusable

A bounded 30-second call succeeded under an artificial 1.43 GiB PyTorch allocator cap. A complete 142-second call required about 1.67 GiB reserved and failed under a 1.64 GiB cap. These allocator experiments are not a substitute for testing a physical lower-memory GPU. The initial supported claim remains the actually tested 4 GiB class.

Overlapping chunks

A five-chunk fixed-boundary evaluation used source ranges approximately 0-30, 28-60, 58-90, 88-120, and 118-end. The resident model processed all chunks in about two seconds total. Transformers timestamp records aggregated exactly back to each chunk’s native text.

A conservative timestamp assembler:

  • identified 18 duplicate overlap words;
  • preserved two uncertain overlap observations;
  • retained final timing through the final recognized content; and
  • reached about 95.2% word-sequence agreement with direct full-file inference.

Direct full-file output is a comparison oracle, not ground truth. Independent chunks legitimately differ near artificial boundaries. Production planning must prefer VAD-confirmed pauses, and assembly must preserve uncertainty rather than delete text to imitate the full-file result.

The first production-path finite-file validation used official Silero-selected boundaries and six descriptors. Its longest complete model input was 35.744 seconds. The resident pipeline processed the 141.792-second source in 6.2-6.5 seconds including VAD, feature preparation, sequential inference, timestamp conversion, and assembly. It produced 321 timed words with no failed chunks, overlap warnings, or VAD coverage gaps. Independent offline processes produced the same result hash.

A growing-source validation wrote canonical blocks through the bounded WAV persistence queue while planner and inference workers consumed disk ranges. Stop finalized the user WAV and drained six descriptors in 5.783 seconds with no failures, warnings, or coverage gaps. Its authoritative result hash matched the finite-file path exactly.

Alternative runtime findings

  • Full-precision ONNX CUDA was extremely fast but consumed about 3.6-3.75 GiB for bounded calls and failed on the complete recording with 4 GiB VRAM.
  • The tested ONNX int8 conversion was slower, used extensive CPU/GPU copies, and produced materially less text on the private recording.
  • GGUF Q8 through Vulkan was complete, compact, cancellable, and fast, but used a third-party conversion and pre-1.0 runtime. It remains a researched fallback, not the initial production deployment.
  • PyTorch FP16 was at least as fast as the tested ONNX bounded path while using much less VRAM and using NVIDIA’s official artifact.

Official Silero VAD

VoicePad will use the official snakers4/silero-vad v6.2.1 release, not the older model inherited through Faster Whisper.

Repository:     snakers4/silero-vad
Release:        v6.2.1
Commit:         7e30209a3e901f9842f81b225f3e93d8199902b1
License:        MIT, Copyright 2020-present Silero Team
Distribution:   silero-vad 6.2.1 official PyPI wheel
Wheel bytes:    9,146,242
Wheel SHA-256:  09de93c4d874bb19c53e62a47dd38be5f163cedad2b5599583231f2a84ef79cb
Wheel entry:    silero_vad/data/silero_vad.onnx
Model bytes:    2,327,524
Model SHA-256:  1a153a22f4509e292a94e67d6f9b85e8deb25b4988682b7e174c65279d8788e3

The official model ran through ONNX Runtime 1.28 CPU with 512-sample 16 kHz frames, the required rolling 64-sample context, and recurrent [2, 1, 128] state. It scanned 141.792 seconds in 0.457 seconds, about 310x real time, and returned the expected speech-probability range.

The released Silero Python package currently requires torchaudio, while no matching torchaudio 2.13 release exists for the selected PyTorch 2.13 stack. VoicePad therefore does not install or import that package. The artifact store verifies the official wheel, safely extracts only the exact ONNX entry, verifies the extracted bytes, and caches them with provenance metadata. Runtime uses a small independent CPU ONNX session. This keeps the official model while avoiding mismatched binary dependencies.

Initial artifact manifest

Official source:

Repository: nvidia/parakeet-tdt-0.6b-v3
Revision:   7c35754d166cca382ad1e53e68b01e7c575f3a1d
License:    CC-BY-4.0

Required files:

File Bytes SHA-256
model.safetensors 2,508,311,120 3a2026366188c8c68598edbbff92f8d11590a08e0ae2e6775544e7b07d6a5e11
config.json 1,153 e747b85e1bdfd300c8b8ac63bac8dd5221f8fe9bc275b48d06c735fcd6971b6e
generation_config.json 289 b141de6ec6d7f982ece13f98f604e3fe1807ea9c0e839185d0ab7064604209d0
processor_config.json 392 8346a93a3b987fa1dec57a78f045cd0817d21786589a5a096b41a57a446fd1d7
tokenizer.json 1,159,960 bd321b096832a3f270bd3b2a88823957920f1a5c5ada71114a26ea729d0cbe91
tokenizer_config.json 290 0b2fe0037599ee335f0b972fa682bf0ece74e4ccfec755cb7daa3405d3d3e874

VoicePad loads the official stored artifact as FP16 without publishing a converted model:

AutoModelForTDT.from_pretrained(
    local_snapshot,
    dtype=torch.float16,
    device_map="cuda",
    local_files_only=True,
)

Dependency baseline

Initial direct runtime dependencies are expected to include exact reviewed versions of:

torch==2.13.0
transformers==5.14.1
accelerate==1.14.0
librosa==0.11.0
huggingface-hub
onnxruntime==1.28.0  # CPU VAD only

Existing NumPy, SciPy, SoundFile, and SoundDevice dependencies remain where used. The lock file pins all transitive CUDA, tokenizer, safetensors, and audio processor packages. A separate CUDA toolkit installation is not required; the NVIDIA driver remains a system prerequisite.

torchaudio is not part of the initial dependency set. Faster Whisper, CTranslate2, Sherpa-ONNX, their CUDA wheel configuration, and obsolete NVIDIA runtime packages are removed only after every application path has cut over.

The tested environment occupied about 5.1 GiB for Python packages plus 2.4 GiB for the official model snapshot. This one-time storage cost is accepted for the initial target.

Deployment-oriented architecture

The application selects a deployment, not a model filename, runtime index, or model family.

@dataclass(frozen=True, slots=True)
class DeploymentDefinition:
    id: str
    model_id: str
    artifact_manifest_id: str
    adapter_id: str
    precision: Precision
    capabilities: DeclaredCapabilities
    resources: ResourceProfile
    processing: ProcessingProfile
    recommended: bool

The initial record is conceptually:

id:                 parakeet-v3.transformers-fp16-cuda
model_id:           nvidia-parakeet-tdt-0.6b-v3
artifact_manifest:  official-parakeet-v3-safetensors
adapter_id:         transformers-parakeet-tdt
precision:          fp16
required_device:    cuda
initial_platform:   linux-x86_64

Identity separation

  • A model identifies the neural model.
  • An artifact manifest identifies immutable files and source revision.
  • An adapter identifies one execution and output contract.
  • A deployment combines those with precision, resources, and processing.
  • A device identifies actual hardware independently of a transient CUDA index.

Hosting coordinates are acquisition data and do not become user-facing IDs. Changing hosting without changing verified bytes does not change deployment identity.

Model and runtime independence

The TUI, CLI, history, and pipeline depend only on a TranscriptionSession:

class TranscriptionSession(Protocol):
    @property
    def deployment(self) -> ActiveDeployment: ...

    @property
    def capabilities(self) -> EffectiveCapabilities: ...

    def transcribe(
        self,
        audio: PreparedAudio,
        intent: TranscriptionIntent,
        cancellation: CancellationToken,
    ) -> BackendResult: ...

    def close(self) -> None: ...

The initial TransformersParakeetTDTAdapter alone knows about AutoProcessor, AutoModelForTDT, FP16 tensors, TDT duration records, and Metaspace token aggregation.

A future Transformers Whisper model may reuse PyTorch device and artifact infrastructure but use a different adapter. A future runtime such as TensorRT or GGUF adds another adapter and deployment. Model-family branching is confined to adapter registration and never appears in application or pipeline code.

The abstraction does not claim every model has the same features. Effective capabilities describe what the loaded deployment can actually accept and return.

Capability contract

@dataclass(frozen=True, slots=True)
class EffectiveCapabilities:
    native_sample_rate: int
    languages: tuple[str, ...]
    accepts_language_hint: bool
    returns_detected_language: bool
    timestamps: TimestampGranularity | None
    native_streaming: bool
    translation_targets: tuple[str, ...]
    context_biasing: ContextBiasingMode
    cancellation: CancellationMode

Initial Parakeet capabilities:

sample rate:                16 kHz
languages:                  25 documented European languages
automatic multilingual ASR: yes
language hint:              not exposed by this adapter
detected-language output:   no
punctuation/capitalization:  yes
timestamps:                 token + predicted duration
native streaming:           no
translation:                no
beam search:                no; greedy only
native prompt/hotwords:     no
cancellation:               after encoder, during generation
batching:                   runtime-capable, unused for one live job

Unsupported intent is rejected or explicitly reported. It is never silently ignored.

Resident model lifecycle

The application owns one engine with this observable state machine:

UNPREPARED
  -> PREPARING_ARTIFACT
  -> LOADING
  -> WARMING
  -> READY
  -> ACTIVE_JOB
  -> FINALIZING
  -> READY

READY -> UNLOADING -> UNPREPARED
READY -> switching deployment -> UNLOADING -> LOADING -> WARMING -> READY
any state -> FAILED when recovery cannot preserve the active contract

Application startup prepares the selected deployment, loads it on CUDA, and warms it with a discarded 30-second zero waveform. The same-shape warm-up was measured to reduce the next real 30-second call to the stable warm range. Ready is emitted only after artifact verification, CUDA placement, and warm-up pass.

The model remains resident across recordings. Each job creates fresh decoder, VAD, planner, assembler, cancellation, and result state. No decoder state leaks between recordings. Stop finalizes one job and returns the engine to ready; it does not unload the model or close the TUI.

Only one job and one model call run at a time. Deployment or device switching is rejected during an active job. An explicit unload action releases model tensors, runs garbage collection, empties the CUDA allocator cache, verifies release as far as PyTorch can report it, and leaves the CUDA context available for later reload. Automatic idle unload is not enabled initially because immediate recording readiness is the product priority.

Artifact preparation and offline behavior

The catalogue is a packaged offline resource. Preparation resolves only a curated immutable manifest. Each required file is downloaded into operation-owned staging storage, size-limited, SHA-256 verified, flushed, and atomically promoted into a deployment snapshot. Readiness requires all files.

A failed or cancelled acquisition never appears ready. An existing verified snapshot remains available after another preparation failure. Partial staging is removable; verified snapshots and legacy caches are not automatically deleted.

The official Silero wheel is a second curated artifact manifest. Extraction is data-only: reject path traversal, duplicate entries, a missing or compressed-size mismatch, and any entry other than the exact declared ONNX path. Neither wheel code nor package metadata is imported or executed. Readiness requires both wheel and extracted-model hashes to match.

Runtime load uses a local path and local_files_only=True. The tested model loaded, inferred, unloaded, and reloaded successfully with HF_HUB_OFFLINE=1. VoicePad does not depend on a network request after preparation.

Audio source and persistence

One source protocol covers immutable files and growing recordings:

class GrowingAudioSource(Protocol):
    sample_rate: int
    channels: int
    committed_samples: int
    is_final: bool

    def read_range(self, start_sample: int, end_sample: int) -> AudioWindow: ...
    def wait_for_update(self, after_sample: int, timeout: float | None) -> tuple[int, bool]: ...

The microphone callback copies buffers only into the bounded persistence queue. The writer appends a float WAV spool, publishes committed sample positions, and atomically finalizes the user-visible PCM WAV. Inference never executes on the microphone callback and cannot block persistence.

Writer backpressure or disk failure stops capture loudly. A finalization failure retains recoverable spool data. Existing WAV files are opened read-only and are never modified or deleted automatically.

File inputs use SoundFile where supported. Any retained FFmpeg conversion uses an argument vector rather than a shell, operation-owned disk storage, explicit cancellation, output bounds, and idempotent cleanup. Supported formats are a product decision recorded by the application, not inferred from whichever libraries happen to be installed.

Canonical audio preparation

Every ASR range becomes:

mono
16,000 Hz
float32 source representation
contiguous one-dimensional array

The adapter’s processor creates model features and moves them to CUDA FP16. VoicePad downmixes and resamples immutable file sources with deterministic source/runtime sample mapping. Live Linux capture requests mono 16 kHz from the shared sound server so growing persisted sample positions are already canonical. It does not peak-normalize by default. Timestamps map back to absolute source samples without cumulative floating-point drift.

CPU VAD and adaptive planning

One official Silero v6.2.1 ONNX session stays on CPU and retains rolling audio context plus recurrent state while scanning sequential committed frames. It emits absolute speech/silence regions and does not know about model chunks. Job start and terminal teardown reset both context and recurrent state; ordinary frame and chunk boundaries do not.

Initial planning policy remains evidence-driven and configurable within safe bounds:

minimum useful boundary:       20 seconds
natural-boundary search:       after 25 seconds
preferred duration:            30 seconds
minimum qualifying silence:    500 milliseconds
natural overlap:               previous pause-to-pause speech unit
maximum semantic overlap:      12 seconds
forced/fallback overlap:       2 seconds
hard runtime input:            60 seconds including overlap

A natural breakpoint is the midpoint of a confirmed qualifying silence. The planner retains an ordered history of breakpoints. Once a logical chunk reaches 25 seconds, the first subsequently confirmed natural breakpoint dispatches it. If no qualifying pause arrives by 30 seconds, the planner forces the boundary at the preferred duration instead of extending continuous speech to the runtime’s hard input limit. A finite-file or recovered backlog applies the same rule in sample order. The hard maximum remains a safety bound on the complete runtime source range, including left context.

Natural overlap uses two consecutive breakpoints:

                 shared semantic context
pause A  <-------------------------------->  pause B
          complete speech unit between pauses

chunk N:      ... ----------------------------> B
chunk N+1:             A ----------------------------> ...
logical N+1:                                  B -----> ...

When chunk N ends at pause B, chunk N+1 starts its source at the immediately preceding natural pause A while its new logical ownership starts at B. Both inferences therefore hear the complete speech unit between A and B, including half of each boundary silence. The overlap contains real shared words rather than only silence, allowing timestamp assembly to compare equivalent speech and preserve continuity.

This does not require waiting for an additional future pause: A is already in breakpoint history when B is selected. In the private official-Silero analysis, pause-to-pause units had a 3.43-second median, 7.65-second 90th percentile, and 10.27-second maximum. The initial 12-second cap covers all observed units with headroom.

If no previous natural breakpoint exists, or A-to-B exceeds 12 seconds, source context is capped to the final 12 seconds and marked as capped semantic context. A forced boundary uses the final two seconds instead. In every case the planner reduces new logical duration as necessary so the complete source range remains at most 60 seconds.

Logical ownership never overlaps. Source ranges overlap only through declared context. Text before pause A can be committed after chunk N; the A-to-B speech unit remains provisional until chunk N+1 confirms it or job finalization accepts its only observation.

Finalization classifies and drains remaining material speech. If recording stops before another ordinary chunk is ready, the final descriptor starts from the last applicable semantic-context point so the previous tail can still be reconciled. A VAD-confirmed silent tail is classified without inference. Every committed sample is classified exactly once as logical ownership or excluded non-speech.

Incremental execution

microphone callback
  -> bounded persistence queue
  -> growing WAV + committed cursor

planner worker
  -> CPU VAD
  -> bounded AudioChunk descriptor queue

inference worker
  -> bounded disk read
  -> canonical preparation
  -> resident PyTorch session under torch.inference_mode()
  -> timestamp conversion
  -> assembler

Queues store descriptors, not prepared audio arrays. At most one prepared model input exists. Slow inference creates a disk-backed backlog. The measured warm throughput is far above real time, but correctness does not rely on that speed. After each successful chunk, the growing job emits a provisional assembled-text update for presentation. The update may change when a later overlap observation arrives; only the terminal FileTranscriptionResult is authoritative.

Recording stop means stop capture, drain persistence, mark the source final, plan the remaining speech, process terminal descriptors, assemble, persist, and return to ready. It is not cancellation and does not trigger another unbounded full-file inference.

Timestamp conversion and words

Transformers returns token strings with start/end times derived from TDT predicted durations. Parakeet’s tokenizer uses Metaspace; decoded timestamp pieces beginning with a space start a new word, and following pieces extend that word. Punctuation pieces remain attached. Aggregating this way reproduced the native decoded text exactly in the fixed-chunk evaluation.

The adapter returns original token records and timed words. It does not invent confidence. The final token may have zero duration and trailing encoder duration may extend beyond the final emitted token, so source coverage uses VAD evidence and tolerances rather than assuming the last token end equals audio end.

Conservative overlap assembly

Only previous-tail and current-prefix words whose absolute times intersect the known pause-to-pause semantic overlap are candidates. A natural overlap carries the complete shared speech unit A-to-B; forced or capped overlap carries the explicit trailing source interval. Comparison normalization is used only for matching; original text is preserved.

The assembler uses monotonic timestamp-compatible sequence alignment. It may collapse a duplicate only when text and timing provide sufficient evidence that both observations represent the same source speech. For a confirmed duplicate, it keeps the observation farther from its physical inference edge. Unmatched or conflicting overlap text is preserved with a warning.

Text before the unresolved overlap is committed. The current tail remains provisional until the next chunk or finalization. The initial evaluation proves that this mechanism is viable but also shows that independent chunks differ at artificial boundaries. VAD-selected boundaries and local regression fixtures must validate the final thresholds before implementation is considered closed.

Completeness

complete=True means:

  • all material logical speech ranges reached terminal processing;
  • no native generation limit or cancellation truncated required work;
  • no chunk failed;
  • no material VAD-confirmed speech lacks plausible timestamp evidence; and
  • assembly did not encounter an unrecoverable protocol error.

It does not claim semantic ASR perfection.

A normal native return is not sufficient by itself. Coverage compares VAD speech with token/word timing and records conservative gaps. A false-positive VAD gap may mark a transcript incomplete; the WAV remains the durable source for retranscription.

Cancellation and failure recovery

A cancellation token is passed to every job. Transformers stopping criteria can stop TDT generation, but only after encoder execution completes. Bounded chunks therefore provide the practical cancellation bound. Cancellation preserves completed chunks and any valid partial generation, marks the result incomplete, and resets job state. The resident model may remain ready when a post-encoder cancel completes normally; CUDA or unknown runtime failures require unloading and reactivation.

Shutdown during recording follows this order:

  1. stop microphone callbacks;
  2. drain/finalize or preserve recoverable audio;
  3. cancel planning and generation;
  4. persist honest partial metadata/text when available;
  5. release workers; and
  6. unload the model only when application shutdown continues.

VoicePad does not free CUDA tensors underneath active model work.

Vocabulary intent

The official Parakeet Transformers implementation supports greedy search only and exposes no native prompt, hotword, or contextual-bias API. VoicePad will not claim native biasing or pass ignored vocabulary options.

The model-neutral request may still express vocabulary intent:

@dataclass(frozen=True, slots=True)
class TranscriptionIntent:
    language: str | None = None
    vocabulary: tuple[str, ...] = ()

The Parakeet adapter rejects nonempty vocabulary requests until the deployment exposes a proven native capability. The pipeline does not rewrite assembled model output.

Application result behavior

The final result includes text, timed words/tokens when available, duration, latency, deployment/model/artifact/runtime/device identity, completeness, chunk outcomes, warnings, and failures.

For complete nonempty text, the application atomically writes Markdown, updates history from the same result text, and copies that exact text. For incomplete nonempty text, it persists complete: false, displays the reason, and does not auto-copy. With no text, it stores metadata-only failure information. Complete no-speech output preserves the WAV and metadata but copies nothing.

After every terminal job outcome the model returns to ready unless the failure invalidated CUDA/model state. The user can immediately begin another recording without restarting the TUI.

Configuration ownership

The application owns strict user configuration. Initial fields include:

deployment_id
device_id
language
chunk minimum/preferred/lookback/maximum
silence duration
natural/forced overlap

Unknown and obsolete fields fail with actionable field/path information. The application does not silently ignore them or overwrite an existing configuration with defaults. Device/deployment changes are rejected during an active job.

Device and resource admission

The initial adapter requires a CUDA-capable NVIDIA GPU and validates:

  • PyTorch CUDA availability;
  • selected stable device identity;
  • FP16 support;
  • total and currently free memory; and
  • actual model parameter placement after load.

Transient CUDA indices are not durable IDs. The deployment resource profile records measured GPU, peak memory, precision, chunk policy, and confidence level of the requirement. Initial public guidance claims the 4 GB physical class tested. The RTX 3050 reports 4,096 MiB through nvidia-smi but exposes 3,953,393,664 bytes to PyTorch, so executable admission uses a 3,900,000,000-byte floor rather than an incorrect binary 4 GiB threshold. Lower-memory support requires physical hardware evidence; allocator-cap experiments are supporting evidence only.

No silent CPU fallback occurs for the CUDA deployment. A future CPU or lower-memory deployment must be explicitly represented and tested.

Windows extension

Windows is a future target, not an initial claim. PyTorch 2.13 provides a CPython 3.13 Windows AMD64 wheel, Transformers is pure Python, and tokenizer and safetensors packages provide Windows ABI wheels. That establishes packaging feasibility, not runtime proof.

Windows support requires a Windows/NVIDIA test for CUDA library resolution, model load/inference, memory, cache paths, microphone persistence, worker shutdown, clipboard, and global hotkey behavior. If the same adapter contract passes, Windows adds a validated platform entry rather than a parallel pipeline. macOS remains out of scope.

Adding another model or runtime

A new model within an existing adapter requires:

  1. immutable official artifact provenance;
  2. a deployment catalogue entry;
  3. capability and resource profiles;
  4. adapter compatibility proof;
  5. timestamp and completeness contract tests;
  6. public quality evidence; and
  7. local representative regression checks.

A model with a different processor/decoder adds a focused adapter. A new runtime adds a deployment and adapter but reuses source, VAD, planning, events, assembly, results, application lifecycle, and persistence.

VoicePad creates a converted model only to solve a measured problem. Conversion must be reproducible from an official pinned source, preserve licensing and provenance, publish exact hashes, pass public WER/numerical comparison, and pass private representative checks. A conversion is always a distinct deployment; it never silently replaces official bytes.

Observability and privacy

Logs may include deployment/device IDs, state transitions, queue depth, sample ranges, timings, memory, hashes, and typed failures. They do not include audio, transcript text, token/word text, credentials, private fixture names, or private paths by default.

Model download is public and requires no token. User WAV files, transcripts, private logs, model binaries, and caches never enter Git or GitHub. Model and Silero licensing/provenance notices are retained. Package publication remains frozen.

Verification strategy

Deterministic CI

  • catalogue and artifact manifest validation;
  • bounded download, cancellation, hashing, and atomic promotion;
  • adapter capability and unsupported-intent contracts;
  • token-to-word timestamp aggregation;
  • planner property tests for complete sample classification;
  • overlap alignment, repeated words, punctuation, mismatch preservation, and final-tail tests;
  • coverage and completeness tests including silent native truncation;
  • resident state-machine and single-job tests;
  • controlled worker/backpressure/cancellation tests;
  • strict configuration, Markdown, history, and clipboard tests.

Local Linux/NVIDIA

  • official pinned model offline load;
  • FP16 CUDA placement;
  • warm-up and first real chunk;
  • repeated session stability;
  • model unload/reload;
  • official Silero wheel/model verification, state continuity, and reset;
  • occupied-memory and out-of-memory handling;
  • private long-recording complete and chunked paths;
  • real recording, stop, immediate next recording, and application shutdown.

Required repository gate

uv run ruff check
uv run ruff format --check
uv run ty check
uv run pytest packages --cov=voicepad --cov=voicepad_core --cov-fail-under=70
pnpm --dir docs build

Optional hardware checks are reported as passed, failed, or unavailable. They never masquerade as CI proof.

Remaining evidence before approval

The architecture is technically coherent and the primary runtime is proven on the target machine. These items remain explicit human/research gates:

  1. Review differing regions between official FP16 and GGUF outputs against the source audio; neither output is ground truth.
  2. Validate the 12-second semantic-overlap cap and final alignment thresholds with VAD-selected natural boundaries, not only fixed synthetic boundaries.
  3. Exercise startup, active recording, stop/drain/copy, immediate second recording, unload/reload, and shutdown through the actual TUI after implementation.
  4. Test Windows on real Windows/NVIDIA hardware before adding support.

Migration sequencing

  1. Approve this design and replace the obsolete draft architecture.
  2. Re-plan tracker issues around deployment-neutral contracts and the initial PyTorch adapter.
  3. Add catalogue/artifact preparation together with the first consumer.
  4. Add the resident official Parakeet session and prove Linux/NVIDIA lifecycle.
  5. Add direct CPU Silero VAD, deterministic planning, timestamp aggregation, assembly, and finite-file transcription.
  6. Add growing-source execution and events.
  7. Cut over file/history/CLI, then TUI/hotkey recording.
  8. Remove legacy backends, dependencies, heuristics, and obsolete configuration.
  9. Complete documentation, privacy/licensing notices, real-surface checks, and migration-wide evidence.

Each step leaves main buildable. Legacy code is removed only after its final consumer cuts over. Existing WAV files and old caches are never automatically migrated or deleted.