diff --git a/AGENTS.md b/AGENTS.md index 61eb9713..83f4fe0e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,11 +9,17 @@ Order of precedence when guidance conflicts: 1. [`PURPOSE.md`](PURPOSE.md) — why ASA exists and the non-negotiable quality invariants. 2. [`CLAUDE.md`](CLAUDE.md) — canonical agent guide: commands, architecture, tripwires, change map. -3. [`docs/ARCHITECTURE_STRATEGY.md`](docs/ARCHITECTURE_STRATEGY.md) — why the three-layer design is shaped the way it is. +3. [`GOAL.md`](GOAL.md) — the current north-star recommendation-proof campaign; per-app `AGENTS.md` files defer to it as well. +4. [`docs/ARCHITECTURE_STRATEGY.md`](docs/ARCHITECTURE_STRATEGY.md) — why the three-layer design is shaped the way it is. App-local entry points: - [`apps/backend/AGENTS.md`](apps/backend/AGENTS.md) - [`apps/ui/AGENTS.md`](apps/ui/AGENTS.md) +Campaign status (read alongside `GOAL.md`): + +- [`apps/backend/NEEDS.md`](apps/backend/NEEDS.md) — living status of the recommendation-proof campaign. +- [`apps/backend/RECOMMENDATION_VERDICT.md`](apps/backend/RECOMMENDATION_VERDICT.md) — provisional Gemini-vs-deterministic write-up (proxy-render caveats inside). + Historical plan and audit documents live in [`docs/history/`](docs/history/) — past-tense, not living docs. diff --git a/CLAUDE.md b/CLAUDE.md index 0359ed3b..01ca1b3c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -81,6 +81,7 @@ Operational and one-shot scripts live in two places. They are not on the request 1. `dev.sh` — full-stack dev launcher (covered above). 2. `test-e2e.sh` / `test-e2e-integration.sh` — e2e harnesses (covered above). 3. `calibrate_confidence.py` — threshold-sweep harness for the pitch / chord / sidechain detectors. Research-only. +4. `build_live12_catalogue.py` — regenerates `data/live12_catalogue.json` (the source-extracted Live 12 device/parameter catalogue) from the upstream `gluon/AbletonLive12_MIDIRemoteScripts` checkout. Static AST extraction; carries no proprietary code. Re-run when the upstream commit shifts or the schema bumps; the published `data/live12_catalogue.schema.json` validates the output. `apps/backend/scripts/`: 1. `bootstrap.sh` — recreate the Python 3.11 venv (covered above). `dev.sh` here is a thin shim that `exec`s the repo-root `scripts/dev.sh` full-stack launcher. @@ -88,18 +89,20 @@ Operational and one-shot scripts live in two places. They are not on the request 3. `evaluate_phase1.py`, `evaluate_polyphonic.py`, `evaluate_structure_sweep.py`, `evaluate_beats.py`, `evaluate_loudness_recs.py`, `evaluate_recommendations.py` — offline evaluation harnesses for the Phase 1 detector battery, the research polyphonic transcriber, structure-segmentation parameter sweeps, the beat/downbeat measurement gate, loudness-recommendation reachability, and the recommendation-quality scorer (`GOAL.md`'s recommendation-proof campaign — scores Phase 2 recs against the `tests/fixtures/recommendation_tracks/` answer-key corpus). Wired to `phase1_evaluation.py` / `polyphonic_evaluation.py` / `beat_evaluation.py` / `loudness_rec_evaluation.py` / `recommendation_evaluation.py`. `build_beat_manifest.py` assembles the beat-eval corpus manifest. `emit_deterministic_recs.ts` is the recommendation harness's deterministic-source bridge (Node 23+ native TS; wraps `apps/ui/src/data/abletonDevices.ts` to score the free path). All research-only. The beat gate's optional neural deps (`beat_this`, `mir_eval`) live in `apps/backend/requirements-eval.txt` — install into a separate venv, never the product venv. 4. `audit_pass1.py`, `genre_check.py`, `replay_catalog_validation.py` — corpus auditing and Live 12 device-catalog validation. `genre_corpus.md` is the corpus manifest. 5. `import_midi_to_ground_truth.py`, `score_polyphonic_clip.py` — corpus-building helpers for the transcription/polyphonic ground-truth fixtures (`tests/fixtures/transcription_tracks/`, `tests/fixtures/polyphonic_tracks/`). Research-only; see those fixtures' READMEs and `docs/LAYER2_EVALUATION.md` / `docs/POLYPHONIC_TRANSCRIPTION_SPIKE.md`. +6. `parity_probe_synth_backends.py` — maintainer probe that renders the same `ClipPlan` through FluidSynth and `symusic.Synthesizer` and reports RMS/peak/spectral-centroid deltas before flipping the `ASA_SAMPLE_SYNTH_BACKEND` auto default. Research/operator-only; not invoked by the runtime. ## Architecture ### Repo Layout — what's on the product path -`apps/backend/`, `apps/ui/`, and `scripts/` are the product path. `packages/` holds forward-looking product code that isn't wired into the request path yet (see below). The remaining top-level directories are off-path and safe to skip unless the task explicitly names them: +`apps/backend/`, `apps/ui/`, `scripts/`, and `data/` are the product path. `packages/` holds forward-looking product code that isn't wired into the request path yet (see below). The full top-level layout (product-path entries called out per item; everything else is off-path and safe to skip unless the task names it): -1. `packages/loudness-spectro-wasm/` — browser-first WebAssembly DSP (ITU-R BS.1770-5 / EBU R128 loudness, A-weighted spectrum, and spectral-reassignment spectrogram). Rust lifted from [openmeters](https://github.com/httpsworldview/openmeters) and compiled via `wasm-bindgen`, GPL-3.0-or-later. **Phase 1: standalone, not yet imported by `apps/ui` or `apps/backend`.** Has its own Cargo workspace, `npm`/`cargo` build, and EBU/ebur128/pyloudnorm validation layers — see [`packages/loudness-spectro-wasm/README.md`](packages/loudness-spectro-wasm/README.md). When integration lands, the canonical Phase 1 LUFS contract still comes from the Essentia path until this is wired in and proven at parity. (The reassigned spectrogram on the product path today comes from librosa via the `reassigned` spectral-enhancement endpoint, not this package.) -2. `audits/` — dated, automated audit reports (e.g. `nightly-2026-05-19.md`). Past-tense paper trail; not imported by either app. (Older one-shot advisory deliverables like the phase 1 audit have been archived under `docs/history/phase1-audit/`.) -3. `incorporations/` — planning docs for incorporating upstream projects (e.g. `forking-plans-2026-05-14.md`). Planning notes only; not code. -4. `docs/` — long-form rationale (`ARCHITECTURE_STRATEGY.md`, `history/`). Read these *before* structural changes; do not treat them as living API docs. -5. `tests/ground_truth/` — labeled-corpus fixtures consumed by `scripts/calibrate_confidence.py` (see `tests/ground_truth/README.md`). Not a test suite — each app owns its own (`apps/backend/tests/`, `apps/ui/tests/`). +1. `data/` — generated, source-extracted Live 12 catalogue (`live12_catalogue.json` + `live12_catalogue.schema.json`) consumed at runtime by `apps/backend/live12_catalogue.py`. Regenerated via `scripts/build_live12_catalogue.py` from the upstream `gluon/AbletonLive12_MIDIRemoteScripts` checkout. Static device/parameter metadata only; no upstream code is committed. +2. `packages/loudness-spectro-wasm/` — browser-first WebAssembly DSP (ITU-R BS.1770-5 / EBU R128 loudness, A-weighted spectrum, and spectral-reassignment spectrogram). Rust lifted from [openmeters](https://github.com/httpsworldview/openmeters) and compiled via `wasm-bindgen`, GPL-3.0-or-later. **Phase 1: standalone, not yet imported by `apps/ui` or `apps/backend`.** Has its own Cargo workspace, `npm`/`cargo` build, and EBU/ebur128/pyloudnorm validation layers — see [`packages/loudness-spectro-wasm/README.md`](packages/loudness-spectro-wasm/README.md). When integration lands, the canonical Phase 1 LUFS contract still comes from the Essentia path until this is wired in and proven at parity. (The reassigned spectrogram on the product path today comes from librosa via the `reassigned` spectral-enhancement endpoint, not this package.) +3. `audits/` — dated, automated audit reports (e.g. `nightly-2026-05-19.md`, `phase2-recommendation-surface-2026-05-24.md`). Past-tense paper trail; not imported by either app. (Older one-shot advisory deliverables like the phase 1 audit have been archived under `docs/history/phase1-audit/`.) +4. `incorporations/` — planning docs for incorporating upstream projects (e.g. `forking-plans-2026-05-14.md`, `beat-this-measurement-gate-2026-05-20.md`). Planning notes only; not code. +5. `docs/` — long-form rationale (`ARCHITECTURE_STRATEGY.md`, `history/`). Read these *before* structural changes; do not treat them as living API docs. +6. `tests/ground_truth/` — labeled-corpus fixtures consumed by `scripts/calibrate_confidence.py` (see `tests/ground_truth/README.md`). Not a test suite — each app owns its own (`apps/backend/tests/`, `apps/ui/tests/`). ### Three-Layer Model @@ -175,10 +178,11 @@ Artifact access goes through `artifact_storage.py` rather than direct disk paths 16. **`phase1_evaluation.py` + `phase1_report_html.py`, `polyphonic_evaluation.py`, `beat_evaluation.py` + `beat_report_html.py`, `loudness_rec_evaluation.py`, `recommendation_evaluation.py`**: Offline evaluation harnesses (deterministic-metric / detector-stability reporting, research-only polyphonic transcription, the beat/downbeat measurement gate that benchmarks CPJKU/beat_this against the shipping kick-accent heuristic, loudness-recommendation reachability, and the recommendation-quality scorer that grades Phase 2 recommendations against known-settings fixtures — see [`apps/backend/NEEDS.md`](apps/backend/NEEDS.md)). Not on the product path; driven by `scripts/evaluate_*.py`. Deleting them restores the product exactly. 17. **`utils/cleanup.py`**: Periodic artifact-cleanup helpers used by the server background-task loop. 18. **`transcription_pianoroll.py`**: Renders the pitch-note translation stage's `transcriptionDetail` as a velocity-encoded `(pitch, time)` uint8 matrix via [`symusic`](https://github.com/Yikai-Liao/symusic). Backs `GET /api/analysis-runs/{run_id}/transcription/pianoroll`. Derived view, never overrides Phase 1; the response cites Phase 1's `bpm` + `timeSignature` so chain of custody is preserved. Note: `transcriptionDetail` is *stripped* from `measurement.result` (see `analysis_runtime.py:685`) and lives in the `pitchNoteTranslation` stage instead — the route reads from there. +19. **`live12_catalogue.py` + `phase2_catalogue_gates.py`**: Source-extracted Live 12 catalogue lookup and Phase 2 output-validation gates. `live12_catalogue.py` loads `data/live12_catalogue.json` (generated by `scripts/build_live12_catalogue.py`), validates it against the published schema, and exposes a `Live12Catalogue` API with case-insensitive `has_device`, exact-match parameter lookup, and a `fuzzy_resolve` escape hatch. `phase2_catalogue_gates.py` cross-checks every `{device, parameter, value, phase1Fields}` record in `mixAndMasterChain`, `abletonRecommendations`, and `secretSauce.workflowSteps` against the catalogue and emits **warn-and-keep** `RECOMMENDATION_UNVERIFIED` events on `validationWarnings` (reasons: `device_unknown`, `parameter_unknown`, `value_out_of_range`, `citation_missing`). NEVER drops a recommendation, NEVER rewrites a parameter — an earlier fuzzy-rewrite path produced confidently-wrong output (wrong EQ band + wrong A/B curve), so the contract is now warn-only. Mirrors `_validate_phase2_citation_paths` in spirit. Wired into `server.py` after `_validate_phase2_citation_paths`; load/parse errors degrade to a single skip warning instead of failing the response. The subprocess isolation means `analyze.py` works as a standalone CLI. Check `apps/backend/JSON_SCHEMA.md` before adding new analyzer output fields. Check `apps/backend/ARCHITECTURE.md` for the full HTTP flow and contract details. -**Phase 2 (`POST /api/phase2`, legacy compat):** Uploads audio to Gemini inline if ≤100 MiB, or via the Gemini Files API if larger. Phase 1 JSON is appended to the system prompt from `prompts/phase2_system.txt`. Also relevant: `prompts/stem_summary_system.txt` and `prompts/live12_device_catalog.json`. Backend defense-in-depth: `server_phase2.py`'s `_validate_phase2_citation_paths` mirrors the frontend citation-existence check and emits `validationWarnings` when a recommendation cites a Phase 1 path that doesn't exist — it flags invented citations rather than failing, since Phase 1 stays authoritative (invariant #1). +**Phase 2 (`POST /api/phase2`, legacy compat):** Uploads audio to Gemini inline if ≤100 MiB, or via the Gemini Files API if larger. Phase 1 JSON is appended to the system prompt from `prompts/phase2_system.txt`. Also relevant: `prompts/stem_summary_system.txt` and `prompts/live12_device_catalog.json` (the *prompt-injected* device catalogue — distinct from the runtime-validation `data/live12_catalogue.json` consumed by `phase2_catalogue_gates.py`). Backend defense-in-depth: `server_phase2.py`'s `_validate_phase2_citation_paths` mirrors the frontend citation-existence check and emits `validationWarnings` when a recommendation cites a Phase 1 path that doesn't exist — it flags invented citations rather than failing, since Phase 1 stays authoritative (invariant #1). `phase2_catalogue_gates.apply_live12_catalogue_gates` runs immediately after for source-catalogue checks (see backend file #19 above). **Python version constraint:** Python 3.11.x required on macOS arm64. Essentia 2.1b6 wheels are only published for 3.11; this constraint may be relaxable if Essentia publishes 3.12+ wheels. @@ -299,6 +303,7 @@ This repo carries parallel guidance for non-Claude agents: 1. **`AGENTS.md`** (root) — pointer for Codex / OpenHands / any tool that looks for `AGENTS.md` by name. Defers to this file. 2. **`apps/backend/AGENTS.md`**, **`apps/ui/AGENTS.md`** — per-app overlays with technology-stack details and app-specific change checklists. 3. **`docs/ARCHITECTURE_STRATEGY.md`** — *why* the three-layer architecture is shaped the way it is. -4. **`docs/history/`** — completed plans and one-shot audits. Past-tense, not living docs. +4. **`GOAL.md`** — the recommendation-proof campaign (north-star goal). `apps/backend/NEEDS.md` is its living status doc; `apps/backend/RECOMMENDATION_VERDICT.md` is the provisional Gemini-vs-deterministic write-up (proxy-render caveats inside). +5. **`docs/history/`** — completed plans and one-shot audits. Past-tense, not living docs. -When information conflicts: `PURPOSE.md` > this file > per-app `AGENTS.md`. +When information conflicts: `PURPOSE.md` > `CLAUDE.md` > `GOAL.md` > per-app `AGENTS.md`. diff --git a/README.md b/README.md index b6b22ea7..b2fdbfc7 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ cd apps/backend && ./venv/bin/python -m unittest discover -s tests |---|---| | [`PURPOSE.md`](PURPOSE.md) | Why ASA exists; non-negotiable quality invariants. | | [`CLAUDE.md`](CLAUDE.md) | Canonical guide for AI coding agents and contributors: commands, architecture, tripwires, change map. | +| [`GOAL.md`](GOAL.md) | The recommendation-proof campaign — ASA's current north-star goal. Status doc: [`apps/backend/NEEDS.md`](apps/backend/NEEDS.md). Provisional verdict: [`apps/backend/RECOMMENDATION_VERDICT.md`](apps/backend/RECOMMENDATION_VERDICT.md). | | [`docs/ARCHITECTURE_STRATEGY.md`](docs/ARCHITECTURE_STRATEGY.md) | Why the three-layer design is shaped the way it is. | | [`docs/SETUP.md`](docs/SETUP.md) | Detailed local setup, env vars, Phase 2 wiring. | | [`apps/backend/ARCHITECTURE.md`](apps/backend/ARCHITECTURE.md) | Backend HTTP flow and contract. | diff --git a/apps/backend/AGENTS.md b/apps/backend/AGENTS.md index 41f7236d..1f4fa35a 100644 --- a/apps/backend/AGENTS.md +++ b/apps/backend/AGENTS.md @@ -123,6 +123,9 @@ python3.11 -m venv venv - `polyphonic_evaluation.py` + `scripts/evaluate_polyphonic.py`: **Research-only.** Offline polyphonic-transcription evaluation harness, not part of the shipped product path. - `beat_evaluation.py` + `beat_report_html.py`: **Research-only.** Beat/downbeat measurement gate benchmarking CPJKU/beat_this against the shipping kick-accent downbeat heuristic. Driven by `scripts/evaluate_beats.py`; deleting it restores the product exactly. - `loudness_rec_evaluation.py`: **Eval/test-only.** Reachability check for the deterministic subset of a loudness recommendation (gain-to-target-LUFS + true-peak ceiling). Must not be imported by `analyze.py` or `server.py`. +- `recommendation_evaluation.py` + `scripts/evaluate_recommendations.py`: **Research-only.** Recommendation-quality scorer for `GOAL.md`'s recommendation-proof campaign. Grades Phase 2 recs against `tests/fixtures/recommendation_tracks/` known-settings fixtures using a role/parameter/direction-band rubric, per-domain breakdown, and a chain-of-custody penalty that mirrors `phase2Validator.ts`. CLI supports `--source baseline|gemini|deterministic`, `--self-test`, `--report`, `--verification-artifact`. Companion: `scripts/emit_deterministic_recs.ts` is the Node 23+ TS bridge that wraps `apps/ui/src/data/abletonDevices.ts` into the scorer's normalized shape. Status doc: `NEEDS.md`; verdict write-up: `RECOMMENDATION_VERDICT.md`. Off the product path. +- `live12_catalogue.py`: Source-extracted Live 12 device/parameter catalogue loader. Reads `data/live12_catalogue.json` (generated by repo-root `scripts/build_live12_catalogue.py` from upstream `gluon/AbletonLive12_MIDIRemoteScripts`), validates against the published schema, and exposes `Live12Catalogue.has_device` (case-insensitive), exact-match parameter lookup, and `fuzzy_resolve`. Static-source extraction carries no `type/min/max/unit/default`; reserved for future runtime-introspection enrichment. Imported by `phase2_catalogue_gates.py` and the catalogue tests. +- `phase2_catalogue_gates.py`: **Warn-and-keep** Live 12 source-catalogue annotation of Phase 2 recommendations. Cross-checks every `{device, parameter, value, phase1Fields}` record against `Live12Catalogue` and emits `RECOMMENDATION_UNVERIFIED` events on `validationWarnings` for `device_unknown`, `parameter_unknown`, `value_out_of_range`, `citation_missing`. NEVER drops or rewrites — an earlier fuzzy-rewrite path produced confidently-wrong output (wrong EQ band + wrong A/B curve), so the contract is warn-only. Wired into `server.py` after `_validate_phase2_citation_paths`. Lives separately from `server_phase2.py` so unit tests can run without the FastAPI/pydantic import chain. - `utils/cleanup.py`: Periodic artifact cleanup helpers used by the server background-task loop. - `tests/test_server.py`: OpenAPI and envelope contract tests. - `tests/test_analyze.py`: generated WAV fixture, `EXPECTED_TOP_LEVEL_KEYS` snapshot, raw payload assertions. @@ -139,7 +142,9 @@ Under `apps/backend/scripts/` (not on the product path): - `bootstrap.sh`: create/recreate the venv with the pinned Python 3.11 dependencies. - `dev.sh`: convenience shim that `exec`s the monorepo root `./scripts/dev.sh` full-stack launcher. It does not start the backend on its own — the root launcher boots both backend and UI. - `render_upload_limit_contract.py`: re-renders the operator-facing upload-limit contract whenever `upload_limits.py` numbers change. -- `evaluate_phase1.py`, `evaluate_structure_sweep.py`, `evaluate_polyphonic.py`, `evaluate_beats.py`, `evaluate_loudness_recs.py`, `build_beat_manifest.py`, `genre_check.py`, `audit_pass1.py`, `replay_catalog_validation.py`: research and audit harnesses for measurement quality, beat/downbeat and loudness-recommendation gates, and prompt-output review. Outputs land under `.runtime/` and are intentionally not wired into the live API. The beat gate's optional neural deps (`beat_this`, `mir_eval`) live in `requirements-eval.txt` — install into a separate venv, never the product venv. +- `evaluate_phase1.py`, `evaluate_structure_sweep.py`, `evaluate_polyphonic.py`, `evaluate_beats.py`, `evaluate_loudness_recs.py`, `evaluate_recommendations.py`, `build_beat_manifest.py`, `genre_check.py`, `audit_pass1.py`, `replay_catalog_validation.py`: research and audit harnesses for measurement quality, beat/downbeat and loudness-recommendation gates, the recommendation-quality scorer, and prompt-output review. Outputs land under `.runtime/` and are intentionally not wired into the live API. The beat gate's optional neural deps (`beat_this`, `mir_eval`) live in `requirements-eval.txt` — install into a separate venv, never the product venv. +- `emit_deterministic_recs.ts`: Node 23+ native-TS bridge for `evaluate_recommendations.py --source deterministic`. Wraps `apps/ui/src/data/abletonDevices.ts` so the deterministic recommendation path can be scored against the same fixtures as Gemini without a TypeScript build step. No `npm install` needed. +- `parity_probe_synth_backends.py`: Maintainer probe that renders the same deterministic `ClipPlan` through both FluidSynth and `symusic.Synthesizer` and reports RMS / peak / spectral-centroid deltas, with a documented tolerance and pass/fail verdict written to `.runtime/parity/synth_parity.json`. Use before flipping the `ASA_SAMPLE_SYNTH_BACKEND` auto default away from FluidSynth — never invoked by the runtime. ## Code Style diff --git a/apps/backend/ARCHITECTURE.md b/apps/backend/ARCHITECTURE.md index 2683d965..ce765ef5 100644 --- a/apps/backend/ARCHITECTURE.md +++ b/apps/backend/ARCHITECTURE.md @@ -24,6 +24,9 @@ | `polyphonic_evaluation.py` + `scripts/evaluate_polyphonic.py` | Research-only offline polyphonic-transcription evaluation harness. Not on the product path. | | `beat_evaluation.py` + `beat_report_html.py` | Research-only beat/downbeat measurement gate. Benchmarks the neural tracker CPJKU/beat_this against the shipping kick-accent downbeat heuristic on a labeled corpus. Off the product path; driven by `scripts/evaluate_beats.py` (corpus manifest from `scripts/build_beat_manifest.py`). Deleting it restores the product exactly. | | `loudness_rec_evaluation.py` + `scripts/evaluate_loudness_recs.py` | Research/test-only reachability check for the deterministic subset of a loudness recommendation (gain-to-target-LUFS + true-peak ceiling). Renders/re-measures audio with ASA's own Essentia measurements as oracle. Must not be imported by `analyze.py` or `server.py`. | +| `recommendation_evaluation.py` + `scripts/evaluate_recommendations.py` | Research-only recommendation-quality scorer for `GOAL.md`'s recommendation-proof campaign. Grades Phase 2 recommendations against `tests/fixtures/recommendation_tracks/` known-settings fixtures using a role/parameter/direction-band rubric and a chain-of-custody penalty (mirrors `phase2Validator.ts` semantics). `--source baseline|gemini|deterministic` switches the recommendation source; `--verification-artifact` emits the per-domain match-rate artifact consumed by `apps/ui/src/data/recommendationVerification.ts`. Off the product path. See `RECOMMENDATION_VERDICT.md` and `NEEDS.md`. | +| `live12_catalogue.py` | Source-extracted Live 12 device/parameter catalogue loader. Loads `data/live12_catalogue.json` (generated by repo-root `scripts/build_live12_catalogue.py` from the upstream `gluon/AbletonLive12_MIDIRemoteScripts`), validates against the published schema, and exposes a `Live12Catalogue` API with case-insensitive `has_device`, exact-match parameter lookup, and a `fuzzy_resolve` escape hatch. Static-source extraction does not carry `type/min/max/unit/default`; the `ParamSpec` slots are reserved for future runtime-introspection enrichment. | +| `phase2_catalogue_gates.py` | Live 12 source-catalogue checks that ANNOTATE Phase 2 recommendations. **Warn-and-keep contract:** every check (`device_unknown`, `parameter_unknown`, `value_out_of_range`, `citation_missing`) emits a `RECOMMENDATION_UNVERIFIED` warning on the `validationWarnings` channel; nothing is dropped or rewritten. Cross-checks every `{device, parameter, value, phase1Fields}` record in `mixAndMasterChain`, `abletonRecommendations`, and `secretSauce.workflowSteps`. Lives separately from `server_phase2.py` so unit tests can exercise it without the FastAPI/pydantic import chain. Wired into `server.py` after `_validate_phase2_citation_paths`. | | `utils/cleanup.py` | Periodic artifact cleanup helpers used by the server background-task loop. | | `tests/test_server.py` | Contract tests for estimate, timeout, and success envelopes. | | `tests/test_analyze.py` | Structural snapshot tests for the raw analyzer JSON output. Owns `EXPECTED_TOP_LEVEL_KEYS` — update it whenever you add a root field. | @@ -319,6 +322,10 @@ When the analyzer never produces a valid JSON object, `timings.fileDurationSecon When an interpretation result is produced (the canonical `…/interpretations` route and the legacy `/api/phase2` wrapper both route through the same handling), `_validate_phase2_citation_paths` (in [`server_phase2.py`](server_phase2.py), called from [`server.py`](server.py)) checks every recommendation's `phase1Fields` citations against the *normalized* authoritative measurement payload — the same shape Gemini is prompted with. Cited dotted paths that don't resolve are flagged as **WARNING-only** entries on the `validationWarnings` channel; they never reject the response and never raise. This is a backend defense-in-depth mirror of the frontend's `validatePhase1FieldCitations` (in `apps/ui/src/services/phase2Validator.ts`), so a non-browser API consumer can't silently accept invented citations. Phase 1 stays authoritative either way. +### Phase 2 Live 12 catalogue gates + +Running immediately after citation-path verification, [`phase2_catalogue_gates.apply_live12_catalogue_gates`](phase2_catalogue_gates.py) cross-checks each `{device, parameter, value, phase1Fields}` record in `mixAndMasterChain`, `abletonRecommendations`, and `secretSauce.workflowSteps` against the source-extracted [`data/live12_catalogue.json`](../../data/live12_catalogue.json). All findings are emitted as warn-and-keep `RECOMMENDATION_UNVERIFIED` events on the same `validationWarnings` channel (reasons: `device_unknown`, `parameter_unknown`, `value_out_of_range`, `citation_missing`); nothing is dropped or rewritten — an earlier fuzzy-rewrite path landed on the wrong EQ band and wrong A/B curve, so the contract is now warn-only. Catalogue load/parse errors degrade to a single `_unverified` warning rather than failing the response. + ## Transcription Pipeline `transcriptionDetail` is produced only when `--transcribe` is active. diff --git a/apps/backend/README.md b/apps/backend/README.md index 21685b99..89d4ab8b 100644 --- a/apps/backend/README.md +++ b/apps/backend/README.md @@ -46,9 +46,14 @@ FastAPI also serves the usual generated endpoints at `/openapi.json`, `/docs`, a - librosa (spectrogram / spectral time-series artifacts) - mido / pretty_midi - pytheory + pyfluidsynth (Phase 3 audition-sample generation) +- symusic (transcription pianoroll rendering; optional alternate synth backend) - FastAPI - Uvicorn +Companion runtime data: + +- `data/live12_catalogue.json` (at the repo root) — source-extracted Live 12 device/parameter catalogue consumed by [`live12_catalogue.py`](live12_catalogue.py) and used by [`phase2_catalogue_gates.py`](phase2_catalogue_gates.py) to annotate Phase 2 recommendations. Regenerated via repo-root `scripts/build_live12_catalogue.py` from the upstream `gluon/AbletonLive12_MIDIRemoteScripts` checkout; the published `data/live12_catalogue.schema.json` validates the output. Distinct from the *prompt-injected* `prompts/live12_device_catalog.json`. + ## Installation ```bash diff --git a/apps/ui/AGENTS.md b/apps/ui/AGENTS.md index e29a0285..705755b8 100644 --- a/apps/ui/AGENTS.md +++ b/apps/ui/AGENTS.md @@ -95,10 +95,12 @@ RUN_GEMINI_LIVE_SMOKE=true VITE_ENABLE_PHASE2_GEMINI=true GEMINI_API_KEY=your_ke - `src/services/analyzer.ts`: phase orchestration — sequences run creation, polling, and display payload projection. - `src/services/httpClient.ts`: shared fetch helpers and request-header injection used by the run/artifact/sample clients. - `src/services/spectralArtifactsClient.ts`: spectrogram and spectral-evolution artifact fetches via `/api/analysis-runs/{run_id}/artifacts/…`. +- `src/services/transcriptionPianorollClient.ts`: velocity-encoded transcription pianoroll matrix via `/api/analysis-runs/{run_id}/transcription/pianoroll`. Backed by `apps/backend/transcription_pianoroll.py`; rendered in `components/TranscriptionPianoroll.tsx` / `TranscriptionPianorollBlock.tsx`. - `src/services/sampleGenerationClient.ts`: Phase 3 audition-sample POST/GET against `/api/analysis-runs/{run_id}/samples` plus per-clip artifact streaming. - `src/services/audioFile.ts`: client-side audio validation, blank-MIME extension fallback, and preview-URL lifecycle. - `src/services/mixDoctor.ts`: client-side spectral-balance scoring against genre profiles. -- `src/services/phase2Validator.ts` + `loudnessGuardrails.ts`: runtime guardrail — chain-of-custody checks of Phase 2 against Phase 1 (BPM, key, LUFS, genre/DSP, numeric bounds, and a `MISSING_LOUDNESS_ACTION` check). `loudnessGuardrails.ts` defines the objective loudness defects (clipping, true-peak overs) a Phase 2 mastering/dynamics card must address. +- `src/services/phase2Validator.ts` + `loudnessGuardrails.ts`: runtime guardrail — chain-of-custody checks of Phase 2 against Phase 1 (BPM, key, LUFS, genre/DSP, numeric bounds, and a `MISSING_LOUDNESS_ACTION` check). `loudnessGuardrails.ts` defines the objective loudness defects (clipping, true-peak overs) a Phase 2 mastering/dynamics card must address. Surfaced in the UI via `components/Phase2ConsistencyReport.tsx`. +- `src/services/recommendationVerification.ts` + `src/data/recommendationVerification.ts` + `components/RecommendationVerificationBadge.tsx`: per-recommendation corpus-verification badge for the recommendation-proof campaign (`../../GOAL.md` sub-goal 4). The data module is a generated artifact from `apps/backend/scripts/evaluate_recommendations.py --verification-artifact` (all-`NONE` until the ground-truth corpus has renders); the service infers a card's domain (mirroring the backend scorer's `infer_domain`) and looks up its confidence band; the badge renders nothing when confidence is `NONE` so the surface degrades gracefully. - `src/services/phase1Picker.ts` + `phaseLabels.ts`: phase-snapshot projection helpers used by the results surface. - `src/services/appliedRecommendations.ts` + `userLabels.ts`: applied-recommendations tracker and persisted label state used by the audit overhaul. - `src/services/fieldAnalytics.ts` + `diagnosticLogs.ts`: instrumentation hooks and diagnostic log capture for the request panel. diff --git a/apps/ui/README.md b/apps/ui/README.md index 39b01bca..0b9f493f 100644 --- a/apps/ui/README.md +++ b/apps/ui/README.md @@ -23,8 +23,11 @@ The app uploads a track to the local DSP backend, shows the estimate and executi - confidence threshold slider (pitch/note mode only; disabled in melody-guide mode with tooltip) - quantize grid and swing controls - browser preview and `.mid` download +- transcription pianoroll heatmap (velocity-encoded `pitch × time`) fetched from the backend; rendered when `transcriptionDetail.noteCount > 0` and grounded in the Phase 1 `bpm` + `timeSignature` so each cell traces back to a measurement - JSON export and markdown report export - Phase 3 audition-sample playback panel — on-demand heuristic WAV/MIDI clips with citation metadata, requested after interpretation completes +- chain-of-custody report (`Phase2ConsistencyReport`) that surfaces `phase2Validator` violations directly under the Phase 2 results +- per-recommendation corpus-verification badge (`RecommendationVerificationBadge`) — renders the confidence band that backs each recommendation domain; hidden when the corpus has no scored fixtures yet (`GOAL.md` sub-goal 4) - collapsible diagnostic log with request IDs, durations, estimate ranges, and backend or Gemini status - semantic theme token system for status colors and surface backgrounds - mobile-responsive layouts across header, results grid, and upload flow