[EPIC] 0.6.2 reliability hardening — HF cache atomicity, qwen3asr safety, minimax memory, vad perf
- Dominant language
- Rust
- Stars
- 0
- Forks
- 1
- Avg merge
- 11m
- Merged PRs (30d)
- 77
Description
## Goal
Ship a coordinated **0.6.2 patch release** that closes every open reliability issue currently actionable in this repo. Each member issue is independently reviewable; together they harden the production surfaces that were rough in 0.6.1:
- **voxora-hf cache** is durable, atomic, and concurrency-safe.
- **voxora-hf net layer** is bounded.
- **voxora-qwen3asr** is failure-safe on cache writes and honest about feature support.
- **voxora-minimax** no longer leaks heap on hot paths.
- **voxora-vad** can run in real time.
The breaking-change member (#190, MiniMaxConfig name collision) is **out of scope** here — it ships on a separate 0.7.0 track.
## Members
### Group A — voxora-hf cache atomicity (the headline)
1. **#185** — `HuggingFaceSource::resolve` needs an advisory lock on `/.lock`. The constant is reserved at `voxora-hf/src/cache.rs:35-36` and the path helper at `cache.rs:148-151`; the lock is never opened. Two concurrent resolves double the bandwidth budget. Wire in `fs2::FileExt::lock_exclusive` (or `flock(2)` wrapped) with a small poll-for-completion timeout and RAII guard.
2. **#186** — `if dest.is_file() { continue; }` at `voxora-hf/src/source.rs:632-633` cannot tell a truncated partial from a complete file. Capture `Content-Length` from the HTTP response head and compare against the on-disk file length on resume. Truncated → re-download.
3. **#189** — `HfClient::get_to_file` at `voxora-hf/src/client.rs:142-189` has *five* error points (chunk read, write, flush, sync_all, rename) that leave the `.bin.partial.-` tmp file behind. Use a `TmpGuard` RAII (the pattern lives at `voxora-testkit/src/fixtures/real.rs:148-156`) so Drop cleans up on every error path and on panic.
4. **#193** — `HuggingFaceSource::resolve` ignores `ResolveOptions::max_bytes` in both the whole-repo and single-file paths, while `voxora-local` honours it. Asymmetric and a budget-bypass. Pre-flight: `total_required_bytes() > opts.max_bytes` → `AsrError::ModelTooLarge`. Mirror the local source's error shape.
### Group B — voxora-hf net layer
5. **#188** — `HfClient` reads the response body on error paths with no size cap (three call sites in `voxora-hf/src/client.rs`, one in `voxora-minimax/src/client.rs:217`). A multi-GiB error body exhausts RSS before `truncate` runs. Cap at 4 KiB on the *read* side; keep `truncate`'s 200-char display cap as-is.
### Group C — voxora-hf perf
6. **#192** — `pick_required_files` is O(N × R). Build a `HashMap<&str, &Sibling>` once, look up by name in O(1). Drop the dead-code `let _ = required;` while we're there. Algorithmic-only rewrite; the wiremock tests stay green as a property.
### Group D — voxora-qwen3asr safety
7. **#187** — `ensure_qwen3_tokenizer_json` at `voxora-qwen3asr/src/engine.rs:359-364` writes `tokenizer.json` directly to its final path. SIGKILL mid-write bricks the next engine load. Mirror the HF canonical pattern: tmp file with `.bin.partial.-` shape, `sync_all`, atomic `rename`, RAII cleanup.
8. **#191** — `voxora-qwen3asr` silently drops `timestamps = true` when `Translate` is not requested; callers get empty `segments` with no warning, while a missing translation returns `AsrError::Unsupported`. Asymmetry. Pre-flight reject for `timestamps`, matching the existing translate pre-flight.
### Group E — voxora-minimax
9. **#184** — `MiniMaxClient::build_multipart` at `voxora-minimax/src/client.rs:199, 201, 207-208` calls `Box::leak` to satisfy `Form<'static>`. The leaked values are *constants* (model name, response format, field names, two timestamp-level tokens). Replace with plain `&'static str` literals.
### Group F — voxora-vad perf
10. **#194** — `voxora-vad/src/energy.rs` computes RMS per sample. Hop-batch the input (10–30 ms frames), maintain a running `sum_sq`, recompute `sqrt(sum_sq / hop_size)` once per hop. Behaviour tests at `voxora-vad/src/energy.rs:408-628` are the contract — they must stay byte-for-byte green.
## Out of scope (separate track)
- **#190** `MiniMaxConfig` name collision (breaking-change). Tracks under 0.7.0 — separate EPIC.
- **#52** Self-hosted GPU runner (already `pending-human`; operator decision).
## Acceptance
This EPIC closes when:
- All 10 member issues are resolved on `origin/main` with GPG-signed commits and individual PRs (one issue ↔ one PR ↔ one or more squash-merges).
- Per-crate CHANGELOG entries reflect the bumps (one-line "0.6.2 — …" entries).
- `cargo fmt --all --check`, `cargo clippy --workspace --all-targets -- -D warnings`, `cargo test --workspace --all-targets`, `RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --workspace` are all green.
- `cargo build --workspace --locked` passes against the committed `Cargo.lock`.
- A coordinated `0.6.2` release tag follows the release cycle in `CONTRIBUTING.md` and the orchestrator in `.github/workflows/orchestrate-release.yml`.
## Strategy
Sequenced to keep reviewable surfaces atomic:
1. Lock + atomic-write primitives (#185 + #189 + a matching guard for #187) land first as a single dependency-minimal PR; the RAII guard type lives in `voxora-hf` and is reused by `voxora-qwen3asr`.
2. Resume-verification (#186) and max_bytes (#193) land second, on the same lock primitive.
3. Net-layer bound (#188) and algo refactor (#192) land third as a perf/correctness pair.
4. qwen3asr timestamps honesty (#191) and minimax leak (#184) and vad hop-batch (#194) round out the cycle as their own PRs.
Each PR keeps `Cargo.lock` in sync (lockfile invariant — never hand-edited, never omitted) and pairs a CHANGELOG entry with the actual diff.
## Labels
epic, bug, refactor, performance, security
## Cross-references
- LocalSource security hardening (prior art for advisory lock + atomic write at the cache layer): EPIC #148 (PRs #149, #150, #151).
- 0.6.1 cycle baseline: tag `voxora X.Y.Z` coordinated-bump policy in `AGENTS.md` § "Version coordination".
Contributor guide
Assessment
This issue has not been assessed yet.