Audit overhaul: chain-of-custody citations, recommendations-first IA, applied tracker, grammar post-process - #41
Conversation
…rammar 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>
slittycode
left a comment
There was a problem hiding this comment.
Verdict: APPROVE
Summary
Large UI overhaul (31 files, +3511/−403) implementing the design audit's full prescription: structured chain-of-custody citations on every card, recommendations-first IA, applied-recommendations tracker, idle value-prop panel, progress card honesty fixes, and a server-side Phase 2 grammar post-process. Phase boundary is clean throughout. Test coverage is exemplary — every new service gets its own test file. The PR description's stated counts (540 frontend / 463 backend passing) are consistent with the test code I read.
Findings
Should fix
server_phase2.py, _fix_grammar_in_record (final return):
return record if updated or True else record # always return; updated flag unusedor True makes this permanently truthy — the else record branch is dead and will confuse the next reader. Both branches return the same value so it's not a functional bug, but return record is what this should say. The comment already admits the flag is unused; finish the cleanup.
Worth considering
analysisResultsViewModel.ts, SONIC_ELEMENT_FIELD_PATHS: unknown Phase 2 element keys silently fall back to the harmonicContent citation set (['key', 'spectralBalance.mids', 'keyConfidence']). Wrong citations are worse than no citations — if Phase 2 ever emits a new key like vocalMelody, it will get harmonicContent's fields cited to the producer with no warning. A console.warn on the fallback path would make this loud enough to catch before it ships in a real run. Low-urgency.
Known limitations acknowledged (not flagging)
_to_gerundproduces wrong gerunds for consonant-doubling verbs ("controling","submiting"). Documented in both the PR description and the test file comment. The hand-mapped exception table is the right fix when it bites.- Regex only matches lowercase
by—"By recreates…"at sentence start is not rewritten. The PR notes role/reason fields don't open with"By"in practice. Acceptable. _PHASE2_BY_NOUN_DENYLISThas"tracks"twice; Pythonfrozensetdeduplicates silently.
Test results
Could not execute locally (no vitest or Python venv in this sandbox). Test code I read is meaningful: screenshot corpus cases, denylist edge cases, null/empty guards, multi-rewrite strings, and the progress-card state machine (the primary fix for the "Analysis complete." lie at 100% while a stage is FAILED). Not coverage padding.
Backend 463/463 (PR-reported). Frontend 540/540, net +118 (PR-reported).
Phase boundary check
Clean.
_apply_phase2_grammar_fixes touches exactly: abletonRecommendations[].{reason, advancedTip}, mixAndMasterChain[].reason, secretSauce.workflowSteps[].{measurementJustification, instruction} — all Phase 2 output fields. Phase 1 data is never read, assigned, or re-derived by this function.
phase1Fields on view-model objects are string[] path references, not values. pickPhase1Value / formatCitedValue are pure read-only. CONFIDENCE_PAIRS export from phase2Validator.ts is a reference to an existing object, not a schema change.
Phase 1 contract unchanged. No diff to analyze.py, JSON_SCHEMA.md, or src/types/measurement.ts. The BPM display change in MeasurementDashboard.tsx (formatNumber(phase1.bpm, 1) → Math.round(phase1.bpm)) is display-only; phase1.bpm is untouched.
Generated by Claude Code
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>
End-to-end implementation of the design audit's prescription. 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 on a single section.
What lands
Chain of custody (audit findings #2 + #3)
CitationBlockprimitive renders aGROUNDED INblock above every Mix Chain / Patches / Sonic Element card. Rows are humanized via a newuserLabels.tsmap (~50 curated entries +humanizeFieldPathfallback so no raw camelCase ever reaches the producer).phase1Picker.tsservice walks dotted paths, formats values with unit-aware rules (bpm→ "157 BPM",spectralBalance.highs→ "+1.1 dB",rt60→ "2.04s", confidence fields → percent, etc.), and computes the worst-confidence band across cited paths.ConfidenceBandBadgefour-band vocabulary (Solid scaffold / Workable draft / Rough sketch / Unreliable) now appears top-right of every card with citations whose fields have a*Confidencesibling inCONFIDENCE_PAIRS.CONFIDENCE_PAIRSexported fromphase2Validator.tsso the validator and citation block share one source of truth.GroundingBadgeListat the Track Layout site;segmentIndexesride through a newextraRowsprop so arrangement-segment citations land inside the same block as measurement citations.Recommendations-first IA (audit finding #1)
MeasurementDashboardmoved from the top of the results scroll to the bottom, wrapped in a singlesection-measurementsanchor.StickyNav's 9section-meas-*pills collapse into one trailingMeasurementsentry. The 9 sub-sections inside the dashboard keep their ids and remain individually scrollable via direct hash links.Applied-recommendations tracker (audit findings #14 + #15)
appliedRecommendations.tsservice maintains a per-file applied-id set in localStorage, keyed by audio content SHA256 (so renaming the file doesn't reset progress).N of M applied(suppressed when zero, so first views aren't littered).Idle value-prop panel (audit finding #5)
IdleValuePropPanelreplaces the 200pxNO SIGNAL DETECTEDatmospheric canvas with a producer-readable explanation: "Drop a reference, get a measurement-cited rebuild plan for Live 12." + flow illustration + 3 honest pacing bullets (local measurement ~30s, AI interpretation typically 4–5 min, native devices only).Header polish (audit #7, #9, #11)
Local DSP Engine v1.6.0eyebrow removed — was the 5th competing identity signal and wrapped to 3 lines at 375px on mobile.Engineering vocab cleanup (audit #8 + N3/N4/N5/N8)
userLabels.tsmap (used everywhere a field path could reach a user).JSON_DATA→Download data,REPORT_MD→Download report.FAMILY: NATIVEchip dropped from meta-badge rows (almost-always-the-same noise).workflowStageprettified at the view-model layer:SOUND_DESIGN→Sound design,MIX→Mix.Developer kill-switch is off…→AI interpretation isn't configured. Set GEMINI_API_KEY on the backend…; status badgeINTERPRETATION CONFIG OFF→NOT CONFIGURED,INTERPRETATION USER OFF→OFF.Patches group structure (audit follow-up)
PatchGroupViewModel+buildPatchGroupsmirrors Mix Chain'sinferProcessingGroupheuristic. Producers see Drum / Bass / Synth / Mid / High-end / Master eyebrows instead of a flat 2-col grid.Input Source collapse (audit N9)
↺ Analyze new file+Adjust settings). Override resets at the start of each new analysis for predictability.Progress panel primary readout (audit #6)
text-[9px] text-secondary/50footnote to the visual focus of the progress card.progress.tone: running / success / failed) so a failed 100% renders red instead of accent-orange (matches the FAILED stage badge below).Phase 2 failure mode (audit N1)
analysisRun.stages.interpretation.statusinstead of the hardcodedPHASE COMPLETE. No more "PHASE COMPLETE" while INTERPRET is still RUNNING or FAILED.StickyNavPhase 2 pills (Sonic / Mix Chain / Patches) render disabled with a hover-reason ("Recommendations not produced this run") instead of silently dropping.error.retryable !== false; non-retryable failures (e.g.,GEMINI_NOT_CONFIGURED) surface the error code inline with the full message in the tooltip.Misc audit follow-ups
Device Chain→Sectionsin StickyNav (N10, less ambiguous with Ableton's own "device chain" meaning).<AudioWaveform />Lucide icon for BASS PROCESSING group eyebrow (#2 #13).Phase 2 grammar post-process
The prompt instruction added in the prior round didn't take — Gemini still emitted 3rd-person singular forms after "by" in role/reason text ("by recreates", "by absorbs", "by shapes"). New server-side
_apply_phase2_grammar_fixesrewrites these to gerunds in-place on:mixAndMasterChain[].reasonabletonRecommendations[].{reason, advancedTip}secretSauce.workflowSteps[].{instruction, measurementJustification}Conservative regex (
\bby \w{4,}s\b) + denylist guards against plural-noun false positives ("by samples", "by beats", "by buses" all stay intact).Test coverage
userLabels,phase1Picker,citationBlock,appliedRecommendations,formatTrackDuration,interpretationSubtitle,workflowStagePrettifier,analysisStatusProgress,idleValuePropPanel,phase2NavReason) plus DOM-level integration tests inanalysisResultsUi.test.ts.test_phase2_grammar_fix.pycovering_to_gerund,_fix_by_gerund_in_text, and_apply_phase2_grammar_fixesagainst the actual screenshot corpus. Full ASA backend suite 463 of 463 pass. One pre-existing unrelated failure intests.test_url_ingest.MimeTypePickerTests.test_octet_stream_triggers_filename_fallback(system MIME registry returnsaudio/x-flacinstead of expectedaudio/flac) predates this branch.npm run buildclean.AnalysisResultschunk 247.95 KB / 60.36 KB gzip.Visual verification
Playwright capture pass against a real 126s track (
apps/backend/.runtime/artifacts/0a1e46db-…mp3). Phase 2 returned in 220s on the run. Screenshots in/tmp/asa-shots-audit-final/confirm every audit surface:GROUNDED INblock visible on every card.SOLID SCAFFOLD · 99%on Acid Bass;WORKABLE DRAFT · 78%on Supersaw Synth).2 OF 8 APPLIEDchip after clicking two applied checkboxes; localStorage round-trip survived a page reload.The grammar post-process is unit-tested but not yet visually re-verified — the next Phase 2 run against the same fixture will show "by recreating" / "by shaping" / "by absorbing" instead of the 3rd-person singular forms.
Known limitations
by controls. If observed in real Phase 2 output, drop a hand-mapped exception into the module rather than implement orthographic stress detection.tests.test_url_ingest.MimeTypePickerTests.test_octet_stream_triggers_filename_fallback) is unaffected by this PR —audio/x-flacvsaudio/flacMIME-registry mismatch from an earlier merged change.Files
31 files changed, +3,511 / −403.
🤖 Generated with Claude Code