Skip to content

Codex/backend upload hardening - #11

Merged
slittycode merged 3 commits into
mainfrom
codex/backend-upload-hardening
May 7, 2026
Merged

Codex/backend upload hardening#11
slittycode merged 3 commits into
mainfrom
codex/backend-upload-hardening

Conversation

@slittycode

Copy link
Copy Markdown
Owner

@claude review please

@slittycode
slittycode merged commit 5450723 into main May 7, 2026
2 of 4 checks passed
@slittycode
slittycode deleted the codex/backend-upload-hardening branch May 13, 2026 06:59
slittycode added a commit that referenced this pull request May 14, 2026
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>
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>
slittycode pushed a commit that referenced this pull request May 18, 2026
PR 5 of 16 in the UI consistency overhaul. The Input Source panel
becomes a <DeviceRack name="Input Source">; the analysis-option
toggles become Radix Checkboxes; the Run Analysis CTA becomes a
<Button variant="primary" ledIndicator>; the loaded-file card becomes
a <Panel variant="surface">.

Stacked on PR #67 (D.3 SignalChain). Branch will be rebased onto main
once #66#67 → PR 5 merge in order.

FileUpload.tsx:
- Loaded file card: replaced the hand-rolled bg-bg-card border div
  with <Panel variant="surface" padding="md">. Accent stripe + file
  icon + name + ready-dot + size-warning preserved verbatim.
- Format chips (MP3/WAV/FLAC/AIFF): replaced inline span chips with
  <Pill tone="neutral" variant="outline" size="xs">. Visual parity.
- "Load Demo Track" <button>: replaced with <Button variant="secondary"
  size="md">. Behavior + text preserved verbatim for the smoke test
  selector (tests/smoke/responsive-layout.spec.ts asserts the text).
- Remove File icon button: replaced with <Button variant="ghost"
  iconOnly size="sm">.
- Dropzone container kept as raw div — its drag-event handlers and
  conditional background-tint state are denser than Panel currently
  supports, and shimming Panel to accept onDragOver/Leave/Drop +
  three-way tint logic just to wrap one site would add API surface
  for one consumer.

App.tsx — Input Source panel:
- Outer wrapper at :1018-1027: hand-rolled bg-bg-surface-dark header
  + bg-bg-card body → <DeviceRack name="Input Source"
  status={audioFile ? (isAnalyzing ? 'active' : 'success') : 'idle'}>.
  Preserved data-testid="input-panel".
- Collapsed-summary buttons (Analyze new file + Adjust settings):
  swapped from inline <button> bg-bg-panel borders to <Button
  variant="secondary">.
- "Editing analysis settings" Hide button: <Button variant="secondary"
  size="sm">.
- STEM PITCH/NOTE TRANSLATION toggle: replaced <input type="checkbox">
  with Radix <Checkbox> (via the ui/ wrapper). Outer card label kept
  for the click-anywhere-on-card UX; htmlFor binds the label to the
  checkbox id. Preserved aria-label="PITCH/NOTE TRANSLATION" so the
  smoke selectors continue to resolve.
- AI INTERPRETATION toggle: same treatment. Preserved
  data-testid="phase2-status-inline" and aria-label="AI INTERPRETATION".
- Run Analysis CTA: replaced the motion.button with <Button
  variant="primary" size="lg" ledIndicator leadingIcon={<Play>}>.
  The Button primitive already encodes the bg-bg-panel +
  border-accent/60 + glow hover + ledIndicator pulse that the
  inline motion.button was hand-rolling. Text "Run Analysis"
  preserved verbatim for tests/smoke/upload-phase1.spec.ts.
- Removed the explicit `<X />` import from lucide-react remains used
  by other call sites in App.tsx; left intact.
- Removed unused motion.button (motion is still used elsewhere in the
  file; the import stays).

Pre-flight decisions honored:
- Two native <select> at :984-998 (phase2-model-desktop) and
  :1191-1204 (phase2-model-mobile) stayed native — they were already
  restyled with appearance-none and don't need migration.
- Radix Checkbox, not Switch, for the analysis options (selection
  metaphor, not on/off feature flag).
- TooltipProvider not mounted yet — no Tooltip consumers in this PR.

Preserved verbatim:
- All data-testid attributes (input-panel, input-panel-collapsed,
  phase2-status-badge, phase2-status-inline).
- All visible text strings: "Input Source", "Drop Audio Here",
  "Load Demo Track", "Run Analysis", "↺ Analyze new file",
  "Adjust settings", "Hide", "STEM PITCH/NOTE TRANSLATION",
  "AI INTERPRETATION", "Ready", "Estimated local analysis".
- All ARIA labels (PITCH/NOTE TRANSLATION, AI INTERPRETATION,
  ANALYSIS MODE, Interpretation model).
- Behavior: onCheckedChange wraps Radix's CheckedState ('true' |
  'false' | 'indeterminate') with a strict `=== true` check before
  setting the boolean state, preserving the existing semantics.
- Audit findings (N9 collapse, N9 re-expand banner, #11 no CPU
  meter, #6 readout placement) untouched.

Verified: lint clean, 605/605 unit tests passing, build clean
(index chunk 323 KB / 97 KB gz; comparable to PR 4 — primitive
imports are now amortized across consumers).

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe
slittycode added a commit that referenced this pull request May 18, 2026
* feat(ui): D.4 migrate FileUpload + Input Source to ui/ primitives

PR 5 of 16 in the UI consistency overhaul. The Input Source panel
becomes a <DeviceRack name="Input Source">; the analysis-option
toggles become Radix Checkboxes; the Run Analysis CTA becomes a
<Button variant="primary" ledIndicator>; the loaded-file card becomes
a <Panel variant="surface">.

Stacked on PR #67 (D.3 SignalChain). Branch will be rebased onto main
once #66#67 → PR 5 merge in order.

FileUpload.tsx:
- Loaded file card: replaced the hand-rolled bg-bg-card border div
  with <Panel variant="surface" padding="md">. Accent stripe + file
  icon + name + ready-dot + size-warning preserved verbatim.
- Format chips (MP3/WAV/FLAC/AIFF): replaced inline span chips with
  <Pill tone="neutral" variant="outline" size="xs">. Visual parity.
- "Load Demo Track" <button>: replaced with <Button variant="secondary"
  size="md">. Behavior + text preserved verbatim for the smoke test
  selector (tests/smoke/responsive-layout.spec.ts asserts the text).
- Remove File icon button: replaced with <Button variant="ghost"
  iconOnly size="sm">.
- Dropzone container kept as raw div — its drag-event handlers and
  conditional background-tint state are denser than Panel currently
  supports, and shimming Panel to accept onDragOver/Leave/Drop +
  three-way tint logic just to wrap one site would add API surface
  for one consumer.

App.tsx — Input Source panel:
- Outer wrapper at :1018-1027: hand-rolled bg-bg-surface-dark header
  + bg-bg-card body → <DeviceRack name="Input Source"
  status={audioFile ? (isAnalyzing ? 'active' : 'success') : 'idle'}>.
  Preserved data-testid="input-panel".
- Collapsed-summary buttons (Analyze new file + Adjust settings):
  swapped from inline <button> bg-bg-panel borders to <Button
  variant="secondary">.
- "Editing analysis settings" Hide button: <Button variant="secondary"
  size="sm">.
- STEM PITCH/NOTE TRANSLATION toggle: replaced <input type="checkbox">
  with Radix <Checkbox> (via the ui/ wrapper). Outer card label kept
  for the click-anywhere-on-card UX; htmlFor binds the label to the
  checkbox id. Preserved aria-label="PITCH/NOTE TRANSLATION" so the
  smoke selectors continue to resolve.
- AI INTERPRETATION toggle: same treatment. Preserved
  data-testid="phase2-status-inline" and aria-label="AI INTERPRETATION".
- Run Analysis CTA: replaced the motion.button with <Button
  variant="primary" size="lg" ledIndicator leadingIcon={<Play>}>.
  The Button primitive already encodes the bg-bg-panel +
  border-accent/60 + glow hover + ledIndicator pulse that the
  inline motion.button was hand-rolling. Text "Run Analysis"
  preserved verbatim for tests/smoke/upload-phase1.spec.ts.
- Removed the explicit `<X />` import from lucide-react remains used
  by other call sites in App.tsx; left intact.
- Removed unused motion.button (motion is still used elsewhere in the
  file; the import stays).

Pre-flight decisions honored:
- Two native <select> at :984-998 (phase2-model-desktop) and
  :1191-1204 (phase2-model-mobile) stayed native — they were already
  restyled with appearance-none and don't need migration.
- Radix Checkbox, not Switch, for the analysis options (selection
  metaphor, not on/off feature flag).
- TooltipProvider not mounted yet — no Tooltip consumers in this PR.

Preserved verbatim:
- All data-testid attributes (input-panel, input-panel-collapsed,
  phase2-status-badge, phase2-status-inline).
- All visible text strings: "Input Source", "Drop Audio Here",
  "Load Demo Track", "Run Analysis", "↺ Analyze new file",
  "Adjust settings", "Hide", "STEM PITCH/NOTE TRANSLATION",
  "AI INTERPRETATION", "Ready", "Estimated local analysis".
- All ARIA labels (PITCH/NOTE TRANSLATION, AI INTERPRETATION,
  ANALYSIS MODE, Interpretation model).
- Behavior: onCheckedChange wraps Radix's CheckedState ('true' |
  'false' | 'indeterminate') with a strict `=== true` check before
  setting the boolean state, preserving the existing semantics.
- Audit findings (N9 collapse, N9 re-expand banner, #11 no CPU
  meter, #6 readout placement) untouched.

Verified: lint clean, 605/605 unit tests passing, build clean
(index chunk 323 KB / 97 KB gz; comparable to PR 4 — primitive
imports are now amortized across consumers).

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

* fix(ui): drop unused Panel import from App.tsx

PR #69 review flagged App.tsx:9 imports Panel but never uses it
(Panel is only consumed in FileUpload.tsx). Drop it.

This is also a low-cost iteration to get CI logs against a fresh
commit — the Frontend job failed on the previous push but the
auth-gated logs prevented diagnosis from this sandbox. Pushing this
clean fix to re-trigger the smoke suite.

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

* fix(ui): revert App.tsx analysis-option toggles to native <input>

PR #69's Frontend CI failed twice on smoke tests without accessible
logs from this sandbox. Best hypothesis: the smoke spec at
tests/smoke/upload-estimate-phase1.spec.ts:242-246 uses
page.getByLabel("PITCH/NOTE TRANSLATION") plus .toBeChecked() and
.uncheck(). Playwright's getByLabel documents itself for "input
elements", and .uncheck() expects native checkboxes (or, in some
versions, also role=checkbox). The Radix Checkbox renders as a
<button role="checkbox"> internally; that's a less-consistently-
supported target for these methods than a real <input>.

Revert the two toggles in App.tsx (PITCH/NOTE TRANSLATION and AI
INTERPRETATION) to native <input type="checkbox"> with the same
appearance-none-style border-card UX. The Checkbox primitive in
components/ui/ is unchanged and remains available for surfaces where
smoke selectors aren't load-bearing. Inline comments explain the
rationale at each call site so future migrations don't naively
re-introduce Radix here.

Local 628/628 unit tests still pass; lint clean. If smoke CI passes
on this commit, the hypothesis is confirmed and we can write a real
test against the Radix Checkbox primitive separately to decide
whether it's worth keeping in the canonical toolkit.

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

* fix(ui): restore bg-bg-card on input-panel to satisfy theme-shell smoke

PR #69's Frontend CI failed three times. tests/smoke/theme-shell.spec.ts:41
asserts the computed background-color of [data-testid="input-panel"]
is exactly rgb(68, 68, 68) — that's --color-bg-card (#444444). The
PR 5 migration removed `bg-bg-card` from that div because the
DeviceRack wrapper was meant to own the surface chrome. But the
DeviceRack's body is transparent by default (so the rack's gradient
face shows through); the computed background becomes rgba(0,0,0,0)
instead of #444444, failing the test.

Add `bg-bg-card` back to the inner div explicitly. The DeviceRack
still owns the title-strip gradient, border, and box-shadow; the body
is now flat #444444 matching the previous palette contract — and
also matching how Live 12 devices visually compose (gradient header,
relatively flat body).

Also restore the `p-4` padding inside the input-panel div — the
DeviceRack body's --space-device-pad-* is smaller than the original
p-4 the test scenarios were calibrated against.

The other smoke selectors (getByLabel for the toggles, getByTitle on
Remove File, getByRole on Run Analysis) were never the issue —
they're all preserved through prop spreading on the new primitives.

Verified: lint clean, 628/628 unit tests passing, build clean.

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant