feat: value-first backport — genre profiles, MixDoctor, broken-pipe fixes - #1
Merged
Conversation
The file declared its own PHASE2_PROMPT_TEMPLATE, PHASE2_RESPONSE_SCHEMA, retry logic, and analyze_phase2() — all superseded when Phase 2 moved server-side in v2.0.0. server.py loads its own prompt from prompts/phase2_system.txt and manages its own retry logic. Verified zero imports across the entire repo (Python, tests, docs, configs) before deleting. Backend test baseline (post-deletion, warm cache): 129 tests, 128 pass, 1 skipped. UI vitest baseline: 131 tests across 18 files, all pass. Note: the repo has pre-existing uncommitted work (analyze.py, JSON_SCHEMA.md, test_analyze.py, types.ts) from in-progress feature development that is not part of this commit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Port acidDetail, reverbDetail, vocalDetail, supersawDetail, bassDetail, and kickDetail from sonic-architect-app reference implementations into analyze.py. Each detector is wired through server.py HTTP forwarding, typed in apps/ui/src/types.ts, documented in JSON_SCHEMA.md, and tested with synthetic fixtures covering bounds, shape, positive/negative cases. 56 analyze tests + 51 server tests + 130 frontend tests all passing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Check off 6 deliverables that have been present since v1.2.0: - scripts/calibrate_confidence.py - apps/ui/src/services/phase2Validator.ts - apps/ui/src/services/fieldAnalytics.ts - apps/backend/analyze_fast.py - docs/confidence_calibration_results.md - docs/field_utilization_report.md Remaining unchecked (not yet in repo): - apps/ui/src/components/Phase2ConsistencyReport.tsx - tests/ground_truth/README.md Strike through the two HOLD gates (Implementation Order and Dependencies sections) — the Codex DSP preflight reframe validation has landed and is superseded by ARCHITECTURE_STRATEGY.md. No restructuring; only status updated to match repo reality. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…harness - analyze_reverb_detail: replace hardcoded fallback values (rt60: 0.3/0.5, tailEnergyRatio: 0.1/0.2) with rt60: null + measured: false when the detector cannot compute real values (short signal / too few transients / no RT60 estimates). Avoids misleading downstream consumers with invented numbers. - Add tests/test_transcription_backends.py: synthetic-audio evaluation harness for BasicPitchBackend and TorchcrepeBackend (conditionally skipped if torchcrepe is not installed). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase B2 (display-component UI type split) is done: - App.tsx state split into measurementResult + symbolicResult - AnalysisResults, SessionMusicianPanel, analysisResultsViewModel all updated to MeasurementResult + explicit symbolic: TranscriptionDetail | null - Invariant #4 updated: display components now receive MeasurementResult directly - All 131 unit tests pass; 40/40 smoke tests pass (2 live skipped) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…al, supersaw, bass, kick) - reverbDetail: replace fake hardcoded fallbacks (0.3s/0.5s RT60) with honest uncertainty schema: rt60=null, tailEnergyRatio=null, measured=false - server.py: forward all six detectors through _build_phase1() - types.ts: add typed Phase1Result interfaces for all six detectors - backendPhase1Client.ts: add parsePhase1Result() parsers for all six detectors with parseGrooveType helper; add null-handling test coverage - fieldAnalytics.ts: register 17 new field paths for Phase 2 citation tracking - test_analyze.py: update ReverbDetailTests for new nullable schema and measured field; add genreDetail to EXPECTED_TOP_LEVEL_KEYS; add import os (removed after hook converted setUp to use Path); fix GenreDetailTests - JSON_SCHEMA.md: add all six detectors + genreDetail to forwarded-fields list (was 17, now 24 sections) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Renders acid, reverb, vocal, supersaw, bass, and kick detector data as a new "Detector Analysis" section (Phase 1 badge). Cards are conditionally shown — section hidden when no detector fields are present. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ixes Phase A: Fix broken data pipes - Wire dynamicCharacter through server.py → types → client parser so Gemini receives logAttackTime/attackTimeStdDev it's told to cite - Remove citation instruction block from phase2_system.txt that contradicted the STRING-typed PHASE2_RESPONSE_SCHEMA Phase B: Complete last-mile UI for computed-but-invisible data - Parse genreDetail in backendPhase1Client (was typed but never parsed) - Add Genre Classification, Sidechain, Synthesis Character detector cards - Add 6-band Spectral Balance visualization in Phase 1 results Phase C: Genre detail hardening - Abstain when fewer than 3 of 7 core features have real values - Return None when primary_score < 0.25 - Cap confidence at 0.4 when top two genres are within 0.05 score gap - Add tests for empty input, sparse input, 3-feature threshold, ambiguity Phase D: Backport genreProfiles + MixDoctor - Port all 35 genre profiles from sonic-architect-app as JSON - Deterministic MixDoctor scoring engine (spectral, dynamics, loudness, stereo evaluation against genre targets, 0-100 health score) - MixDoctorPanel UI with auto-profile selection from genreDetail - Integrate MixDoctor report into Markdown/JSON export Also fixes stale 20MiB → 100MiB doc reference in CLAUDE.md. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add MixDoctor scoring engine (spectral balance, dynamics, PLR, loudness, stereo vs genre targets) - Add MixDoctor panel with profile selector, delta chart, diagnostic cards, band-issue details - Add genre profiles data for 35 genres with spectral/dynamics/PLR/loudness targets - Add genre classification, sidechain detection, synthesis character detector cards in UI - Add spectral balance six-band visualization in Phase 1 results - Add MixDoctor section to markdown and JSON exports; dynamicCharacter in markdown export - Harden genreDetail parsing and typing in frontend client - Add dense techno boundary regression test (145 BPM) - Align synthesis character labels to three-tier thresholds (clean subtractive / FM-acid / wavetable-noise) - Add CODEX.md and PURPOSE.md project documentation files Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This was referenced May 12, 2026
slittycode
added a commit
that referenced
this pull request
May 13, 2026
PR #22 shipped a four-band confidence ladder (Solid scaffold / Workable draft / Rough sketch / Unreliable) but the PILL_CLASSES map collapsed them into only two tones — accent for solid + workable, warning for rough + unreliable. A producer couldn't visually distinguish Solid from Workable, or Rough from Unreliable, at a glance. Map the four bands to a green → orange → amber → red traffic-light ladder so the visual hierarchy matches the producer's mental model ("best to worst at a glance"): Solid scaffold (≥80%) → success (#00ff9d green) Workable draft (50-79%) → accent (#ff8800 orange) Rough sketch (25-49%) → warning (#ffb800 amber) Unreliable (<25%) → error (#ff3333 red) All four CSS variables already exist in apps/ui/src/index.css and are the canonical severity vocabulary across MeasurementPrimitives, DiagnosticLog, and FileUpload (success = ready, accent = active, warning = caution, error = critical). No theme additions; no new shades. Opacity tuning unified at /30 border, /10 bg, text-{color} to match MeasurementPrimitives.BADGE_TONE_CLASSES. Color carries the differentiation now — the previous per-band opacity wobble didn't communicate anything. NoteDraftBlock's overrideTone="rough" path (used for full-mix-fallback and legacy render states) now renders amber, which is the right tone for "treat with caution, not unreliable junk." No wiring change needed. Tests: new tests/services/confidenceBandBadge.test.ts renders all four confidence values and asserts each emits the expected color tokens. A pairwise-distinct assertion survives future opacity tweaks. Two override-path tests lock in the full-mix-fallback / legacy visual (high-confidence input with overrideTone="rough" produces warning tokens, not green; low-confidence input stays warning, not red). No existing tests touched — confidenceBand asserts id/label/percent (not classes); panel + smoke tests assert on text. Targets PR #22 (claude/thirsty-easley-faeaea) as the base. Plan in ~/.claude/plans/re-evaluate-asa-s-session-musician-sharded-balloon.md ("Follow-up plan #1" section). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
May 13, 2026
…27) PR #22 shipped a four-band confidence ladder (Solid scaffold / Workable draft / Rough sketch / Unreliable) but the PILL_CLASSES map collapsed them into only two tones — accent for solid + workable, warning for rough + unreliable. A producer couldn't visually distinguish Solid from Workable, or Rough from Unreliable, at a glance. Map the four bands to a green → orange → amber → red traffic-light ladder so the visual hierarchy matches the producer's mental model ("best to worst at a glance"): Solid scaffold (≥80%) → success (#00ff9d green) Workable draft (50-79%) → accent (#ff8800 orange) Rough sketch (25-49%) → warning (#ffb800 amber) Unreliable (<25%) → error (#ff3333 red) All four CSS variables already exist in apps/ui/src/index.css and are the canonical severity vocabulary across MeasurementPrimitives, DiagnosticLog, and FileUpload (success = ready, accent = active, warning = caution, error = critical). No theme additions; no new shades. Opacity tuning unified at /30 border, /10 bg, text-{color} to match MeasurementPrimitives.BADGE_TONE_CLASSES. Color carries the differentiation now — the previous per-band opacity wobble didn't communicate anything. NoteDraftBlock's overrideTone="rough" path (used for full-mix-fallback and legacy render states) now renders amber, which is the right tone for "treat with caution, not unreliable junk." No wiring change needed. Tests: new tests/services/confidenceBandBadge.test.ts renders all four confidence values and asserts each emits the expected color tokens. A pairwise-distinct assertion survives future opacity tweaks. Two override-path tests lock in the full-mix-fallback / legacy visual (high-confidence input with overrideTone="rough" produces warning tokens, not green; low-confidence input stays warning, not red). No existing tests touched — confidenceBand asserts id/label/percent (not classes); panel + smoke tests assert on text. Targets PR #22 (claude/thirsty-easley-faeaea) as the base. Plan in ~/.claude/plans/re-evaluate-asa-s-session-musician-sharded-balloon.md ("Follow-up plan #1" section). Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
May 13, 2026
…g completion (#18) * feat: Phase 1.A–1.D depth additions, Track 2 audit, decision-gate v3 + catalog completion Substantial measurement-depth pass on the ASA Phase 1 pipeline plus a trustworthy multi-model decision-gate harness. Default Gemini model (gemini-2.5-flash) now PASSes the stem-aware gate on the Vtss bench track; Live 12 catalog completion drops UNKNOWN_PARAMETER 16 → 1 across all v3.1 snapshots. ## Depth additions (Phase 1.A – 1.D) - Phase 1.A cheap wins (5): per-frame short-term LUFS curve, per-frame 7-band spectral balance time series, instantaneous tempoCurve, per-second stereo correlationCurve, 256-point arrangement noveltyCurve (up from 64). - Phase 1.B stem-first refactor: per-stem subtree under ``stemAnalysis.{drums,bass,other,vocals}`` with the same shape as the full-mix analyzers. Phase 2 can now cite element-specific measurements. - Phase 1.C #0 real time-signature detection (onset-accent autocorrelation, replaces the always-4/4 fallback). - Phase 1.C #1 per-band transient density across the 7 spectralBalance bands (kick / hi-hat density anchors). - Phase 1.C #2 per-band stereo correlations (Utility-width-per-band recommendations). - Phase 1.C #3 ``grooveDetail.perDrumSwing.{kick,snare,hihat}`` from the mid-band beat-loudness signal. - Phase 1.C #4 snareDetail / hihatDetail band-limited drum character (hits, attack sharpness, body/snap energy ratio, decay). - Phase 1.C #5 saturationDetail (clipped sample count, peakRatio95to50, rmsToPeakRatioDb, saturationLikely). - Phase 1.C #6 32nd-note sidechain resolution + envelopeShape32 (legacy envelopeShape preserved via max-pairing downsample); pumpingRate now four-valued including thirty_second. - Phase 1.D #5 RT60 reverb per stem with perBandRt60 (low / lowMids / highMids / highs) + preDelayMs via Schroeder-equivalent slope fit. Wired through stemAnalysis.{stem}.reverbDetail. ## Track 2 audit pass 1 + measurement-quality fixes - New ``scripts/audit_pass1.py`` harness — structured 12-section markdown audit comparing real-track Phase 1 measurements against declared ground truth and sanity rules. - Bug fix: ``bassDetail.averageDecayMs`` was 0 ms on real bass material (Codex PDF finding reproduced). Root cause: decay loop searched from onset rather than peak, and checked the raw oscillating waveform rather than an envelope. Fixed with a 20 ms RMS envelope + peak-anchored search. Synthetic validation: 350 ms theoretical → 346 ms measured; 35 ms → 37 ms. Real-track Vtss: 0 ms (FAIL) → 66–125 ms (PASS). - Bug fix: ``vocalDetail.hasVocals = true`` false positive on instrumental synth content. Added temporal-formant-stability check (sustained synth leads have near-static "formants"; real vocals shift formants 100+ Hz with syllables); tightened formant tolerance 200 → 100 Hz; raised threshold 0.45 → 0.55. No-stem run on Vtss now correctly reports hasVocals=false at conf 0.48. - Pre-existing import bugs in ``analyze_segments.py``: ``_compute_stereo_metrics`` (from dsp_utils) and ``Counter`` (from collections) were undefined. ``segmentStereo`` + ``chordDetail`` now populate correctly. ## Phase 2 contract + validator + decision gate - Phase 2 prompt v3: per-stem path enumeration, citation contract (``phase1Fields`` required on every recommendation), "Phase-2 self-reference is not a citation" rule, dedicated sections for snare/hi-hat character, saturation, sidechain envelope, reverb detail. - Phase 2 schema (``server_phase2.py``): ``phase1Fields`` array required on ``mixAndMasterChain``, ``abletonRecommendations``, and ``secretSauce.workflowSteps`` items. - Frontend validator (``phase2Validator.ts``) overhaul: * 8 violation types (NUMERIC_OVERRIDE, GENRE_IGNORES_DSP, BOUNDS_VIOLATION, MISSING_CITATION, TRIVIAL_CITATIONS, NEW_FIELD_UNCITED, LOW_CONFIDENCE_NOT_HEDGED, RECOMMENDATION_SALVAGED). * Bidirectional + wildcard ``pathCoversTracked`` matcher so leaf citations satisfy parent tracked paths and ``stemAnalysis.*`` wildcards work. * 19 tracked Phase 1.A + Phase 1.C/D field paths (was 7). - Decision-gate vitests (4): single-, multi-, real-, and stem-aware multi-model comparators that read ``/tmp/decision_gate_*.json`` snapshots and emit per-model markdown reports. ## Live 12 catalog completion (UNKNOWN_PARAMETER closure) - Auto Filter: ``parameterAliases`` flat-map ``{"Filter Resonance": "Resonance", "Filter Frequency": "Frequency"}``. Closes the instrument-side long-form naming bleed. - Glue Compressor: ``allowedParameters`` expanded by 4 (Range, Sidechain, Sidechain Gain, Sidechain Dry/Wet). - ``_validate_phase2_catalog_entry`` resolves aliases before the membership check; startup-time loader validates the new ``parameterAliases`` shape. - New ``Phase2CatalogValidationTests`` (11 cases) covers alias resolution, expansion, scoping per-device, and negative cases. - New ``scripts/replay_catalog_validation.py`` — no-Gemini-spend harness that re-validates v3.1 snapshots against the live catalog. Verdict: UNKNOWN_PARAMETER drops 16 → 1 across all 8 snapshots; the remaining hit is ``EQ Eight / "Band 8 Filter Type"`` (separate device, v3.2-target). ## Test state - Backend: 374 tests pass (was 363; +11 ``Phase2CatalogValidationTests``). - Frontend: 296 tests pass (was 286; +10 validator bidirectional / wildcard tests). - UI lint clean (``tsc --noEmit``). ## Out of scope / follow-ups (documented in SESSION_BLITZ + plan) - Pro-model citation-depth iteration (3-pro and 3.1-pro still cite far less Phase 1.C/D than 2.5-flash). - gemini-3.1-pro-preview ``DROPPED_INVALID_ARRAY_ITEM`` salvage regression seen in earlier v3 runs. - Compressor "Sustain", "Ableton Project Settings", "Mixer" device hits — v3.2 prompt-fix targets. - Phase 1.D #1 madmom integration (prototype proven, install path documented at ``.runtime/reports/madmom_install_prototype_2026-05-12.md``). - Phase 1.D #2 chord progression deeper labelling. - ``JSON_SCHEMA.md`` documentation of the catalog schema (``parameterAliases`` field). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(backend): add coverage for Phase 1.C analyzers, per-stem orchestrator, and dsp_utils (#19) Closes the test-coverage gaps flagged in the PR review. Backend: - test_analyze.py: new BandDrumDetailTests, SnareDetailTests, HihatDetailTests, TransientDensityDetailTests, SaturationDetailTests, RunPerStemAnalysesTests. Each covers null/short/silent input guards, schema-field presence, and at least one behavioral assertion against a synthetic signal that should trip the detector. RunPerStemAnalysesTests covers stems=None, empty dict, all loads failing, mono+stereo success, partial-analyzer failure, and the stereo-load-skipped path. - BassDetailTests: regression test for averageDecayMs > 30 ms on a synthetic decaying-pulse signal — guards the envelope-based fix from sliding back to the pre-fix sub-millisecond range. - test_dsp_utils.py (new): direct tests for _pearson_corr, _downsample_lufs_array, _downsample_band_energies_curve, _compute_tempo_curve_from_ticks, and _compute_stereo_correlation_curve, with closed-form expected values for perfect/anti/orthogonal correlation, constant-input NaN handling, tempo-change detection, and the silent-sub None policy. Docs / comments: - JSON_SCHEMA.md: flag bassDetail.fundamentalHz as a ZCR approximation that biases upward on harmonic-rich basses; steer Phase 2 to prefer pitchDetail when stems are available and avoid narrow filter-Q moves within ±15 Hz of the value. - phase2Validator.ts: rewrite the pathCoversTracked docstring so it matches what the code actually does (a wildcard token covers all longer citation paths under the same prefix; "stemAnalysis.*" does match "stemAnalysis.drums.spectralBalance"). Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This was referenced May 14, 2026
slittycode
added a commit
that referenced
this pull request
May 16, 2026
…branch, store prune (#51) * audit: chain-of-custody, recommendations-first IA, applied tracker, grammar post-process Implements the design audit's prescription end-to-end (findings #1–#15 + N1/N2/N7/N9/N10 + follow-ups). The product's chain-of-custody promise — every Phase 2 recommendation traces back to the Phase 1 measurement that justifies it — is now visually first-class on every card, not a 9px monospace footnote. What ships: * Chain-of-custody (findings #2 + #3). New CitationBlock primitive renders a structured "GROUNDED IN" block above every Mix Chain / Patches / Sonic Element card with humanized labels (FIELD_LABELS map, ~50 entries + humanizeFieldPath fallback) and a ConfidenceBandBadge pill computed from the worst confidence among cited fields. Also retires GroundingBadgeList at the Track Layout site; segmentIndexes ride through a new extraRows prop. * Recommendations-first IA (#1). MeasurementDashboard moved to the bottom of the results scroll; StickyNav's 9 measurement pills collapse to one trailing "Measurements" entry. Producers hit Style → Sonic → Mix Chain → Patches → Session before the measurement evidence. * Header polish (#7, #9, #11). CPU meter removed (browser-tab CPU is misleading during backend analysis), "Local DSP Engine v1.6.0" eyebrow removed (resolves mobile 3-line wrap), Dense DAW Lab demoted from accent chip to quiet text link. * Engineering vocab cleanup (#8 + N3/N4/N5/N8). New userLabels.ts service translates field paths to producer-readable labels at every render. Button labels renamed (Download data / Download report). FAMILY: NATIVE chip dropped from meta-badge rows. workflowStage prettified at the view-model layer ("Sound design" not "SOUND_DESIGN"). AI Interpretation gated copy reworded ("AI interpretation isn't configured…" not "Developer kill-switch is off"). * Applied-recommendations tracker (#14 + #15). Per-card checkbox affordance + section- header "N of M applied" chip + localStorage persistence keyed by audio content SHA256. Producer can rename their file without losing their progress. * Idle value-prop panel (#5). Replaces the 200px "NO SIGNAL DETECTED" canvas with a producer-readable explanation of what ASA does, with honest pacing copy (4–5 min Phase 2 wait, not 2–5 min). * Patches group structure. Mirrors Mix Chain's emoji-eyebrow grouping (Drums / Bass / Synth / Master) so producers can jump to the bass patch without scanning 8 cards. * Input Source collapse (N9). Post-analysis, the Input Source panel collapses to a compact summary with filename + duration + "Analyze new file" + "Adjust settings". Frees the top of the page for the results the user came for. * AnalysisStatusPanel primary readout (#6). Stage diagnostic message promoted from 9px footnote to the visual focus of the progress card. Pre-existing tone-aware fill (running / success / failed) preserved. * Phase 2 failure mode (N1). Header subtitle derives from interpretation stage status (no more "PHASE COMPLETE" while INTERPRET still RUNNING/FAILED). StickyNav Phase 2 pills render disabled with hover-reason when sections didn't populate. Retry button gated on error.retryable; non-retryable failures surface the error code inline. * Misc audit follow-ups: BPM reconciled across exec card + Core Metrics tile (N2); Signal Monitor STANDBY canvas hidden when audio isn't playing, freeing ~160px (N7); StickyNav label "Device Chain" → "Sections" (N10, less ambiguous with Ableton's own effects-routing meaning); BASS group icon swapped from 🫧 → Lucide AudioWaveform (#13); toggle helper paragraphs switched from all-caps mono walls to sans-serif sentence case (#4 revised). * Phase 2 grammar post-process (audit final round). The prompt instruction added earlier didn't take — Gemini still emits "by recreates / by absorbs / by shapes" 3rd-person singular forms after "by" in role/reason text. Server-side _apply_phase2_grammar_fixes rewrites these to gerunds in-place on mixAndMasterChain[].reason, abletonRecommendations[].{reason,advancedTip}, and secretSauce.workflowSteps[].{instruction,measurementJustification}. Conservative regex (\bby \w{4,}s\b) + denylist guards against plural-noun false positives. Test coverage: * UI: 46 test files / 540 tests pass (was 39/422 before the audit). New service tests: userLabels (21), phase1Picker (25), citationBlock (12), appliedRecommendations (16), formatTrackDuration (12), interpretationSubtitle (10), workflowStagePrettifier (8), analysisStatusProgress (6), idleValuePropPanel + phase2NavReason. New DOM tests in analysisResultsUi.test.ts cover Track Layout citation, applied-checkbox flow, mix- chain citation rendering. * Backend: 16 new unit tests in test_phase2_grammar_fix.py cover _to_gerund, _fix_by_gerund_in_text, _apply_phase2_grammar_fixes including the actual screenshot corpus (recreates → recreating, shapes → shaping, matches → matching, etc.). Full suite: 463 of 463 ASA tests pass; 1 unrelated pre-existing failure in tests.test_url_ingest predates this branch. Visual verification: Playwright capture pass against a real 126s track confirmed every surface (15 screenshots in /tmp/asa-shots-audit-final/). Phase 2 returned in 220s; localStorage round-trip verified on applied-checkbox toggles. Documented limitations: * The gerund rule is algorithmic — verbs requiring consonant doubling (control → controlling, submit → submitting) degrade to "controling" / "submiting". Still better than "by controls". Drop a hand-mapped exception into the module if observed in real output. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Fix smoke tests for retired/renamed surfaces CI surfaced 7 smoke test failures from this branch's audit changes that hadn't been propagated to the smoke spec assertions: * `tests/smoke/ui-details.spec.ts` × 3 - `NO SIGNAL DETECTED` → `IdleValuePropPanel` (audit #5) - `JSON_DATA` / `REPORT_MD` button labels → `Download data` / `Download report` * `tests/smoke/responsive-layout.spec.ts` × 4 - `NO SIGNAL DETECTED` (×2) → `IdleValuePropPanel` - `CPU` text-presence checks removed; the two viewport-shape tests now assert just the model-selector responsive behavior (audit #11 retired the CPU meter; there's no element to assert) * `tests/smoke/file-validation.spec.ts` × 1 - `re-upload after results resets to file-selected state`: the test used `Remove File` (FileUpload component's affordance) to clear after results were visible. Post-N9 collapse, the Input Source panel replaces FileUpload with a compact summary card whose "↺ Analyze new file" button calls the same handleFileClear. Switched the test to target the new affordance. Also updated the e2e exports spec for label consistency (not in the failing CI job, but the same renames apply): * `tests/e2e/phase1-exports.spec.ts` - `downloadTextArtifact(page, /JSON_DATA/i)` → `/Download data/i` - `downloadTextArtifact(page, /REPORT_MD/i)` → `/Download report/i` Verified locally against the live stack: 45 of 46 smoke tests pass, 1 skipped (was unrelated). The previously-failing 7 are all green. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * audit: nightly Phase-1 invariant + test guardrail (2026-05-14) 3 unit tests failing — all fixture-dependent live decision-gate comparators that assert instead of skipping when Gemini snapshots are absent. No Phase-boundary violations found. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix: address review findings — decision-gate skip, gerund exceptions, dead branch, store prune - Skip decision_gate.{multi,real,stems}.live.test.ts when no /tmp snapshots are present (matches the existing decision_gate.live.test.ts pattern). Unblocks npm test on CI. - _to_gerund: add _GERUND_IRREGULARS map for consonant-doubling verbs (controls→controlling, submits→submitting, runs→running, etc.) — English doubling is stress-conditional, not worth implementing algorithmically. - _fix_grammar_in_record: drop the always-true conditional return and the unused `updated` flag. - appliedRecommendations: bound the localStorage store to MAX_TRACKED_FILES=50 (least-recently-updated wins eviction) so it can't grow without bound. * fix: formatTrackDuration carries seconds boundary; drop dead IdleSignalMonitor - formatTrackDuration: round seconds to total first, then derive mins/secs. Previously Math.round(seconds % 60) could yield 60, producing "0:60" for inputs like 59.5. New tests cover 59.5, 59.9, 119.5, 3599.7. - Delete IdleSignalMonitor.tsx (replaced by IdleValuePropPanel; not imported anywhere). Strip the "kept for future use" comments per CLAUDE.md. --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This was referenced May 17, 2026
slittycode
added a commit
that referenced
this pull request
May 17, 2026
…esh (#57), nightly audit doc (#59) * audits: nightly 2026-05-16 — 5 issues (4 test failures + 1 review item) 3 vitest decision_gate.*.live.test.ts failures (missing /tmp snapshot gating), 1 Playwright upload-estimate-phase1.spec.ts timeout, and one mixDoctor.estimatePlr fallback flagged for human boundary review. No Phase 1 ground-truth mutations found. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs(claude): add repo-layout, scripts map, Phase 3 UI entry, CSV recipe Closes four orientation gaps in CLAUDE.md: a top-of-Architecture map that flags advisory/, experiments/, docs/, and tests/ground_truth/ as off-path; a Scripts-at-a-glance section covering scripts/ and apps/backend/scripts/; a pointer from the Phase 3 paragraph to SamplePlayback.tsx + sampleGenerationClient.ts; and a curl example plus allowlisted field paths on the csv_export.py module entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: add Layer 2 transcription evaluation guide Co-Authored-By: Oz <oz-agent@warp.dev> * fix(ui): stop Phase 2 results surface from leaking engine output Removes four classes of broken/placeholder content that made the Phase 2 recommendations read as engine output instead of producer-facing advice (audit finding #1). 1. Mix Chain card role text — `buildRoleSentence` used to fabricate "{stage phrase} by {verb}" by lowercasing the first letter of Gemini's `reason`. Because `reason` is a present-tense clause, every card produced ungrammatical splices like "Controls bass energy by ensures the extreme low-end mono…". Now renders the reason verbatim with a capitalized first letter and trailing period; HIGH-END cue suffix preserved as "(for …)". 2. Patch Framework `patchRole` — was a 7-key category-keyed fallback (`mapPatchRole`) that stamped duplicated placeholders ("Primary tone generator" on every SYNTHESIS card). Removed the field, the fallback, the JSX paragraph, and the contribution to the `inferProcessingGroup` text-concat. The category chip + per-card `whyThisWorks` already carry the bucket and explanation. 3. Patches/Mix Chain overlap — `buildPatchCards` now case-insensitively filters out devices that also appear in `mixAndMasterChain` so the Patches section stops re-listing chain devices. Synthetic fallbacks (Stereo Width, MIDI Clip Guide) are built from Phase 1 only and bypass the filter intentionally. 4. Interpretation Caution raw-JSON dump — the panel rendered `originalValue` verbatim, which for dropped recommendations is a JSON-dumped `AbletonRecommendation` (see `_stringify_warning_value` in `server_phase2.py`). Added `formatDroppedValue` helper that parses JSON-shaped values and renders a compact "device: X · parameter: Y · value: Z" summary. Non-JSON strings pass through; invalid JSON falls back to a truncated raw string. Also resolves the `$Saturator` symptom — Gemini's hallucinated `$`-prefixed device name now surfaces as a readable summary line instead of leaking through a raw JSON dump. 5. System Diagnostics dev-only leak — `validateNewFieldCoverage` emits "Phase 1 field 'X' is present… this warning is benign" coverage signals meant for the engine team, not producers. Added optional `audience?: 'dev' | 'user'` to `ValidationViolation`, marked `NEW_FIELD_UNCITED` as dev, and updated `Phase2ConsistencyReport` to filter dev-audience violations from the rendered table AND from the header counts (so the header doesn't read "5 warnings shown" above an empty table). The underlying `ValidationReport` still carries every violation for tests and offline analysis. Tripwire note: the backend grammar fix `_apply_phase2_grammar_fixes` in `server_phase2.py` only runs on the legacy `/api/phase2` endpoint, not the analysis-runs path the UI uses. After this change it becomes a redundant no-op against the new render shape; left untouched. Tests: 16 new unit cases across viewModel, UI rendering, validator, and consistency report. `npm run verify` passes (lint + 565 unit tests + build + 44 smoke tests). Live UI verified against saved run 855f1376… — all nine text-leak probes (`by ensures`, `by ducks`, `by generates`, `Primary tone generator`, `Texture and movement stage`, `Stereo placement stage`, raw `\$Saturator`, `this warning is benign`, "is present in the measurement payload but no Phase 2…") return false. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(ui): surface primary citation in collapsed card headers Audit Finding #3 — "the chain-of-custody citation, the product's whole differentiator, renders as a footnote." Adds a one-line `CitationHeadline` primitive inside the collapsed header of every Mix Chain / Patch / Sonic Element card so producers see the measurement evidence at scan-time without expanding. The expanded CitationBlock stays in the body unchanged — this is the collapsed-state companion, not a replacement. Each headline reads `{label} {value} →` with the arrow pointing into the device h4/h3 that follows in the title row, making the implicit "measurement justified device" statement visible — exactly the audit's literal example "Crest factor 8.2 dB → Glue Compressor." Confidence sibling pills (Solid / Workable / Rough / Unreliable, same ladder as ConfidenceBandBadge) ride alongside the value when the primary field has a paired *Confidence sibling. This preserves the chain-of-custody invariant: low-confidence measurements visibly hedge in the collapsed view too, not just after expansion. Implementation: 1. `CitationHeadline` added as a sibling export in `apps/ui/src/components/CitationBlock.tsx`. Shares the file's imports, the `CONFIDENCE_PILL_CLASSES` map, and the audit lineage. Composes existing pure helpers (`pickPhase1Value`, `formatCitedValue`, `humanizeFieldPath`, `pickPhase1Confidence`, `getConfidenceBand`, `formatBandPillLabel`) — no new resolver logic. 2. Mount points in `AnalysisResults.tsx`: - Mix Chain card header (~line 2056): between the title row and the role paragraph. - Patch card header (~line 2215): between the title row and the MetaBadgeList. - Sonic Element card header (~line 1912): between the title row and the summary paragraph. 3. Each mount is guarded by `card.phase1Fields.length > 0`. Cards without cited fields fall back to today's exact layout. Live verification against saved run 855f1376… (the audit reproducer): all 10 Mix Chain cards, 2 Patch cards, and 7 Sonic Element cards render the headline correctly with appropriate confidence pills (PUMPING STRENGTH 47% [ROUGH SKETCH 30%], SUPERSAW DETECTED [WORKABLE DRAFT 78%], ACID BASS DETECTED [SOLID SCAFFOLD 99%], NOTE TRANSCRIPTION 61% [WORKABLE DRAFT 61%], KEY C Minor [SOLID SCAFFOLD 93%], TEMPO 145 BPM [SOLID SCAFFOLD 100%]). Mobile (375px) verified — headlines fit, pills stay aligned, no overflow. Tests: 15 new cases — 10 in `tests/services/citationHeadline.test.ts` (mirrors `citationBlock.test.ts`) covering label/value/arrow render, null fallback, confidence-pill bands, value formatter parity, typography hooks. 5 in `analysisResultsUi.test.ts` covering Mix Chain / Patch / Sonic integration, empty-fields fallback, and the low-confidence Unreliable-band path. `npm run verify` passes: lint + 575 unit tests + build + 44 smoke. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(ui): unify five confidence vocabularies onto the canonical band ladder Audit Finding #4 — the producer used to see five competing vocabularies for "how much should I trust this number?" — HIGH/MED/LOW pills on Detected Characteristics cards, High/Moderate/Low chips on Confidence Notes, bare CONF X% text on Key/Character metric cards, SCORE X.XX badges on the BPM card (two render paths), and the canonical four-band ladder used only by the Session Musician panel. The chain-of-custody invariant in PURPOSE.md requires low-confidence measurements to produce visibly hedged advice — five vocabularies fragment that signal so the producer can't build a single mental model of trust. This PR promotes the existing four-band ladder (apps/ui/src/services/sessionMusician/confidenceBand.ts) to the canonical primitive across every confidence surface. Frontend-only; Gemini still emits HIGH/MED/LOW and the UI converts locally via a new `toConfidenceBand` normalizer. What changed: 1. New normalizer `toConfidenceBand(value)` in `confidenceBand.ts` accepts numeric 0-1 floats, 0-100 integers, Gemini string enums (HIGH/MED/LOW + High/Moderate/Low variants), and percent strings ("62%"). Returns the matching `ConfidenceBand` or null for unparseable input. The string enums map to band-mid values (HIGH→0.9, MED→0.6, LOW→0.3) so `formatBandPillLabel` reads as honest hedges. 2. `ConfidenceBandBadge` gains: - `variant: 'full' | 'compact'` — `compact` omits the copy paragraph so the pill can sit inline in card corners and metric-card footers. `full` (default) preserves the Session Musician panel behavior unchanged. - `band?: ConfidenceBand` override prop — when supplied, skips the `getConfidenceBand` round-trip. Useful when the caller pre-converts a string enum. 3. V1: Detected Characteristics cards (AnalysisResults.tsx ~1650) — bespoke HIGH/MED/LOW ternary pill replaced with `<ConfidenceBandBadge variant="compact" band={toConfidenceBand(item.confidence)} />`. `characteristicPillClass` helper kept alive only for the unrelated characteristic-name chips on the Character metric card (line 1000); flagged as a follow-up. 4. V2: Confidence Notes chips (AnalysisResults.tsx ~1187) — `toConfidenceBadges` viewModel return shape changed from `{ label, level }` (3-level legacy enum) to `{ label, band }` (canonical four-band ladder). `confidenceClass` helper deleted. 5. V3: Plain `CONF X%` text (3 sites) — replaced with `<ConfidenceBandBadge variant="compact" confidence={...} />` in: - AnalysisResults.tsx Key card footer - AnalysisResults.tsx Character card footer - MeasurementDashboard.tsx alternate Key card 6. V4: `SCORE X.XX` badges (2 sites) — replaced with band pills in AnalysisResults.tsx Tempo card and MeasurementDashboard.tsx Tempo card. `formatBpmScore` deleted from both files. Cross-Check ✓/✗ StatusBadge in MeasurementDashboard preserved — that's an agreement signal (do multiple BPM detectors agree?), not confidence. 7. Dead code retired: `normalizeConfidenceLevel`, `parseConfidenceScalar`, `confidenceClass`, two copies of `formatBpmScore`. `ConfidenceLevel` type and `MelodyInsightsViewModel.confidenceLabel` kept alive — they drive non-pill key/value text rows in Sonic Element melody insights (flagged for follow-up migration). 3-level → 4-band mismatch documented as an intentional refinement: the old `normalizeConfidenceLevel` mapped scalar 0.5-0.79 AND "medium" both to "Moderate"; the new normalizer maps "medium" to 0.6 → workable (consistent), and scalar 0.25-0.49 to "Rough sketch" (more granular than the old "Low" bucket). Tests: 20 new cases across 4 files — 1. `confidenceBand.test.ts` — 9 cases for `toConfidenceBand` (null/undefined fallback, 0-1 floats, 0-100 integers, all string enum variants, percent strings, NaN/Infinity, negative clamping). 2. `confidenceBandBadge.test.ts` — 4 cases for the compact variant and the `band` override prop (label-only when no confidence, tone override, Gemini-string routing). 3. `analysisResultsViewModel.test.ts` — rewrote the existing `toConfidenceBadges` shape assertion; added a `band: null` fallback case. 4. `analysisResultsUi.test.ts` — 5 integration cases proving each V1-V4 site renders band pill text and not the old vocabularies. Live UI verified against saved run 855f1376… — 36 band pills visible across the page (21 Solid scaffold, 7 Workable draft, 5 Rough sketch, 3 Unreliable). Zero `CONF X%` leakage. Detected Characteristics cards show the chain-of-custody hedge working as designed (Detuned Supersaw renders WORKABLE DRAFT in orange while the other 4 detections render SOLID SCAFFOLD in green). Cross-Check ✓/✗ structurally preserved (the saved run doesn't populate bpmAgreement, so it doesn't render, but the code path is untouched). `npm run verify` passes: lint + ~600 unit + build + 44 smoke. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs: refresh stale items against current architecture CHANGELOG.md "Unreleased" was missing nine shipped features (Phase 3 audition samples, URL ingest, admin DELETE bypass, source-audio route, CSV export, publicStatus, reassigned spectrogram, chain-of-custody audit overhaul, BatchedBandpass centralization, hosted runtime foundation, backend monolith split). Added them. CLAUDE.md's analyze.py CLI example only listed four flags; expanded to match the full surface (--standard, --pitch-note-only, --stem-dir, --stem-output-dir, --pitch-note-backend). Expanded the Frontend key service files list from 9 to 16 entries to cover the services that landed during the audit overhaul (httpClient, sampleGenerationClient, appliedRecommendations + userLabels, phase1Picker + phaseLabels, audioFile, fieldAnalytics + diagnosticLogs, sessionMusician helpers). apps/ui/AGENTS.md File Map likewise expanded from 4 to 14 service entries against the same list. docs/SAMPLE_GENERATION.md's "Snapshot integration" section claimed an `AnalysisRunSnapshot.stages.sampleGeneration` shape that does not exist in apps/ui/src/types/backend.ts — the snapshot tracks only measurement, pitchNoteTranslation, and interpretation. Replaced the stale TS block with a description that matches the actual on-demand artifact path (consistent with the doc's own line 47). apps/backend/AGENTS.md and apps/backend/ARCHITECTURE.md now flag symbolic_extract.py as orphaned and broken — it imports a removed BasicPitchBackend symbol from analyze.py, would raise ImportError at load, and is not referenced from any other module. Slated for removal. docs/history/README.md had `library-review-torchfx-2026-05-13.md` listed twice; removed the duplicate. docs/ARCHITECTURE_STRATEGY.md "Last updated" stamp bumped to May 2026 with a note that no shifts to the three-layer thesis or library decisions accompanied the recent feature work. Date stamps in apps/backend/AGENTS.md and apps/ui/AGENTS.md refreshed to 2026-05-17. https://claude.ai/code/session_01QEmFAUJSoRgTqPcFTfcnYW * fix(ui): audit quick-hits bundle (StickyNav, Mix Chain, Mix Doctor, header) Closes six items from the audit's "Quick hits (low ROI, real)" list. Each is independent; bundled into one PR because they're all small surgical fixes. 1. QH1 — StickyNav clipped "MEASUREMENTS" at 1440px. Was overflow-x-auto + min-w-max, which produced an invisible horizontal scroll (macOS hides scrollbars by default; last pill looked clipped with no scroll affordance). Switched to flex-wrap so pills flow onto multiple rows at narrow widths; whitespace-nowrap prevents intra- label wrap. 2. QH2 — Mix Chain card-number order badges removed. Cards are grouped by processing stage AFTER ordering, so the order numbers appeared out-of-sequence within each group ("1, 6, 8, 9 / 2, 4 / 5, 7 / 3 / 10"), which read as a presentation bug. The visual sequence within each group is already meaningful; the badge added confusion without information. Dropped. 3. QH4 — Mix Doctor Band Diagnostics table now shows the target range alongside the optimal. Previously the column rendered only the optimal dB (e.g. "-22.0"), so two bands with similar Delta dB could land in different Issue buckets without the producer being able to see why. The Issue is determined by absolute thresholds (target.minDb / maxDb), not by the diff from optimal. Showing the range makes the verdict legible at a glance — Sub Bass -6.4 norm, range -16 to -8, exceeds the upper bound by 1.6 dB → TOO-LOUD; Low Mids -14.5 norm, range -26 to -14, sits at the boundary → OPTIMAL. Threshold logic itself was correct; this is a display fix. 4. QH5 — "Dense DAW Lab" link removed from the header. A prior audit pass had de-emphasized it to muted text, but the new audit re- flagged it for sitting in the primary flow's header without context for what it is. Route stays accessible via direct URL (the getAppViewHref helper is preserved for future re-introduction behind a settings menu, and main.tsx still routes to the DenseDawConcept component when activeView === 'daw-concept'). 5. QH6 — header model selector visually demoted. Dropped the "Interpretation Model" label and shrank the styling from a bordered dropdown to a discreet text-secondary inline select. The audit flagged the prior styling as foregrounding an AI-model choice an intermediate producer has no basis to make. Selector stays accessible (smoke spec requires it visible at desktop viewport; `responsive-layout.spec.ts` and `upload-estimate-phase1.spec.ts` both guard the `phase2-model-desktop` testid) but now reads as background metadata. Aria-label + title attribute carry the long- form context for assistive tech and hover discoverability. 6. QH7 — Mix Doctor section title renamed from "MixDoctor" to "Mix Doctor". The audit flagged that the section header rendered as "Mixdoctor" with a lowercase 'd', inconsistent with every other section name. Root cause: `formatWord` in `utils/displayText.ts` case-normalizes single-token CamelCase to sentence case, so "MixDoctor" was rendering as "Mixdoctor". Two words ("Mix Doctor") matches every other section name's pattern (Style Profile, Project Setup, Track Layout, Mix & Master Chain, Patch Framework, etc.) and renders cleanly through the existing pipeline. `npm run verify` passes: lint + ~600 unit + build + 44 smoke. Live UI verified against saved run 855f1376… with probes confirming all six fixes: - StickyNav.overflow-x-auto wrapper gone (pills wrap to 2 rows) - Zero Mix Chain order badges in #section-mix-chain - "Target (range)" header present, "Target dB" header gone - "Dense DAW Lab" text absent from page - "Interpretation Model" label absent from page - "Mix Doctor" section title present, "Mixdoctor" absent Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: Oz <oz-agent@warp.dev>
slittycode
added a commit
that referenced
this pull request
May 17, 2026
* docs(claude): add repo-layout, scripts map, Phase 3 UI entry, CSV recipe Closes four orientation gaps in CLAUDE.md: a top-of-Architecture map that flags advisory/, experiments/, docs/, and tests/ground_truth/ as off-path; a Scripts-at-a-glance section covering scripts/ and apps/backend/scripts/; a pointer from the Phase 3 paragraph to SamplePlayback.tsx + sampleGenerationClient.ts; and a curl example plus allowlisted field paths on the csv_export.py module entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: add Layer 2 transcription evaluation guide Co-Authored-By: Oz <oz-agent@warp.dev> * fix(ui): stop Phase 2 results surface from leaking engine output Removes four classes of broken/placeholder content that made the Phase 2 recommendations read as engine output instead of producer-facing advice (audit finding #1). 1. Mix Chain card role text — `buildRoleSentence` used to fabricate "{stage phrase} by {verb}" by lowercasing the first letter of Gemini's `reason`. Because `reason` is a present-tense clause, every card produced ungrammatical splices like "Controls bass energy by ensures the extreme low-end mono…". Now renders the reason verbatim with a capitalized first letter and trailing period; HIGH-END cue suffix preserved as "(for …)". 2. Patch Framework `patchRole` — was a 7-key category-keyed fallback (`mapPatchRole`) that stamped duplicated placeholders ("Primary tone generator" on every SYNTHESIS card). Removed the field, the fallback, the JSX paragraph, and the contribution to the `inferProcessingGroup` text-concat. The category chip + per-card `whyThisWorks` already carry the bucket and explanation. 3. Patches/Mix Chain overlap — `buildPatchCards` now case-insensitively filters out devices that also appear in `mixAndMasterChain` so the Patches section stops re-listing chain devices. Synthetic fallbacks (Stereo Width, MIDI Clip Guide) are built from Phase 1 only and bypass the filter intentionally. 4. Interpretation Caution raw-JSON dump — the panel rendered `originalValue` verbatim, which for dropped recommendations is a JSON-dumped `AbletonRecommendation` (see `_stringify_warning_value` in `server_phase2.py`). Added `formatDroppedValue` helper that parses JSON-shaped values and renders a compact "device: X · parameter: Y · value: Z" summary. Non-JSON strings pass through; invalid JSON falls back to a truncated raw string. Also resolves the `$Saturator` symptom — Gemini's hallucinated `$`-prefixed device name now surfaces as a readable summary line instead of leaking through a raw JSON dump. 5. System Diagnostics dev-only leak — `validateNewFieldCoverage` emits "Phase 1 field 'X' is present… this warning is benign" coverage signals meant for the engine team, not producers. Added optional `audience?: 'dev' | 'user'` to `ValidationViolation`, marked `NEW_FIELD_UNCITED` as dev, and updated `Phase2ConsistencyReport` to filter dev-audience violations from the rendered table AND from the header counts (so the header doesn't read "5 warnings shown" above an empty table). The underlying `ValidationReport` still carries every violation for tests and offline analysis. Tripwire note: the backend grammar fix `_apply_phase2_grammar_fixes` in `server_phase2.py` only runs on the legacy `/api/phase2` endpoint, not the analysis-runs path the UI uses. After this change it becomes a redundant no-op against the new render shape; left untouched. Tests: 16 new unit cases across viewModel, UI rendering, validator, and consistency report. `npm run verify` passes (lint + 565 unit tests + build + 44 smoke tests). Live UI verified against saved run 855f1376… — all nine text-leak probes (`by ensures`, `by ducks`, `by generates`, `Primary tone generator`, `Texture and movement stage`, `Stereo placement stage`, raw `\$Saturator`, `this warning is benign`, "is present in the measurement payload but no Phase 2…") return false. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Oz <oz-agent@warp.dev>
slittycode
added a commit
that referenced
this pull request
May 17, 2026
* docs(claude): add repo-layout, scripts map, Phase 3 UI entry, CSV recipe Closes four orientation gaps in CLAUDE.md: a top-of-Architecture map that flags advisory/, experiments/, docs/, and tests/ground_truth/ as off-path; a Scripts-at-a-glance section covering scripts/ and apps/backend/scripts/; a pointer from the Phase 3 paragraph to SamplePlayback.tsx + sampleGenerationClient.ts; and a curl example plus allowlisted field paths on the csv_export.py module entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: add Layer 2 transcription evaluation guide Co-Authored-By: Oz <oz-agent@warp.dev> * fix(ui): stop Phase 2 results surface from leaking engine output Removes four classes of broken/placeholder content that made the Phase 2 recommendations read as engine output instead of producer-facing advice (audit finding #1). 1. Mix Chain card role text — `buildRoleSentence` used to fabricate "{stage phrase} by {verb}" by lowercasing the first letter of Gemini's `reason`. Because `reason` is a present-tense clause, every card produced ungrammatical splices like "Controls bass energy by ensures the extreme low-end mono…". Now renders the reason verbatim with a capitalized first letter and trailing period; HIGH-END cue suffix preserved as "(for …)". 2. Patch Framework `patchRole` — was a 7-key category-keyed fallback (`mapPatchRole`) that stamped duplicated placeholders ("Primary tone generator" on every SYNTHESIS card). Removed the field, the fallback, the JSX paragraph, and the contribution to the `inferProcessingGroup` text-concat. The category chip + per-card `whyThisWorks` already carry the bucket and explanation. 3. Patches/Mix Chain overlap — `buildPatchCards` now case-insensitively filters out devices that also appear in `mixAndMasterChain` so the Patches section stops re-listing chain devices. Synthetic fallbacks (Stereo Width, MIDI Clip Guide) are built from Phase 1 only and bypass the filter intentionally. 4. Interpretation Caution raw-JSON dump — the panel rendered `originalValue` verbatim, which for dropped recommendations is a JSON-dumped `AbletonRecommendation` (see `_stringify_warning_value` in `server_phase2.py`). Added `formatDroppedValue` helper that parses JSON-shaped values and renders a compact "device: X · parameter: Y · value: Z" summary. Non-JSON strings pass through; invalid JSON falls back to a truncated raw string. Also resolves the `$Saturator` symptom — Gemini's hallucinated `$`-prefixed device name now surfaces as a readable summary line instead of leaking through a raw JSON dump. 5. System Diagnostics dev-only leak — `validateNewFieldCoverage` emits "Phase 1 field 'X' is present… this warning is benign" coverage signals meant for the engine team, not producers. Added optional `audience?: 'dev' | 'user'` to `ValidationViolation`, marked `NEW_FIELD_UNCITED` as dev, and updated `Phase2ConsistencyReport` to filter dev-audience violations from the rendered table AND from the header counts (so the header doesn't read "5 warnings shown" above an empty table). The underlying `ValidationReport` still carries every violation for tests and offline analysis. Tripwire note: the backend grammar fix `_apply_phase2_grammar_fixes` in `server_phase2.py` only runs on the legacy `/api/phase2` endpoint, not the analysis-runs path the UI uses. After this change it becomes a redundant no-op against the new render shape; left untouched. Tests: 16 new unit cases across viewModel, UI rendering, validator, and consistency report. `npm run verify` passes (lint + 565 unit tests + build + 44 smoke tests). Live UI verified against saved run 855f1376… — all nine text-leak probes (`by ensures`, `by ducks`, `by generates`, `Primary tone generator`, `Texture and movement stage`, `Stereo placement stage`, raw `\$Saturator`, `this warning is benign`, "is present in the measurement payload but no Phase 2…") return false. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(ui): surface primary citation in collapsed card headers Audit Finding #3 — "the chain-of-custody citation, the product's whole differentiator, renders as a footnote." Adds a one-line `CitationHeadline` primitive inside the collapsed header of every Mix Chain / Patch / Sonic Element card so producers see the measurement evidence at scan-time without expanding. The expanded CitationBlock stays in the body unchanged — this is the collapsed-state companion, not a replacement. Each headline reads `{label} {value} →` with the arrow pointing into the device h4/h3 that follows in the title row, making the implicit "measurement justified device" statement visible — exactly the audit's literal example "Crest factor 8.2 dB → Glue Compressor." Confidence sibling pills (Solid / Workable / Rough / Unreliable, same ladder as ConfidenceBandBadge) ride alongside the value when the primary field has a paired *Confidence sibling. This preserves the chain-of-custody invariant: low-confidence measurements visibly hedge in the collapsed view too, not just after expansion. Implementation: 1. `CitationHeadline` added as a sibling export in `apps/ui/src/components/CitationBlock.tsx`. Shares the file's imports, the `CONFIDENCE_PILL_CLASSES` map, and the audit lineage. Composes existing pure helpers (`pickPhase1Value`, `formatCitedValue`, `humanizeFieldPath`, `pickPhase1Confidence`, `getConfidenceBand`, `formatBandPillLabel`) — no new resolver logic. 2. Mount points in `AnalysisResults.tsx`: - Mix Chain card header (~line 2056): between the title row and the role paragraph. - Patch card header (~line 2215): between the title row and the MetaBadgeList. - Sonic Element card header (~line 1912): between the title row and the summary paragraph. 3. Each mount is guarded by `card.phase1Fields.length > 0`. Cards without cited fields fall back to today's exact layout. Live verification against saved run 855f1376… (the audit reproducer): all 10 Mix Chain cards, 2 Patch cards, and 7 Sonic Element cards render the headline correctly with appropriate confidence pills (PUMPING STRENGTH 47% [ROUGH SKETCH 30%], SUPERSAW DETECTED [WORKABLE DRAFT 78%], ACID BASS DETECTED [SOLID SCAFFOLD 99%], NOTE TRANSCRIPTION 61% [WORKABLE DRAFT 61%], KEY C Minor [SOLID SCAFFOLD 93%], TEMPO 145 BPM [SOLID SCAFFOLD 100%]). Mobile (375px) verified — headlines fit, pills stay aligned, no overflow. Tests: 15 new cases — 10 in `tests/services/citationHeadline.test.ts` (mirrors `citationBlock.test.ts`) covering label/value/arrow render, null fallback, confidence-pill bands, value formatter parity, typography hooks. 5 in `analysisResultsUi.test.ts` covering Mix Chain / Patch / Sonic integration, empty-fields fallback, and the low-confidence Unreliable-band path. `npm run verify` passes: lint + 575 unit tests + build + 44 smoke. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Oz <oz-agent@warp.dev>
slittycode
added a commit
that referenced
this pull request
May 17, 2026
…ladder (#56) * docs(claude): add repo-layout, scripts map, Phase 3 UI entry, CSV recipe Closes four orientation gaps in CLAUDE.md: a top-of-Architecture map that flags advisory/, experiments/, docs/, and tests/ground_truth/ as off-path; a Scripts-at-a-glance section covering scripts/ and apps/backend/scripts/; a pointer from the Phase 3 paragraph to SamplePlayback.tsx + sampleGenerationClient.ts; and a curl example plus allowlisted field paths on the csv_export.py module entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: add Layer 2 transcription evaluation guide Co-Authored-By: Oz <oz-agent@warp.dev> * fix(ui): stop Phase 2 results surface from leaking engine output Removes four classes of broken/placeholder content that made the Phase 2 recommendations read as engine output instead of producer-facing advice (audit finding #1). 1. Mix Chain card role text — `buildRoleSentence` used to fabricate "{stage phrase} by {verb}" by lowercasing the first letter of Gemini's `reason`. Because `reason` is a present-tense clause, every card produced ungrammatical splices like "Controls bass energy by ensures the extreme low-end mono…". Now renders the reason verbatim with a capitalized first letter and trailing period; HIGH-END cue suffix preserved as "(for …)". 2. Patch Framework `patchRole` — was a 7-key category-keyed fallback (`mapPatchRole`) that stamped duplicated placeholders ("Primary tone generator" on every SYNTHESIS card). Removed the field, the fallback, the JSX paragraph, and the contribution to the `inferProcessingGroup` text-concat. The category chip + per-card `whyThisWorks` already carry the bucket and explanation. 3. Patches/Mix Chain overlap — `buildPatchCards` now case-insensitively filters out devices that also appear in `mixAndMasterChain` so the Patches section stops re-listing chain devices. Synthetic fallbacks (Stereo Width, MIDI Clip Guide) are built from Phase 1 only and bypass the filter intentionally. 4. Interpretation Caution raw-JSON dump — the panel rendered `originalValue` verbatim, which for dropped recommendations is a JSON-dumped `AbletonRecommendation` (see `_stringify_warning_value` in `server_phase2.py`). Added `formatDroppedValue` helper that parses JSON-shaped values and renders a compact "device: X · parameter: Y · value: Z" summary. Non-JSON strings pass through; invalid JSON falls back to a truncated raw string. Also resolves the `$Saturator` symptom — Gemini's hallucinated `$`-prefixed device name now surfaces as a readable summary line instead of leaking through a raw JSON dump. 5. System Diagnostics dev-only leak — `validateNewFieldCoverage` emits "Phase 1 field 'X' is present… this warning is benign" coverage signals meant for the engine team, not producers. Added optional `audience?: 'dev' | 'user'` to `ValidationViolation`, marked `NEW_FIELD_UNCITED` as dev, and updated `Phase2ConsistencyReport` to filter dev-audience violations from the rendered table AND from the header counts (so the header doesn't read "5 warnings shown" above an empty table). The underlying `ValidationReport` still carries every violation for tests and offline analysis. Tripwire note: the backend grammar fix `_apply_phase2_grammar_fixes` in `server_phase2.py` only runs on the legacy `/api/phase2` endpoint, not the analysis-runs path the UI uses. After this change it becomes a redundant no-op against the new render shape; left untouched. Tests: 16 new unit cases across viewModel, UI rendering, validator, and consistency report. `npm run verify` passes (lint + 565 unit tests + build + 44 smoke tests). Live UI verified against saved run 855f1376… — all nine text-leak probes (`by ensures`, `by ducks`, `by generates`, `Primary tone generator`, `Texture and movement stage`, `Stereo placement stage`, raw `\$Saturator`, `this warning is benign`, "is present in the measurement payload but no Phase 2…") return false. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(ui): surface primary citation in collapsed card headers Audit Finding #3 — "the chain-of-custody citation, the product's whole differentiator, renders as a footnote." Adds a one-line `CitationHeadline` primitive inside the collapsed header of every Mix Chain / Patch / Sonic Element card so producers see the measurement evidence at scan-time without expanding. The expanded CitationBlock stays in the body unchanged — this is the collapsed-state companion, not a replacement. Each headline reads `{label} {value} →` with the arrow pointing into the device h4/h3 that follows in the title row, making the implicit "measurement justified device" statement visible — exactly the audit's literal example "Crest factor 8.2 dB → Glue Compressor." Confidence sibling pills (Solid / Workable / Rough / Unreliable, same ladder as ConfidenceBandBadge) ride alongside the value when the primary field has a paired *Confidence sibling. This preserves the chain-of-custody invariant: low-confidence measurements visibly hedge in the collapsed view too, not just after expansion. Implementation: 1. `CitationHeadline` added as a sibling export in `apps/ui/src/components/CitationBlock.tsx`. Shares the file's imports, the `CONFIDENCE_PILL_CLASSES` map, and the audit lineage. Composes existing pure helpers (`pickPhase1Value`, `formatCitedValue`, `humanizeFieldPath`, `pickPhase1Confidence`, `getConfidenceBand`, `formatBandPillLabel`) — no new resolver logic. 2. Mount points in `AnalysisResults.tsx`: - Mix Chain card header (~line 2056): between the title row and the role paragraph. - Patch card header (~line 2215): between the title row and the MetaBadgeList. - Sonic Element card header (~line 1912): between the title row and the summary paragraph. 3. Each mount is guarded by `card.phase1Fields.length > 0`. Cards without cited fields fall back to today's exact layout. Live verification against saved run 855f1376… (the audit reproducer): all 10 Mix Chain cards, 2 Patch cards, and 7 Sonic Element cards render the headline correctly with appropriate confidence pills (PUMPING STRENGTH 47% [ROUGH SKETCH 30%], SUPERSAW DETECTED [WORKABLE DRAFT 78%], ACID BASS DETECTED [SOLID SCAFFOLD 99%], NOTE TRANSCRIPTION 61% [WORKABLE DRAFT 61%], KEY C Minor [SOLID SCAFFOLD 93%], TEMPO 145 BPM [SOLID SCAFFOLD 100%]). Mobile (375px) verified — headlines fit, pills stay aligned, no overflow. Tests: 15 new cases — 10 in `tests/services/citationHeadline.test.ts` (mirrors `citationBlock.test.ts`) covering label/value/arrow render, null fallback, confidence-pill bands, value formatter parity, typography hooks. 5 in `analysisResultsUi.test.ts` covering Mix Chain / Patch / Sonic integration, empty-fields fallback, and the low-confidence Unreliable-band path. `npm run verify` passes: lint + 575 unit tests + build + 44 smoke. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(ui): unify five confidence vocabularies onto the canonical band ladder Audit Finding #4 — the producer used to see five competing vocabularies for "how much should I trust this number?" — HIGH/MED/LOW pills on Detected Characteristics cards, High/Moderate/Low chips on Confidence Notes, bare CONF X% text on Key/Character metric cards, SCORE X.XX badges on the BPM card (two render paths), and the canonical four-band ladder used only by the Session Musician panel. The chain-of-custody invariant in PURPOSE.md requires low-confidence measurements to produce visibly hedged advice — five vocabularies fragment that signal so the producer can't build a single mental model of trust. This PR promotes the existing four-band ladder (apps/ui/src/services/sessionMusician/confidenceBand.ts) to the canonical primitive across every confidence surface. Frontend-only; Gemini still emits HIGH/MED/LOW and the UI converts locally via a new `toConfidenceBand` normalizer. What changed: 1. New normalizer `toConfidenceBand(value)` in `confidenceBand.ts` accepts numeric 0-1 floats, 0-100 integers, Gemini string enums (HIGH/MED/LOW + High/Moderate/Low variants), and percent strings ("62%"). Returns the matching `ConfidenceBand` or null for unparseable input. The string enums map to band-mid values (HIGH→0.9, MED→0.6, LOW→0.3) so `formatBandPillLabel` reads as honest hedges. 2. `ConfidenceBandBadge` gains: - `variant: 'full' | 'compact'` — `compact` omits the copy paragraph so the pill can sit inline in card corners and metric-card footers. `full` (default) preserves the Session Musician panel behavior unchanged. - `band?: ConfidenceBand` override prop — when supplied, skips the `getConfidenceBand` round-trip. Useful when the caller pre-converts a string enum. 3. V1: Detected Characteristics cards (AnalysisResults.tsx ~1650) — bespoke HIGH/MED/LOW ternary pill replaced with `<ConfidenceBandBadge variant="compact" band={toConfidenceBand(item.confidence)} />`. `characteristicPillClass` helper kept alive only for the unrelated characteristic-name chips on the Character metric card (line 1000); flagged as a follow-up. 4. V2: Confidence Notes chips (AnalysisResults.tsx ~1187) — `toConfidenceBadges` viewModel return shape changed from `{ label, level }` (3-level legacy enum) to `{ label, band }` (canonical four-band ladder). `confidenceClass` helper deleted. 5. V3: Plain `CONF X%` text (3 sites) — replaced with `<ConfidenceBandBadge variant="compact" confidence={...} />` in: - AnalysisResults.tsx Key card footer - AnalysisResults.tsx Character card footer - MeasurementDashboard.tsx alternate Key card 6. V4: `SCORE X.XX` badges (2 sites) — replaced with band pills in AnalysisResults.tsx Tempo card and MeasurementDashboard.tsx Tempo card. `formatBpmScore` deleted from both files. Cross-Check ✓/✗ StatusBadge in MeasurementDashboard preserved — that's an agreement signal (do multiple BPM detectors agree?), not confidence. 7. Dead code retired: `normalizeConfidenceLevel`, `parseConfidenceScalar`, `confidenceClass`, two copies of `formatBpmScore`. `ConfidenceLevel` type and `MelodyInsightsViewModel.confidenceLabel` kept alive — they drive non-pill key/value text rows in Sonic Element melody insights (flagged for follow-up migration). 3-level → 4-band mismatch documented as an intentional refinement: the old `normalizeConfidenceLevel` mapped scalar 0.5-0.79 AND "medium" both to "Moderate"; the new normalizer maps "medium" to 0.6 → workable (consistent), and scalar 0.25-0.49 to "Rough sketch" (more granular than the old "Low" bucket). Tests: 20 new cases across 4 files — 1. `confidenceBand.test.ts` — 9 cases for `toConfidenceBand` (null/undefined fallback, 0-1 floats, 0-100 integers, all string enum variants, percent strings, NaN/Infinity, negative clamping). 2. `confidenceBandBadge.test.ts` — 4 cases for the compact variant and the `band` override prop (label-only when no confidence, tone override, Gemini-string routing). 3. `analysisResultsViewModel.test.ts` — rewrote the existing `toConfidenceBadges` shape assertion; added a `band: null` fallback case. 4. `analysisResultsUi.test.ts` — 5 integration cases proving each V1-V4 site renders band pill text and not the old vocabularies. Live UI verified against saved run 855f1376… — 36 band pills visible across the page (21 Solid scaffold, 7 Workable draft, 5 Rough sketch, 3 Unreliable). Zero `CONF X%` leakage. Detected Characteristics cards show the chain-of-custody hedge working as designed (Detuned Supersaw renders WORKABLE DRAFT in orange while the other 4 detections render SOLID SCAFFOLD in green). Cross-Check ✓/✗ structurally preserved (the saved run doesn't populate bpmAgreement, so it doesn't render, but the code path is untouched). `npm run verify` passes: lint + ~600 unit + build + 44 smoke. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Oz <oz-agent@warp.dev>
slittycode
added a commit
that referenced
this pull request
May 17, 2026
…eader) (#58) * docs(claude): add repo-layout, scripts map, Phase 3 UI entry, CSV recipe Closes four orientation gaps in CLAUDE.md: a top-of-Architecture map that flags advisory/, experiments/, docs/, and tests/ground_truth/ as off-path; a Scripts-at-a-glance section covering scripts/ and apps/backend/scripts/; a pointer from the Phase 3 paragraph to SamplePlayback.tsx + sampleGenerationClient.ts; and a curl example plus allowlisted field paths on the csv_export.py module entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: add Layer 2 transcription evaluation guide Co-Authored-By: Oz <oz-agent@warp.dev> * fix(ui): stop Phase 2 results surface from leaking engine output Removes four classes of broken/placeholder content that made the Phase 2 recommendations read as engine output instead of producer-facing advice (audit finding #1). 1. Mix Chain card role text — `buildRoleSentence` used to fabricate "{stage phrase} by {verb}" by lowercasing the first letter of Gemini's `reason`. Because `reason` is a present-tense clause, every card produced ungrammatical splices like "Controls bass energy by ensures the extreme low-end mono…". Now renders the reason verbatim with a capitalized first letter and trailing period; HIGH-END cue suffix preserved as "(for …)". 2. Patch Framework `patchRole` — was a 7-key category-keyed fallback (`mapPatchRole`) that stamped duplicated placeholders ("Primary tone generator" on every SYNTHESIS card). Removed the field, the fallback, the JSX paragraph, and the contribution to the `inferProcessingGroup` text-concat. The category chip + per-card `whyThisWorks` already carry the bucket and explanation. 3. Patches/Mix Chain overlap — `buildPatchCards` now case-insensitively filters out devices that also appear in `mixAndMasterChain` so the Patches section stops re-listing chain devices. Synthetic fallbacks (Stereo Width, MIDI Clip Guide) are built from Phase 1 only and bypass the filter intentionally. 4. Interpretation Caution raw-JSON dump — the panel rendered `originalValue` verbatim, which for dropped recommendations is a JSON-dumped `AbletonRecommendation` (see `_stringify_warning_value` in `server_phase2.py`). Added `formatDroppedValue` helper that parses JSON-shaped values and renders a compact "device: X · parameter: Y · value: Z" summary. Non-JSON strings pass through; invalid JSON falls back to a truncated raw string. Also resolves the `$Saturator` symptom — Gemini's hallucinated `$`-prefixed device name now surfaces as a readable summary line instead of leaking through a raw JSON dump. 5. System Diagnostics dev-only leak — `validateNewFieldCoverage` emits "Phase 1 field 'X' is present… this warning is benign" coverage signals meant for the engine team, not producers. Added optional `audience?: 'dev' | 'user'` to `ValidationViolation`, marked `NEW_FIELD_UNCITED` as dev, and updated `Phase2ConsistencyReport` to filter dev-audience violations from the rendered table AND from the header counts (so the header doesn't read "5 warnings shown" above an empty table). The underlying `ValidationReport` still carries every violation for tests and offline analysis. Tripwire note: the backend grammar fix `_apply_phase2_grammar_fixes` in `server_phase2.py` only runs on the legacy `/api/phase2` endpoint, not the analysis-runs path the UI uses. After this change it becomes a redundant no-op against the new render shape; left untouched. Tests: 16 new unit cases across viewModel, UI rendering, validator, and consistency report. `npm run verify` passes (lint + 565 unit tests + build + 44 smoke tests). Live UI verified against saved run 855f1376… — all nine text-leak probes (`by ensures`, `by ducks`, `by generates`, `Primary tone generator`, `Texture and movement stage`, `Stereo placement stage`, raw `\$Saturator`, `this warning is benign`, "is present in the measurement payload but no Phase 2…") return false. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(ui): surface primary citation in collapsed card headers Audit Finding #3 — "the chain-of-custody citation, the product's whole differentiator, renders as a footnote." Adds a one-line `CitationHeadline` primitive inside the collapsed header of every Mix Chain / Patch / Sonic Element card so producers see the measurement evidence at scan-time without expanding. The expanded CitationBlock stays in the body unchanged — this is the collapsed-state companion, not a replacement. Each headline reads `{label} {value} →` with the arrow pointing into the device h4/h3 that follows in the title row, making the implicit "measurement justified device" statement visible — exactly the audit's literal example "Crest factor 8.2 dB → Glue Compressor." Confidence sibling pills (Solid / Workable / Rough / Unreliable, same ladder as ConfidenceBandBadge) ride alongside the value when the primary field has a paired *Confidence sibling. This preserves the chain-of-custody invariant: low-confidence measurements visibly hedge in the collapsed view too, not just after expansion. Implementation: 1. `CitationHeadline` added as a sibling export in `apps/ui/src/components/CitationBlock.tsx`. Shares the file's imports, the `CONFIDENCE_PILL_CLASSES` map, and the audit lineage. Composes existing pure helpers (`pickPhase1Value`, `formatCitedValue`, `humanizeFieldPath`, `pickPhase1Confidence`, `getConfidenceBand`, `formatBandPillLabel`) — no new resolver logic. 2. Mount points in `AnalysisResults.tsx`: - Mix Chain card header (~line 2056): between the title row and the role paragraph. - Patch card header (~line 2215): between the title row and the MetaBadgeList. - Sonic Element card header (~line 1912): between the title row and the summary paragraph. 3. Each mount is guarded by `card.phase1Fields.length > 0`. Cards without cited fields fall back to today's exact layout. Live verification against saved run 855f1376… (the audit reproducer): all 10 Mix Chain cards, 2 Patch cards, and 7 Sonic Element cards render the headline correctly with appropriate confidence pills (PUMPING STRENGTH 47% [ROUGH SKETCH 30%], SUPERSAW DETECTED [WORKABLE DRAFT 78%], ACID BASS DETECTED [SOLID SCAFFOLD 99%], NOTE TRANSCRIPTION 61% [WORKABLE DRAFT 61%], KEY C Minor [SOLID SCAFFOLD 93%], TEMPO 145 BPM [SOLID SCAFFOLD 100%]). Mobile (375px) verified — headlines fit, pills stay aligned, no overflow. Tests: 15 new cases — 10 in `tests/services/citationHeadline.test.ts` (mirrors `citationBlock.test.ts`) covering label/value/arrow render, null fallback, confidence-pill bands, value formatter parity, typography hooks. 5 in `analysisResultsUi.test.ts` covering Mix Chain / Patch / Sonic integration, empty-fields fallback, and the low-confidence Unreliable-band path. `npm run verify` passes: lint + 575 unit tests + build + 44 smoke. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(ui): unify five confidence vocabularies onto the canonical band ladder Audit Finding #4 — the producer used to see five competing vocabularies for "how much should I trust this number?" — HIGH/MED/LOW pills on Detected Characteristics cards, High/Moderate/Low chips on Confidence Notes, bare CONF X% text on Key/Character metric cards, SCORE X.XX badges on the BPM card (two render paths), and the canonical four-band ladder used only by the Session Musician panel. The chain-of-custody invariant in PURPOSE.md requires low-confidence measurements to produce visibly hedged advice — five vocabularies fragment that signal so the producer can't build a single mental model of trust. This PR promotes the existing four-band ladder (apps/ui/src/services/sessionMusician/confidenceBand.ts) to the canonical primitive across every confidence surface. Frontend-only; Gemini still emits HIGH/MED/LOW and the UI converts locally via a new `toConfidenceBand` normalizer. What changed: 1. New normalizer `toConfidenceBand(value)` in `confidenceBand.ts` accepts numeric 0-1 floats, 0-100 integers, Gemini string enums (HIGH/MED/LOW + High/Moderate/Low variants), and percent strings ("62%"). Returns the matching `ConfidenceBand` or null for unparseable input. The string enums map to band-mid values (HIGH→0.9, MED→0.6, LOW→0.3) so `formatBandPillLabel` reads as honest hedges. 2. `ConfidenceBandBadge` gains: - `variant: 'full' | 'compact'` — `compact` omits the copy paragraph so the pill can sit inline in card corners and metric-card footers. `full` (default) preserves the Session Musician panel behavior unchanged. - `band?: ConfidenceBand` override prop — when supplied, skips the `getConfidenceBand` round-trip. Useful when the caller pre-converts a string enum. 3. V1: Detected Characteristics cards (AnalysisResults.tsx ~1650) — bespoke HIGH/MED/LOW ternary pill replaced with `<ConfidenceBandBadge variant="compact" band={toConfidenceBand(item.confidence)} />`. `characteristicPillClass` helper kept alive only for the unrelated characteristic-name chips on the Character metric card (line 1000); flagged as a follow-up. 4. V2: Confidence Notes chips (AnalysisResults.tsx ~1187) — `toConfidenceBadges` viewModel return shape changed from `{ label, level }` (3-level legacy enum) to `{ label, band }` (canonical four-band ladder). `confidenceClass` helper deleted. 5. V3: Plain `CONF X%` text (3 sites) — replaced with `<ConfidenceBandBadge variant="compact" confidence={...} />` in: - AnalysisResults.tsx Key card footer - AnalysisResults.tsx Character card footer - MeasurementDashboard.tsx alternate Key card 6. V4: `SCORE X.XX` badges (2 sites) — replaced with band pills in AnalysisResults.tsx Tempo card and MeasurementDashboard.tsx Tempo card. `formatBpmScore` deleted from both files. Cross-Check ✓/✗ StatusBadge in MeasurementDashboard preserved — that's an agreement signal (do multiple BPM detectors agree?), not confidence. 7. Dead code retired: `normalizeConfidenceLevel`, `parseConfidenceScalar`, `confidenceClass`, two copies of `formatBpmScore`. `ConfidenceLevel` type and `MelodyInsightsViewModel.confidenceLabel` kept alive — they drive non-pill key/value text rows in Sonic Element melody insights (flagged for follow-up migration). 3-level → 4-band mismatch documented as an intentional refinement: the old `normalizeConfidenceLevel` mapped scalar 0.5-0.79 AND "medium" both to "Moderate"; the new normalizer maps "medium" to 0.6 → workable (consistent), and scalar 0.25-0.49 to "Rough sketch" (more granular than the old "Low" bucket). Tests: 20 new cases across 4 files — 1. `confidenceBand.test.ts` — 9 cases for `toConfidenceBand` (null/undefined fallback, 0-1 floats, 0-100 integers, all string enum variants, percent strings, NaN/Infinity, negative clamping). 2. `confidenceBandBadge.test.ts` — 4 cases for the compact variant and the `band` override prop (label-only when no confidence, tone override, Gemini-string routing). 3. `analysisResultsViewModel.test.ts` — rewrote the existing `toConfidenceBadges` shape assertion; added a `band: null` fallback case. 4. `analysisResultsUi.test.ts` — 5 integration cases proving each V1-V4 site renders band pill text and not the old vocabularies. Live UI verified against saved run 855f1376… — 36 band pills visible across the page (21 Solid scaffold, 7 Workable draft, 5 Rough sketch, 3 Unreliable). Zero `CONF X%` leakage. Detected Characteristics cards show the chain-of-custody hedge working as designed (Detuned Supersaw renders WORKABLE DRAFT in orange while the other 4 detections render SOLID SCAFFOLD in green). Cross-Check ✓/✗ structurally preserved (the saved run doesn't populate bpmAgreement, so it doesn't render, but the code path is untouched). `npm run verify` passes: lint + ~600 unit + build + 44 smoke. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(ui): audit quick-hits bundle (StickyNav, Mix Chain, Mix Doctor, header) Closes six items from the audit's "Quick hits (low ROI, real)" list. Each is independent; bundled into one PR because they're all small surgical fixes. 1. QH1 — StickyNav clipped "MEASUREMENTS" at 1440px. Was overflow-x-auto + min-w-max, which produced an invisible horizontal scroll (macOS hides scrollbars by default; last pill looked clipped with no scroll affordance). Switched to flex-wrap so pills flow onto multiple rows at narrow widths; whitespace-nowrap prevents intra- label wrap. 2. QH2 — Mix Chain card-number order badges removed. Cards are grouped by processing stage AFTER ordering, so the order numbers appeared out-of-sequence within each group ("1, 6, 8, 9 / 2, 4 / 5, 7 / 3 / 10"), which read as a presentation bug. The visual sequence within each group is already meaningful; the badge added confusion without information. Dropped. 3. QH4 — Mix Doctor Band Diagnostics table now shows the target range alongside the optimal. Previously the column rendered only the optimal dB (e.g. "-22.0"), so two bands with similar Delta dB could land in different Issue buckets without the producer being able to see why. The Issue is determined by absolute thresholds (target.minDb / maxDb), not by the diff from optimal. Showing the range makes the verdict legible at a glance — Sub Bass -6.4 norm, range -16 to -8, exceeds the upper bound by 1.6 dB → TOO-LOUD; Low Mids -14.5 norm, range -26 to -14, sits at the boundary → OPTIMAL. Threshold logic itself was correct; this is a display fix. 4. QH5 — "Dense DAW Lab" link removed from the header. A prior audit pass had de-emphasized it to muted text, but the new audit re- flagged it for sitting in the primary flow's header without context for what it is. Route stays accessible via direct URL (the getAppViewHref helper is preserved for future re-introduction behind a settings menu, and main.tsx still routes to the DenseDawConcept component when activeView === 'daw-concept'). 5. QH6 — header model selector visually demoted. Dropped the "Interpretation Model" label and shrank the styling from a bordered dropdown to a discreet text-secondary inline select. The audit flagged the prior styling as foregrounding an AI-model choice an intermediate producer has no basis to make. Selector stays accessible (smoke spec requires it visible at desktop viewport; `responsive-layout.spec.ts` and `upload-estimate-phase1.spec.ts` both guard the `phase2-model-desktop` testid) but now reads as background metadata. Aria-label + title attribute carry the long- form context for assistive tech and hover discoverability. 6. QH7 — Mix Doctor section title renamed from "MixDoctor" to "Mix Doctor". The audit flagged that the section header rendered as "Mixdoctor" with a lowercase 'd', inconsistent with every other section name. Root cause: `formatWord` in `utils/displayText.ts` case-normalizes single-token CamelCase to sentence case, so "MixDoctor" was rendering as "Mixdoctor". Two words ("Mix Doctor") matches every other section name's pattern (Style Profile, Project Setup, Track Layout, Mix & Master Chain, Patch Framework, etc.) and renders cleanly through the existing pipeline. `npm run verify` passes: lint + ~600 unit + build + 44 smoke. Live UI verified against saved run 855f1376… with probes confirming all six fixes: - StickyNav.overflow-x-auto wrapper gone (pills wrap to 2 rows) - Zero Mix Chain order badges in #section-mix-chain - "Target (range)" header present, "Target dB" header gone - "Dense DAW Lab" text absent from page - "Interpretation Model" label absent from page - "Mix Doctor" section title present, "Mixdoctor" absent Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Oz <oz-agent@warp.dev>
slittycode
added a commit
that referenced
this pull request
May 18, 2026
* docs(claude): add repo-layout, scripts map, Phase 3 UI entry, CSV recipe Closes four orientation gaps in CLAUDE.md: a top-of-Architecture map that flags advisory/, experiments/, docs/, and tests/ground_truth/ as off-path; a Scripts-at-a-glance section covering scripts/ and apps/backend/scripts/; a pointer from the Phase 3 paragraph to SamplePlayback.tsx + sampleGenerationClient.ts; and a curl example plus allowlisted field paths on the csv_export.py module entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: add Layer 2 transcription evaluation guide Co-Authored-By: Oz <oz-agent@warp.dev> * fix(ui): stop Phase 2 results surface from leaking engine output Removes four classes of broken/placeholder content that made the Phase 2 recommendations read as engine output instead of producer-facing advice (audit finding #1). 1. Mix Chain card role text — `buildRoleSentence` used to fabricate "{stage phrase} by {verb}" by lowercasing the first letter of Gemini's `reason`. Because `reason` is a present-tense clause, every card produced ungrammatical splices like "Controls bass energy by ensures the extreme low-end mono…". Now renders the reason verbatim with a capitalized first letter and trailing period; HIGH-END cue suffix preserved as "(for …)". 2. Patch Framework `patchRole` — was a 7-key category-keyed fallback (`mapPatchRole`) that stamped duplicated placeholders ("Primary tone generator" on every SYNTHESIS card). Removed the field, the fallback, the JSX paragraph, and the contribution to the `inferProcessingGroup` text-concat. The category chip + per-card `whyThisWorks` already carry the bucket and explanation. 3. Patches/Mix Chain overlap — `buildPatchCards` now case-insensitively filters out devices that also appear in `mixAndMasterChain` so the Patches section stops re-listing chain devices. Synthetic fallbacks (Stereo Width, MIDI Clip Guide) are built from Phase 1 only and bypass the filter intentionally. 4. Interpretation Caution raw-JSON dump — the panel rendered `originalValue` verbatim, which for dropped recommendations is a JSON-dumped `AbletonRecommendation` (see `_stringify_warning_value` in `server_phase2.py`). Added `formatDroppedValue` helper that parses JSON-shaped values and renders a compact "device: X · parameter: Y · value: Z" summary. Non-JSON strings pass through; invalid JSON falls back to a truncated raw string. Also resolves the `$Saturator` symptom — Gemini's hallucinated `$`-prefixed device name now surfaces as a readable summary line instead of leaking through a raw JSON dump. 5. System Diagnostics dev-only leak — `validateNewFieldCoverage` emits "Phase 1 field 'X' is present… this warning is benign" coverage signals meant for the engine team, not producers. Added optional `audience?: 'dev' | 'user'` to `ValidationViolation`, marked `NEW_FIELD_UNCITED` as dev, and updated `Phase2ConsistencyReport` to filter dev-audience violations from the rendered table AND from the header counts (so the header doesn't read "5 warnings shown" above an empty table). The underlying `ValidationReport` still carries every violation for tests and offline analysis. Tripwire note: the backend grammar fix `_apply_phase2_grammar_fixes` in `server_phase2.py` only runs on the legacy `/api/phase2` endpoint, not the analysis-runs path the UI uses. After this change it becomes a redundant no-op against the new render shape; left untouched. Tests: 16 new unit cases across viewModel, UI rendering, validator, and consistency report. `npm run verify` passes (lint + 565 unit tests + build + 44 smoke tests). Live UI verified against saved run 855f1376… — all nine text-leak probes (`by ensures`, `by ducks`, `by generates`, `Primary tone generator`, `Texture and movement stage`, `Stereo placement stage`, raw `\$Saturator`, `this warning is benign`, "is present in the measurement payload but no Phase 2…") return false. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(eval): Layer 2 transcription harness + polyphonic scoring workflow Wires Layer 2 (torchcrepe) transcription into the Phase 1 evaluation harness behind --include-transcription, with a stepped-sine self-test that runs even when the corpus is empty and a transcriptionTracks slot in the manifest fixture. Adds new interactive tooling for the polyphonic-transcription spike: score_polyphonic_clip.py walks each clip/candidate pair and writes scorecards back into the report JSON, import_midi_to_ground_truth.py seeds ground-truth references, and summarize_midi_file emits richer diagnostic flags (note_clutter, octave_junk, dense_chords_unusable, sparse_likely_undertranscribed) to steer reviewer attention without bypassing the manual usefulness gates. Documents the corpus + scoring workflow and the distinction between the polyphonic_tracks/ research corpus and the transcription_tracks/ Layer 2 bench. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Oz <oz-agent@warp.dev>
5 tasks
slittycode
pushed a commit
that referenced
this pull request
May 18, 2026
PR 6 of 16 in the UI consistency overhaul. Promotes the AnalysisResults page header to <SectionHeader> and wraps the top TEMPO/KEY/METER/ CHARACTER metric strip in a <DeviceRack name="Measurement Summary"> with <MetricTile> cards inside. Stacked on PR #69 (D.4 FileUpload). Will rebase onto main after #69 merges. Header (AnalysisResults.tsx:835-880 → :835-878): - <h1> + Download buttons row → <SectionHeader size="lg" variant="inline" eyebrow="ASA Results" titleRole="page-title"> with both Download buttons in the action slot. - The Activity icon prefix on the h1 disappears — the SectionHeader LED dot already carries the "results are live" semantic. - The subtitle (sourceFileName · interpretationSubtitle) stays as a separate <p> sibling, preserving data-testid="analysis-results- subtitle" and the existing text-role-meta styling. Indented pl-4 to align under the title after the LED. - Download data: <button> → <Button variant="secondary" leadingIcon=FileJson>. Preserved data-testid="analysis-export-json". - Download report: <button> → <Button variant="primary" leadingIcon=FileText>. Preserved data-testid="analysis-export-markdown". Metric strip (AnalysisResults.tsx:884-1012 → :882-1010): - Outer <div className="grid grid-cols-2 md:grid-cols-4 gap-3"> wrapped in <DeviceRack name="Measurement Summary" density="dense" status="success">. The grid layout stays as-is inside the rack body so the responsive 2-col / 4-col break still works. - 4 × AccentMetricCard → 4 × <MetricTile size="lg" accent="accent">. API parity drop-in: • label (was a flex-span with icon+text) → icon prop + label string. Cleaner separation; the MetricTile primitive places the icon in the eyebrow row. • value / unit / headerRight / footer pass through unchanged. • The hand-rolled text-[1.5rem] override on the CHARACTER value disappears — MetricTile size="lg" already renders text-2xl which is the same 1.5rem. Imports: - AccentMetricCard stays imported from ./MeasurementPrimitives. Other usages of it remain at :1249-1258 and :1444-1448 (Patches / Sonic Elements sections — migrated in later PRs D.5b/c/d). - Added Button / DeviceRack / MetricTile / SectionHeader from ./ui. Preserved verbatim: - All data-testid attributes (analysis-results-root, analysis-results- subtitle, analysis-export-json, analysis-export-markdown). - All visible text strings: "Analysis Results", "Download data", "Download report", "TEMPO", "BPM", "KEY SIG", "METER", "CHARACTER", "SCANNING...". - The PhaseSourceBadge + ConfidenceBandBadge + MetricBar + StatusBadge + TokenBadgeList rendering inside each tile is untouched. - Audit Finding #4 commentary preserved (canonical band-pill vocabulary). - Audit Finding #1 ordering comment preserved (MeasurementDashboard rendering at the bottom of the scroll). - The lowConfidenceIndicator + characteristicPills logic untouched. Verified: lint clean, 628/628 unit tests passing, build clean (AnalysisResults chunk 255 KB / 63 KB gz; index chunk 323 KB / 97 KB gz — both stable vs PR 5). https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe
slittycode
added a commit
that referenced
this pull request
May 18, 2026
PR 6 of 16 in the UI consistency overhaul. Promotes the AnalysisResults page header to <SectionHeader> and wraps the top TEMPO/KEY/METER/ CHARACTER metric strip in a <DeviceRack name="Measurement Summary"> with <MetricTile> cards inside. Stacked on PR #69 (D.4 FileUpload). Will rebase onto main after #69 merges. Header (AnalysisResults.tsx:835-880 → :835-878): - <h1> + Download buttons row → <SectionHeader size="lg" variant="inline" eyebrow="ASA Results" titleRole="page-title"> with both Download buttons in the action slot. - The Activity icon prefix on the h1 disappears — the SectionHeader LED dot already carries the "results are live" semantic. - The subtitle (sourceFileName · interpretationSubtitle) stays as a separate <p> sibling, preserving data-testid="analysis-results- subtitle" and the existing text-role-meta styling. Indented pl-4 to align under the title after the LED. - Download data: <button> → <Button variant="secondary" leadingIcon=FileJson>. Preserved data-testid="analysis-export-json". - Download report: <button> → <Button variant="primary" leadingIcon=FileText>. Preserved data-testid="analysis-export-markdown". Metric strip (AnalysisResults.tsx:884-1012 → :882-1010): - Outer <div className="grid grid-cols-2 md:grid-cols-4 gap-3"> wrapped in <DeviceRack name="Measurement Summary" density="dense" status="success">. The grid layout stays as-is inside the rack body so the responsive 2-col / 4-col break still works. - 4 × AccentMetricCard → 4 × <MetricTile size="lg" accent="accent">. API parity drop-in: • label (was a flex-span with icon+text) → icon prop + label string. Cleaner separation; the MetricTile primitive places the icon in the eyebrow row. • value / unit / headerRight / footer pass through unchanged. • The hand-rolled text-[1.5rem] override on the CHARACTER value disappears — MetricTile size="lg" already renders text-2xl which is the same 1.5rem. Imports: - AccentMetricCard stays imported from ./MeasurementPrimitives. Other usages of it remain at :1249-1258 and :1444-1448 (Patches / Sonic Elements sections — migrated in later PRs D.5b/c/d). - Added Button / DeviceRack / MetricTile / SectionHeader from ./ui. Preserved verbatim: - All data-testid attributes (analysis-results-root, analysis-results- subtitle, analysis-export-json, analysis-export-markdown). - All visible text strings: "Analysis Results", "Download data", "Download report", "TEMPO", "BPM", "KEY SIG", "METER", "CHARACTER", "SCANNING...". - The PhaseSourceBadge + ConfidenceBandBadge + MetricBar + StatusBadge + TokenBadgeList rendering inside each tile is untouched. - Audit Finding #4 commentary preserved (canonical band-pill vocabulary). - Audit Finding #1 ordering comment preserved (MeasurementDashboard rendering at the bottom of the scroll). - The lowConfidenceIndicator + characteristicPills logic untouched. Verified: lint clean, 628/628 unit tests passing, build clean (AnalysisResults chunk 255 KB / 63 KB gz; index chunk 323 KB / 97 KB gz — both stable vs PR 5). https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe Co-authored-by: Claude <noreply@anthropic.com>
This was referenced May 29, 2026
slittycode
added a commit
that referenced
this pull request
May 30, 2026
…ounding gate (#122) Adds MT3 (Magenta Multi-Task Multitrack) as a flag-gated, additive polyphonic-transcription stage emitting per-instrument MIDI, and gates interpretation on in-flight MT3/pitch-note stages so Gemini Phase 2 reliably sees the additive grounding. Additive only — Phase 1 stays authoritative (PURPOSE.md #1); camelCase contract and types.ts untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2 tasks
slittycode
pushed a commit
that referenced
this pull request
Jun 2, 2026
Phase 1 v2 emits truePeak: null for digital silence (no defined dBTP), but the frontend type declared it `number` and several display sites called .toFixed unguarded — a latent crash on silent input. Worse, the HTTP envelope coerced null -> 0.0, falsely reporting a full-scale peak for a silent track (violating measurement-honesty invariants #1/#4). Backend: - server_phase1._build_phase1 now preserves null via _coerce_nullable_number instead of defaulting to 0.0; PLR fallback already guards None. - Tests: truePeak passthrough/null + PLR-null-when-peak-null. Frontend: - Phase1Result.truePeak typed `number | null`. - analysisResultsViewModel: formatTruePeak (renders "—" for null, matching the house formatNumber convention) and masterCeilingDb (falls back to the -0.3 dB cap when there is no peak to duck below); applied at all 6 sites. - MeasurementDashboard: omit the true-peak marker, PLR gap fill, and PLR annotation for silence (LUFS marker still renders). - backendPhase1Client: accept an explicit null truePeak (legacy path) while still rejecting a malformed/missing field. - exportUtils markdown renders "—" for a null peak. - Tests: null-silence card renders "—" without crashing; helper unit cases. Docs: JSON_SCHEMA truePeak note now states null == digital silence. https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q
This was referenced Jun 2, 2026
slittycode
added a commit
that referenced
this pull request
Jun 2, 2026
…t PLR (#128) * fix(loudness): Phase 1 v2 - truePeak to dBTP, bpmConfidence to 0-1, coherent PLR Phase 1 schema v2 migration for the loudness fields, motivated by a consumer audit: ~12 UI/recommendation consumers already assumed `truePeak` was dBTP (several were latent bugs), while only the loudness guardrail treated it as the linear amplitude proxy v1 actually emitted. - truePeak now emitted in dBTP (0.0 == full scale, >0 == inter-sample over); null for silence. bpmConfidence normalized to 0-1 (raw Essentia ~0-5.32 / 5). - PLR is now a coherent dB-domain subtraction (truePeak_dBTP - lufsIntegrated), fixing the prior linear-minus-dB unit mismatch, across analyze_core, the HTTP fallback, and the phase1 eval consistency gate. - Add top-level `phase1Version` ("phase1.v2") so consumers can detect the generation; loudness guardrail over-check moves to dBTP (>0); the validator drops its special-case bpmConfidence threshold (now shares the 0.4 hedge). - Re-baseline the golden (truePeak 0.0, plr 5.6); add dedicated analyze_plr and dBTP true-peak unit tests; update fixture expectations, types, JSON_SCHEMA. - ADR 0002 records the v2 bump, superseding ADR 0001 for these two fields. Verified: backend unittest suite green; frontend tsc + 716 unit tests + build green. https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q * chore: ignore Python bytecode repo-wide The .gitignore only covered apps/backend/__pycache__/, so bytecode generated outside the backend (e.g. scripts/__pycache__/ when build_live12_catalogue.py is imported) showed up as untracked. Generalize the __pycache__/ and *.py[cod] rules to the whole repo. https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q * fix(loudness): v2 follow-ups - phase1 eval manifest targets + dBTP labels - Backend CI fix: phase1_eval_manifest.json truePeak/plr thresholds still encoded v1 linear values (click_120 truePeak 0.9 / plr 24.8; sine_220 0.5 / 7.4). Migrated to the v2 dBTP measurements (click -0.9 / 23.2; sine -6.0 / 1.0). The plr:consistency gate already passes; only the manifest's expected targets were stale. - Review (#128): three analysisResultsViewModel.ts display sites labeled the now-correct truePeak value as "dB"; corrected to "dBTP". Verified: tests.test_phase1_evaluation green; frontend tsc + 716 unit green. https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q * Harden nullable truePeak across the v2 contract Phase 1 v2 emits truePeak: null for digital silence (no defined dBTP), but the frontend type declared it `number` and several display sites called .toFixed unguarded — a latent crash on silent input. Worse, the HTTP envelope coerced null -> 0.0, falsely reporting a full-scale peak for a silent track (violating measurement-honesty invariants #1/#4). Backend: - server_phase1._build_phase1 now preserves null via _coerce_nullable_number instead of defaulting to 0.0; PLR fallback already guards None. - Tests: truePeak passthrough/null + PLR-null-when-peak-null. Frontend: - Phase1Result.truePeak typed `number | null`. - analysisResultsViewModel: formatTruePeak (renders "—" for null, matching the house formatNumber convention) and masterCeilingDb (falls back to the -0.3 dB cap when there is no peak to duck below); applied at all 6 sites. - MeasurementDashboard: omit the true-peak marker, PLR gap fill, and PLR annotation for silence (LUFS marker still renders). - backendPhase1Client: accept an explicit null truePeak (legacy path) while still rejecting a malformed/missing field. - exportUtils markdown renders "—" for a null peak. - Tests: null-silence card renders "—" without crashing; helper unit cases. Docs: JSON_SCHEMA truePeak note now states null == digital silence. https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q --------- Co-authored-by: Claude <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
Jun 2, 2026
* WS3a: asa-dsp ↔ Essentia loudness parity harness + report Before the loudness-spectro-wasm core (asa-dsp, openmeters-derived BS.1770) can back any ASA product path (WS3b backend, WS3c browser readout), it has to agree with Essentia — ASA's authoritative Phase 1 loudness source, since Phase 1 measurements are ground truth (PURPOSE.md invariant #1). scripts/essentia_parity.py runs a deterministic synthetic corpus (tones, sweep, white/pink noise, decorrelated stereo) through BOTH paths at each file's native rate (no resampling): - asa-dsp via the native measure-cli binary (source-identical to the WASM core, so WS3a needs no wasm toolchain), and - Essentia via AudioLoader -> LoudnessEBUR128, mirroring analyze_core.analyze_loudness, plus a dBTP true peak from TruePeakDetector. Primary gate: integrated LUFS within +/-0.1 LU on every file. Result (docs/essentia-parity-report.md): 7/8 signals agree within +/-0.02 LU (most within +/-0.002) — essentially perfect BS.1770 parity on tones and noise. The sole breach is the 20 Hz->20 kHz log sweep at -0.102 LU (0.002 over the gate), a pathological non-program signal. Verdict is reported as the harness computed it (FAIL by that single borderline case); the realistic-signal agreement is the substantive finding. Surfaced at the WS3a hard checkpoint. README documents the harness as Validation helper #4. https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q * WS3a review: simplify run(), flag true-peak divergence for WS3c Addresses the PR #129 review: - run() no longer calls build_report() and discards the text; it returns rows only. build_report() now runs exactly once, in main(). Pure refactor — the harness reproduces identical numbers. - Report caveats now explicitly flag the broadband true-peak divergence (white_noise ~0.6 dBTP gap): WS3c must not expose a browser true-peak readout without its own gate. Integrated loudness is unaffected. https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q --------- Co-authored-by: Claude <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
Jun 2, 2026
…default-off) (#132) * WS3b: optional asa-dsp loudness backend behind ASA_LOUDNESS_BACKEND Wires the loudness-spectro-wasm core (asa-dsp) into the backend as a default-off alternative loudness source for the LUFS scalars, selected by ASA_LOUDNESS_BACKEND=essentia|wasm (default essentia). Architecture (deviates from the plan's pyo3 sketch, deliberately): - Invokes the already-built native measure-cli binary on a temp WAV rather than binding via pyo3. This sidesteps the workspace's `panic = "abort"` profile (which blocks pyo3), needs no separate Cargo workspace, and — being pure Python on this side — cannot red the loudness-wasm CI job. The backend has already decoded the audio, so the temp WAV makes it format-agnostic. - Scope is the four LUFS scalars (integrated/range/momentary-max/short-term-max) — the EBU loudness the WS3a parity harness validated at ±0.1 LU. truePeak stays Essentia (asa-dsp's true peak diverges on broadband per #129) and lufsCurve stays Essentia (the CLI emits scalars, not per-frame curves). So this is a scalar override, not a full engine replacement. - Default stays Essentia (PURPOSE.md invariant #1); any failure (missing binary, non-zero exit, unparseable/null output) degrades gracefully back to Essentia. apps/backend/loudness_backend.py + a call after analyze_loudness in analyze.py. Tests mock the subprocess (the binary isn't built in the Python CI job): backend selection, scalar override + curve passthrough, and every fall-back path. The default path is unchanged — test_analyze + the golden snapshot still pass. https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q * WS3b review: fix repo-root path off-by-one; don't clobber Essentia with null Addresses the PR #132 REQUEST CHANGES: - BLOCKING: _REPO_ROOT used parents[1] (= apps/), so the default measure-cli path was apps/packages/loudness-spectro-wasm/... which doesn't exist — the feature silently degraded to Essentia unless ASA_MEASURE_CLI was set. Fixed to parents[2] (repo root). Regression test asserts the default path resolves to the real <repo>/packages/loudness-spectro-wasm dir. - Should-fix: only override a LUFS scalar when the CLI value is non-null, so a null lra (undefined for short content where integrated is still valid) can't clobber Essentia's reading. Regression test covers it. 12/12 loudness_backend tests pass; the warn path now correctly points at <repo>/packages/... (no apps/ prefix). https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q --------- Co-authored-by: Claude <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
Jun 2, 2026
* WS3c: in-browser loudness parity readout (flag-gated, WAV-only) Adds an additive browser-side loudness readout that measures the uploaded WAV with the asa-dsp (WASM core) and compares the LUFS scalars against the authoritative Phase 1 Essentia values. Phase 1 stays the source of truth (PURPOSE.md invariant #1); this is a diagnostic, default-off behind VITE_ENABLE_BROWSER_LOUDNESS. Services (pure, fully unit-tested): - wavDecoder.ts: decodes uncompressed WAV (PCM 16/24/32 + float32, incl. WAVE_FORMAT_EXTENSIBLE) to interleaved f32 at the file's NATIVE rate — no AudioContext.decodeAudioData resampling, which would break ±0.1 LU EBU conformance. WAV only; FLAC/MP3 report "WAV only" rather than guessing. - parity.ts: computeLoudnessParity → per-field deltas vs Phase 1, gated at ±0.1 LU. True peak is deliberately excluded (asa-dsp diverges on broadband per #129). - loader.ts: guarded dynamic import of the WASM module from a runtime URL (VITE_BROWSER_LOUDNESS_WASM_URL) with @vite-ignore so the build never tries to resolve the (unbuilt, gitignored) package; returns null → "unavailable" on any failure. The result adapter (NaN→null, free()) is unit-tested. UI: BrowserLoudnessPanel (flag-gated mount in AnalysisResults) reuses the retained App `audioFile`; graceful states for non-WAV, wasm-unavailable, error. Verification limits (honest): the WAV decoder + parity math + result adapter are unit-tested; the actual WASM run and browser behavior are NOT verified here (no browser, and building web-wasm needs wasm-bindgen-cli). Activation requires building packages/loudness-spectro-wasm and serving pkg/ — documented in the unavailable state. 729 unit tests pass; build clean (no static wasm import). https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q * WS3c review: fix EXTENSIBLE guard off-by-2, drop redundant import.meta cast - wavDecoder: WAVE_FORMAT_EXTENSIBLE guard needs chunkSize >= 26 (not 24) to safely read the 2-byte SubFormat tag at body+24; add a test that decodes a real 40-byte EXTENSIBLE fmt chunk (the branch was previously untested). - loader: VITE_BROWSER_LOUDNESS_WASM_URL is declared in vite-env.d.ts, so the import.meta cast is unnecessary — read import.meta.env directly. https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q --------- Co-authored-by: Claude <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
Jun 5, 2026
…#143) Phase 2 emits device recommendations across three free-shaped arrays (abletonRecommendations, mixAndMasterChain, secretSauce.workflowSteps) with free-text values and no machine-checkable freeze. This adds a normalized, versioned, schema-validated contract for that surface. - schemas/recommendations.v1.schema.json: Draft 2020-12 schema. Envelope {version, recommendations[]}; each entry {device, parameter, value, unit, range, cited_measurements[]}, additionalProperties:false, cited_measurements minItems:1. - recommendations_contract.py: deterministic projection of the three Phase 2 card arrays into the normalized shape, validated against the literal schema file via jsonschema (not a hand-rolled mirror — avoids the drift ADR 0001 warned about). Derived/additive: never overrides Phase 1 (invariant #1); admits only cited cards (invariant #2); unit/range are nullable best-effort since the static Live 12 catalogue carries no min/max (invariant #4). - server.py: attaches the validated envelope to the producer_summary interpretation result; it travels into GET /api/analysis-runs/{run_id} under stages.interpretation.result.recommendations (degrades to absent on error). - interpretation.ts: TS mirror (RecommendationsContract). - ADR 0003 + CLAUDE.md index document the contract and its semver/compat policy. Tests: test_recommendations_contract.py (schema validity, projection-validates, round-trip, freeze), a server wiring test, and a runtime snapshot-survival test. Full backend suite green (1153), tsc --noEmit green. jsonschema==4.26.0 pinned. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jun 5, 2026
slittycode
added a commit
that referenced
this pull request
Jul 4, 2026
#204) Behavior-neutral: analyze_time_signature computed per-candidate accent evidence (dominance + per-bar-position onset means for bar lengths 3-7) and threw it away. Now it rides the payload as an additive, full-mode-only top-level field, strongest candidate first, empty on every fallback branch: timeSignatureCandidates: [{timeSignature, dominance, positionMeans[]}] fundamentalsQuality.domains.meter.evidence gains {bestCandidate, margin, candidateCount} derived from it, so the trust layer can say HOW ambiguous the meter read was rather than just that it was ambiguous. This is the evidence surface the meter improvement track (accuracy program Phases B/C — the measured weak layer) builds on; downstream must never treat a candidate as an override of timeSignature (invariant #1). Contract mirrors (tripwire #4): JSON_SCHEMA.md inventory + full-only list + field table; server_phase1._build_phase1 forwarding (caught by the golden's forwarding guard); src/types/measurement.ts + backendPhase1Client.ts reconstructor (drops malformed entries); phase1FullPayload fixture; golden re-baselined (new key, structural). Tests: meter-evidence summary (backend), parser filtering (Vitest), full-mode key set. Backend suite 1295 OK; fundamentals gate 15/15; frontend verify green (877+ unit, 51 smoke). Claude-Session: https://claude.ai/code/session_01T4wfz87k6kzJqkKLKZE7YL Co-authored-by: Claude <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
Jul 13, 2026
…acy PR-G3) Adds bpmOctaveEvidence (full-mode only): simple-ratio candidates (1:2, 2:3, 1:1, 3:2, 2:1 of the shipped bpm, clamped 40-220) scored against two inter-onset streams — low-band kick articulation (integer multiples, fit divided by the median multiple) plus the full-band hat/snare grid (admits the 8th-note 0.5 multiple), which pins the notated tempo when the kick only marks bars. Never overrides bpm (invariant #1); abstains (null) on sparse/beatless material. Measured on the corpus: recovers all three broken octave cases (174 -> 173.8, 190 -> 189.8, halftime-174 -> 175.5 via 3:2) with zero false alarms on the 10 correctly-shipped rhythm clips; the beatless ambient clip abstains. Promoted as an active tempoOctave:preferredBpm gate on 14 rhythm clips (13 pass; grid_7_8_140 stays informational — its bpm is meter-smeared, not octave-wrong: PR-G4 territory). Both sides landed: JSON_SCHEMA.md + full-only docstring, measurement.ts types, backendPhase1Client reconstructor, phase1FullPayload fixture; golden deliberately re-baselined (bpmOctaveEvidence key + one rounding-step lufsShortTermMax nudge within designed tolerance). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
Jul 13, 2026
…acy PR-G3) Adds bpmOctaveEvidence (full-mode only): simple-ratio candidates (1:2, 2:3, 1:1, 3:2, 2:1 of the shipped bpm, clamped 40-220) scored against two inter-onset streams — low-band kick articulation (integer multiples, fit divided by the median multiple) plus the full-band hat/snare grid (admits the 8th-note 0.5 multiple), which pins the notated tempo when the kick only marks bars. Never overrides bpm (invariant #1); abstains (null) on sparse/beatless material. Measured on the corpus: recovers all three broken octave cases (174 -> 173.8, 190 -> 189.8, halftime-174 -> 175.5 via 3:2) with zero false alarms on the 10 correctly-shipped rhythm clips; the beatless ambient clip abstains. Promoted as an active tempoOctave:preferredBpm gate on 14 rhythm clips (13 pass; grid_7_8_140 stays informational — its bpm is meter-smeared, not octave-wrong: PR-G4 territory). Both sides landed: JSON_SCHEMA.md + full-only docstring, measurement.ts types, backendPhase1Client reconstructor, phase1FullPayload fixture; golden deliberately re-baselined (bpmOctaveEvidence key + one rounding-step lufsShortTermMax nudge within designed tolerance). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
Jul 13, 2026
…(PR-G4) (#213) * feat(rhythm): surfacing-only tempo-octave evidence at extremes (accuracy PR-G3) Adds bpmOctaveEvidence (full-mode only): simple-ratio candidates (1:2, 2:3, 1:1, 3:2, 2:1 of the shipped bpm, clamped 40-220) scored against two inter-onset streams — low-band kick articulation (integer multiples, fit divided by the median multiple) plus the full-band hat/snare grid (admits the 8th-note 0.5 multiple), which pins the notated tempo when the kick only marks bars. Never overrides bpm (invariant #1); abstains (null) on sparse/beatless material. Measured on the corpus: recovers all three broken octave cases (174 -> 173.8, 190 -> 189.8, halftime-174 -> 175.5 via 3:2) with zero false alarms on the 10 correctly-shipped rhythm clips; the beatless ambient clip abstains. Promoted as an active tempoOctave:preferredBpm gate on 14 rhythm clips (13 pass; grid_7_8_140 stays informational — its bpm is meter-smeared, not octave-wrong: PR-G4 territory). Both sides landed: JSON_SCHEMA.md + full-only docstring, measurement.ts types, backendPhase1Client reconstructor, phase1FullPayload fixture; golden deliberately re-baselined (bpmOctaveEvidence key + one rounding-step lufsShortTermMax nudge within designed tolerance). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ci): wire bpmOctaveEvidence into server_phase1 forwarder and audio-fixture key set Two omissions from PR-G3 that CI caught on the rebased branch: - server_phase1._build_phase1 did not forward bpmOctaveEvidence to the frontend (test_phase1_golden.BuildPhase1KeySupersetTests caught it) - EXPECTED_TOP_LEVEL_KEYS in test_audio_fixture.py did not include it (AudioFixtureSmokeTest.test_output_contains_all_expected_keys caught it) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NCtxwnhn8nzHBjzeehPtf8 * feat(meter): loudness-accent stream for meter + downbeats on non-4/4 (accuracy PR-G4) The onset-count accent stream is loudness-blind: a kick on every beat yields identical counts at every bar position regardless of which beat is accented — measured on the odd-meter corpus clips as 3/4 edging 4/4 by 0.8%, shipping 4/4 at confidence 0.03. analyze_time_signature now folds the per-beat low-band loudness (beat_data, hoisted above the meter call) at each candidate bar length as a second accent stream: max-over-phase dominance, clamped at 10, neutral under 4 bars, harmonic folds collapsed by echo PROMINENCE (0.262 true-3/4 vs 0.000 true-6/8 measured; raw echo/peak is ~0.72 for both). Decision runs on the count x loudness product with a 15% override margin when loudness contributed; count-only callers (including --fast) keep the 20% margin byte-identical. Burn-down: 3/4, 6/8, 7/8 meter AND downbeats (F1 0.875 each) flip from knownGaps to gating checks; all ten 4/4 rhythm clips (incl. broken-kick 2-step/halftime/breakbeat) unchanged. Remaining gaps are all tempo-family. timeSignatureCandidates entries gain an additive loudnessDominance key, landed on both sides; golden passes without re-baseline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
Jul 13, 2026
…R-G5) (#214) * feat(rhythm): surfacing-only tempo-octave evidence at extremes (accuracy PR-G3) Adds bpmOctaveEvidence (full-mode only): simple-ratio candidates (1:2, 2:3, 1:1, 3:2, 2:1 of the shipped bpm, clamped 40-220) scored against two inter-onset streams — low-band kick articulation (integer multiples, fit divided by the median multiple) plus the full-band hat/snare grid (admits the 8th-note 0.5 multiple), which pins the notated tempo when the kick only marks bars. Never overrides bpm (invariant #1); abstains (null) on sparse/beatless material. Measured on the corpus: recovers all three broken octave cases (174 -> 173.8, 190 -> 189.8, halftime-174 -> 175.5 via 3:2) with zero false alarms on the 10 correctly-shipped rhythm clips; the beatless ambient clip abstains. Promoted as an active tempoOctave:preferredBpm gate on 14 rhythm clips (13 pass; grid_7_8_140 stays informational — its bpm is meter-smeared, not octave-wrong: PR-G4 territory). Both sides landed: JSON_SCHEMA.md + full-only docstring, measurement.ts types, backendPhase1Client reconstructor, phase1FullPayload fixture; golden deliberately re-baselined (bpmOctaveEvidence key + one rounding-step lufsShortTermMax nudge within designed tolerance). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ci): wire bpmOctaveEvidence into server_phase1 forwarder and audio-fixture key set Two omissions from PR-G3 that CI caught on the rebased branch: - server_phase1._build_phase1 did not forward bpmOctaveEvidence to the frontend (test_phase1_golden.BuildPhase1KeySupersetTests caught it) - EXPECTED_TOP_LEVEL_KEYS in test_audio_fixture.py did not include it (AudioFixtureSmokeTest.test_output_contains_all_expected_keys caught it) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NCtxwnhn8nzHBjzeehPtf8 * feat(meter): loudness-accent stream for meter + downbeats on non-4/4 (accuracy PR-G4) The onset-count accent stream is loudness-blind: a kick on every beat yields identical counts at every bar position regardless of which beat is accented — measured on the odd-meter corpus clips as 3/4 edging 4/4 by 0.8%, shipping 4/4 at confidence 0.03. analyze_time_signature now folds the per-beat low-band loudness (beat_data, hoisted above the meter call) at each candidate bar length as a second accent stream: max-over-phase dominance, clamped at 10, neutral under 4 bars, harmonic folds collapsed by echo PROMINENCE (0.262 true-3/4 vs 0.000 true-6/8 measured; raw echo/peak is ~0.72 for both). Decision runs on the count x loudness product with a 15% override margin when loudness contributed; count-only callers (including --fast) keep the 20% margin byte-identical. Burn-down: 3/4, 6/8, 7/8 meter AND downbeats (F1 0.875 each) flip from knownGaps to gating checks; all ten 4/4 rhythm clips (incl. broken-kick 2-step/halftime/breakbeat) unchanged. Remaining gaps are all tempo-family. timeSignatureCandidates entries gain an additive loudnessDominance key, landed on both sides; golden passes without re-baseline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(rhythm): detect 16th-grid shuffle in the swing measurement (accuracy PR-G5) compute_swing_detail probes the 16th grid when the 8th grid reads straight or absent: same long/short-split logic at half scale (straight center 0.25, thresholds 0.265/0.235). UKG/2-step shuffle lives on the 16ths and was previously invisible. 8th-swung material keeps priority and is byte-identical; straight material cannot fabricate a shuffle (no split at 0.25); sparse material still abstains. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(eval): gate 16th-shuffle DETECTION; document the ratio-value frame-resolution gap (PR-G5) New swing:gridResolution harness check (expected.swingGrid) promotes shuffle detection to an active gate on shuffle16_130_62 — swingDetail now reads swung/16th/conf 0.992 where it read None. The ratio value stays a knownGap with its measured diagnosis: onset detection quantizes to 11.6ms hops with a one-frame-per-side inward bias, compressing 62% shuffle to a 55% read (verified not a fixture artifact: a 50ms closed hat renders byte-identical medians). Value correction without a gate would repeat the confidently-wrong pattern; it stays a future, separately gated onset-timing refinement. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 13, 2026
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.
Summary
dynamicCharacterthroughserver.pyso Gemini receives the kick/compressor values it's told to cite; remove the citation instruction that contradicted the STRING-typed response schemaTest plan
tsc --noEmit)npm run test:smoke)./scripts/dev.sh→ upload audio → verify all new cards render)🤖 Generated with Claude Code