Preserve signal monitor playback during analysis and harden run flow - #3
Merged
Conversation
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>
slittycode
pushed a commit
that referenced
this pull request
May 16, 2026
Three fixes from the review on #50: - Plan 6 DoD: add apps/ui/src/types/measurement.ts to the field-addition checklist alongside JSON_SCHEMA.md and EXPECTED_TOP_LEVEL_KEYS. CLAUDE.md tripwire #3 — Python emits camelCase JSON directly, so a Python-side addition without the TS counterpart disappears silently from the UI. - Plan 5 DoD: add a testable item enforcing the "preview is approximate, not a Phase 1 measurement" guardrail that previously lived only in the Risks prose. - Plan 5 prose: disambiguate Phase 3 audition (shipped, #45) from Phase 3 synth-patch generation (open, patchSmith.ts) and fix the broken link that pointed at README.md instead of the PR / design doc. https://claude.ai/code/session_017Pt2pELLM2qNQxoZrWQt4M
slittycode
added a commit
that referenced
this pull request
May 16, 2026
…+ Harmonia) (#50) * docs(incorporations): forking plans for 5 audio-MIR discoveries (ASA + Harmonia) Five concrete incorporation plans for upstreams surfaced in the 2026-05-13 / 2026-05-14 audio-MIR discovery passes, mirroring the seven-part track structure of docs/external-repo-review-2026-05-13.md (Source / What to lift / Why / Approach / Cross-check oracle / Definition of done / Risks): - Plan 4 — audio-analyzer-rs (MIT, Rust binary MCP server): cross-check oracle first; fork-to-WASM gated on a concrete in-browser need that ASA doesn't have today. - Plan 5 — resonators (dual MIT/Apache-2.0, published npm WASM): incorporate as a dependency for a new browser-side spectral preview; the near-drop-in. - Plan 6 — jivetalking (GPL-3.0-only, Go): clean-room port of the measure-then-derive-parameters pattern into Phase 2 recommendation logic; license blocks any code path. - Plan 7 — ChordMiniApp (MIT, Next.js + Flask + Firebase): reference-only UX + architecture review for Harmonia, with explicit adopt / adapt / reject per surface. - Plan 8 — chordonomicon (Apache-2.0 scripts, CC-BY-NC-4.0 dataset): HF dataset eval harness for Harmonia; NonCommercial license gates product inclusion. Plans 4 and 5 cross-reference each other (both Rust → WASM stories on paper, with different ROI profiles). Plan 6 composes with the prior review's Track 1 finding (ASA's Essentia loudness is correct on stereo) by sourcing measurement inputs from existing Phase 1 rather than a new measurement layer. Plan 7 ↔ Plan 8 compose: 7 sets the chord-presentation UX bar, 8 provides labeled ground truth. License discipline: all five licenses confirmed against the upstream (MIT, dual MIT/Apache, GPL-3.0-only, MIT, Apache-2.0 scripts + CC-BY-NC-4.0 data). Two plans carry license risk — jivetalking (GPL, no source-reading for the port) and chordonomicon (NC, no commercial inclusion); both make license confirmation item 1 of their DoD. The task brief referenced a separate routine-discoveries repository with discoveries/*.md source-of-truth files and an asa-2026-05-13.md template. Those files are not present in this checkout (scoped to ableton-sonic-analyzer). The plan was written from the task description + direct upstream research + the prior external-repo-review for house style; see the Environment note in the document header. https://claude.ai/code/session_017Pt2pELLM2qNQxoZrWQt4M * docs(incorporations): address review feedback on forking plans Three fixes from the review on #50: - Plan 6 DoD: add apps/ui/src/types/measurement.ts to the field-addition checklist alongside JSON_SCHEMA.md and EXPECTED_TOP_LEVEL_KEYS. CLAUDE.md tripwire #3 — Python emits camelCase JSON directly, so a Python-side addition without the TS counterpart disappears silently from the UI. - Plan 5 DoD: add a testable item enforcing the "preview is approximate, not a Phase 1 measurement" guardrail that previously lived only in the Risks prose. - Plan 5 prose: disambiguate Phase 3 audition (shipped, #45) from Phase 3 synth-patch generation (open, patchSmith.ts) and fix the broken link that pointed at README.md instead of the PR / design doc. https://claude.ai/code/session_017Pt2pELLM2qNQxoZrWQt4M --------- Co-authored-by: Claude <noreply@anthropic.com>
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> * 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>
This was referenced May 29, 2026
slittycode
added a commit
that referenced
this pull request
May 31, 2026
Follow-up to the 2026-05-30 review fixes (PR #125). The resurrection guards and the #12 cache path already had regressions, but the two terminalizers this branch ADDED — #7 (_execute_measurement_run source-artifact guard) and #3/#6 (_execute_interpretation_attempt setup wrapper) — rode on the pre-existing MT3 sibling test (test_source_artifact_failure_terminalizes_attempt) with no regression of their own. A reverted guard would have stayed green. Add StageSetupFailureTerminalizationTests, one test per stage, mirroring the MT3 sibling: reserve the stage, make source/grounding resolution raise, and assert the stage terminalizes to 'failed' with the expected error code instead of wedging in 'running'. Verified meaningful — both error out when the terminalizer's except is neutered. Backend 1071 green (+2). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
May 31, 2026
Follow-up to the 2026-05-30 review fixes (PR #125). The resurrection guards and the #12 cache path already had regressions, but the two terminalizers this branch ADDED — #7 (_execute_measurement_run source-artifact guard) and #3/#6 (_execute_interpretation_attempt setup wrapper) — rode on the pre-existing MT3 sibling test (test_source_artifact_failure_terminalizes_attempt) with no regression of their own. A reverted guard would have stayed green. Add StageSetupFailureTerminalizationTests, one test per stage, mirroring the MT3 sibling: reserve the stage, make source/grounding resolution raise, and assert the stage terminalizes to 'failed' with the expected error code instead of wedging in 'running'. Verified meaningful — both error out when the terminalizer's except is neutered. Backend 1071 green (+2). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
May 31, 2026
…ift, error boundary (#125) * fix: staged-run lifecycle reliability + chain-of-custody/contract fixes From the 2026-05-30 full review (audits/full-review-2026-05-30.md). Staged-run lifecycle (P1/P2 reliability cluster): - Register pitch-note + MT3 subprocesses for interruption so interrupt/delete actually terminate them instead of orphaning multi-GB children up to their 600s/1800s timeout; route both through the proven _run_streamed_subprocess. - Terminalize interpretation and measurement attempts on setup failure (were left 'running' forever with no reaper). - Guard every attempt/measurement transition against resurrecting an interrupted row, and gate the measurement follow-up enqueue on the guarded update so a cancelled run can't spawn a fresh pipeline. - Frontend monitorAnalysisRun: overall wall-clock budget + per-poll deadline (was an unbounded poll loop with no per-request timeout). Chain-of-custody + quick wins: - Phase 1 parser now carries reverbDetail.perBandRt60 / preDelayMs and vocalDetail.stemEnergyRatio / stemOtherCorrelation -- prompt-citable fields it silently dropped, which broke the citation-existence check. - normalizeKey no longer rewrites "major" into "majoror" (false key contradiction on a valid "Maj" abbreviation). - Define _FILE_CACHE_LOCK / _FILE_CACHE_TTL_SECONDS and import timedelta -- latent NameError in the (currently unwired) temp-file cache path. - Add a React ErrorBoundary around the lazy AnalysisResults Suspense so one render or chunk-load failure can't blank the whole app. Tests: backend resurrection + temp-file-cache regressions (+ caught a second undefined name, timedelta); frontend parser/validator/citation contract, normalizeKey, and polling-timeout regressions. Backend 1068 green, frontend 713 green, lint + production build clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: address review follow-ups + ErrorBoundary fallback smoke test Bundles the P3/LOW findings from the sequential review of e3e0f69 (verdict: ship-as-is) plus the ErrorBoundary fallback-render coverage. 1. Backend orphan guard (analysis_runtime.py): _enqueue_requested_followups now re-asserts measurement.status == 'completed' before creating follow-up rows. Closes the cross-transaction window where an interrupt committing between complete_measurement's guarded update and the (separate) enqueue transaction would leave inert 'queued' rows on a cancelled run. 2. Faithful MT3 mock-timeout (server.py): the mock branch of _run_streamed_subprocess now coerces bytes streams to text via _stream_text, matching the real branch's text=True contract — so a TimeoutExpired carrying bytes reaches the timeout-specific branch instead of a str-in-bytes TypeError that the broad except masked. Test tightened to assert the timeout message. 3. Bound the mid-loop interpretation POST (analyzer.ts): extracted the race-with-neutralized-fetch logic into an exported raceWithDeadline<T> helper and wrapped the stem-summary createInterpretationAttempt POST in it, closing the last unbounded await in the poll loop. 4. ErrorBoundary docstring: corrected the "Try again" claim — React.lazy caches a rejected import, so it recovers a render-throw but not a chunk-load failure ("Reload page" does). No behavior change. 5. ErrorBoundary fallback smoke test (tests/smoke/error-boundary.spec.ts): drives a mocked run to the results view, aborts the lazy AnalysisResults chunk, and asserts the fallback renders (alert + Try again/Reload). Verified meaningful: it fails when the boundary is removed. Updates the unit-test caveat comment. Tests: backend 1069 green (+1 orphan-race regression), frontend 716 green (+3 raceWithDeadline) + lint clean + the new smoke spec passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: lock the measurement + interpretation setup-failure terminalizers Follow-up to the 2026-05-30 review fixes (PR #125). The resurrection guards and the #12 cache path already had regressions, but the two terminalizers this branch ADDED — #7 (_execute_measurement_run source-artifact guard) and #3/#6 (_execute_interpretation_attempt setup wrapper) — rode on the pre-existing MT3 sibling test (test_source_artifact_failure_terminalizes_attempt) with no regression of their own. A reverted guard would have stayed green. Add StageSetupFailureTerminalizationTests, one test per stage, mirroring the MT3 sibling: reserve the stage, make source/grounding resolution raise, and assert the stage terminalizes to 'failed' with the expected error code instead of wedging in 'running'. Verified meaningful — both error out when the terminalizer's except is neutered. Backend 1071 green (+2). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jun 11, 2026
slittycode
pushed a commit
that referenced
this pull request
Jun 17, 2026
slittycode
added a commit
that referenced
this pull request
Jun 17, 2026
…d drops
* feat(backend): phase2-export.v1 — one-call handoff envelope for asa-ableton + the rec-proof harness
The ASA -> asa-ableton boundary (and the GOAL.md fixture loop) ran on
hand-extracted interpretation JSON: the result lives embedded in the run
snapshot, the warn-and-keep validationWarnings in a different subtree
(attempt diagnostics), and provenance in a third. asa-ableton's Gate
alpha fixture and every per-fixture phase2.json in the owner plan were
produced by snapshot surgery — an unversioned, fragile handoff.
1. phase2_export.py + GET /api/analysis-runs/{run_id}/export/phase2:
single self-contained phase2-export.v1 envelope — the stored
producer_summary interpretation result verbatim (incl. the frozen
recommendations.v1 projection), the authoritative Phase 1 payload its
citations resolve against (invariant #2 verifiable offline), the full
validationWarnings trail, and provenance. Thin lookup-and-serve,
csv_export.py pattern; 404 RUN_NOT_FOUND / PHASE2_EXPORT_NOT_AVAILABLE.
Derived and read-only — exports stored state, rewrites nothing.
2. recommendation_evaluation.coerce_phase2_payload: --phase2 (and a
fixture-dir phase2.json) now accepts either a bare Phase2Result or
the export envelope, so the downloaded file feeds the scorer as-is.
3. docs/ASA_ABLETON_BOUNDARY.md: the cross-repo contract — file-coupled
not code-coupled, consumer guidance (prefer recommendations.v1
entries; dedupe is a consumer concern — Gate alpha's 60.7%-vs-50%
skip-rate accounting; flagged != invalid), and the v1 freeze policy.
Tests: +15 (10 builder incl. envelope-key freeze, 3 route, 2 unwrap).
tests.test_server 227 OK; adjacent suites 110 OK; full discover matches
the unmodified-tree baseline exactly (env-only matplotlib/torch gaps).
https://claude.ai/code/session_01YB5QFBLL4nbqPSdtcnxrLy
* feat(fixtures): re-author techno/dnb ground-truth fixtures as hard techno 145 + UKG 2-step 132
Owner confirmed genre fit for house/melodic-techno/acid but swapped the other
two recommendation-corpus fixtures to genres they actually produce:
- techno_rumble_130 -> hard_techno_rumble_145: distorted sine kick (Drum Buss
on the kick chain, Boom 45 Hz), rumble reverb return + held sub, 145 BPM.
Stays the pilot fixture (smallest spec, 9 device entries).
- dnb_reese_174 -> ukg_2step_shuffle_132: 2-step shuffle at 132 BPM, G minor.
Swing lives in the committed MIDI (57% 16ths), asserted via
grooveDetail.hihatSwing / perDrumSwing.snare / bassDetail.grooveType.
Retires the fixture whose proxy fingerprint misread BPM half-time (174->116).
Both new fixtures ship spec-only (phase1Fingerprint: null, no fingerprint
file) — the distrusted _synthetic proxy fingerprints are retired with the old
slugs, and load_fixture tolerates the missing file (citation checks SKIP until
the real Ableton render lands). truePeak intents now use dBTP per ADR 0002
(old manifests carried stale linear targets). New MIDI clips bake correct
tempo metas and were byte-verified (the retired dnb melody encoded ~116 BPM,
not 174). ukg also ships audio_drums.mid so the shuffle answer key is
reproducible without hand-programmed groove.
Docs updated: NEEDS.md (genre confirmation resolved, inventory + build queue),
plans/owner-actions-recommendation-proof-plan.md (pilot + render order),
RECOMMENDATION_VERDICT.md (corpus-composition note; old per-fixture proxy
numbers not comparable).
Verified: evaluate_recommendations.py --fixture <both> --source baseline
(catalog-valid, zero issues), --self-test PASS, 34/34
tests.test_recommendation_evaluation.
https://claude.ai/code/session_01YB5QFBLL4nbqPSdtcnxrLy
* docs: demote abletonDevices.ts to research-only baseline (NEEDS-WIRING decision)
Owner resolved the wire-or-demote choice from NEEDS.md's dead-code finding:
the deterministic recommendation engine stays as the scored free baseline in
the GOAL.md sub-goal 3 three-source comparison but is NOT to be wired into
the product. Score-driven product improvements land on the Phase 2 provider
path instead.
Recorded in: the module header (abletonDevices.ts), the eval bridge header,
NEEDS.md (decision + the now-moot citation-emit candidate), BACKLOG.md,
RECOMMENDATION_VERDICT.md, and CLAUDE.md's Backport Candidates line.
No code changes — comments and docs only. Verified: npm run lint green,
evaluate_recommendations.py --self-test PASS.
https://claude.ai/code/session_01YB5QFBLL4nbqPSdtcnxrLy
* feat(eval): add claude source to the recommendation harness + generation script
--source claude in evaluate_recommendations.py scores a stored Phase2Result
from the Claude CLI provider (sibling phase2.claude.json, same ingestion as
the gemini source). scripts/gen_claude_phase2.py produces those files at zero
Gemini cost: it runs each fixture's stored Phase 1 fingerprint through the
exact server path (server._run_interpretation_request) with
ASA_PHASE2_PROVIDER=claude, so output flows the identical parse/citation/
catalogue/recommendations.v1 validation tail as Gemini.
Research-only (mirrors the evaluate_* convention); deleting both restores the
product exactly. Verified: --self-test PASS; --source claude SKIPs cleanly
when no phase2.claude.json exists.
https://claude.ai/code/session_01YB5QFBLL4nbqPSdtcnxrLy
* feat(eval): Claude-provider Phase 2 evidence for acid + house proxy fixtures
Generated via scripts/gen_claude_phase2.py through the exact server path
(ASA_PHASE2_PROVIDER=claude, model sonnet, MAX_THINKING_TOKENS=0): both fully
cited (custody penalty 1.000), zero validation warnings, 31 recommendations.v1
envelope entries each.
Scored with --source claude against the same proxy fingerprints the recorded
Gemini numbers used: acid 0.485 (Gemini 0.172), house 0.424 (Gemini 0.343).
melodic_techno_arp_124 evidence + the verdict write-up follow.
https://claude.ai/code/session_01YB5QFBLL4nbqPSdtcnxrLy
* feat(eval): melodic_techno Claude evidence + provider head-to-head verdict
Completes the zero-Gemini-cost Claude scoring on the proxy corpus:
- melodic_techno_arp_124 phase2.claude.json: 13 rec cards, fully cited, zero
warnings, aggregate 0.424 — resolving the "Gemini 0 recs" outlier as
Gemini-side (the fixture's fingerprint is interpretable).
- RECOMMENDATION_VERDICT.md: dated head-to-head section. Claude (sonnet,
text-only) 0.485/0.424/0.424 vs recorded Gemini 0.172/0.343/0.000 on
identical fingerprints; mean 0.444 vs 0.172 on the shared subset. Caveats
(proxy corpus, modality asymmetry, model class) stated inline.
- NEEDS.md sub-goal 3: status updated; source list now includes claude.
- docs/PHASE2_PROVIDER.md: 2026-06-11 addendum — scoring result + operating
notes (MAX_THINKING_TOKENS=0 for headless calls, model pinning, 300-365s
measured latency, session-limit failure mode).
- CLAUDE.md: one-line MAX_THINKING_TOKENS note on ASA_CLAUDE_TIMEOUT_SECONDS.
- gen_claude_phase2.py: defaults MAX_THINKING_TOKENS=0 and an 1800s timeout
(overridable) so the next run doesn't rediscover the thinking-budget trap.
Verified: --self-test PASS; full-corpus --source claude scores 3, SKIPs the
2 spec-only fixtures cleanly.
https://claude.ai/code/session_01YB5QFBLL4nbqPSdtcnxrLy
* test(ui): cross-boundary citation contract guards parser field drops
The Phase 1 contract spans analyze.py output, JSON_SCHEMA.md, and types.ts
with no generated source of truth (tripwire #3). Most of parsePhase1Result
passes detail blocks through verbatim, but ~12 parseOptional* reconstructors
rebuild field-by-field — and a backend field one of them forgets to forward
is silently dropped, breaking valid Phase 2 citations to it. This is the
exact mechanism that dropped reverbDetail.preDelayMs / perBandRt60.* and the
vocalDetail stem proxies.
Add tests/services/phase1CitationContract.test.ts: feed a comprehensive
payload through the real parsePhase1Result, run the real citation walker
(collectPhase1FieldPaths) on the result, and assert every citable path
survives. Each field is populated non-null, so a path is present only if the
reconstructor carried it through — drop a field in any parseOptional* and its
assertion fails by name. Verified teeth: re-injecting the preDelayMs drop
fails exactly that one assertion and no other.
Also point tripwire #3 at the new guard.
Verified: 59 contract assertions green; full tests/services suite 809 green;
npm run lint clean.
https://claude.ai/code/session_01YB5QFBLL4nbqPSdtcnxrLy
* ci: trigger CI run for PR #163
---------
Co-authored-by: Claude <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.
@