Skip to content

Single .tsa analysis file: container in the lib, peaks moved from desktop, legacy sidecars migrated - #35

Merged
robmorgan merged 5 commits into
mainfrom
lib/tsa-container
Aug 5, 2026
Merged

Single .tsa analysis file: container in the lib, peaks moved from desktop, legacy sidecars migrated#35
robmorgan merged 5 commits into
mainfrom
lib/tsa-container

Conversation

@robmorgan

Copy link
Copy Markdown
Owner

Summary

Consolidates the two per-track sidecars (.tsanalysis.json + .tspeaks) into a single .tsa analysis container, owned by the library so downstream apps (Halo) get the same format.

  • io::tsa — chunked binary container: 28-byte identity header (sample rate, mono length, FNV content hash — one identity validated once for every chunk) + versioned ARTF (artifact JSON) and PEAK (u8-quantized base pyramid level) chunks. Unknown tags/versions skip forward-compatibly; duplicates, truncation, and trailing bytes are structural errors; readers never panic on hostile input. Two API layers: bytes (AnalysisFile::to_bytes / from_bytes / from_bytes_validated) for apps that store analysis blobs in their own database keyed by content_hash, and sidecar file wrappers (read/write_analysis_file, read_analysis_file_validated, analysis_file_path) with atomic temp+rename writes.
  • analysis::waveform — the 3-band waveform peaks pyramid moves from the desktop crate into the library (BandPeaks, PeakLevel, NUM_BANDS, now Clone).
  • Deprecationsread_preanalysis_json / write_preanalysis_json are #[deprecated] (still working) pointing at the container API. PreAnalysisArtifact::matches_identity added; matches_source delegates to it.
  • CLIanalyze writes <input>.tsa by default with both chunks (a complete offline pre-analysis tool); -o out.json keeps legacy JSON with a deprecation note; --pre-analysis sniffs magic bytes and accepts either format with the existing warn-and-fall-back contract.
  • Desktop — one .tsa per track: valid legacy JSON artifacts are absorbed on load (no re-analysis), peaks recompute instead of parsing old .tspeaks (milliseconds), one container write before the Track send (crash resilience) and one after analysis. Legacy sidecars are deleted only once the on-disk container holds valid artifact + peaks chunks for exactly this audio; Halo's .halo.* variants are never touched.

Zero new dependencies. Breaking (0.x → minor bump): the CLI default output format changed; changelog records it.

Testing

  • 30 new lib tests: container corruption matrix (magic/version/identity/truncation/oversized-length/trailing/duplicate-chunk), forward-compat (unknown tag + unknown chunk version), per-chunk degradation (garbage JSON, stale peaks params, pre-v4 artifact schema), quantization roundtrips, atomic-write hygiene, matches_identity parity, .tsa pipeline roundtrip.
  • Desktop worker tests rework: single-.tsa layout, legacy-JSON migration proven via a marker value (exact float equality is off the table — serde_json's default f64 parse is ±1 ulp), partial-legacy deletion, stale-legacy-not-absorbed, stale-generation persistence.
  • Full CI suite green on both crates: root fmt/clippy (±cli feature)/tests/RUSTDOCFLAGS="-D warnings" docs; desktop fmt/clippy/48 tests.
  • Live app verification: a track carrying both legacy sidecars converged to one .tsa (logs: migrating legacy JSON sidecar, Removed superseded legacy sidecar ×2, .halo.tsanalysis.json untouched); relaunch = pure container hit; truncated .tsa silently rebuilt. CLI smoke: analyze → 13.9 KB .tsa, --pre-analysis in.wav.tsa validates and applies it.

🤖 Generated with Claude Code

robmorgan and others added 5 commits August 5, 2026 07:11
Phase 1-2 of the single-analysis-file consolidation:

- analysis::waveform — the 3-band peaks pyramid moves from the desktop
  crate into the library (BandPeaks/PeakLevel/NUM_BANDS, now Clone), so
  downstream apps get waveform peaks without reimplementing the analyzer.
- io::tsa — the .tsa container: 28-byte identity header (sample rate,
  mono length, FNV content hash) + sequential versioned chunks (ARTF =
  artifact JSON, PEAK = u8-quantized base pyramid level). Unknown tags
  and unknown chunk versions skip forward-compatibly; duplicates,
  truncation, and trailing bytes are structural errors; readers never
  panic on hostile input. Two API layers: bytes (to_bytes/from_bytes/
  from_bytes_validated — for blob stores like a library database) and
  file wrappers with an atomic temp+rename writer.
- PreAnalysisArtifact::matches_identity — matches_source semantics for
  callers that already hold the (rate, length, hash) identity;
  matches_source now delegates to it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
read/write_preanalysis_json carry #[deprecated] pointing at the
io::tsa container (kept working for downstream consumers). The CLI
analyze subcommand now writes the full .tsa container — artifact plus
waveform peaks — next to the input by default; an explicit -o *.json
keeps the legacy format with a deprecation note. --pre-analysis accepts
either format, sniffed by magic bytes, with the same warn-and-fall-back
contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The load worker now maintains one .tsa container per track instead of
.tspeaks + .tsanalysis.json: validated container read up front, legacy
JSON artifacts absorbed (skipping re-analysis), peaks recomputed when
absent (milliseconds — the .tspeaks parser is not worth keeping), one
write before the Track send for crash resilience and one after analysis.
Legacy sidecars are deleted only once the on-disk container holds valid
artifact + peaks chunks for exactly this audio; Halo's .halo.* variants
are never touched. The desktop peaks module and cache are deleted in
favor of the library's analysis::waveform and io::tsa.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
README's offline pre-analysis example now builds an AnalysisFile (with
the bytes-layer note for database-backed apps), the changelog records
the breaking CLI default, deprecations, and additions, and ROADMAP
Stage 12's hardening surface points at the .tsa loader plus the
deprecated JSON path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@robmorgan
robmorgan merged commit c59ac6f into main Aug 5, 2026
10 checks passed
@robmorgan
robmorgan deleted the lib/tsa-container branch August 5, 2026 01:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant