Skip to content

S10.7 — Liquid Glass release UX (Now Playing + shell)#61

Merged
ramith merged 42 commits into
mainfrom
sprint/s10-7-liquid-glass
Jul 18, 2026
Merged

S10.7 — Liquid Glass release UX (Now Playing + shell)#61
ramith merged 42 commits into
mainfrom
sprint/s10-7-liquid-glass

Conversation

@ramith

@ramith ramith commented Jul 18, 2026

Copy link
Copy Markdown
Owner

The S10.7 sprint branch: the 8a Liquid Glass design adopted on Now Playing + the global shell, on a permanent token foundation (founder decision: the entire GUI eventually redesigns around these tokens; R1 gates on this sprint + the S10.8 tab sweep).

What's inside (8 founder-accepted milestones + close-out)

  • DesignTokenKit (pure library): RGBA token data, WCAG compositing math, the RT/IC surface resolver, glow-field geometry, peak-hold tracker, layout arithmetic — all headlessly tested (205 tests / 34 suites).
  • .glassPanel(_:in:) — the ONE sanctioned surface painter (system Material for overlays; token'd translucent fills + edge strata for app glass; derived opaque fallbacks under Reduce Transparency / Increase Contrast).
  • The 8a Now Playing restructure: hero band + hero-right analyzer lens, queue-flex + fixed 260pt glass inspector, view-local queue filter, carved sliders (full keyboard/VoiceOver parity).
  • Chrome + footer restyle: glass device pill with live sample-rate readout (numericText), footer scrubber on the shared carved groove, Dynamic-Type clamps on both fixed bands.
  • Art-sampled glows (D8): dominant colors from the current artwork, clamped into token bounds — the R4 audit folds the full clamp-corner/brand lattice, so no album cover can break the contrast budget by construction.
  • The permanent R4 contrast audit — pure WCAG math over Kit data; it caught real design defects at least four times during the sprint (all fixed or encoded as placement rules). swift test reports 0 known issues.
  • Playback fixes found by the founder loop + break-it round: the device-switch/seek wrong-song bug (passthrough epoch guard), end-of-queue never stopping (nil-hook-under-live-epoch), device-death engine/picker divergence, a Stop-vs-config-change zombie-playback race, the Enhanced sample-rate readout, and an MPMediaItemArtwork main-actor crash.
  • Governance: 4 semgrep layer-policy tripwires (hardened at the break-it round), Kit-purity + Dynamic-Type-clamp gate guards, per-PR founder screenshot acceptance recorded in docs/sprints/s10-7-visual-matrix.md (the ledger).

Verification

Every commit passed the local strict gate (semgrep / swiftlint --strict / periphery / clang-tidy / leaks + plant-a-leak) + 205 headless tests. Founder-accepted per milestone via the screenshot loop. Remaining before un-drafting: the founder's final full A–H matrix pass, the Instruments/FPS run, and the ledger's cell debt (expiry 2026-08-15).

Draft: opened for CI coverage + review visibility while the sprint close-out finishes.

🤖 Generated with Claude Code

ramith added 30 commits July 17, 2026 11:11
Design-reference snapshots (docs/design/*/NowPlayingView.swift, from the
S10.7 handoff) are artifacts, never compiled — committing one tripped the
pre-commit SwiftLint pass. Root fix is scope, not bypass: docs/ is declared
out of scope in .swiftlint.yml (excluded), .swiftformat (--exclude), and the
hook's staged-file collection (explicit paths don't honor config excludes).
The founder-approved release design for the Now Playing screen + shell:
8a = 7a Liquid Glass material layer over the 5a layout. INTEGRATION.md maps
it onto this repo's governed UI architecture (AppShell/ChromeBar/
NowPlayingBar/DesignSystem) and carries the 6-PR incremental plan the
S10.7 sprint is built around. NowPlayingView.swift + the HTML files are
visual references only, NOT drop-ins.
…an + rig seeds

Defines the Liquid Glass polish sprint per the founder-locked scope (Now
Playing + shell; trailing 260pt inspector; transport stays in footer;
chrome stays a band). Core architecture from the 2026-07-17 online research
pass (Apple docs/HIG/WWDC25-26 + SDK-verified API inventory): a three-
regime layer policy — system glass only where macOS provides it; app
glass-look surfaces are token-governed content-layer fills + edge
decoration (nothing renders beneath our flow panels, so backdrop-sampling
material/glassEffect would be cited 'ghost glass'); plain content styling
elsewhere. Zero hand-applied glassEffect/Material this sprint, enforced by
a planned strict-gate tripwire. Also updates sprint-plan: S10.3 fully
shipped (#59+#60), S10.5 annotated CUT, S10.7 row + refreshed §Status.

Pending: SME panel (swiftui-pro + macos-design + qa-expert rig redesign) +
architect/the-fool gate; founder decisions D5-D9 open.
…ided rig, R0 Kit

Panel round 1 (swiftui-pro + macos-design + qa-expert) integrated:
- Reframe (founder): this IS the release UX (R1 re-gated on S10.7+S10.8;
  'nothing elementary ships'); entire GUI eventually redesigns around
  Liquid Glass — Glass tokens are that foundation. Incremental delivery
  with founder screenshots at every milestone.
- macos-design: D10 dark-base re-tune decision (8a is tuned vs #0e1013,
  app ships #1E1E1E); mandated light-appearance translation grammar
  (never invert); transport-less hero recomposed (154pt, centered vs
  lens); designed first-launch/empty state; footer non-text 3:1 rules;
  firm D6 (hero-right lens, flex 400-560) + D9 (native segmented).
- swiftui-pro: placement-test inventory corrected (3 existing Material
  overlays legitimately answer 'yes' -> .glassPanel(.overlay) role);
  PR-5 scrolling architecture mandated (no outer ScrollView - queue
  virtualization); CarvedSliderTrack split (geometry vs commit policy)
  with keyboard-parity acceptance; signal-path badge state mapping
  before the widget retires; re-chunk (grid->PR5, scrubber re-plumb->PR6);
  conditional phaseAnimator pattern; @ScaledMetric badges; blur-free
  GlowField construction.
- qa-expert: §7 DECIDED — R0 DesignTokenKit extraction (executable
  target untestable otherwise); four semgrep tripwires (dry-run: 6
  pre-existing sites, migration plan); R3 snapshots REJECTED w/ entry
  criteria; R4 upgraded to permanent contrast unit test (composite
  enumeration); R5 manual Instruments recipe; R6 matrix A-H bound to
  the founder screenshot loop; SlotFitTests; the-fool attack brief §7.4.
… fool PASS-W/-AMENDMENTS

All gate amendments landed (verdicts recorded in the doc header):

Architect: staged token declarations (hostile-Periphery / SongsList
lesson — role charter reserved, code lands with first consumer); single-
source invariant extended to the LEGACY palette (D10's audit must not
audit a hand-mirror; R4 gains window/card/panel x label pairs); 'variable
content' defined so the placement test survives D8 (smooth per-track-
tinted fields stay Regime B; sampled glows clamp into token ranges);
PR 1 split at the code/governance seam (1a Kit+overlay+migrations, 1b
semgrep rules+guards+template); Kit admission charter + purity guard;
role growth pre-authorized for S10.8; footer band keeps "panel"
(AppShell byte-untouched).

The-fool (all claims grep-verified before folding): dry-run inventory
CORRECTED (4 overlay migrations incl. PlaylistDetailView:290, 2 Library
TEMPs, per-site named-color dispositions incl. a new statusError token
for the hand-painted meter red); matrix re-aimed (forced-visible overlay
cells in PR 1a, single-run A/B rule, H at PR 5/6, deferred-cell ledger);
.overlay role tightly specified (Material + call-site shape + native
RT/IC, no strata) + one-off pixel-diff evidence; lens 0dB-label exposure
added to R4 (palette max-luminance backdrop); lens a11y position
REPLACED (exposed frame + accessibilityAction, not sighted-only); hero
height derived-not-fixed with a max-type layout test; pre-mortem
mitigations: PR-2 tuning stopping rule, D10 interim story + base-compat
micro-pass, rule-2 never-suppressed (edit-the-rule governance), legal-
token-recomposition named as the top evasion class.
…r + D8 art-sampled glows IN

Founder resolved every open decision: D5 sample-rate readout YES; D6 8a
hero-right lens; D9 native segmented control; D10 app-wide dark-base
re-tune YES; and OVERRODE the defer recommendations on D7 (queue filter
field -> PR 5, view-local, space-suppressing, jump-clears-filter) and D8
(album-art-sampled glow colors -> new PR 7: dominant-color extraction
clamped into token chroma/alpha ranges, per-track cache, RM-gated
recolor, brand-color fallback; R4 audits the clamp BOUNDS so contrast
holds by construction). Section 9 rewritten as RESOLVED; PR order is now
1a->1b->2..7; sprint-plan S10.7 row updated (SP 6->7).
…ver, .overlay role

The testability spine of the Liquid Glass release UX (design §3.2 R0),
zero visual change:

- Sources/DesignTokenKit (library target, ZERO SwiftUI/AppKit imports):
  RGBAColor + AppearancePair (with defaulted increased-contrast
  variants), sRGB alpha-over compositing + WCAG luminance/contrast math,
  Palette (every app color as data, byte-identical to the former
  DesignSystem literals, + a registry), SlotWidths, and the pure
  resolveSurface(role:appearance:flags:) contract with the one
  Material-backed role: .overlay(.ultraThin/.bar).
- App side: DesignSystemGlass.swift bridges Kit data -> SwiftUI
  (explicit-sRGB, one path) and ships .glassPanel(_:in:) as the
  env->resolver->paint shim; DesignSystem.Color re-exports Kit values
  (single-source invariant — no RGBA literal remains on the app side);
  dynamic() gains the two accessibilityHighContrast appearance names
  with call-site-compatible defaults.
- Migrations (all pixel-identical): the four floating overlays
  (ErrorBanner, QueueToast, EQ recall banner, PlaylistDetail restore
  pill — the .bar substrate preserved) now route through
  .glassPanel(.overlay); the loudness meters' hand-painted Color.red
  becomes the new statusError token (same per-appearance values).
- Rig (design §7): RES resolver cube, TOK palette invariants, SLOT
  fixed-slot fit net, and the PERMANENT R4 contrast audit — whose first
  run caught three genuine PRE-EXISTING shortfalls the S9 audit missed
  (it measured labels against the window, not the card/panel
  composites): labelTertiary dark on panel⊕window = 4.46:1 (pinned,
  resolves with the PR-2 D10 re-tune) and statusError off-window /
  light surfaces 3.0–4.3:1 (pinned, resolves with the PR-6 meters
  restyle). All pinned via withKnownIssue so the fix flips them loud.

swift build green; swift test: 168 tests / 29 suites pass, 6 known
issues (the pinned shortfalls above). Founder acceptance next: matrix
A,B in a single run + the three forced-visible overlay cells +
the one-off before/after pixel diff.
…riphery-clean resolver

Review verdict was approve-with-fixes; all applied:

- BLOCKER-1: statusError encoded the PRE-macOS-26 palette red
  (#FF3B30/#FF453A); macOS 26 refreshed it — Color.red actually resolves
  to #FF383C light / #FF4245 dark (probed three ways: Color.resolve(in:),
  NSColor(Color.red), NSColor.systemRed). Values corrected as exact
  fractions, so the meters swap is now genuinely pixel-identical. R4
  ratios re-verified: hard dark-window assertion 4.85 (green); all five
  pins still pinned (4.28/4.09/3.05/3.57/3.57); known-issue count 6.
- BLOCKER-2: periphery (hostile, strict) flagged AccessibilityFlags'
  never-read fields. Adopted the design doc's literal resolver signature
  (role:appearance:reduceTransparency:increasedContrast:) with the same
  `_ =` staging idiom; struct dropped until the fill roles read flags.
  `periphery scan`: No unused code detected (run this time, stated here).
- MAJOR-1: R4-LEG-03's five shortfalls now pinned PER (appearance,
  surface) pair — a partial fix (e.g. PR-2's dark-composite shift) flips
  its own pair loud instead of hiding in a shared block.
- MINOR-1: SlotFit measures regular weight (the slot-constrained time
  label's real config; semibold is the unconstrained tooltip).
- MINOR-2: LibrarySidebar rename-error caption `.red` -> statusError
  (the inventory miss; pixel-identical post-BLOCKER-1).
- MINOR-3: iconFill gradient stops moved into Palette (registry rows) —
  "no RGBA literal on the app side" now holds file-wide.
- NIT-2: TOK-04 asserts rowSelected appearance-independence too.

swift build green; swift test 168/29 pass w/ 6 known issues; periphery
clean. Founder A/B + overlay-cell screenshots are valid to capture from
THIS commit (evidence against f4ad777 would have caught the red shift).
… guards, matrix template

The rig that makes the PR-1a token architecture self-enforcing (design
§7 R1/R6):

- .semgrep.yml: ui-no-adhoc-material (materials incl. .background(.bar)
  outside the 4 definition files), no-liquid-glass-api (FROZEN — never
  suppressed; adoption edits the rule inside a design review),
  ui-no-color-literal (constructor forms + implicit/explicit palette
  members in paint contexts + shadow colors; Color.clear and
  hierarchical styles deliberately allowed), ui-no-appearance-branching
  (\.colorScheme reads / preferredColorScheme / NSApp.appearance= —
  S9-T now enforced).
- strict-gate.sh: Kit-purity guard (DesignTokenKit must never import
  SwiftUI/AppKit) + fixed-band Dynamic-Type-clamp guard (NowPlayingBar;
  ChromeBar joins in PR 6).
- Dispositions from the pinned dry-run: ChromeBar app-mark glyph
  Color.white -> onAccent (pixel-identical; the token exists for exactly
  this); 4 TEMP suppressions with the house grammar — 2x LibrarySidebar
  .background(.bar) (S10.8 sweep, expiry 2026-08-15), PlaylistDetailView
  dead-file triangle .orange (unify onto statusWarning in S10.8 — a NEW
  dry-run find, same class as the meters red), NowPlayingBar scrubber
  shadow literal (becomes a Glass token in PR 6, expiry 2026-08-01).
- docs/sprints/s10-7-visual-matrix.md: the committed R6 template —
  states A-H w/ toggle instructions, per-PR required cells, the R5
  Instruments recipe, the PR-5 VO/keyboard script, and the ledger with
  PR 1a recorded as ACCEPTED (captured cells + 5 deferred cells under
  TEMP grammar, expiry = sprint end).

Verified: semgrep green over the tree with suppressions accountable
(check-suppressions.sh green); plant-a-violation test fired all three
new nets (glassEffect, .red paint, Kit UI-import) and the tree is clean
after revert; swift build + 168 tests green.
…le milestone

- D10 (founder-locked): Palette.window dark #1E1E1E -> the 8a deep base
  #0e1013, app-wide via the token (every tab inherits; S10.8 sweeps their
  surface tints). Light untouched.
- The R4 flip mechanism fired exactly as designed on the FIRST run
  against the deep base: three dark pins went "unexpectedly passed" and
  were PROMOTED to hard assertions (labelTertiary on panel⊕window — the
  PR-1a find, now fixed by the base itself; statusError on dark
  card/panel). Known issues 6 -> 3 (only the light statusError pins
  remain, scheduled PR 6).
- Ambient glow field (design §3.3): Kit gains the three 8a glow color
  pairs (dark = spec alphas .28/.12/.10; light = grammar-rule-5 thirds),
  GlowFieldSpec geometry + falloff data, and the pure glowFieldIsVisible
  resolver (RES-04: suppressed under RT AND under IC alone). App side:
  GlowField renders three stop-authored radial gradients (no .blur —
  zero filter passes), mounted via .background on NowPlayingTabView,
  hit-transparent + a11y-hidden; other tabs keep the plain deep base
  until S10.8 (D1).
- R4 glow pairs landed and CAUGHT A REAL DESIGN PROBLEM before any
  pixel shipped: labelTertiary on the teal core fails AA at spec alpha
  (3.98:1). Resolution is a PLACEMENT RULE, not an alpha dilution or a
  waiver — the 8a mock itself puts only the hero (large text, 3:1) on
  that core. Model refined to geometry: single cores at max alpha for
  label/secondary, pairwise overlaps at mid-stop attenuation (cores are
  disjoint), tertiary audited everywhere its rule allows (R4-GLOW-01/02/
  04); rule recorded in the design doc §3.3.
- New tests: RES-04 visibility cube, GLOW-01/02 grammar+falloff
  invariants, the three R4-GLOW suites.

Verified: swift build green; 174 tests / 30 suites pass w/ 3 known
issues; semgrep green; periphery clean. Founder milestone next: matrix
cells A,B,C,D,H vs the 8a mock + dark-mode glance at the other four
tabs (the D10 interim story: contrast provably improves — R4 legacy
pairs — cohesion intentionally lags until S10.8).
…y, light suppressed, seams feathered

Review verdict was "not ready for founder round 1"; all findings applied
so the two tuning rounds are spent on taste, not defects:

- BLOCKER 1: the circular RadialGradient clipped by a non-circular
  Ellipse left a hard cyan rim on the minor axis (alpha 0.048 step at
  the boundary). Now a CIRCULAR gradient scaled into the spec ellipse
  (Circle + scaleEffect) — every isoline is the ellipse; clear at every
  edge direction.
- BLOCKER 2: glow centers were guesses; now DERIVED FROM THE MOCK'S CSS
  (teal 0.214/0.067, lime 0.786/0.973, blue 0.634/0.621 — blue is an
  interior midfield glow, was ~430pt off). PR-6 window-space equivalents
  recorded for the shell-mount migration.
- MAJOR 3 (Option B): the mock's falloff is an exact-LINEAR radial ramp
  (its blur only rounds the apex) — profile now 0.45 @ 0.55 via a shared
  falloffFraction(at:) that BOTH the render gradient and the audit read.
  The abstract cores+overlaps audit model is replaced by a GEOMETRIC
  grid audit sampling the real three-glow composite from GlowFieldSpec
  at the reference AND min-window geometries — founder center-tuning
  re-verifies automatically; the impossible worst-cases are gone.
- MAJOR 4: light-mode glows mathematically DARKEN the near-white window
  (teal composite ΔL −7.2% = a stain; no alpha fixes it). The resolver
  is now appearance-aware: glows render in DARK only; light ships
  pixel-identical to today. The grammar thirds stay recorded as the
  S10.8 re-derivation starting point (luminance-positive pastels).
- MAJOR 5: the interim card⊕glow composite (right pane over the lime
  core) fails tertiary AA (4.23) — audited as R4-GLOW-05 with label/
  secondary asserted hard and the one tertiary defect pinned to the
  PR-5 pane restyle.
- MAJOR 6: 24pt top/bottom seam feather (LinearGradient mask, zero
  filter passes) so the flat chrome/footer bands never meet a lit field
  across a 0.5pt hairline (ΔRGB +39 unfeathered). STOPGAP — removed in
  PR 6 when the bands go glass.
- GlowFieldGate lives in DesignSystemGlass.swift — the sanctioned
  appearance reader (our own rule 4 would have fired on the view).

Verified: 176 tests / 30 suites green with 4 known issues (3× statusError
light -> PR 6; 1× card⊕glow tertiary -> PR 5); semgrep green; periphery
clean.
Founder decision (round 1 of 2): glow geometry goes FULLY proportional —
ellipse sizes become fractions of the tab (teal 0.643x0.940, lime
0.679x1.007, blue 0.375x0.638, derived from the mock's 1120x720 card
translated to tab space), reproducing the mock's coverage at ANY window
size. Fixed point-sizes read as three subtle pools on a large window
(~40% width coverage where the mock has ~64%).

The R4 geometric audit re-ran against the larger real overlaps and
stays green: 176 tests / 30 suites, 4 known issues (unchanged set).
…fill role

- Kit: PeakHoldTracker (pure, TIME-FED — no wall clock, so freeze-on-
  pause is structural; hold 0.6s then linear decay 1.25/s; hostile-input
  clamps), GlassDecor (the 8a edge-decoration strata as data: specular
  rim, glass hairline — the FIRST token with real Increase-Contrast
  variants per §3.2 — dark-only bottom bleed, appearance-tuned shadow,
  lens radius 20), Palette.lensFill (8a rgba(16,18,21,.42) dark; white-
  glass light), and the resolver's first FILL path: .lens resolves
  translucent normally, OPAQUE (fill⊕window, derived) under RT — and
  under IC even without RT (RES-02 doctrine).
- App: .glassPanel fill branch composes the strata (fill + bleed under
  content; top-weighted rim + hairline strokes above; soft deep shadow,
  light = half/tighter). SpectrumAnalyzerView: bars + per-cell peak-hold
  caps (2pt, bar color @50%, 4pt above; drawn only once separated from
  the bar; RM-gated animation) inside the lens; heights still sourced
  from spectrumBars; bar field stays 50pt (outer frame 66 = +2x8 lens
  padding). AudioViewModel publishes peakCaps from the SAME 20 Hz tick
  (one @observable invalidation), isPlaying-gated belt-and-braces.
- Tests: PH-01..09 (derived expectations from the config), RES-01/02
  now real (fill role), R4-LENS-01 (lens⊕glow grid dark + light lens +
  RT/IC opaque fallbacks x label/labelSecondary), TOK-03 gains its
  first DELIBERATE opt-in (glassHairline must be STRONGER under IC).
- Matrix ledger: PR 2 recorded as ACCEPTED (glow tokens frozen; C/D/H
  deferred with reasons).

188 tests / 31 suites green (4 known issues, unchanged set); semgrep
green; periphery clean. Founder cells next: A, B, C, G + the Instruments
row (the first perf checkpoint — caps animate over the 20 Hz field).
Founder cells A/B/C/D/G all captured and verified — including the RT
cell showing the resolver suppress the glow field and opacify the lens
LIVE (the headless contract, confirmed on screen). Review finding from
the light cells: the first-pass light shadow (literal half-of-dark,
0.30 @ 18) read as a gray smudge on the light window — tuned to
0.15 @ 12 / y 5 (native light panels sit near 0.15); dark untouched.
Instruments row parked by the founder (ledger TEMP; any track works
when resumed). PR-4's B cell re-verifies the shadow tune.
…ate machine, empty state

- HeroBand replaces the compact NowPlayingWidget card (retired; periphery
  enforces full deletion): heroTitle token (.largeTitle + heavy — Dynamic-
  Type-mapped, fixed 28pt banned) with the 8a teal halo applied by a
  .heroTitle() modifier in the sanctioned appearance-reader file
  (dark-only, grammar rule 6); artist line; and the FULL signal-path
  badge mapping as .glassPanel(.badge) capsules — PURE (accent dot,
  monochrome) / ENHANCED + pulsing dot / rate / bits / decoder(pure) /
  intensity% / XF / "(Pure unavailable)" warning / interrupted. The
  pulse is the mandated conditional-phaseAnimator (unmount = stop);
  badge heights are @ScaledMetric off the 22pt token; track changes
  crossfade + badge-state flips animate, all RM-gated; the empty state
  is designed (quiet placeholder, no halo, badge row space-reserved so
  first play never reflows).
- Kit: badgeFill pair, hero halo/pulse tokens, the .badge fill role
  (same derived RT/IC-opaque contract as .lens), pulseIsActive predicate.
- The audit's THIRD real catch on new work: labelSecondary on
  badge⊕teal-core = 4.26:1 (22 sampled points) — the white badge fill
  brightens the backdrop like the card⊕lime case. Encoded as a rule:
  badge text is the primary label or a status color, never the dimmed
  hierarchy (chips carry hierarchy in the capsule, not dimmed text).
  statusWarning-on-light-badge pinned (pre-existing orange class,
  scheduled with PR-6/S10.8 unification).
- Tests: RES-01/02 now table-driven over both fill roles; PG-01..04
  pulse-gate table; R4-BADGE-01 (badge⊕glow grid + light + RT/IC).

190 tests / 31 suites green (5 known issues, all pinned + scheduled);
semgrep green; periphery clean.
…founder crash 2x today

Both 2026-07-17 crashes (17:32, 19:16 — EXC_BREAKPOINT on MediaPlayer's
"accessQueue" thread) share one signature: closure #1 in
NowPlayingController.push(_:artwork:). The artwork request handler,
defined inside the @mainactor controller, INHERITED MainActor isolation;
MediaPlayer invokes it on its own serial queue when serializing artwork
(jpegDataWithSize:, e.g. a Control Center render after a device switch),
and Swift 6's dynamic isolation check (dispatch_assert_queue) traps.

Fix: an explicit @sendable handler capturing only the immutable image
value — severs the isolation inference, touches no controller state.
Latent since S10.4 (#58); surfaced by today's Bluetooth device-switch
sessions. Cherry-pickable to main independently of S10.7.
…60pt inspector, queue filter

The one structural PR (design §5), founder decisions D2/D6/D7:

- NowPlayingTabView: the 50/50 containerRelativeFrame split dissolves
  into VStack(HeroRow / HStack(queue-flex | InspectorColumn)) over the
  glow field. MANDATED scrolling architecture: no outer ScrollView
  (queue virtualization + jump-to-now-playing live), hero hugs
  intrinsic height, inspector scrolls INSIDE its panel chrome.
  LeftPanelView / RightPanelView / NowPlayingInfoView deleted
  (periphery-verified — contents redistributed, nothing orphaned).
- HeroRow: HeroBand centered against the LENS (D6: flex 400-560 x 122).
  The lens gains its instrument dressing now that the frame exists:
  "0 dB" reference + 20Hz-20kHz axis strips OUTSIDE the bar field (the
  §7-R4 pair-9 placement resolution — labels never overlay near-white
  lime bars), four gridline hairlines behind the bars. The double-
  click→Monitoring affordance moves onto the lens with hover rim
  brighten + keyboard focus/Return + an exposed a11y element carrying
  the named action (the fool's accessibility replacement).
- InspectorColumn (D2): master gain + Reimagine intensity (both now on
  the shared CarvedSlider — 8a carved track/knob visuals in the
  sanctioned appearance file, LIVE-commit interaction with full
  keyboard/VO parity: focusable + arrow-step + adjustable), the
  decoder/bit-depth detail line (relocated from the hero badges),
  loudness meters, crossfeed. Pad-inside-then-frame (the S9 lesson's
  PR-5 form). The .panel fill role lands (8a rgba(30,33,38,.5)).
- Queue filter (D7, founder override): view-local, reuses the S9.6
  FacetTextFilter primitive (title + relative path — AudioFile carries
  no artist; documented delta from §5), transport-Space suppressed
  while focused, Escape clears, jump-to-now-playing clears the filter
  first, No-Matches state, and reorder drag is DISABLED while filtered
  (moving relative to hidden rows is incoherent; menu moves stay).
- Rig: RES table + .panel row; TOK-01 radii chain (panel ≥ lens);
  R4-PANEL-01 (panel⊕glow right-region grid incl. tertiary — its
  designed home) ; R4-GLOW-05 RETIRED by construction (the card fill
  it audited no longer exists; rows-on-glow is R4-GLOW-02's job);
  LAY-01/02 layout-arithmetic suite (the §5 width/height budget as
  assertions, max-type headroom documented).

193 tests / 32 suites green (4 known issues, all light-palette pins
scheduled for PR 6); semgrep green; periphery clean.
…ation, slider drag math

swiftui-pro chunk review (approve-with-fixes) applied in full:

- BLOCKER: Jump-to-Now-Playing under an active filter now SEQUENCES —
  the owner clears the filter this transaction (full list mounts, all
  row ids registered), then bumps the scroll request-ID next main-actor
  turn. The old same-transaction bump scrolled against the filtered
  tree: a filtered-out target no-op'd, No-Matches never fired onChange.
  Header/controls take a callback, not the request-ID binding.
- HeroBand: bits/decoder capsules DELETED (the §5 relocation had added
  the inspector copy without the move); spoken summary keeps them
  deliberately (one VO stop for the whole path).
- CarvedSlider: pointer maps onto the KNOB'S inset travel (knob/2 …
  width−knob/2) so mouse-down at the extremes is value-neutral (live
  gain no longer jumps ~0.6 dB on grab); knob size shared via
  GlassDecor.sliderKnobSize (Kit) so math and visuals cannot drift;
  track fills the 20pt hit target (GR top-aligned it at 14pt); a
  focused slider consumes ←/→ even at the bounds (NSSlider parity).
- Filter Escape: `.onExitCommand` added (the field editor's
  cancelOperation can eat Escape before onKeyPress); Escape now hands
  key focus to the queue — queueFocused lifted to PlaylistView and
  passed as FocusState.Binding, so ↑/↓ work immediately after clear.
- ShellMetrics now FORWARDS NowPlayingLayout (single-source): the
  shell lays out from the same Kit values LAY-01/02 assert against.
- InspectorColumn scroll content clips to the radius-22 panel shape
  (rows no longer render square into the corner cutouts).
- Queue rows: ForEach keyed by stable QueueItem.id again (VisibleRow
  wrapper carries the real index); grip hidden while filtered
  (dragPayload nil — the row API's own non-reorderable state).
- FacetTextFilter: localizedStandardContains — case- AND diacritic-
  insensitive per the §5 contract ("beyonce" matches "Beyoncé");
  benefits all four consumers; diacritic test added.
- Lens: default accessibility action added (VO-press works directly).
  Space deliberately NOT added: the transport's menu key-equivalent
  matches first (same audit that removed the queue's dead handler).
- Hygiene: knob fill is a Kit token (knobFill pair, PR-6 light-mode
  hook); carvedTrack + knobFill join the Palette.all registry; the
  resolver names every fill role explicitly (no default: inheritance);
  stale NowPlayingInfoView MARKs trimmed; INTEGRATION.md 50/50 line
  annotated as superseded.

Gate: 194 tests / 32 suites green (4 known-issue pins, PR-6 scheduled);
strict-gate PASSED (semgrep, swiftlint --strict, periphery, clang-tidy,
leaks + plant-a-leak).
…ification riders

The traversal script now states the Keyboard-navigation-ON prerequisite
(custom .focusable views join Tab only with it on) and the Return-not-
Space lens activation rationale; a rider list ties the bbf36cb review
fixes to the founder round so each fix gets eyeballed, not assumed.
…r row

Founder screenshot-round request: the strength dropdown sat on its own
line under the toggle; it now trails the toggle in a single HStack
(picker joins the row only while crossfeed is on).
…ault+42pt + full keyboard/VO walk)

Founder screenshot round green across the board: C proves the RT→opaque
resolver swap live, E proves the stronger-hairline HC variant, 42pt proves
the badge row scales without clipping, and the keyboard walk confirms
Return-on-lens → Monitoring (the one behaviour code review couldn't verify).
-- kHz-under-Enhanced routed to PR 6 (owns the D5 sample-rate readout).
…-- kHz")

The Enhanced path stored SignalPathInfo without achievedSampleRate, so it
stayed 0 and both the hero badge and footer signal slot showed "-- kHz"
while playing (founder finding, PR-5 round). The Pure path already carried
the rate via makeSignalPathInfo; Enhanced now does the same.

- startEnhancedPlayback captures engine.outputNode.outputFormat rate at
  store time (engine settled + playing on the engine queue — the Pure
  store-time discipline, never a MainActor engine read).
- reestablishEnhancedAfterConfigChange refreshes the one field after an
  engine restart so a switch to a different-rate device can't leave it
  stale (mutate, not store, so fallback/interrupt flags stand).

Enhanced shows the DEVICE output rate (honest per the field doc + the D5
device-pill context): a 48 kHz device reads "48 kHz". The PR-6 device-pill
readout (D5) then surfaces the same value with a numericText transition.
…ht AA pins

Founder decision ("split text vs fill"): the vivid status* tokens become the
FILL/indicator colors (meter hot-bar, status dots — WCAG 3:1 non-text); new
status*Text variants carry the darker AA-legible shade for text/glyph sites
(4.5:1). On the deep dark base the vivid value already clears text AA, so the
text variants differ from the fills only in LIGHT — where vivid orange/red on
near-white was illegible.

- Palette: statusErrorText (#A3000F light) + statusWarningText (#6E4400 light);
  dark = the vivid value unchanged. Both join Palette.all.
- Text/glyph sites → *Text variant: CLIP label, sidebar rename error, the
  Pure-fallback + disconnected warnings (settings/hero/footer/error banner).
- Fills/dots stay vivid: the meter hot-bar and the signal-path dots (the dots
  reinforce adjacent text → decorative, not audited at 3:1).
- ContrastAudit: R4-LEG-03 now asserts statusErrorText clears text AA AND the
  vivid statusError fill clears non-text 3:1 on every surface, both appearances;
  R4-BADGE-01 audits statusWarningText on the light badge. The 4 withKnownIssue
  pins are RETIRED — `swift test` now reports 0 known issues (was 4).

Dark mode is visually unchanged (text variants == vivid in dark); only light
text/glyphs darken. 194 tests / 32 suites green, 0 known issues.
…cText)

- Pill restyled from the flat card + hand-drawn hairline to the .badge glass
  role (the white-8% "small-control fill" — the same recipe the 8a mock's
  device pill uses); Capsule shape, rim + hairline + RT/IC from the resolver.
- D5: the device's live sample rate reads on the pill, trailing the name, with
  a numericText digit transition when it changes (track/device switch). Now
  that Enhanced publishes its rate (b78b3ad), it's populated on both paths.
- Reserved fixed slot (SlotWidths.chromeSampleRate = 66pt, SLOT-02 asserts the
  widest real string "176.4 kHz" fits) shown only while a rate is known, so the
  pill's fixed width — and the tabs' x-origin (the founder's "fixed top-left")
  — never move. Pill widened 200→252 to keep the name legible beside the rate.
- Pill a11y value now includes the rate; the readout Text is a11y-hidden (folded
  into the pill value, not double-spoken).

195 tests / 32 suites green, 0 known issues.
…eral retired

The footer scrubber (the app's only scrubber) now shares the 8a carved surface
with the inspector sliders — deferred-commit seek + hover thumb + tooltip
intact (the design's semantics-preserved requirement), only the VISUAL unified.

- Extracted two shared primitives from PR 5's CarvedTrack (behaviour identical
  by construction — the slider's fill still reaches the knob centre at every
  position): CarvedGroove (token base + inset shade + teal fill w/ dark-only
  glow, parameterised by fill fraction / height / fill color / glow) and
  CarvedKnob (token circle + bottom shade). CarvedTrack now composes them.
- FooterScrubber's hand-drawn track → CarvedGroove; fill follows the play/
  pause/interrupted state color, and the dark-only teal glow shows ONLY while
  actually playing (no teal glow under a grey/dim fill). Thumb → CarvedKnob.
- The NowPlayingBar:303 shadow literal (.black.opacity(0.3), the last PR-1 TEMP
  nosemgrep) is RETIRED by adoption — the carved knob's bottom-shade cue
  replaces the drop shadow; no shadow token needed. One fewer suppression.
- carvedTrackHeight (5pt) added to the Kit (shared by both carved surfaces);
  footer scrubberTrackHeight removed, thumbSize 10→14 to match the carved knob.

Gate: 195 tests / 32 suites green, 0 known issues; strict-gate PASSED
(semgrep/swiftlint/periphery/clang-tidy/leaks+plant).
The chrome is a fixed 60pt band like the footer; the device pill + segmented
tabs grow with text size, so accessibility sizes could overflow it. Add the
.dynamicTypeSize(.small ... .xLarge) clamp and enroll ChromeBar in the
strict-gate clamp guard (design §8 — "ChromeBar joins this list in PR 6").
…comment accuracy

swiftui-pro review verdict was approve/ship (no blocker/major); applied the
three worthwhile non-blocking items:

- MINOR-1: R4-LEG-03 now audits statusWarningText (text AA) on window/card/
  panel too, not just the error variant — closes the "forever-green" hole
  for the warning glyph's non-badge surfaces (banner/footer/settings). Passes
  by math (light ≥7:1). The vivid statusWarning is deliberately NOT asserted
  at 3:1 (it's only the decorative status dot, which reinforces adjacent text).
- MINOR-2: the chrome rate readout gets minimumScaleFactor(0.7) so a 9-char
  hi-res rate ("176.4 kHz") at the clamped .xLarge max shrinks to fit rather
  than tail-truncating away the "kHz" unit (SLOT-02 proves default-size fit).
- NIT: PlaylistDetailView's pre-existing .orange TEMP now points at
  statusWarningText (the AA text variant, not the vivid fill) for the S10.8
  unification.

MINOR-3 (pill re-evals at 20 Hz while playing) left as-is — correctly scoped
to only the pill, cheap leaf re-eval, no visual thrash (review: not blocking).

195 tests / 32 suites / 0 known issues; strict-gate PASSED.
… "48 k…")

Founder screenshot: the footer read "Enhanced · 48 k…" — the 120pt slot was
sized for the "--" placeholder era and truncated the moment the Enhanced path
started publishing a real rate (b78b3ad). The promised-with-that-fix slot
widening had only been delivered for the chrome pill (SLOT-02), not the footer.

- SlotWidths.footerSignalSlot = 150 (Kit), sized for the widest legitimate
  composition: 6pt status dot + 5pt gap + "Enhanced · 176.4 kHz".
- Footer.signalSlotWidth re-exports it (same pattern as the time-label slot).
- SLOT-03 asserts the full dot+text composition fits with margin.
- Footer min-width budget at the 880pt window: 766pt total — still fits.

196 tests / 32 suites / 0 known issues.
…ver rendered

Founder round: four screenshots showed the pill with NO rate while the hero
badge and footer (plain views on the same signalPath property) updated live —
and the digits changed only on a device switch. Cause: macOS does not reliably
re-render a Menu's custom LABEL when observed data changes; the label refreshes
when the menu itself is rebuilt (device switch), which matches every observation.

The readout is now a sibling Text beside the Menu inside the shared glass
capsule (Menu = icon + name + system chevron; then spacer; then the fixed
66pt rate slot). Updates like any view; numericText roll preserved; the Menu
keeps the pill's a11y label/value/hint (value still folds the rate in); the
capsule + fixed 252pt frame moved to the outer HStack — same look, same
tabs-x-origin invariance. Long names compress before the spacer or slot give.

196 tests / 32 suites / 0 known issues; file lint/format clean.
ramith added 9 commits July 18, 2026 15:51
…er rolls to the wrong song

Founder repro: "every time I change the device mid play, sometimes the app
starts playing a different song, and (sometimes) the selected track and the
played track differ."

Root cause: player.stop() FIRES any pending .dataPlayedBack completion —
indistinguishable from a real end-of-track. seekEnhancedBestEffort (both the
device-switch re-establish AND user seek on a 48 kHz passthrough file) stops
the player while the gapless hook is armed, so the stale completion invoked
the seam handler: it consumed the on-deck track and scheduled it against the
current track's re-scheduled remainder — wrong audio, and a transition-count
bump that desyncs the queue selection from what's audible. "Sometimes" = the
race (completion drain order) x whether a next track was armed. A second
latent defect rode along: a stale fire consumed the ONE-SHOT seam hook even
when it aborted, silently killing gapless at the track's real ending.

Fix — the passthrough twin of the resampler's proven generation idiom:
- passthroughGeneration (owned by resampleQueue like all seam state).
- Every passthrough schedule (playFile initial, seam chain, best-effort
  segment) captures the epoch it was installed under; the completion abandons
  at fire time on a mismatch — upstream of the one-shot hook, so the hook
  survives for the legitimate EOF.
- Bumps: stopEnhancedResamplerLocked (covers stop/track-change/reconfigure/
  shutdown and the resampler→passthrough seam) and seekEnhancedBestEffort
  (bump BEFORE the stop, new segment under the new epoch). Queue discipline
  unchanged: sync from engineQueue/configChangeQueue only (C-1 direction).

196 tests / 32 suites / 0 known issues; strict-gate PASSED.
… hardenings

swift-expert review of 92aef6b: APPROVE (epoch guard proven correct across
every interleaving it covers); applied its findings:

- MAJOR-1: the converter-failure seam fallback left resampleSession pointing
  at the OLD track's exhausted session while a passthrough schedule + armed
  hook were live — a later seek then misrouted to seekEnhancedResampler,
  whose stop() bumped only resampleGeneration → the live passthrough
  completion fired under an unbumped epoch and rolled the seam: the same
  wrong-song bug escaping through stale state. Fixed both ends: the fallback
  now clears resampleSession (the P1-2 rule, mirroring the empty-prime
  branch), AND seekEnhancedResampler bumps passthroughGeneration too
  (defense-in-depth — stale routing can never fire a live passthrough
  completion again).
- MINOR-3: pauseForDeviceLoss now stopEnhancedResampler()s before its
  player.stop() — a queued device-loss callback racing a just-started
  Enhanced session could stop a live-epoch player with an armed hook.
- NIT-4: stopEnhancedResamplerLocked + scheduleFileChainingPassthrough get
  dispatchPrecondition(.onQueue(resampleQueue)) — the contract was
  comment-only while primeEnhancedResamplerLocked already asserted it.

Not applied (recorded for the break-it round): MINOR-2 capture-vs-schedule
sub-ms window (pre-existing transport-vs-config-change family, strictly
improved by the epoch guard); NIT-5 headless guard-predicate test; NIT-6
pre-existing end-of-queue sentinel question (runtime log check).

196 tests / 32 suites / 0 known issues; strict-gate PASSED.
…break-it list seeded

Founder-verified: pill rate readout, carved scrubber, H (teal play button by
design), and the wrong-song fix ("device switching works"). 42pt chrome-clamp
cell deferred (content scaling proven in the PR-5 round; clamp gate-asserted;
founder's Settings stuck in per-app Custom). Five break-it items filed.
The glow field now tints itself from the current album art (founder decision
D8), inside the §3.1 pre-binding: every sampled color is CLAMPED into
token-defined bounds, so the R4 audit keeps auditing an enumerable worst case
and no cover can break the contrast budget.

Kit (pure, headless):
- SampledGlow: per-slot sRGB channel ceilings (teal .62 — tightest, alpha .28
  under the hero/queue-head; lime/blue .95 at their lower alphas), hue-
  preserving proportional scale-down (never per-channel truncation), token
  DARK alpha forced (alphas are the audited quantity, never sampled),
  aesthetic floors (near-black / near-gray → brand fallback). Dominant-color
  selection: 12 hue buckets, weight = saturation × value (a vivid accent
  out-votes a dull field), achromatic pixels don't vote, deterministic.
- GlowFieldSpec.compositeBackdrop gains a per-slot override palette — the
  render fold and the audit fold share the convention.
- RGBAColor.fromPixel(UInt8) — the extractor's entry into the Kit color space.

App:
- ArtworkGlowSampler (@observable @mainactor): 24×24 sRGB downsample → pixel
  votes → Kit selection+clamp; per-track FIFO cache (64); synchronous (576
  pixels — no Sendable laundering). nil artwork — including the token-guarded
  resolve gap right after a track change — resets to brand.
- GlowField renders the per-slot overrides; recolor is a DISCRETE restyle:
  one crossfade, Reduce-Motion → a cut (§3.3, never a continuous animation).
- NowPlayingTabView wires artwork changes → sampler (initial: true covers
  mounting with a track already playing).

Rig:
- R4-GLOW-D8 (own suite; the base audit is at the type-length limit): folds
  the grid with the clamp-box CORNER (ceiling-gray per slot at token alpha —
  provably ≥ any admissible palette by channel monotonicity) and asserts the
  SAME pairs as R4-GLOW-01/02/04 plus the lens/badge composites. Passing it
  proves the ENTIRE sampled color space.
- SampledGlowTests: box membership, hue preservation, floors, idempotence,
  selection determinism/achromatic behavior, constants sanity.

204 tests / 34 suites / 0 known issues; strict-gate PASSED.
…amp/decode hardening

swiftui-pro verdict was request-changes; applied everything, plus one
strengthening beyond the review:

- BLOCKER: the palette cache key was AudioFile.relativePath — hard-coded ""
  by the queue adapter for EVERY track, so the first album's palette was
  served for the whole session (recolored once, never again). Key is now the
  file's absolute URL (AudioFile.id).
- MAJOR-2 + stronger: the "corner dominates everything" claim missed mixed
  palettes with BRAND fallbacks (brand teal's green/blue exceed the 0.62 teal
  ceiling — and, beyond the review's proposed single extra fold, brand blue's
  1.0 channel exceeds ITS 0.95 corner too). R4-GLOW-D8 now folds the full 2³
  per-slot {corner, brand} lattice — exact coverage of the reachable union by
  per-slot monotonicity, no accidental-pass margins. Comments re-scoped.
- MAJOR-3: the D8 fold now audits the panel⊕field pairs too (all three label
  rungs, right side — tertiary's designed home), matching the §7 R4 table.
- MINOR-4: clamp floors evaluated on the SCALED color (what would render) —
  barely-chromatic brights whose spread collapses under scale-down reject to
  brand instead of rendering as sub-floor gray; clamp genuinely idempotent
  now; regression case added.
- MINOR-5: loose-file embedded art decodes via CGImageSource thumbnail capped
  at 512px (library-path parity) — no more full-res main-actor decode of a
  3000²+ cover on track change.
- NITs: premultiplied-alpha vote semantics documented both sides;
  compositeBackdrop guards a short override array instead of trapping.

204 tests / 34 suites / 0 known issues; strict-gate PASSED.
…t enters close-out

Founder confirmed per-track glow recolor on a probe-picked 6-album demo queue
("ok I see what you meant it changes") after the first test track turned out
to be a teal-graded cover sampling ≈ brand teal (pipeline verified against the
real DB artwork; 41/43 library covers sample, 2 brand-fallback by design).
B/H/missing-art/crossfade-feel ride the sprint-end A–H pass. sprint-plan
§Status updated (the one prose status surface).
…ery, stop-race, error copy

qa-expert sprint-end break-it findings, engine chunk:

- BLOCKER-1 (empirically proven): playback NEVER stopped at end of queue
  (repeat off) — setNextTrack(nil) at the final seam cleared the EOF hooks,
  so the last real completion was swallowed: isPlaying stayed true forever,
  the footer counted past the duration, the engine rendered silence. The
  three passthrough fire-sites and the resampler seam now route through
  fire*EOFOrEnd: a nil hook under a LIVE epoch is provably a genuine EOF
  (every non-EOF stop bumps the epoch first) → the shared ended-state
  surface fires (gaplessPlaybackEnded + intent cleared) and the VM stops
  cleanly. Single-track queues had the same hole (hooks never armed).
- MAJOR-1: engine target dying (mid-play disconnect / PIN re-assert failure)
  left currentDeviceID pointing at the corpse — picker and engine diverged,
  and every Pure viability check failed against the dead id ("Pure
  unavailable" until a manual re-pick). refreshDevices now re-asserts the
  surviving selection to the engine when its target is no longer listed.
- MAJOR-2: a user Stop racing a config-change re-establish could restart
  audio under a stopped UI (zombie playback) — the re-establish now
  re-validates the play intent immediately before touching the player and
  undoes its engine restart when the intent was cleared mid-flight.
- error-0 class: the described BT-disconnect→play repro is NOT reachable on
  current code (qa trace); the one reachable window — play from the menu
  bar/Control Center during app-quit teardown — is closed by gating
  isEngineReady FIRST in shutdown. AudioBridgeError gains LocalizedError
  (the banner rendered "…error 0."), and startPlayback self-heals
  engineNotInitialized via retryInitialization.
- Pure-mode lens (fool): the spectrum FROZE at the last Enhanced frame at
  full opacity during Pure playback (no tap, bars never zeroed) — tickSpectrum
  zeroes bars/caps once when the active path is Pure.
- MINOR-3: 20 Hz same-value writes suppressed (pollEngineReadouts with
  equality guards) — signalPath/loudness observers no longer re-evaluate
  20x/s while idle; makes SignalPathInfo's Equatable doc-claim true.

205 tests / 34 suites green; strict-gate PASSED.
…re hardening

The rig/UI chunk of the sprint-end break-it round (fool + qa findings):

- R4 domain drift (fool): the row-tint folds still sampled x >= 0.5 ("the
  50/50 pane") — PR 5 moved the queue to the LEFT flex region, where the teal
  core reaches. Row tints now audit at EVERY grid point in both the concrete
  test and the D8 lattice (over-auditing is free; under-auditing was the hole).
  Row tints PASS everywhere — the hole was in the model, not the pixels.
- Bleed stratum (fool→REAL defect): folding the panel's dark bottom light-
  bleed measured tertiary at 4.18 < 4.5 where the inspector's last rows could
  REST on it. Constraint encoded, look untouched (house doctrine): the
  inspector's bottom content inset now IS GlassDecor.bleedHeight (same token,
  cannot drift) — text never rests on the bleed; transient scroll crossings
  accepted (seam-feather class). The lattice documents the rule.
- R4-CONTROL-01 (fool): the §7 pair-4 audit the design owed since PR 6 —
  pill label + rate readout on badgeFill over the chrome band, both
  appearances + the RT/IC composite. Passes.
- Queue guards (qa): Delete no longer removes a row the filter is hiding;
  the drop-target highlight no longer latches when a filter is typed
  mid-drag; the filter's dead relativePath candidate dropped (empty for
  every library track — title-only, honestly documented).
- Tripwire hardening (fool): semgrep excludes PATH-ANCHORED (bare basenames
  exempted ANY same-named file); colorspace prefix generalized (displayP3
  evaded the sRGB-only group); .overlay/.border join the paint-context
  alternation; NSColor.systemX statics matched; the Kit purity guard now
  catches `import struct SwiftUI.Color`-style scoped imports.
- seamFeather comment corrected (the "removed in PR 6" plan didn't happen —
  D4 kept the bands quiet; permanent until a shell-glass wave).

205 tests / 34 suites green; strict-gate PASSED (semgrep re-verified against
the hardened rules).
@ramith
ramith marked this pull request as ready for review July 18, 2026 14:10
ramith added 3 commits July 18, 2026 20:00
… identical-gate promise

PR #61 run 1 failed on 74 files this sprint never touched: the runner brewed
SwiftFormat 0.62.1 (wrapIfStatementBodies behavior changed) against the local
0.61.1, and SwiftLint was one minor ahead too (0.65.0 vs 0.64.1) — it just
never got a turn. The strict gate is only meaningful if local and hosted CI
run IDENTICAL style tools (design §7.2), so:

- strict-gate.sh asserts SWIFTFORMAT_PIN=0.61.1 / SWIFTLINT_PIN=0.64.1 at the
  tool check — any skew now fails loudly with instructions (bump the pin +
  reformat in the same commit) instead of red-herring style errors.
- strict-ci.yml installs both tools as pinned release binaries (brew no
  longer supplies them), prepended on PATH ahead of the runner image's copies.

Ledger: founder verified the end-of-queue fix live ("queue ended"); founder
DECIDED the queue-arrows behavior stands by design ("arrows behaviour is ok").
…polish wave

DEVIATIONS.md (founder-commissioned external audit vs the 8a card) moved to
its referenced home under docs/design/now-playing-7a/. The companion triage
(docs/sprints/s10-8-deviations-plan.md) reconciles it with the S10.7 decision
record: 6 report items are stale/by-design (solid bars + caps, lens recipe,
carved sliders, carved scrubber all SHIPPED; the "wrong hue" glow is D8
art-sampling working), the rest become the S10.8 part-1 PR ladder (A–G) with
three founder decisions surfaced (D9 reopen for the capsule tabs — recommend
yes; band strata vs real blur — recommend strata; True-peak rename only with
a true-peak measurement — recommend enabling it in libebur128).
…ons recorded, §Status → S10.8

Founder 2026-07-19: capsule tabs IN (D9 reopened + decided), "perf test is
ok" (Instruments R5 recipe — closes the PR-3/PR-5 deferred rows), decisions
2-3 proceeding per recommendation. Retro captures the sprint's process
lessons (runtime bugs founder-caught vs static rig-caught → R3 snapshot
decision moves BEFORE S10.8; tool-pin lesson; ledger-discipline leak;
day-one branch push). sprint-plan §Status is the one prose surface: S10.7
closed, S10.8 two-part scope current.
@ramith
ramith merged commit 416c7a6 into main Jul 18, 2026
1 check passed
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