feat: land the recommendation-proof campaign (GOAL.md + scorer + corpus + UI badge) - #114
Conversation
Brings the north-star campaign spec from the local 'worktree-goal-doc' branch onto main so ASA has its current extended-effort objective recorded in the canonical doc set. Sits below PURPOSE.md (the why) and above BACKLOG.md (candidate features), per the authority order. The doc is forward-looking — sub-goal sections describe what the work *looks like*, not what's already on main. Implementation lives on the 'worktree-goal-doc' branch (scorer, fixtures, deterministic-source bridge, UI verification badge) and is owner-render-gated; landing the implementation is a separate decision the trailing 'Implementation status' section flags. Verified every reference in GOAL.md resolves on main before writing (audit file, prompt paths, sibling eval scripts, fixture dirs, UI primitives). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lands the backend half of the recommendation-proof campaign (GOAL.md
sub-goals 1+2): a research-only scorer that grades Phase 2
recommendations against known Live 12 device settings, plus a corpus of
five catalog-valid spec fixtures (house, techno, melodic techno, dnb,
acid) and supporting status docs.
What's in:
- recommendation_evaluation.py — source-agnostic scorer implementing the
role/parameter/direction-band rubric from GOAL.md. Per-domain
breakdown over the seven production domains, device equivalence
classes (Compressor ↔ Glue Compressor earns credit), per-unit value
tolerance bands, and a chain-of-custody penalty that ports
phase2Validator.ts semantics. Includes a measurableIntent equivalence
credit so equivalent measurement routes earn partial score.
- scripts/evaluate_recommendations.py — CLI runner with
--source {baseline,gemini,deterministic}, --self-test, --report,
--json, --verification-artifact. Mirrors the existing eval-harness
pattern; off the product path; deletable without affecting product.
- scripts/emit_deterministic_recs.ts — Node 23+ native TS bridge that
wraps the product's abletonDevices.ts and emits the scorer's
normalized rec shape. Single source of truth for the free path; no
Python re-port.
- tests/test_recommendation_evaluation.py — 29 tests; full backend
suite 792 OK.
- tests/fixtures/recommendation_tracks/ — five owner-plausible
electronic-genre spec fixtures + a _TEMPLATE + corpus README +
authoring checklist. Each fixture has a catalog-valid deviceSpec, a
measurableIntent fingerprint, and a per-track README. Audio is
gitignored (renders are the owner's half — see NEEDS.md); a tiny
audio_melody.mid per fixture is the proxy melodic source.
- NEEDS.md — living campaign status doc + render-gated queue.
- RECOMMENDATION_VERDICT.md — provisional Gemini-vs-deterministic
verdict on synthetic-proxy renders (Gemini 0.227 vs deterministic
0.000; deterministic loses on chain-of-custody despite competitive
raw coverage). Awaits real Ableton renders to become authoritative.
Known follow-up the next agent should not skip: the
recommendation_evaluation.py port of phase2Validator.ts semantics is
current as of 0eac1ee; main's validator has drifted (~131 lines
changed since). Re-verify port parity before any sub-goal 3 prompt
iteration that depends on the custody penalty.
Verified: cd apps/backend && ./venv/bin/python -m unittest discover -s
tests — Ran 792 tests in 73.241s, OK (skipped=1).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lands the frontend half of the recommendation-proof campaign (GOAL.md sub-goal 4): a confidence/verification badge that maps each Phase 2 recommendation card to a corpus support band, mounted on both the mix-chain and patch recommendation cards in AnalysisResults.tsx. Built on the shared ui/ Pill primitive — no one-off styled box. Honors invariant #4 by rendering nothing when corpus support is NONE (graceful pre-render degradation; the band only surfaces once the backend scorer has scored real fixtures and regenerated the data artifact). What's in: - src/components/RecommendationVerificationBadge.tsx — the badge. - src/services/recommendationVerification.ts — domain inference (mirrors the backend scorer's infer_domain) + lookup against the generated artifact; node-testable. - src/data/recommendationVerification.ts — typed artifact (all-NONE pre-render; regenerated from apps/backend/scripts/evaluate_recommendations.py --verification-artifact, per its header comment). - tests/services/recommendationVerification.test.ts — 8 tests verifying the lookup path, the domain inference, and that an injected populated artifact surfaces the HIGH band (proves the wiring; the pre-render all-NONE state is the current honest content). - AnalysisResults.tsx — RecommendationVerificationBadge mounted on both the mix-chain card header and the patch card header, beside the existing category pill. Verified: - cd apps/ui && npm run lint — clean (tsc). - npm run test:unit — 671 passed across 51 files. - npm run build — built in 1.53s; AnalysisResults bundle 256.77 kB. - Smoke skipped (needs live stack). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
slittycode
left a comment
There was a problem hiding this comment.
Verdict: APPROVE
Summary
Lands the recommendation-proof campaign: a research/eval harness (recommendation_evaluation.py, 1 206 lines) that scores Phase 2 recs against known-settings fixtures, a CLI runner, a Node 23+ TS bridge for the deterministic path, 5 genre fixture specs, 29 backend tests, a per-recommendation UI badge (8 frontend tests), and the GOAL.md north-star doc. The harness is strictly eval — it is not imported by analyze.py, server.py, or any server module; deleting it leaves the product exactly as it was. Phase boundary is clean throughout. The PR description is unusually honest about the caveats; the code matches.
Tests: Cannot locally verify (no venv or node_modules in this review environment). PR reports 792 OK backend / 671 passed frontend. Test quality is high — the suite exercises scoring semantics meaningfully (direction-scoring, equivalence credit, custody-penalty ordering invariant, determinism), not coverage padding.
Findings
Should fix
recommendationVerification.ts data vs. PR claim (apps/ui/src/data/recommendationVerification.ts): The PR description says "badge stays invisible until renders land." That's only true for the four NONE domains. bass (LOW / "Lightly checked") and master (MED / "Corpus-checked") will render today because those bands came from the synthetic-proxy scoring. The data file's own ⚠️ comment says these values are from synthetic proxies, so it's not hidden — but "Lightly checked" on a domain scored against numpy approximations, not Ableton renders, is easy to misread as real corpus evidence. Either ship the all-NONE pre-render artifact as the initial state and regenerate after real renders land, or ensure the tooltip copy makes the synthetic-proxy provenance explicit (the current tooltip only says Corpus support: 5 fixture(s) with no proxy caveat).
Worth considering
Keyword ordering divergence (recommendation_evaluation.py line ~626 vs recommendationVerification.ts line ~14): Python lists hat → hi-hat → hihat; TypeScript lists hi-hat → hihat → hat. The TS comment says it mirrors Python ordering — it doesn't. No functional difference (all three map to groove), but the comment is wrong and the divergence will bite if someone adds a keyword near hat that maps to a different domain and only updates one side.
Custody port drift: Already documented in the PR. Flagging it's the one thing that could make sub-goal 3 produce a misleading penalty score if run before the port is re-verified against the current phase2Validator.ts. The NEEDS.md note is sufficient; no action needed for this merge.
Test results
Cannot run locally (no venv, no node_modules). Reading the test code directly: ScoringTests.test_full_coverage_uncited_must_not_outscore_lower_coverage_cited is the most important gate (GOAL.md 2.d), and it's exercised correctly. AuthoredFixtureCatalogTests.test_example_fixtures_are_catalog_valid verifies all 7 domains are covered by the 5 committed fixtures — confirmed by inspection (all 5 manifests specify all 7 domains). CorpusVerificationTests.test_support_drives_confidence_band correctly validates the two-factor confidence logic. Frontend tests cover domain inference, graceful degradation, and the populated-artifact path.
Phase boundary check
Clean. recommendation_evaluation.py appears only in tests/, scripts/, and doc files — zero imports from analyze.py, server.py, or any router. The frontend badge reads a static pre-generated artifact, not live Phase 1 data; AnalysisResults.tsx passes only trackContext and category to the badge — both come from Phase 2 rec objects, not Phase 1 measurements. No Phase 1 fields are re-derived or overridden anywhere in this diff.
Generated by Claude Code
The recommendation-proof campaign was authored on the `worktree-goal-doc` branch and landed via PR #114 (commit 348498d), but the status sections in GOAL.md and apps/backend/NEEDS.md still described the pre-merge state. - GOAL.md "Implementation status": rewrite to reflect that the scorer, fixtures, deterministic-source bridge, UI badge, and verdict write-up are on main, and the remaining work is the owner-gated Live 12 renders. - NEEDS.md: drop the now-merged "RETROACTIVE #5" branch-convergence item and reframe the "Branch convergence needed for 3.2" section as audit-context (the audit branch and Tier-1/2 fixes are on main via 5610ca5/ade2ae5; the campaign sits on top of them). - NEEDS.md provisional finding: replace "coordinate with branch convergence below" with a pointer to the NEEDS-WIRING dead-code finding, since the branch-convergence prerequisite no longer exists. https://claude.ai/code/session_01Kh5q91wQoF8EF2BD41RMWN Co-authored-by: Claude <noreply@anthropic.com>
Lands ASA's recommendation-proof campaign — the four-sub-goal effort
to turn recommendation quality from a manual ear-test into a
measurable, repeatable score, grounded in Live 12 projects with known
device settings. Brings the work that was sitting uncommitted on the
local `worktree-goal-doc` branch into `main`.
What lands
`GOAL.md` (commit 1) — the north-star spec. Sits below `PURPOSE.md`
(the why) and above `BACKLOG.md` (candidate features). Four sequenced
sub-goals: (1) ground-truth corpus + fixture format, (2) the scorer,
(3) iterate + Gemini verdict, (4) proof in product. Authority order
documented at the top.
Backend (commit 2): the scorer + answer-key corpus.
implementing the role/parameter/direction-band rubric, with device
equivalence classes (Compressor ↔ Glue Compressor earns credit),
per-unit value tolerance bands, a chain-of-custody penalty that
ports `phase2Validator.ts` semantics, and a `measurableIntent`
equivalence credit so equivalent measurement routes earn partial
score.
(`--source {baseline,gemini,deterministic}`, `--self-test`,
`--report`, `--json`, `--verification-artifact`).
wraps `apps/ui/src/data/abletonDevices.ts` so the free path can be
scored without a Python re-port.
acid), each with a manifest, a Phase 1 fingerprint, a per-track
README, and a tiny proxy melody MIDI. Rendered audio is gitignored
(renders are the owner's half — see `NEEDS.md`).
verdict on synthetic-proxy renders (Gemini 0.227 vs deterministic
0.000; deterministic loses on chain-of-custody despite competitive
raw coverage).
Frontend (commit 3): per-recommendation verification badge.
cards and the patch cards in `AnalysisResults.tsx`.
rendering nothing when corpus support is `NONE`.
`CLAUDE.md` — synced to reference the new harness module, the
deterministic-source bridge, and the UI badge stack.
Honest caveats
scored against numpy approximations of the specs, not Ableton
renders, under an earlier "continue without input" directive. The
known-settings axes (role recall, value accuracy) are provisional
until the owner dials the specs in Live 12 and renders them
(48 kHz / 24-bit FLAC; checklist in
`apps/backend/tests/fixtures/recommendation_tracks/README.md`).
custody penalty ports `phase2Validator.ts` semantics as of commit
`0eac1ee5`. Main's validator has drifted (~131 lines changed) — re-
verify port parity before any sub-goal 3 prompt iteration that
depends on the custody penalty.
in `src/data/recommendationVerification.ts` is all-`NONE` pre-
render. Wiring is proven by a unit test that injects a populated
artifact; visible content waits on renders.
bridge consumes an `AudioFeatures` object. Turning a real Phase 1
fingerprint into `AudioFeatures` should reuse the app's
`analyzer.ts` projection rather than re-deriving — only then is the
deterministic score a real verdict input.
Verification
→ Ran 792 tests in 73.241s, OK (skipped=1).
After merge
Worth a follow-up decision: clean up the local `worktree-goal-doc`
branch and its worktree at `.claude/worktrees/goal-doc/` once this
lands, or leave them for reference until the real renders happen?
🤖 Generated with Claude Code