From 0caf7b2cd30937bcc06f595e931cd6c4429ef484 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 20:42:21 +0000 Subject: [PATCH 1/2] docs: catch up agent/architecture docs to current code CLAUDE.md, ARCHITECTURE.md, READMEs, and AGENTS.md files had drifted past several recent shipped changes. This commit refreshes them so an agent reading the docs sees what the code actually contains: - Document `live12_catalogue.py` and `phase2_catalogue_gates.py` (the warn-and-keep Live 12 source-catalogue gate added in #119) across CLAUDE.md, apps/backend/ARCHITECTURE.md, apps/backend/AGENTS.md, and apps/backend/README.md. Clarify how the new `data/live12_catalogue.json` differs from the prompt-injected `prompts/live12_device_catalog.json`. - Add the `data/` directory to the CLAUDE.md repo-layout map and root README; surface the recommendation-proof campaign docs (GOAL.md, NEEDS.md, RECOMMENDATION_VERDICT.md) in the doc tables and precedence order. - Document the missing operator scripts: top-level `scripts/build_live12_catalogue.py` and `apps/backend/scripts/parity_probe_synth_backends.py`. Add `evaluate_recommendations.py` and `emit_deterministic_recs.ts` to the backend AGENTS.md script list. - UI: document `RecommendationVerificationBadge`, `Phase2ConsistencyReport`, and the transcription pianoroll client/components in `apps/ui/README.md` and `apps/ui/AGENTS.md`. https://claude.ai/code/session_01KKfCTxJ3Bbadbd2naTagSb --- AGENTS.md | 8 +++++++- CLAUDE.md | 23 ++++++++++++++--------- README.md | 1 + apps/backend/AGENTS.md | 7 ++++++- apps/backend/ARCHITECTURE.md | 7 +++++++ apps/backend/README.md | 5 +++++ apps/ui/AGENTS.md | 4 +++- apps/ui/README.md | 3 +++ 8 files changed, 46 insertions(+), 12 deletions(-) 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..4529d9c2 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 remaining top-level directories are off-path and safe to skip unless the task explicitly names them: -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 From 744731d14ca8f8f98f604b8d31cbf2739c67cae5 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 21:11:55 +0000 Subject: [PATCH 2/2] docs: fix CLAUDE.md repo-layout intro contradiction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Repo Layout intro declared `data/` on the product path but then opened the numbered inventory with "The remaining top-level directories are off-path and safe to skip" — contradicting itself for `data/` (and arguably `packages/`). Reword the intro to frame the list as the full top-level layout (with product-path entries called out per item) instead of an off-path inventory. No content moves; only the framing sentence changes. Surfaced by PR #121 review. https://claude.ai/code/session_01KKfCTxJ3Bbadbd2naTagSb --- CLAUDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 4529d9c2..01ca1b3c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -95,7 +95,7 @@ Operational and one-shot scripts live in two places. They are not on the request ### Repo Layout — what's on the product path -`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 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. `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.)