Skip to content

feat(tuner): gate playback until you've tuned — hold autoplay + no-trap Skip/Back/Esc (working-tuning PR 4)#666

Merged
byrongamatos merged 2 commits into
mainfrom
feat/v3-tuner-playback-gate
Jul 1, 2026
Merged

feat(tuner): gate playback until you've tuned — hold autoplay + no-trap Skip/Back/Esc (working-tuning PR 4)#666
byrongamatos merged 2 commits into
mainfrom
feat/v3-tuner-playback-gate

Conversation

@ChrisBeWithYou

Copy link
Copy Markdown
Contributor

Working-tuning PR 4 (the gate). Stacked on #660 → base is feat/v3-tuner-uses-working-tuning; review/merge after the #655#660 stack.

What

When the opt-in tuner auto-open fires because a song needs a different tuning, playback now waits behind the tuner instead of starting underneath it — the "tune before you play" model — and it's never a one-way trap.

How

  • Core hook window.feedBack.holdAutoplay() (static/app.js) — mirrors the existing holdAutoExit(). The tuner claims the hold synchronously on song:loading (beating the song:ready autostart that caused the original flash), and release() — or a 12s fail-open backstop — runs the deferred start. Generation-guarded (a new song invalidates a stale hold); manual Play always wins. The hook is generic — a test asserts app.js never references the tuner's internals.
  • No one-way trap (plugins/tuner/screen.js + plugins/tuner/utils/ui.js):
    • Skip = "I've tuned" → plays and records the song's tuning as the instrument's current working tuning (the explicit write-point PR 3 left as assumed).
    • Back to library button + Esc → leave the song, record nothing (reuses requestExitSong; Esc is the existing player "Back to library" shortcut).
    • The in-panel × is dropped for an auto-open — Skip / Back / Esc are the dismiss surface. This also keeps the write honest: Skip is the only on-player dismiss that records, so leaving never falsely records a tuning.

Test

node --test — 36 green: the ported gate tests (claim on song:loading, release on dismiss, feature-off no-claim, the core hook + fail-open backstop), #660's working-tuning tests, and a new static test for the Skip / Back-to-library / Esc escape-hatch wiring.

⚠ Pre-ship smoke-test (per the design charrette)

This is a core playback change. On desktop with a real mic, verify: (1) Esc/Back exits cleanly mid-gate (not swallowed by the tuner), and (2) the tuner's mic doesn't contend with note_detect's scoring input under ASIO / exclusive mode.

🤖 Generated with Claude Code
https://claude.ai/code/session_01QbexxfTt8q2tAn436MqGWF

ChrisBeWithYou and others added 2 commits July 1, 2026 11:13
…ap Skip/Back/Esc (working-tuning PR 4)

When the opt-in auto-open fires because a song needs a different tuning, playback
now WAITS behind the tuner instead of starting underneath it — the "tune before
you play" model. Built on a new generic core hook window.feedBack.holdAutoplay()
(mirrors holdAutoExit): the tuner claims the hold synchronously on song:loading
(beating the song:ready autostart) and releases it — or a 12s fail-open backstop
does — so a wedged plugin can never strand a song. Generation-guarded; manual
Play always wins.

No one-way trap:
- Skip = "I've tuned" -> plays and records the song's tuning as the instrument's
  current working tuning (the explicit write-point PR 3 left as 'assumed').
- Back to library / Esc -> leave the song, record nothing (reuses requestExitSong;
  Esc is the existing player shortcut).
- The in-panel x is dropped for an auto-open — Skip/Back/Esc are the dismiss
  surface. This also keeps the write honest: Skip is the only on-player dismiss
  that records, so leaving never falsely records a tuning.

Stacked on #660 (working-tuning PR 3). Core app.js gains only the generic hook
(a test asserts it never references the tuner's internals); shell-agnostic.

Needs a desktop smoke-test that the tuner mic doesn't contend with note_detect's
scoring input under ASIO/exclusive mode (per the design charrette).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QbexxfTt8q2tAn436MqGWF
… (PR #666 review)

Review fixes for the autoplay gate:

- The 12s fail-open backstop could start playback UNDER a legitimately-open tuner
  (a slow / mic-verify retune > 12s). holdAutoplay()'s release now carries a
  .settle() that cancels the backstop; the tuner calls it once the tuner is
  confirmed open (_gateClaimed), so the hold becomes deliberate and only a
  dismiss / song switch releases it. (Fail-open still covers "claimed but wedged
  before deciding".)
- The async song:ready handler could release a NEWER song's gate after its await
  (global _gateClaimed, no guard). It now snapshots _autoOpenGeneration and bails
  if a newer song took over.
- holdAutoplay guarded by song generation, not per-hold — a stale release from an
  earlier hold could clear a later one. Each hold now mints a unique token that
  release()/settle() must match.

Tests: source-level assertions for the token, settle(), the settle-on-open call,
and the song:ready gen-guard. 45 tuner+speed tests green. Codex-reviewed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@byrongamatos
byrongamatos force-pushed the feat/v3-tuner-playback-gate branch from 7592e0d to ba957a2 Compare July 1, 2026 09:18
@byrongamatos
byrongamatos changed the base branch from feat/v3-tuner-uses-working-tuning to main July 1, 2026 09:18
@byrongamatos
byrongamatos merged commit 115c96a into main Jul 1, 2026
1 of 3 checks passed
@byrongamatos
byrongamatos deleted the feat/v3-tuner-playback-gate branch July 1, 2026 09:34
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.

2 participants