Losselot
Audio forensics meets AI-assisted development.
Losselot started as a tool to detect fake lossless audio files. It evolved into something more: a living experiment in how AI and humans can build software together, with every decision tracked and queryable.
What This Project Is
Two things at once:
-
An Audio Forensics Tool - Detect if your FLAC/WAV files are actually transcoded from MP3s. Uses spectral analysis, binary metadata parsing, and lo-fi detection to expose the truth.
-
A Living Museum - Every algorithm choice, every rejected approach, every "why did we do it this way?" is captured in a queryable decision graph. Watch the project evolve in real-time.
Explore the Live Site | Browse the Decision Graph
Quick Start
git clone https://github.com/notactuallytreyanastasio/losselot.git
cd losselot
cargo build --release
./target/release/losselot serve examples/ --port 3000Open localhost:3000 - you'll see the interactive analysis UI.
No test files? Generate them:
cd examples && ./generate_test_files.sh
Downloads
| Platform | Download |
|---|---|
| Mac (Apple Silicon) | Download |
| Mac (Intel) | Download |
| Windows | Download |
| Linux (AppImage) | Download |
| Linux (CLI) | Download |
The Verdicts
| Verdict | Score | Meaning |
|---|---|---|
| CLEAN | 0-34 | Genuine lossless - natural frequency content |
| SUSPECT | 35-64 | Something's off - investigate further |
| TRANSCODE | 65-100 | Fake lossless - clear compression damage |
Audio Analysis Deep Dive
How Detection Works
Spectral Analysis
Lossy codecs remove high frequencies to save space. The "scars" are permanent:
| Source | Typical Cutoff |
|---|---|
| 128kbps MP3 | ~16kHz |
| 192kbps MP3 | ~18kHz |
| 320kbps MP3 | ~20kHz |
| True Lossless | ~22kHz |
Binary Analysis (MP3)
For MP3 files, we read encoder metadata (LAME headers). A "320kbps" file with a 16kHz lowpass was definitely transcoded from 128kbps - the encoder honestly reports what it kept.
Lo-Fi Detection (CFCC)
Not all high-frequency rolloff is compression damage. Tape recordings have natural rolloff.
The difference:
- MP3: Brick-wall cutoff at fixed frequency
- Tape: Gradual rolloff that varies with dynamics
We use Cross-Frequency Coherence (CFCC) to distinguish them.
Re-encoding Detection
Multiple encoder signatures = multiple lossy passes = cumulative damage. We detect LAME, FFmpeg, Fraunhofer, and chains between them.
Detection Flags
Spectral:
severe_hf_damage- Major frequency losshf_cutoff_detected- Clear lossy cutoff founddead_ultrasonic_band- No content above 20kHz
Re-encoding:
multi_encoder_sigs- Multiple encoders detectedencoding_chain(LAME → FFmpeg)- Specific chain identifiedlame_reencoded_x2- Re-encoded through LAME twice
Supported Formats
FLAC, WAV, AIFF, MP3, M4A, AAC, OGG, Opus, ALAC
Decision Graph & Memory System
The Problem
Claude (and LLMs generally) lose context. Sessions end, memory compacts, decisions evaporate. Six months later, no one remembers why we chose CFCC over temporal variance analysis.
The Solution
Every decision is tracked in a queryable graph that persists forever:
# See all decisions ever made ./target/release/losselot db nodes # See how they connect ./target/release/losselot db edges # Full graph as JSON ./target/release/losselot db graph
Node Types
| Type | Purpose |
|---|---|
| Goal | High-level objectives |
| Decision | Choice points with options |
| Option | Approaches considered |
| Action | What was implemented |
| Outcome | What happened |
| Observation | Technical insights |
Confidence Weights
Every node can have a confidence score (0-100):
# Add with confidence ./target/release/losselot db add-node -t decision "Use CFCC" -c 85
- 70-100: High confidence - proven approach
- 40-69: Medium - reasonable, some uncertainty
- 0-39: Low - experimental
Commit Linking
Link decisions to specific code changes:
./target/release/losselot db add-node -t action "Implemented feature" -c 90 --commit abc123The demo UI shows clickable commit badges that link to GitHub.
Why This Matters
- Queryable history: "Why didn't we use temporal variance?" → search the graph
- Rejected approaches preserved: What didn't work is as valuable as what did
- Code-decision traceability: Every commit linked to its reasoning
- Survives context loss: The graph outlives any session
The Live Graph
Browse it here - 75+ nodes, chains grouped by topic, confidence badges, commit links.
Claude Integration & CLAUDE.md
The CLAUDE.md Contract
The project includes a CLAUDE.md file that tells Claude:
- Always start by reading the decision graph - recover context
- Log everything - observations, decisions, actions, outcomes
- Link commits to decisions - full traceability
- Sync the graph before deploying - keep the live site current
Makefile Shortcuts
# Decision Graph Commands make obs T="Found interesting pattern" C=80 # Observation make decision T="Choose approach" C=70 # Decision point make action T="Implemented fix" C=95 # Implementation make outcome T="Tests pass" C=90 # Result make link FROM=1 TO=2 REASON="because" # Connect nodes make sync-graph # Export to live site # Web Viewer Commands (React + TypeScript + Vite) make web # Sync graph data and start dev server make web-dev # Start dev server at http://localhost:3001 make web-build # Build production bundle make web-typecheck # Run TypeScript type checking make web-test # Run web tests
Note: All commands should be run via make targets. This is the primary entrypoint for Claude skills and automation.
TypeScript Types
The frontend has TypeScript types mirroring the Rust backend:
DecisionNode,DecisionEdge,GraphData(graph.ts)AnalysisResult,BinaryAnalysis,SpectralAnalysis(analysis.ts)
Tests run in CI alongside Rust tests.
The Living Museum Concept
This isn't documentation written after the fact. It's a real-time record of how software gets built - captured as decisions happen, not reconstructed from memory later.
Command Line Reference
# Analyze files ./losselot ~/Music/ # Folder ./losselot file.flac # Single file ./losselot -o report.html ~/Music/ # Custom output # Web UI ./losselot serve ~/Music/ --port 3000 # Quick scan (no spectral) ./losselot --no-spectral ~/Music/ # Decision graph ./losselot db nodes # List nodes ./losselot db edges # List edges ./losselot db graph # Full JSON ./losselot db add-node -t TYPE "Title" [-c CONF] [--commit HASH] ./losselot db add-edge FROM TO [-t TYPE] [-r REASON]
Exit codes: 0=clean, 1=suspect, 2=transcode
Build from Source
# Requires Rust (rustup.rs) git clone https://github.com/notactuallytreyanastasio/losselot.git cd losselot cargo build --release
Run Tests
cargo test # Rust tests cd docs && npm test # TypeScript tests
Generate Test Files
cd examples && ./generate_test_files.sh
Creates files demonstrating:
- Clean 320kbps and V0 VBR encodes
- 128kbps → 320kbps transcodes
- Multiple re-encoding passes
- Mixed encoder chains
The Story
Losselot started as "does this FLAC actually contain lossless audio?" and evolved into "can we build software in a way where every decision is preserved, queryable, and linked to code?"
The audio forensics works great. But the real experiment is whether decision graphs can solve the "why did we do it this way?" problem that plagues every long-running project.
License
MIT - Do whatever you want with it.
