# Neon UI Mixer Control Precision Proof

- status: `ui_mixer_control_ready`
- target: `https://riddlenode.com/neon-lab/games/drum-sequencer?song=monkberry-moon-delight-tab&mix=profile&view=trainer&instrument=guitar`
- public app bundle: `https://riddlenode.com/neon-lab/`
- proof lane: `npm run proof:sequencer:riddlenode-mixer-control`
- track: `guitar`
- candidate movement: `0.53 -> 0.50`
- restored level: `0.53`
- proof API edit helper used: `false`
- findings: `0`

## Good Catch Card

Plain-English title:
Riddle caught the real slider rounding away a proof-backed mix level.

One-sentence summary:
A durable guitar candidate set the mix contract to `0.53`, but the actual browser range input could only step by `0.05` and exposed `0.55` on the UI path.

What went wrong:
The existing proof API helper could apply subtle candidate levels, but the real mixer slider was coarser than the ratchet's `0.02` and `0.03` level moves.

What Riddle caught:
The new UI-only proof moved the actual guitar slider without calling `setMixerLevelForProof`, then found the UI path could not restore the `0.53` durable level exactly until the slider step changed to `0.01`.

What changed:
LilArcade added stable proof selectors for mixer controls, added `proof:sequencer:riddlenode-mixer-control`, and changed the mixer level sliders from `step="0.05"` to `step="0.01"`.

What this does not prove:
It does not prove guitar `0.50` or `0.53` sounds better. It proves the real UI slider can represent, move, render, and restore the proof-backed level under deterministic guardrails.

## Why Normal Testing Missed It

The app contract and proof API helper could already set mixer levels directly, so app-contract and candidate-loop proofs could pass while the real range input remained too coarse for subtle ratchet candidates. The issue only appeared when the proof forced the browser to use the same UI control a person would use.

## Before-Fix Evidence

| Field | Value |
| --- | --- |
| Durable guitar level | `0.53` |
| HTML range step | `0.05` |
| Observed input level | `0.55` |
| Finding | `restore_level_mismatch` |
| Reason | The browser range input rounded the durable level to the nearest `0.05` step. |

## Fixed UI-Control Result

| Field | Value |
| --- | --- |
| Status | `ui_mixer_control_ready` |
| Proof API edit helper used | `false` |
| Contract before | `0.53` |
| Contract after | `0.50` |
| Level delta | `-0.03` |
| Restore level | `0.53` |
| Restored level | `0.53` |
| Clipping | `false` |
| Low level | `false` |
| Peak | `0.4347` |
| RMS | `0.0635` |
| Headroom | `7.24 dB` |

## Artifact Links

- UI mixer control summary: `ui-mixer-control-summary.md`
- UI mixer control summary data: `ui-mixer-control-summary.json`
- UI mixer control generated profile: `ui-mixer-control-generated-profile.json`
- UI control screenshot after movement: `ui-mixer-control-after.png`
- Final UI control screenshot: `ui-mixer-control-final.png`

## Validation

- `npm run test:neon`: passed, `105` tests.
- `npm run build`: passed.
- `npm run publish:neon-preview`: passed and published the full `/neon-lab/` bundle.
- `npm run proof:sequencer:riddlenode-mixer-control`: passed with `proofApiEditUsed=false`, guitar `0.53 -> 0.50 -> 0.53`, and `0` findings.
- `npm run proof:sequencer:riddlenode-current-target`: passed with `2` active durable overrides and `0` findings.
- GitHub CI for LilArcade PR #534 at `d608e1c`: passed.

## Boundary

This proves deterministic real-UI control behavior and audio guardrails. It does not prove subjective mix quality, listener preference, or that any guitar level is the best musical choice.
