fix: handoff audit — enforce config validation, repair broken provider paths, add guardrails#53
Draft
betmoar wants to merge 1 commit into
Draft
fix: handoff audit — enforce config validation, repair broken provider paths, add guardrails#53betmoar wants to merge 1 commit into
betmoar wants to merge 1 commit into
Conversation
…r paths, add guardrails Phase-3/4 output of the 2026-07 principal-architect handoff audit. Every fix is locked by tests/test_handoff_invariants.py (19 tests, invariants I1-I8) and documented in docs/ARCHITECTURE.md / docs/BACKLOG.md. Correctness (production path): - Config validation rules were declared but NEVER executed: BaseConfig._validate() had an empty body. It now runs every registered rule against the dataclass fields, plus a new cross-field rule (overlap_duration < segment_length). - split_audio: guard against non-positive step (segment_length <= overlap_duration previously hung in an infinite while-loop; config is mutable post-load so the config-time check alone is insufficient). - ACRCloud provider was unusable end-to-end: the factory constructed it with no credentials (TypeError) and identify_track took raw bytes while the pipeline passes AudioSegment. Factory now reads TRACKLISTIFY_ACR_ACCESS_KEY/_SECRET/_HOST (ConfigError with actionable message when missing); identify_track accepts AudioSegment or bytes. - Provider fallback was a complete no-op: fallback_enabled, fallback_providers and --no-fallback were read by nothing. IdentificationManager now walks a primary+fallback provider chain per segment. - Circuit breaker never received request outcomes; new public RateLimiter.record_result() is called on every provider success/failure. - Cache TTL was disabled (set() stored ttl=None which shadowed the strategy default -> is_valid always True) and the cache index was only persisted from cleanup()/clear(), so entries written by one process were invisible to the next and then deleted as "orphans". set()/delete() now persist the index; index save fsyncs before rename. Smaller fixes: - cli: fail fast with an actionable error when ffmpeg is missing. - exporters: output_dir mkdir(parents=True). - Spotify 429 now carries structured retry_after on RateLimitError. - commitizen version_provider poetry -> pep621 (cz bump was broken since the uv migration). - scripts/generate_config_docs.py called a method that never existed; now uses ConfigDocGenerator. - Removed dead module-level identify_tracks() that iterated a string as segments. Infrastructure: - .github/workflows/ci.yml: ruff lint + format check, .env.example drift check, pytest on 3.11/3.12/3.13 (repo previously had NO CI). - docs/ARCHITECTURE.md: load-bearing map, implicit contracts, landmines, touch-X-update-Y couplings. - docs/PLAYBOOKS.md: step-by-step procedures for recurring changes. - docs/BACKLOG.md: residual risk register + prioritized backlog. - CLAUDE.md: corrected stale gotchas, new invariant pointers. - .env.example regenerated (ACR credential vars incl. new _HOST). 388 tests pass (369 existing + 19 new invariants).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Handoff audit: fixes + self-enforcing guardrails
Full principal-architect audit of the codebase (recon → deep audit → remediation → infrastructure). Every fix is locked by an invariant test in
tests/test_handoff_invariants.py(I1–I8), and the judgment behind them is encoded in three new docs so it survives the handoff.Correctness fixes (production path)
BaseConfig._validate()had an empty body; the ~13 type/range/path rules registered in_setup_validationwere dead code_validate()now feeds dataclass fields toConfigValidator; added cross-field ruleoverlap_duration < segment_length(I1)split_audiowhenoverlap_duration >= segment_length(step ≤ 0 → while-loop never advances)ACRCloudProvider()with no credentials (instantTypeError), andidentify_tracktook rawbyteswhile the pipeline passesAudioSegment-p acrcloudcrashed unconditionallyTRACKLISTIFY_ACR_ACCESS_KEY/_SECRET/_HOSTfrom env (secrets deliberately kept off the config dataclass), raisesConfigErrornaming the exact vars when missing;identify_trackacceptsAudioSegmentor bytes (I3, I4)fallback_enabled,fallback_providers, and--no-fallbackwere read by nothingIdentificationManagerwalks a primary→fallback provider chain per segment, with per-provider rate limiting (I5)_update_circuit_breakerhad zero production callersRateLimiter.record_result(), called on every provider outcome (I6)set()storedttl: None, which shadowed the strategy default inmetadata.get("ttl", default_ttl), sois_validalways returned Trueset()stamps the cache-level TTL (I7)cleanup()/clear()set/delete; index save now fsyncs before rename (I8)Smaller: CLI fail-fast when ffmpeg missing (was a cryptic per-segment error); exporter
mkdir(parents=True); Spotify 429 carries structuredretry_after;cz bumprepaired (version_providerwas still"poetry"post-uv-migration);scripts/generate_config_docs.pycalled a method that never existed; removed dead module-levelidentify_tracks()that iterated a string as segments.Infrastructure (the actual deliverable)
.github/workflows/ci.yml) — the repo had no CI at all. Now: ruff lint + format check,.env.exampledrift check (generate_env_example.py --check), pytest on 3.11/3.12/3.13 with ffmpeg installed.tests/test_handoff_invariants.py— 19 tests locking the 8 named invariants above; header explains each.docs/ARCHITECTURE.md— load-bearing inventory (ranked by blast radius), implicit contracts, landmines (e.g.min_confidenceis 0–1 whileTrack.confidenceis 0–100 and the knob is deliberately unwired; the cache subsystem is still not wired into the pipeline), and touch-X-update-Y couplings.docs/PLAYBOOKS.md— literal step-by-step procedures (add a provider / config option / output format; touch split_audio / rate limiter / config loading; 3am "no tracks identified" triage), each ending with exact verification commands and the real trap that motivated it.docs/BACKLOG.md— residual risk register: P1 wire the cache into identification (now safe; plan included), P2min_confidencedecision, P2 Spotify enrichment wiring vs. the broken client-credentials/me/*playlist path, P3 deaddownloaders/spotify.py, dev_cli cleanup, security polish. Plus a "fixed, do not re-fix" table.Verification
uv run python -m pytest -q→ 388 passed (369 pre-existing + 19 new), 2 environment skipsuv run ruff check/ruff format --checkonsrc/ tests/ scripts/→ cleanuv run python scripts/generate_env_example.py --check→ in syncAsyncApp.split_audio→ 4 segments at the correct 50 s stride, sorted, non-empty; step guard fires with the actionable messageACRCloudProviderTypeError via factory, out-of-rangeTRACKLISTIFY_SEGMENT_LENGTH=5000accepted pre-fix, rejected post-fix)🤖 Generated with Claude Code
https://claude.ai/code/session_01LRC2BkXVyNBfa2a6w844uU
Generated by Claude Code