Skip to content

Promote virtuoso-dev → main (v0.2.2) — the 2026-07-14 batch - #4

Merged
ChrisBeWithYou merged 22 commits into
mainfrom
virtuoso-dev
Jul 16, 2026
Merged

ChrisBeWithYou merged 22 commits into
mainfrom
virtuoso-dev

Conversation

@ChrisBeWithYou

@ChrisBeWithYou ChrisBeWithYou commented Jul 10, 2026 •

Copy link
Copy Markdown
Collaborator

Promote virtuoso-dev (0.2.2) → main (0.2.0)

The full 2026-07-14 batch. virtuoso-dev reconciled with main (absorbed Byron's Gold-rung #11), then landed every open feature branch and re-versioned the line to 0.2.2 (patch above main's 0.2.0). Monotonic; nothing dropped.

What ships to main (beyond the Gold rung already there)

How it was assembled

main → dev reconcile (161801c, → 0.2.1); topbar stack merge (c022065); coach merge (8cc4076); ear-mode merge (4f9bf8e, → 0.2.2); cherry-pick of #5's late tuningMidis commit (f0c78c6). Every merge conflicted only on the two version tokens — no code overlap between the features and Byron's Gold rung. Superseded PRs: #5–#10 closed (content landed here), #12 / #13 auto-merged.

Verify

  • 18/21 smoke green — the 3 reds (backing-engine, progress, variation) are the documented pre-existing base failures; smoke-gold (from Gold improv rung — harmonic-comb verified jam conformity (career passports) #11), smoke-coach-rx, and the stack's suites all pass.
  • node --check clean; version tokens consistent (plugin.json + VIRTUOSO_VERSION = 0.2.2).
  • ⭐ Standing owe: smoke MOCKS the contained verifier, so the graded surfaces stay real-desktop×DI-dogfood-verified only. (A combined dev build was dropped to the installed app this session.)

🤖 Generated with Claude Code

…-flare (v0.1.10)

Two grading-DISPLAY bugs; detection was always correct.

Card 0%/0-of-0 hero: shareCardAction preferred note_detect's
renderResultsCard, whose hero reads note_detect's own 0/0 last-session
counters (the contained verifier scored, not nd's UI) while Virtuoso's
real numbers only reached the stats[] override row. Retire the delegation
so Virtuoso draws its own verdict-led, skinned card (the ratified v0.1.4
"keep our renderer, don't consume note_detect's" decision); this also
restores the skins + mastery crest the delegation silently suppressed on
desktop. _ndCardApi / _resultsCardData / _virCardOverlay kept dormant for
a possible future host-level card API.

3D highway hit-flare invisible: highway_3d's feedBack#254 per-note
provider (getNoteState) is authoritative over the window-event marks and
culls a gem ~100ms past the strike line unless the provider returns a live
verdict that frame. Our provider only reported 'hit' after the async
contained verdict (~150-400ms late), so the gem was gone before green
could paint. Add a display-only { state:'active', live:true } keep-alive
from the live YIN ear (mirrors note_detect's own noteStateFor): pitch-gated
no looser than the credit floor, level-gated, +/-0.12s window; it renders
no judgment and drives no credit - credit stays verifier-only via
_ptScored. Contract confirmed present in highway_3d 3.30.0 (desktop bundle)
and 3.31.3 (core).

Verified on the installed desktop (real DI dogfood).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 10, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Virtuoso is updated to version 0.2.2 with dedicated tapping ladders, instrument-aware generation, coach and preview UX, host-topbar parity, a classic 2D highway fallback, provisional pitch display states, and expanded deterministic Playwright smoke coverage.

Changes

Virtuoso practice and interaction update

Layer / File(s) Summary
Practice pathways and generation
screen.js, screen.html, plugin.json
Adds guitar and bass tapping ladders, separates articulation and tapped-arpeggio generation, and makes fretboard, chord, sweep, drop-tuning, and bass low-fifth behavior instrument-aware.
Grading and preview UX
screen.js, screen.html
Adds validated provisional live pitch states, coach and pocket guidance, a pathway-focused “Practice next” action, and ungraded “Hear it” playback.
Host parity and rendering
screen.js, screen.html
Adds host tuner/profile/setup integration, merged tuning options, dropdown-based view switching, piano view filtering, progress-menu wiring, and a classic highway_2d fallback renderer.
Validation and documentation
.claude/skills/run-virtuoso/*, ROADMAP.md, docs/*
Updates smoke suites for deterministic instrument state and dropdown renderers, adds coach regression coverage, and documents Ear Mode, host parity, and release status.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant VirtuosoUI
  participant HostBus
  participant HostAPI
  User->>VirtuosoUI: open header tuner, profile, or setup
  VirtuosoUI->>HostAPI: read tunings, settings, or profile
  HostAPI-->>VirtuosoUI: return host data
  VirtuosoUI->>HostBus: subscribe to host changes
  HostBus-->>VirtuosoUI: update header and tuning state
Loading
sequenceDiagram
  participant User
  participant ResultsModal
  participant CoachPrescription
  participant PathwaySelector
  participant PreviewPlayback
  User->>ResultsModal: inspect verdict
  ResultsModal->>CoachPrescription: compute recommendation
  User->>PathwaySelector: choose Practice next
  PathwaySelector->>PreviewPlayback: focus recommended practice
  User->>PreviewPlayback: select Hear it
  PreviewPlayback-->>User: play target notes without scoring
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: promoting virtuoso-dev to main as the v0.2.2 batch.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch virtuoso-dev

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
screen.js (1)

23328-23340: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Update the contradictory share-card roadmap.

ROADMAP.md Lines 19-24 still directs future work to consume note_detect’s renderer and records delegation as built. Update it so a later change does not restore this retired, faulty path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@screen.js` around lines 23328 - 23340, Update the share-card roadmap entries
around the note_detect renderer to document that Virtuoso retains its own
renderer and that delegation to note_detect is retired, correcting any statement
that delegation is built or planned. Preserve the rationale that the local
renderer supplies accurate contained-verifier metrics and the required
skins/crest, so future work does not restore the deprecated path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@screen.js`:
- Around line 22500-22524: In the live keep-alive logic, missing recent level
samples must not count as valid level evidence. Update the `leveled` calculation
in the `_ptLive` block to require `ptHasSamplesIn(lo, now)` and
`ptPeakLevelIn(lo, now) >= PT_LVL_FLOOR_MIN` whenever `_lvlMode !== 'none'`;
only bypass this check when level mode is disabled.

---

Nitpick comments:
In `@screen.js`:
- Around line 23328-23340: Update the share-card roadmap entries around the
note_detect renderer to document that Virtuoso retains its own renderer and that
delegation to note_detect is retired, correcting any statement that delegation
is built or planned. Preserve the rationale that the local renderer supplies
accurate contained-verifier metrics and the required skins/crest, so future work
does not restore the deprecated path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 625674b4-57d8-4d78-8115-b4a4892754ce

📥 Commits

Reviewing files that changed from the base of the PR and between d8fda47 and 5fa6f13.

📒 Files selected for processing (2)
  • plugin.json
  • screen.js

Comment thread screen.js
Comment on lines +22500 to +22524
// Live keep-alive for the borrowed highway (feedBack#254 provider): while this note
// straddles the strike line AND the live ear hears its target pitch NOW, return a
// display-only { state:'active', live:true } so the host gem stays lit until the async
// contained verdict credits it — the provider-cull would otherwise delete the gem
// ~100ms past the line, before our verdict lands (the "hits invisible on the 3D
// highway" bug). MIRRORS note_detect's own noteStateFor provisional glow: it renders
// NO judgment and drives NO credit ('hit'/'miss'/credit stay verifier-only, above);
// the 2D strip's string check ignores the object, so that surface stays credited-only.
// live:true routes it through the highway's non-latched 'hit-live' branch, so it
// extinguishes on mute / relights on re-strike and can never stick a false green.
if (_ptLive && _ptLive.c >= PT_CONF_INPUT) {
const age = now - _ptLive.t, om = _ptOpenMidis[note.s];
if (age > -0.05 && age < 0.08 && om != null && now >= tn.t - 0.12 && now <= end + 0.12) {
const cents = Math.abs(1200 * Math.log2(_ptLive.f / midiToFreq(om + note.f)));
const tol = note.bn ? PT_BEND_CENTS : 50; // no looser than the credit floor
// level/onset evidence — the SAME host-mirror tap the credit gate trusts, so a
// confident YIN lock on an inaudible residual can't false-light the gem.
let leveled = true;
if (_lvlMode !== 'none') { const lo = now - PT_LVL_RECENT_WIN; leveled = !ptHasSamplesIn(lo, now) || ptPeakLevelIn(lo, now) >= PT_LVL_FLOOR_MIN; }
if (cents <= tol && leveled) {
const alpha = 0.55 + 0.4 * Math.max(0, Math.min(1, (_ptLive.c - PT_CONF_INPUT) / (1 - PT_CONF_INPUT)));
return { state: 'active', live: true, alpha };
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not treat missing level samples as positive evidence.

Line 22518 sets leveled to true when no recent samples exist. A confident residual can therefore light the gem without the required level evidence.

Proposed fix
-        if (_lvlMode !== 'none') { const lo = now - PT_LVL_RECENT_WIN; leveled = !ptHasSamplesIn(lo, now) || ptPeakLevelIn(lo, now) >= PT_LVL_FLOOR_MIN; }
+        if (_lvlMode !== 'none') { const lo = now - PT_LVL_RECENT_WIN; leveled = ptHasSamplesIn(lo, now) && ptPeakLevelIn(lo, now) >= PT_LVL_FLOOR_MIN; }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Live keep-alive for the borrowed highway (feedBack#254 provider): while this note
// straddles the strike line AND the live ear hears its target pitch NOW, return a
// display-only { state:'active', live:true } so the host gem stays lit until the async
// contained verdict credits it — the provider-cull would otherwise delete the gem
// ~100ms past the line, before our verdict lands (the "hits invisible on the 3D
// highway" bug). MIRRORS note_detect's own noteStateFor provisional glow: it renders
// NO judgment and drives NO credit ('hit'/'miss'/credit stay verifier-only, above);
// the 2D strip's string check ignores the object, so that surface stays credited-only.
// live:true routes it through the highway's non-latched 'hit-live' branch, so it
// extinguishes on mute / relights on re-strike and can never stick a false green.
if (_ptLive && _ptLive.c >= PT_CONF_INPUT) {
const age = now - _ptLive.t, om = _ptOpenMidis[note.s];
if (age > -0.05 && age < 0.08 && om != null && now >= tn.t - 0.12 && now <= end + 0.12) {
const cents = Math.abs(1200 * Math.log2(_ptLive.f / midiToFreq(om + note.f)));
const tol = note.bn ? PT_BEND_CENTS : 50; // no looser than the credit floor
// level/onset evidence — the SAME host-mirror tap the credit gate trusts, so a
// confident YIN lock on an inaudible residual can't false-light the gem.
let leveled = true;
if (_lvlMode !== 'none') { const lo = now - PT_LVL_RECENT_WIN; leveled = !ptHasSamplesIn(lo, now) || ptPeakLevelIn(lo, now) >= PT_LVL_FLOOR_MIN; }
if (cents <= tol && leveled) {
const alpha = 0.55 + 0.4 * Math.max(0, Math.min(1, (_ptLive.c - PT_CONF_INPUT) / (1 - PT_CONF_INPUT)));
return { state: 'active', live: true, alpha };
}
}
}
// Live keep-alive for the borrowed highway (feedBack#254 provider): while this note
// straddles the strike line AND the live ear hears its target pitch NOW, return a
// display-only { state:'active', live:true } so the host gem stays lit until the async
// contained verdict credits it — the provider-cull would otherwise delete the gem
// ~100ms past the line, before our verdict lands (the "hits invisible on the 3D
// highway" bug). MIRRORS note_detect's own noteStateFor provisional glow: it renders
// NO judgment and drives NO credit ('hit'/'miss'/credit stay verifier-only, above);
// the 2D strip's string check ignores the object, so that surface stays credited-only.
// live:true routes it through the highway's non-latched 'hit-live' branch, so it
// extinguishes on mute / relights on re-strike and can never stick a false green.
if (_ptLive && _ptLive.c >= PT_CONF_INPUT) {
const age = now - _ptLive.t, om = _ptOpenMidis[note.s];
if (age > -0.05 && age < 0.08 && om != null && now >= tn.t - 0.12 && now <= end + 0.12) {
const cents = Math.abs(1200 * Math.log2(_ptLive.f / midiToFreq(om + note.f)));
const tol = note.bn ? PT_BEND_CENTS : 50; // no looser than the credit floor
// level/onset evidence — the SAME host-mirror tap the credit gate trusts, so a
// confident YIN lock on an inaudible residual can't false-light the gem.
let leveled = true;
if (_lvlMode !== 'none') { const lo = now - PT_LVL_RECENT_WIN; leveled = ptHasSamplesIn(lo, now) && ptPeakLevelIn(lo, now) >= PT_LVL_FLOOR_MIN; }
if (cents <= tol && leveled) {
const alpha = 0.55 + 0.4 * Math.max(0, Math.min(1, (_ptLive.c - PT_CONF_INPUT) / (1 - PT_CONF_INPUT)));
return { state: 'active', live: true, alpha };
}
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@screen.js` around lines 22500 - 22524, In the live keep-alive logic, missing
recent level samples must not count as valid level evidence. Update the
`leveled` calculation in the `_ptLive` block to require `ptHasSamplesIn(lo,
now)` and `ptPeakLevelIn(lo, now) >= PT_LVL_FLOOR_MIN` whenever `_lvlMode !==
'none'`; only bypass this check when level mode is disabled.

…ion refs

New 2026-07-10 STOPPED HERE: card 0/0 (retired the stale note_detect card
delegation) + 3D highway flare (live-ear keep-alive vs the feedBack#254
provider cull), the Program-Files installed-app deploy gotcha, and the
version corrections (highway_3d 3.30.0 desktop / 3.31.3 core; note_detect
1.28.0 desktop) that supersede the stale 3.26/1.19 refs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ROADMAP.md`:
- Around line 29-30: Mark the earlier note_detect results-card delegation plan
in the “Planned” section as superseded or remove it entirely. Update references
around the local renderer decision so future work consistently uses Virtuoso’s
own renderShareCardImage implementation and does not suggest consuming
_ndCardApi or related delegation APIs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 305810c8-6dc0-4efc-80d2-d440de3617ea

📥 Commits

Reviewing files that changed from the base of the PR and between 5fa6f13 and 6f60d70.

📒 Files selected for processing (1)
  • ROADMAP.md

Comment thread ROADMAP.md
ChrisBeWithYou and others added 9 commits July 10, 2026 01:31
… settings sync (v0.1.11)

"fullscreen":true hides the host topbar on our screen, so the header now
mirrors its three badges in the host's exact order (compact 32px density
variants of the host cards). Spec: docs/topbar-host-parity.md.

HOST CHECK (feedback-compatibility, 2026-07-10, vs FeedBack 0.3.0-alpha.1):
- Tuner: BORROW launch (window.tuner.toggle, feature-detected) + MIRROR card
  (live note via bus 'tuner:frame'). Flip: a host-embeddable badge component.
- Instrument: BORROW persistence (GET/POST /api/settings, GET /api/tunings,
  workingTuning.setCurrentInstrument, 'instrument:changed') + MIRROR badge/
  panel UI. Host = source of truth for guitar/bass; piano (gated) never POSTs.
  Flip: host adding piano to STRING_COUNTS.
- Profile: MIRROR chip (avatar+streak+rank, /api/profile*) + LINK
  (goScreen('v3-profile')); renders nothing when !onboarded. Never a second
  profile/economy implementation.

Sync semantics: user panel changes funnel through instrumentStoreSave (the L1
declaration boundary) -> debounced POST + workingTuning + instrument:changed +
tuner-config push (gated on window.tuner: a blind POST 404s and trips the
smoke console-error guards). Boot reconcile: local L1 store wins and pushes
host-ward (pre-merge tunings live only locally; host default is
indistinguishable from untouched); fresh install adopts host; live external
'instrument:changed' always pulls. Per-rung overrides never write (anti-leak).
Interop maps through ABSOLUTE open-string midis only. note_detect reads
neither workingTuning nor /api/settings - the per-call verify ctx stays.

Also: progress chip hidden (pending the profile-merge design; P-sheet opens
via new gear-menu "Progress (P)" + the P hotkey), setup label now
instrument-strings-tuning, Floating-tuner courtesy button retired (the badge
is that affordance), Tune... relabeled "Tune to this tuning...", tuning
dropdown gains the host named-tuning group (stable built-in ids preserved -
deliberate deviation from spec's host-name-wins: id stability beats label
provenance), host-parity uniform standards added as offset:true presets
(C#/C std 6-str, Bb/A std 7-str, E std 8-str), and the latent
guitar_8_standard.tuning [2,0,...] metadata bug fixed to [0,...] (host
8-string base is F#; the old offset would detune string 0 a whole step on a
host round-trip).

Smoke: 16/19 - the 3 reds (backing-engine voice-leading, progress, variation)
fail identically on base (pre-existing).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS2YFb6UUSwJVV6CmEa25i
…ds (v0.1.12)

Audit verdict: "8-string exercises ignore the extra strings" was DESIGN, not
plumbing - string count/tuning reach every builder, but CAGED/Open anchor the
top-six EADGBE subset by construction (correct for CAGED itself) and 'caged'
was the universal beginner/session default, so most exercises never touched
strings 7-8 (renderers draw all N lanes; the bottom two just sat empty).

Fix (guitar-pedagogy + bass-pedagogy + metal-idiom panel):
- defaultFretboardSystem(instrument, count): bass -> 'position' (bass never
  uses guitar shape systems; the pathway path already coded this - now
  Custom/beginner + sessions agree), guitar N>6 -> '3nps' (THE extended-range
  scale system - tiles every string, so 7/8-string runs actually reach the
  low B/F#), guitar 6 -> 'caged' (unchanged). Explicit advanced-mode picks and
  rung-coded systems always win; a rung that TEACHES a CAGED shape stays a
  CAGED lesson. Wired into readConfig + buildSegmentConfig.
- Instrument-first guards (defense-in-depth) on every count-gated shape path:
  resolveCurrentShape, cagedShapeNotesForChord, templateFromShape, sweep
  wantShape. Kills a REAL latent pitch bug: a 6-string bass satisfies the old
  count-only >=6 guards, but the CAGED templates bake EADGBE's G->B major 3rd
  - on an all-4ths B-E-A-D-G-C bass the top two strings land a semitone flat.
  Only UI suppression protected it (hidden shape controls); programmatic
  configs (preset import, host sync) were exposed.

Smoke: new smoke-strings rows (12) default routing (8-string default=3nps,
reaches s=0/s=1, no-unison holds; 6-string stays caged; explicit caged
preserved) + (13) bass never resolves a shape, forced 6-string-bass caged
sweeps stay diatonic, 5-string scale plays the low-B string. Suite green;
full run 16/19 with only the pre-existing base reds (backing-engine voice-
leading row, progress, variation); contained-verifier + level-gate-async
flaked under concurrency, green solo.

Deferred by panel verdict (ROADMAP open thread): opt-in extended-range CAGED
window-continuation (never a re-rooted box), bassRootGrip low-fifth downward
reach, drop-tuning s0+s1+s2 barre primitive.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS2YFb6UUSwJVV6CmEa25i
…own (v0.1.13)

The in-tree 2D highway was only reachable as a silent fallback (builtin_2d is
the Jumping Tab borrow slot; highway_3d falls back to it too). New first-class
kind 'highway_2d' -> makeBuiltin2DRenderer, always available, any string count.

Five stringed views + Piano Roll outgrew the segmented pill row, so the view
switcher is now a dropdown (#virtuoso-view-select) in the same control family
as the mode/settings selects. change -> onViewSwitch (same path a pill click
took); syncViewSwitcher mirrors what's ACTUALLY rendered (saved-pref restore,
>8-string force) by value-set only, so syncing never re-triggers a switch.
Piano/stringed option visibility keeps the old CSS mechanism, retargeted from
.virtuoso-view-btn[data-renderer] to option[value]. highway_2d is
fretboard-strip capable; theme toggle stays Tab/Notation-only; hand-marks
off-view stays 3D-only. Retired .virtuoso-view-btn/.virtuoso-view-tabs CSS
kept (local shot-*.mjs scripts + skin overrides still reference it) with a
RETIRED note; no live markup uses it.

Harness: driver.mjs + all suites drive the dropdown (value + bubbled change);
readiness waits retargeted to #virtuoso-view-select; smoke-renderers gains a
highway_2d row (enforcePixels: true - it draws into the in-tree canvas);
smoke-highway-settings cycles it through the attach/detach churn loop;
driver.mjs screenshot/all-renderers accept highway_2d.

Smoke: 16/19 - same 3 pre-existing base reds (backing-engine voice-leading,
progress, variation). Screenshot-verified: dropdown renders, 2D highway
attaches + draws lanes/gems.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS2YFb6UUSwJVV6CmEa25i
…ailable

Clarified target: the 2D Highway view means Byron's Classic 2D Highway (host
core), not Jumping Tab and not merely our in-tree renderer. HOST CHECK
(2026-07-10): it is createHighway()'s private _defaultRenderer
(static/highway.js), picker id 'default' (RESERVED), no feedBackViz factory,
closure-fed + websocket-fed (no chart setter) -> NOT borrowable today; verdict
MIRROR, flips to BORROW when the host ships a bundle-driven
feedBackViz_highway_2d factory (asked in got-feedBack/feedBack#835).

Wiring is borrow-FIRST via a factory-global probe only (vizFactoryFor - no
speculative script fetch; the URL doesn't exist yet and a blind 404 would trip
the smoke console-guards). The moment the host registers the factory, every
install upgrades to the real Classic 2D Highway with zero plugin changes;
until then the in-tree 2D highway renders the slot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS2YFb6UUSwJVV6CmEa25i
…ination)

The v0.1.11 host-settings sync treats an empty localStorage as a fresh
install and ADOPTS host config on boot. Smoke pages always boot with empty
localStorage, and any suite whose panel drives dispatch real change events
WRITES THROUGH to the persistent host config - so a later suite (or a later
run) could boot into whatever instrument the previous one left (the panel
flipped to bass mid-suite; smoke-strings rows 3/4 went red, and this is the
likely mechanism behind the contained-verifier/level-gate-async flakes under
the parallel run-all).

Fix: every suite (and driver.mjs) seeds localStorage['virtuoso.instrument']
with the 6-string-standard default via addInitScript BEFORE page scripts run.
The boot reconcile then takes the local-wins path - deterministic panel state
regardless of suite ordering, and each boot heals the host config for the
suites after it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS2YFb6UUSwJVV6CmEa25i
…rre (v0.1.14)

powerChordGrip hardcoded the standard-4ths offsets (+2 on s1/s2), so in every
tuning where the s0->s1 interval is a FIFTH (drop-D/C/B, drop-A, the bass
drops, DADGAD, Open D) the '5'/'5oct' grip sounded root + MAJOR 6TH instead of
root + 5th - a real wrong pitch on the strum_comp/pickStrumGrip path.

Fret offsets are now computed BY PITCH against the actual open intervals:
in a drop tuning the grip collapses to the iconic one-finger SAME-FRET barre
{s0:F, s1:F, s2:F} = root-5th-octave; standard tuning keeps {F, F+2, F+2}
unchanged. Guitar-pedagogy 2026-07-12: gate on the interval (DADGAD/Open D are
TRUE positives - the barre is the idiomatic voicing there), apply by default
(the barre is the only correct voicing in a drop tuning), F=0 is a valid open
voicing; templateFromPositions already fingers a same-fret row as a shared
index barre (fg 1) by construction. Negative-fret guard bails on hostile
custom tunings. (buildPedalRiffExercise's own s1/s2/s3 power chord was already
interval-correct - untouched.)

Smoke: smoke-strings row (14) - drop-D 5oct = same-fret barre sounding
root-5th-octave; standard grip unchanged. Full run 16/19 (the 3 pre-existing
base reds); the contained-verifier/level-gate flakes stayed green with the
suite seeding from the previous commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS2YFb6UUSwJVV6CmEa25i
bassRootGrip only reached UP (fifth s+1, octave s+2); the fifth BELOW the
root - the reach the low string exists for, and the whole point of a
5-string's B - had no primitive (deferred from the string-count audit).

bassRootGrip gains lowFifth: the P5 below the root on the string below,
computed by pitch with an exact-midi check. Guards (bass-pedagogy spec
2026-07-12): root.s>=1 (a string exists below) AND root.f>=2 (at f0/f1 the
pc-math octave-push wraps the note UP a 4th - the opposite of intent); null
when unavailable.

root_fifth_octave gains the opt-in rfoPattern='low5' variant: R-low5-5-8
(root on the downbeat, traverses both fifths + the octave); an unreachable
low fifth degrades to the upper 5th so the pattern stays playable everywhere.
The default R-5-8-5 box is deliberately untouched (the canonical pre-scales
lesson) - guarded byte-identical by smoke. Scope per spec: root_fifth_octave
ONLY (octave_groove stays pure R-8 per the soul-motown ruling; the
right_hand_technique modes are pitch-invisible by design). No root-selection
bias - nearest-to-prev stands. The Root-5th-Octave rung gains a final vary
step teaching the reach (rfoPattern coded in base so it never leaks across
the rung's own vary steps).

Smoke: smoke-strings row (15) - low5 geometry (R, root-7 on the string
below, 5th, octave), default-parity (field absent === 'r5o', byte-identical),
4-string null-degrade to the upper 5th. Full run 16/19 (the 3 pre-existing
base reds).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS2YFb6UUSwJVV6CmEa25i
…nderer (v0.1.16)

The `highway_2d` view slot rendered our horizontal-lane tab scroller, not the
2D highway Christian meant: Byron's "Classic 2D Highway" — the perspective
falling-fret highway that is createHighway()'s private _defaultRenderer
(static/highway.js + static/js/highway-{geometry,draw,state-primitives}.js).
HOST CHECK 2026-07-10 verdict was MIRROR (core/closure-fed, no viz factory to
borrow; issue got-feedBack/feedBack#835 asks the host to expose one, which
would flip this to a pure borrow).

New makeClassic2DHighwayRenderer() — a faithful port reading our bundle, with
the host constants/geometry lifted from source: #080810 BG, the perspective
project() (VISIBLE_SECONDS 3.0 / Z_CAM 2.2 / Z_MAX 10.0, strike line at
y=0.82), the centered fretX() trapezoid, converging fret lines, the beat
sweep, the DEFAULT_STRING_COLORS/DIM/BRIGHT palette, the glowing now line,
falling perspective-scaled fret-number gems (rounded-rect body + dim glow +
white fret number, open-string wide bar, dead-note X, technique glyphs),
sustain trails, and the amber fret-number rail with the live anchor-window
highlight. Fret window follows the chart anchors via the host's smoothed
displayMaxFret (0.4 rate), falling back to the notes' max fret when a chart
carries no anchors. Lefty mirror + inverted string order honored.

resolveRendererFactory's highway_2d slot now falls back to this (was
makeBuiltin2DRenderer); the borrow-first vizFactoryFor('highway_2d') probe is
unchanged, so the host factory still wins when #835 ships. makeBuiltin2DRenderer
stays the Jumping-Tab (builtin_2d) fallback and the >8-string path, untouched.

Deliberate approximations (documented in the renderer header, not oversights):
the exotic host overlays our bundle doesn't feed — unison-bend connectors,
strum-group brackets, master-difficulty phrase filtering, lyrics, chord frames
— are omitted; and a CREDITED hit keeps Virtuoso's own green-gem grammar
(#22c55e + glow, the cross-surface "a hit is unmistakable" guarantee from the
2026-07-09 tester fix) rather than the host's string-bright lit gem, so a hit
reads as clearly here as on our Tab/Notation surfaces (consume-the-judge:
credit stays verifier-only; this is display).

Verification: screenshot-confirmed static (receding trapezoid, fret lines,
strike line, string palette, 0-8 amber rail) AND playing (numbered gems
falling with perspective scale + sustain trails, near gem at the strike line).
Full smoke 16/19 - only the 3 pre-existing base reds (backing-engine,
progress, variation); smoke-renderers' pixel-enforced highway_2d row green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS2YFb6UUSwJVV6CmEa25i
…, v0.1.11)

New Concepts pack carved out of Legato (the lone leg_tapping rung re-homed as
tap_articulation). Designed by a 6-agent panel: learning-design (chair),
guitar/bass-pedagogy, metal-idiom, harmony-theory, rhythm-meter.

Engine: split buildTappingExercise on a tapArp flag. Plain 'tapping' is now the
single-string tap->pull articulation drill (replaces the off-idiom "scalar tap
+12 across the neck"); tapArp:true routes to buildTappedArpeggioExercise /
buildTapCascadePath — idiomatic tap->pull->hammer cascade cells reusing the
sweepArpeggioPositions grid (guitar 3-note EVH cell, bass 2-note tap->pull;
wide interval always routed to the RH tap; fret cap 22; no-unison seam pass for
the dim7 symmetry). tapArp plumbed like sweepStrings (hidden field + readConfig
+ anti-leak default).

Rungs: guitar 5 (tap_articulation -> tap_cell -> tap_cascade -> tap_sevenths ->
tap_changes, descending ceilings, tap_cascade density-ramps triplet->sextuplet);
bass 4 (btap_articulation -> btap_arpeggio -> btap_groove -> btap_apply,
register-clamped frets 5-12). One concept_tapping band (bass rungs
instrument-hidden). Skill-tree edges, form-cues, and screen.html options wired.

Verified: node --check + probe-tapping-ladders (9/9 rungs) + smoke-generators
134/134, core-purity 40/40, strings, renderers 8/8. Generation-path (core) work
— no HOST CHECK needed. Deferred to v2: bass two-hand independence, bass
open-string cascades, guitar tapped-pedal-point.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS2YFb6UUSwJVV6CmEa25i

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
screen.js (1)

22748-22762: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Missing level samples still satisfy the leveled gate — unresolved from prior review.

leveled is true whenever there are no recent samples (!ptHasSamplesIn(...)), which is the opposite of the intended guard described in the comment above it: a confident residual with zero level evidence still lights the keep-alive gem.

Proposed fix
-        if (_lvlMode !== 'none') { const lo = now - PT_LVL_RECENT_WIN; leveled = !ptHasSamplesIn(lo, now) || ptPeakLevelIn(lo, now) >= PT_LVL_FLOOR_MIN; }
+        if (_lvlMode !== 'none') { const lo = now - PT_LVL_RECENT_WIN; leveled = ptHasSamplesIn(lo, now) && ptPeakLevelIn(lo, now) >= PT_LVL_FLOOR_MIN; }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@screen.js` around lines 22748 - 22762, The level-evidence gate in the
live-note activation block must reject missing recent samples: update the
`leveled` assignment inside the `_lvlMode !== 'none'` branch so it is true only
when recent samples exist and `ptPeakLevelIn(lo, now)` meets `PT_LVL_FLOOR_MIN`,
while preserving the existing behavior when `_lvlMode` is `'none'`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@screen.js`:
- Around line 22748-22762: The level-evidence gate in the live-note activation
block must reject missing recent samples: update the `leveled` assignment inside
the `_lvlMode !== 'none'` branch so it is true only when recent samples exist
and `ptPeakLevelIn(lo, now)` meets `PT_LVL_FLOOR_MIN`, while preserving the
existing behavior when `_lvlMode` is `'none'`.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cf39986d-d697-48cb-9dbc-508d53a829f5

📥 Commits

Reviewing files that changed from the base of the PR and between 6f60d70 and cb6cc0d.

📒 Files selected for processing (4)
  • ROADMAP.md
  • plugin.json
  • screen.html
  • screen.js

@ChrisBeWithYou ChrisBeWithYou changed the title fix(grading-display): copy-card accuracy + 3D highway hit-flare (v0.1.10) feat: tapping ladders (v0.1.11) + grading-display fixes (v0.1.10) Jul 14, 2026
cb6cc0d pushed; PR #4 now carries both v0.1.10 grading fixes and v0.1.11 tapping
ladders. Corrects the STOPPED-HERE handoff + NEXT.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS2YFb6UUSwJVV6CmEa25i

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ROADMAP.md (1)

40-40: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Update the beta version to 0.1.11.

This instruction still says 0.1.10-beta.1, but line 34 states that PR #4 combines both releases and skips a standalone 0.1.10 tag. Following this handoff would produce a stale beta label.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ROADMAP.md` at line 40, Update the beta version referenced in the NEXT
roadmap entry to 0.1.11, replacing the stale 0.1.10-beta.1 value while
preserving the existing release command and handoff details.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ROADMAP.md`:
- Line 33: Update the deferred-work note in ROADMAP.md by replacing “Not gated
the ladder on these” with “The ladder was not gated on these.”

---

Outside diff comments:
In `@ROADMAP.md`:
- Line 40: Update the beta version referenced in the NEXT roadmap entry to
0.1.11, replacing the stale 0.1.10-beta.1 value while preserving the existing
release command and handoff details.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6132cb70-a417-48b1-b48a-d0a051e6ef7b

📥 Commits

Reviewing files that changed from the base of the PR and between cb6cc0d and 13a9c8f.

📒 Files selected for processing (1)
  • ROADMAP.md

Comment thread ROADMAP.md
- **Engine:** `buildTappingExercise` split — plain `tapping` = the single-string tap→pull ARTICULATION drill (replaces the off-idiom "scalar tap +12 across the neck" metal-idiom flagged); new `tapArp` flag → `buildTappedArpeggioExercise`/`buildTapCascadePath` = idiomatic tap→pull→hammer CASCADE cells (reuses `sweepArpeggioPositions` grid; guitar = 3-note EVH cell, bass = 2-note tap→pull; wide interval always routed to the RH tap; fret cap 22; no-unison adjacency pass incl. the dim7-symmetry seam). `tapArp` plumbed like `sweepStrings` (hidden field + readConfig + anti-leak default).
- **Rungs:** guitar 5 (`tap_articulation`→`tap_cell`→`tap_cascade`→`tap_sevenths`→`tap_changes`, descending tempo ceilings, `tap_cascade` density-ramps triplet→sixteenth_triplet); bass 4 (`btap_articulation`→`btap_arpeggio`→`btap_groove`→`btap_apply`, register-clamped frets 5–12). Both in the one `concept_tapping` band (bass rungs instrument-hidden). Edges + FORM_CUE + `screen.html` options wired.
- **Verified:** `node --check` + `probe-tapping-ladders.mjs` (9/9 rungs: startup guards pass, tp/po/ho present, frets ≤22, no adjacent unison, bass on bass strings) + smoke-generators 134/134, core-purity 40/40, strings, renderers 8/8. Screenshots confirm the `17ᵀ→5ᵖ→9ʰ` cell renders in Tab. **No HOST CHECK needed — generation-path (core) work, our USP, not shell.**
- **DEFERRED (v2, engine gaps flagged by the panel):** bass two-hand independence (needs a two-voice engine), bass open-string cascades, guitar tapped-pedal-point color rung. Not gated the ladder on these.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the grammar in the deferred-work note.

Change “Not gated the ladder on these” to “The ladder was not gated on these.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ROADMAP.md` at line 33, Update the deferred-work note in ROADMAP.md by
replacing “Not gated the ladder on these” with “The ladder was not gated on
these.”

Source: Linters/SAST tools

ChrisBeWithYou and others added 6 commits July 14, 2026 07:50
The results modal already NAMES a run's dominant fault (the miss-heatmap +
lean-strip heroes); this adds the FIX — one prescriptive "Practice next"
button that loads a real Ladder rung for that fault. Display-only,
downstream-only: reads the verdicts already produced (heat.fgMiss/transMiss,
leanMs, nearMiss, the bass felt verdict) and re-judges nothing.

- coachRxFor(): pure fault->drill mapping (finger / string-crossing / timing
  lean / near-miss), instrument-aware, never the rung just run (sibling fallback).
- Suppressed on a clean clear (the climb CTA is the prescription there) and when
  the mid-run downshift chip already fired.
- Validated by guitar-pedagogy + bass-pedagogy + learning-design: crossing ->
  pick_alternate (not string-skip); bass timing -> bass_rh_pulse (above the
  sub-70 Hz detector floor); pinky copy matches the legato drill it routes to.
- smoke-coach-rx.mjs unit-tests the pure mapping via the __virtuosoCoach hook —
  immune to the "smoke mocks the verifier" blind spot.

gap-roadmap PR 1.3. No HOST CHECK: extends already-shipped shell display code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS2YFb6UUSwJVV6CmEa25i
A bass feltGate run showed only the verdict WORD (Dragging/Settling/Locked),
which conflates three distinct pocket faults the felt engine already measures
and that have DIFFERENT fixes. buildPocketDiagnosis names the dominant one in a
sentence, bucketed the same way feltHoldAnalyze picks the verdict (drift ->
lean -> jitter):

- drift = a tempo trend across the phrase (slowing / speeding) — the fix splits
  by direction (slowing: keep the subdivision alive; speeding: don't chase it).
- steady lean = a consistent offset (lean in earlier / relax and let it come).
- jitter = loose-but-there (keep working it, no rush). locked = green win.

Descriptive + a light feel cue; never a score. Bass-pedagogy validated
2026-07-14 (the direction-split drift fix was its one must-fix). Rendered as a
.virtuoso-results-pocket line under the verdict word; smoke-coach-rx.mjs guards
the mapping via the __virtuosoCoach hook.

gap-roadmap PR 1.5. No HOST CHECK: extends already-shipped shell display.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS2YFb6UUSwJVV6CmEa25i
The timing prescription said "You were behind the click" off the run MEDIAN —
diffuse. worstLeanBars() reads the per-bar lean strip the scorer already
snapshots and names WHERE it broke down when the lean localizes to a region:
"You dragged most in bars 5-6 — lock your pulse to the beat." A whole-run lean
(a region spanning >=75% of the bars) stays generic, and a run with no clearly
hot bars keeps the median framing. Pure selection over already-blessed data
(ptLeanBarsSnapshot); no re-judging.

gap-roadmap PR 1.4. Guarded by smoke-coach-rx.mjs (localized + global-lean
cases). No HOST CHECK: extends already-shipped shell display.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS2YFb6UUSwJVV6CmEa25i
…1.20)

Re-diagnosing from scratch every run could flip the prescribed focus
finger->timing->crossing so the player never stays on one fix long enough to
land it (learning-design 2026-07-14 flagged this as load-bearing for the
gating). coachRxFor is refactored into coachRxCandidates (the full ranked,
category-tagged fault list) + a thin top-of-list coachRxFor; applyCoachHysteresis
keeps reinforcing last run's focus while it still trips a threshold — up to 2
consecutive runs — before a new top fault takes over. Focus persists per spec
(mode + rung) in localStorage; the hysteresis decision is pure and smoke-tested.

Behavior of the single prescription is unchanged when there's no history.

gap-roadmap PR 1.3 refinement. Guarded by smoke-coach-rx.mjs. No HOST CHECK.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS2YFb6UUSwJVV6CmEa25i
The first slice of Ear Mode (docs/ear-mode.md): a "Hear it" transport button
that plays the current exercise's NOTES as audio, UNGRADED — no scorer, no
session, no RAF clock. It schedules the notes-only voice through the same audio
bus playback uses (schedulePreviewAudio standalone; the note helpers lazily build
the bus, so it's self-contained), so the player can HEAR the phrase — the "call"
a future Echo rung will have them reproduce. Notes only (no metronome/backing);
an A-B loop bounds the phrase; a graded Play supersedes it; a second click stops.

The enabler for 2.2/2.3 — BUILD-on-ours per the cleared HOST CHECK (docs/ear-mode.md):
nothing host-borrowable, our own contained engine, zero detector dependency.
Verified by shot-listen.mjs (toggles + schedules without throwing + Play supersedes).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS2YFb6UUSwJVV6CmEa25i
main advanced to 0.2.0 via Byron's PR #11 (Gold improv rung — career
passports, harmonic-comb-verified jam conformity) merged directly to main,
while virtuoso-dev accumulated the grading-display fixes (0.1.10) + tapping
ladders (0.1.11) on the 0.1.9 base. The two lines never crossed. This merges
main into dev so dev is a superset — Byron's Gold rung + our patch work — and
resets the version line to 0.2.1 (a patch above main's 0.2.0 minor), so the
eventual dev→main promotion is monotonic and drops nothing. Only the two
version tokens (plugin.json + VIRTUOSO_VERSION) conflicted; screen.html /
ROADMAP / package.json (playwright bump) + smoke-gold.mjs auto-merged.
@ChrisBeWithYou ChrisBeWithYou changed the title feat: tapping ladders (v0.1.11) + grading-display fixes (v0.1.10) feat: promote dev → main (v0.2.1) — tapping ladders + grading fixes (post-reconcile) Jul 14, 2026
ChrisBeWithYou and others added 4 commits July 14, 2026 16:40
The host now serves the tuning catalog as exact integer midis alongside the
reference-scaled frequencies (feedBack#829, merged). hostTuningEntries prefers
those - no client-side log2 reconstruction, no rounding risk at non-440
reference pitches. The freq->midi fallback stays for hosts predating #829.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS2YFb6UUSwJVV6CmEa25i

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ROADMAP.md (1)

54-59: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Reconcile the release state with this promotion.

These entries still say main is 0.1.9 and that PR #4 will move it to 0.1.11, while this PR objective says the Gold rung is already on main at 0.2.0 and this promotion produces 0.2.1. Update the authoritative roadmap so future release and beta-cut steps do not use obsolete versions or merge status.

Also applies to: 66-72

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ROADMAP.md` around lines 54 - 59, The release-state roadmap entries around
the tapping-ladders status and the related lines 66–72 use obsolete versions and
merge assumptions. Update the authoritative roadmap to state that the Gold rung
is already on main at 0.2.0, this promotion produces 0.2.1, and revise the
PR/merge and beta-cut instructions accordingly so they no longer reference the
superseded 0.1.9–0.1.11 sequence.
🧹 Nitpick comments (7)
docs/ear-mode.md (1)

51-53: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Specify the fenced-block language.

Use ```text for this ASCII layout so the documentation passes MD040.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/ear-mode.md` around lines 51 - 53, Update the fenced ASCII layout block
in the documentation to declare the text language explicitly with the `text`
fence marker, preserving the layout content unchanged.

Source: Linters/SAST tools

.claude/skills/run-virtuoso/smoke-host-surface.mjs (1)

82-88: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated instrument-seed boilerplate.

Same block as driver.mjs and 4 other smoke files; see the consolidated comment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/run-virtuoso/smoke-host-surface.mjs around lines 82 - 88,
Remove the duplicated inline instrument-store seeding from the
smoke-host-surface setup and reuse the shared helper or centralized
initialization used by driver.mjs and the other smoke files. Preserve the
existing seeded guitar_6_standard behavior before page scripts execute.
.claude/skills/run-virtuoso/smoke-level-gate-async.mjs (1)

72-78: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated instrument-seed boilerplate.

Same block as driver.mjs and 4 other smoke files; see the consolidated comment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/run-virtuoso/smoke-level-gate-async.mjs around lines 72 - 78,
Remove the duplicated localStorage instrument-seeding addInitScript from the
smoke-level gate flow and reuse the shared helper or centralized setup used by
driver.mjs and the other smoke files. Preserve the deterministic
guitar_6_standard initialization before page scripts run.
.claude/skills/run-virtuoso/smoke-gems.mjs (1)

37-43: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated instrument-seed boilerplate.

Same block as driver.mjs and 4 other smoke files; see the consolidated comment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/run-virtuoso/smoke-gems.mjs around lines 37 - 43, Extract the
repeated localStorage instrument-seeding init script from the smoke test into
the shared helper used by driver.mjs and the other smoke files, then call that
helper here before page scripts run. Preserve the existing guitar_6_standard
seed and error-tolerant behavior while removing the duplicated boilerplate from
this file.
.claude/skills/run-virtuoso/smoke-audioctx.mjs (1)

46-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated instrument-seed boilerplate.

Identical to the block in driver.mjs and 4 other smoke files; see the consolidated comment for a shared-helper suggestion.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/run-virtuoso/smoke-audioctx.mjs around lines 46 - 52, Replace
the inline localStorage seeding block before page scripts run in the
smoke-audioctx setup with the shared instrument-seeding helper used by
driver.mjs and the other smoke files. Preserve the existing timing and
deterministic guitar_6_standard initialization while removing the duplicated
addInitScript boilerplate.
.claude/skills/run-virtuoso/smoke-backing-engine.mjs (1)

49-55: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated instrument-seed boilerplate.

Same block as driver.mjs and 4 other smoke files; see the consolidated comment.

Also applies to: 67-67

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/run-virtuoso/smoke-backing-engine.mjs around lines 49 - 55,
Remove the duplicated localStorage instrument-seeding block from the smoke test
and reuse the shared setup introduced for driver.mjs and the other smoke files.
Preserve the deterministic guitar_6_standard initialization by invoking the
existing centralized helper at the same initialization point before page scripts
run.
.claude/skills/run-virtuoso/smoke-contained-verifier.mjs (1)

85-91: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the instrument-seeding addInitScript into a shared helper.

The exact same 7-line block (identical comment text and identical page.addInitScript(() => { try { localStorage.setItem("virtuoso.instrument", ...) } catch (_) {} }) call) is pasted into 7 separate smoke files. If the seeded JSON shape (or the rationale/host-sync version referenced in the comment) ever needs to change, every copy has to be updated in lockstep or the suites silently drift out of sync.

  • .claude/skills/run-virtuoso/smoke-contained-verifier.mjs#L85-L91: replace the inline block with a call to a shared seedInstrumentStore(page) helper.
  • .claude/skills/run-virtuoso/smoke-core-purity.mjs#L112-L118: same replacement.
  • .claude/skills/run-virtuoso/smoke-generators.mjs#L107-L113: same replacement.
  • .claude/skills/run-virtuoso/smoke-herta.mjs#L19-L25: same replacement.
  • .claude/skills/run-virtuoso/smoke-highway-settings.mjs#L41-L47: same replacement.
  • .claude/skills/run-virtuoso/smoke-strings.mjs#L28-L34: same replacement.
  • .claude/skills/run-virtuoso/smoke-variation.mjs#L182-L188: same replacement.
♻️ Proposed shared helper
// e.g. .claude/skills/run-virtuoso/smoke-shared.mjs
export async function seedInstrumentStore(page) {
  // Seed the L1 instrument store BEFORE page scripts run: the host-settings
  // sync (v0.1.11) treats an empty localStorage as a fresh install and ADOPTS
  // host config — which persists whatever instrument the PREVIOUS suite's panel
  // drives wrote through (cross-suite contamination; the panel flipped to bass
  // mid-suite). With the store seeded, the local-wins boot path holds the
  // deterministic 6-string default AND heals the host config for later suites.
  await page.addInitScript(() => {
    try { localStorage.setItem("virtuoso.instrument", JSON.stringify({ stringSetup: "guitar_6_standard", customOpenMidis: "" })); } catch (_) {}
  });
}

Then each of the 7 call sites becomes:

-  // Seed the L1 instrument store BEFORE page scripts run: ...
-  await page.addInitScript(() => { try { localStorage.setItem("virtuoso.instrument", JSON.stringify({ stringSetup: "guitar_6_standard", customOpenMidis: "" })); } catch (_) {} });
+  await seedInstrumentStore(page);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/run-virtuoso/smoke-contained-verifier.mjs around lines 85 -
91, Extract the duplicated instrument-seeding addInitScript into a shared
seedInstrumentStore(page) helper, preserving its existing JSON payload and
initialization behavior. In
.claude/skills/run-virtuoso/smoke-contained-verifier.mjs lines 85-91,
.claude/skills/run-virtuoso/smoke-core-purity.mjs lines 112-118,
.claude/skills/run-virtuoso/smoke-generators.mjs lines 107-113,
.claude/skills/run-virtuoso/smoke-herta.mjs lines 19-25,
.claude/skills/run-virtuoso/smoke-highway-settings.mjs lines 41-47,
.claude/skills/run-virtuoso/smoke-strings.mjs lines 28-34, and
.claude/skills/run-virtuoso/smoke-variation.mjs lines 182-188, replace each
inline block with seedInstrumentStore(page) and import the shared helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.claude/skills/run-virtuoso/smoke-highway-settings.mjs:
- Around line 56-66: Update the setView helper in smoke-highway-settings.mjs to
validate that `#virtuoso-view-select` exists and contains the requested option
before changing its value; throw a clear error such as “View option not found”
when either check fails. Preserve the existing change-event dispatch and
settling behavior for valid highway_3d selections, including those used in the
cycling loop.

In `@docs/topbar-host-parity.md`:
- Around line 263-267: Update the “Failure modes” section in
docs/topbar-host-parity.md to match the supplied screen.js behavior by removing
the promise of a one-shot console.warn, while preserving the documented fallback
behavior and silent write-through skipping.
- Around line 164-170: Update the tuner frame event documentation in the display
section to specify the actual payload shape as {note, freq, cents, hasSignal},
replacing hz and including hasSignal. Keep the surrounding rendering and
idle-state guidance unchanged.
- Around line 3-11: Update docs/topbar-host-parity.md to reflect the existing
host integrations in screen.js, removing the outdated “no code changes yet”
status and zero-reference claim. Refresh the host-check evidence and affected
sections around the status and implementation notes to match the current
implementation and verification state.

In `@ROADMAP.md`:
- Around line 57-59: Update the ROADMAP verification note to distinguish tapping
generation verification from grading-display verification: retain “No HOST CHECK
needed” only for the tapping path, and explicitly state that grading fixes still
require installed-desktop verification with the real detector before release.
Keep the existing note that smoke tests mock the contained verifier and grading
verification remains outstanding.

---

Outside diff comments:
In `@ROADMAP.md`:
- Around line 54-59: The release-state roadmap entries around the
tapping-ladders status and the related lines 66–72 use obsolete versions and
merge assumptions. Update the authoritative roadmap to state that the Gold rung
is already on main at 0.2.0, this promotion produces 0.2.1, and revise the
PR/merge and beta-cut instructions accordingly so they no longer reference the
superseded 0.1.9–0.1.11 sequence.

---

Nitpick comments:
In @.claude/skills/run-virtuoso/smoke-audioctx.mjs:
- Around line 46-52: Replace the inline localStorage seeding block before page
scripts run in the smoke-audioctx setup with the shared instrument-seeding
helper used by driver.mjs and the other smoke files. Preserve the existing
timing and deterministic guitar_6_standard initialization while removing the
duplicated addInitScript boilerplate.

In @.claude/skills/run-virtuoso/smoke-backing-engine.mjs:
- Around line 49-55: Remove the duplicated localStorage instrument-seeding block
from the smoke test and reuse the shared setup introduced for driver.mjs and the
other smoke files. Preserve the deterministic guitar_6_standard initialization
by invoking the existing centralized helper at the same initialization point
before page scripts run.

In @.claude/skills/run-virtuoso/smoke-contained-verifier.mjs:
- Around line 85-91: Extract the duplicated instrument-seeding addInitScript
into a shared seedInstrumentStore(page) helper, preserving its existing JSON
payload and initialization behavior. In
.claude/skills/run-virtuoso/smoke-contained-verifier.mjs lines 85-91,
.claude/skills/run-virtuoso/smoke-core-purity.mjs lines 112-118,
.claude/skills/run-virtuoso/smoke-generators.mjs lines 107-113,
.claude/skills/run-virtuoso/smoke-herta.mjs lines 19-25,
.claude/skills/run-virtuoso/smoke-highway-settings.mjs lines 41-47,
.claude/skills/run-virtuoso/smoke-strings.mjs lines 28-34, and
.claude/skills/run-virtuoso/smoke-variation.mjs lines 182-188, replace each
inline block with seedInstrumentStore(page) and import the shared helper.

In @.claude/skills/run-virtuoso/smoke-gems.mjs:
- Around line 37-43: Extract the repeated localStorage instrument-seeding init
script from the smoke test into the shared helper used by driver.mjs and the
other smoke files, then call that helper here before page scripts run. Preserve
the existing guitar_6_standard seed and error-tolerant behavior while removing
the duplicated boilerplate from this file.

In @.claude/skills/run-virtuoso/smoke-host-surface.mjs:
- Around line 82-88: Remove the duplicated inline instrument-store seeding from
the smoke-host-surface setup and reuse the shared helper or centralized
initialization used by driver.mjs and the other smoke files. Preserve the
existing seeded guitar_6_standard behavior before page scripts execute.

In @.claude/skills/run-virtuoso/smoke-level-gate-async.mjs:
- Around line 72-78: Remove the duplicated localStorage instrument-seeding
addInitScript from the smoke-level gate flow and reuse the shared helper or
centralized setup used by driver.mjs and the other smoke files. Preserve the
deterministic guitar_6_standard initialization before page scripts run.

In `@docs/ear-mode.md`:
- Around line 51-53: Update the fenced ASCII layout block in the documentation
to declare the text language explicitly with the `text` fence marker, preserving
the layout content unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 679ab708-62ef-4ddb-8b10-d2463763d078

📥 Commits

Reviewing files that changed from the base of the PR and between 13a9c8f and 4f9bf8e.

📒 Files selected for processing (28)
  • .claude/skills/run-virtuoso/driver.mjs
  • .claude/skills/run-virtuoso/package.json
  • .claude/skills/run-virtuoso/smoke-audioctx.mjs
  • .claude/skills/run-virtuoso/smoke-backing-engine.mjs
  • .claude/skills/run-virtuoso/smoke-coach-rx.mjs
  • .claude/skills/run-virtuoso/smoke-connect.mjs
  • .claude/skills/run-virtuoso/smoke-contained-verifier.mjs
  • .claude/skills/run-virtuoso/smoke-core-purity.mjs
  • .claude/skills/run-virtuoso/smoke-gems.mjs
  • .claude/skills/run-virtuoso/smoke-generators.mjs
  • .claude/skills/run-virtuoso/smoke-herta.mjs
  • .claude/skills/run-virtuoso/smoke-highway-settings.mjs
  • .claude/skills/run-virtuoso/smoke-host-surface.mjs
  • .claude/skills/run-virtuoso/smoke-level-gate-async.mjs
  • .claude/skills/run-virtuoso/smoke-meter-subdiv.mjs
  • .claude/skills/run-virtuoso/smoke-over-barline.mjs
  • .claude/skills/run-virtuoso/smoke-progress.mjs
  • .claude/skills/run-virtuoso/smoke-renderers.mjs
  • .claude/skills/run-virtuoso/smoke-scoring-e2e.mjs
  • .claude/skills/run-virtuoso/smoke-session-sync.mjs
  • .claude/skills/run-virtuoso/smoke-strings.mjs
  • .claude/skills/run-virtuoso/smoke-variation.mjs
  • ROADMAP.md
  • docs/ear-mode.md
  • docs/topbar-host-parity.md
  • plugin.json
  • screen.html
  • screen.js

Comment on lines +56 to +66
await page.waitForSelector("#virtuoso-view-select", { timeout: 5000 });

// Ensure the 3D highway is the active view, then settle.
const hwBtn = await page.$('.virtuoso-view-btn[data-renderer="highway_3d"]');
if (hwBtn) { await hwBtn.click(); await page.waitForTimeout(600); }
// Ensure the 3D highway is the active view, then settle. (View selector is
// a dropdown since v0.1.13 — value + change, the user event path.)
const setView = (k) => page.evaluate((kind) => {
const sel = document.querySelector("#virtuoso-view-select");
if (!sel) return;
sel.value = kind;
sel.dispatchEvent(new Event("change", { bubbles: true }));
}, k);
await setView("highway_3d"); await page.waitForTimeout(600);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

setView silently no-ops instead of failing when the option is missing.

Unlike switchRenderer in smoke-renderers.mjs, which checks the option exists and throws Renderer option not found if not, this setView just returns when sel (or the matching <option>) is absent. Since the test's entire premise hinges on highway_3d actually being the active view at the baseline snapshot (line 66) and through the cycling loop (line 85-87), a missing/renamed option would silently produce a false PASS instead of a loud failure — masking exactly the kind of regression this suite exists to catch.

🐛 Proposed fix — validate before switching
     const setView = (k) => page.evaluate((kind) => {
       const sel = document.querySelector("`#virtuoso-view-select`");
-      if (!sel) return;
+      if (!sel || ![...sel.options].some((o) => o.value === kind)) return false;
       sel.value = kind;
       sel.dispatchEvent(new Event("change", { bubbles: true }));
+      return true;
     }, k);
-    await setView("highway_3d"); await page.waitForTimeout(600);
+    if (!(await setView("highway_3d"))) throw new Error("Renderer option not found: highway_3d");
+    await page.waitForTimeout(600);
     for (const kind of ["builtin_2d", "highway_2d", "tab_2d", "notation_2d", "highway_3d"]) {
-      await setView(kind); await page.waitForTimeout(400);
+      if (!(await setView(kind))) throw new Error(`Renderer option not found: ${kind}`);
+      await page.waitForTimeout(400);
     }

Also applies to: 85-87

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/run-virtuoso/smoke-highway-settings.mjs around lines 56 - 66,
Update the setView helper in smoke-highway-settings.mjs to validate that
`#virtuoso-view-select` exists and contains the requested option before changing
its value; throw a clear error such as “View option not found” when either check
fails. Preserve the existing change-event dispatch and settling behavior for
valid highway_3d selections, including those used in the cycling loop.

Comment on lines +3 to +11
> **Status: SPEC (2026-07-10, ux lane) — no code changes yet.**
> Why now: Virtuoso's `"fullscreen": true` opt-in hides the host topbar entirely on our
> screen, so the three host-standard topbar elements (Open Tuner, Instrument selector,
> Profile) vanish for the player while inside Virtuoso. Part 1 rule 1: match the host
> standard — bring them into our header, in the host's order, speaking the host's
> visual language, without inventing variants.
>
> Host recon verified 2026-07-10 against the local FeedBack clone (v0.3.0-alpha.1,
> `static/v3/shell.js` + `badges.js` + `profile.js`, `server.py`).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Refresh the implementation status and host-check evidence.

This document still says “no code changes yet” and claims screen.js has zero host-integration references, but the supplied implementation already contains these integrations in screen.js, Lines 18495–18760. Leaving this stale snapshot in the release docs will mislead contributors and verification work.

Also applies to: 29-31

🧰 Tools
🪛 LanguageTool

[grammar] ~4-~4: Ensure spelling is correct
Context: ...ullscreen": true` opt-in hides the host topbar entirely on our > screen, so the three ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~5-~5: Ensure spelling is correct
Context: ...ur > screen, so the three host-standard topbar elements (Open Tuner, Instrument select...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/topbar-host-parity.md` around lines 3 - 11, Update
docs/topbar-host-parity.md to reflect the existing host integrations in
screen.js, removing the outdated “no code changes yet” status and zero-reference
claim. Refresh the host-check evidence and affected sections around the status
and implementation notes to match the current implementation and verification
state.

Comment on lines +164 to +170
- **Click = `window.tuner.toggle()`**, feature-detected exactly like the host badge; if
`window.tuner` is absent the badge does not render (no dead control — honesty rule).
- **Display** (compact mirror of the host card): a small vertical/segment meter glyph +
the live note name. Subscribe `hostBus().on('tuner:frame', cb)` → `{note, hz, cents}`;
when no frames are flowing (tuner closed), show the resting glyph + "Tuner" label —
matching the host card's idle state, at our scale. No hz sub-label at 32px (fold it
into the `title` tooltip: "Open Tuner — 440hz").

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Document the actual tuner event shape.

The current consumer handles note, cents, and hasSignal, while the supplied host contract exposes frequency as freq, not hz. Update this section to use {note, freq, cents, hasSignal} so future integrations do not read a nonexistent field.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/topbar-host-parity.md` around lines 164 - 170, Update the tuner frame
event documentation in the display section to specify the actual payload shape
as {note, freq, cents, hasSignal}, replacing hz and including hasSignal. Keep
the surrounding rendering and idle-state guidance unchanged.

Comment on lines +263 to +267
**Failure modes:**
- `/api/settings` or `/api/tunings` unreachable (older host, headless smoke oddity,
network) → panel behaves exactly as today: `STRING_SETUPS` + `TUNING_PRESETS` +
Virtuoso's own persistence. Write-throughs are skipped silently; a one-shot
console.warn, never a user-facing error.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Align failure-mode documentation with the implementation.

The supplied screen.js paths catch /api/settings and /api/tunings failures silently, but this section promises a one-shot console.warn. Either implement deduplicated warning telemetry or remove that promise from the documentation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/topbar-host-parity.md` around lines 263 - 267, Update the “Failure
modes” section in docs/topbar-host-parity.md to match the supplied screen.js
behavior by removing the promise of a one-shot console.warn, while preserving
the documented fallback behavior and silent write-through skipping.

Comment thread ROADMAP.md
Comment on lines +57 to +59
- **Verified:** `node --check` + `probe-tapping-ladders.mjs` (9/9 rungs: startup guards pass, tp/po/ho present, frets ≤22, no adjacent unison, bass on bass strings) + smoke-generators 134/134, core-purity 40/40, strings, renderers 8/8. Screenshots confirm the `17ᵀ→5ᵖ→9ʰ` cell renders in Tab. **No HOST CHECK needed — generation-path (core) work, our USP, not shell.**
- **DEFERRED (v2, engine gaps flagged by the panel):** bass two-hand independence (needs a two-voice engine), bass open-string cascades, guitar tapped-pedal-point color rung. Not gated the ladder on these.
- **NEXT:** review/merge **PR #4** (dev→main — now the combined grading+tapping PR, https://github.com/got-feedBack/feedBack-plugin-virtuoso/pull/4); on merge `main` goes 0.1.9→0.1.11 (skips a standalone 0.1.10 tag — both land together). Then re-cut beta (`node scripts/cut-beta.mjs --push`). Smoke still MOCKS the contained verifier, so the grading fixes remain real-desktop-DI-dogfood-verified only (standing owe).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Separate tapping verification from grading verification.

“No HOST CHECK needed” is valid only for the tapping generation path. This combined PR also ships grading-display fixes, whose installed-desktop/real-detector verification is a release requirement; the current note says that verification is still owed.

🧰 Tools
🪛 LanguageTool

[grammar] ~59-~59: Ensure spelling is correct
Context: ...g. Not gated the ladder on these. - NEXT: review/merge PR #4 (dev→main — no...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ROADMAP.md` around lines 57 - 59, Update the ROADMAP verification note to
distinguish tapping generation verification from grading-display verification:
retain “No HOST CHECK needed” only for the tapping path, and explicitly state
that grading fixes still require installed-desktop verification with the real
detector before release. Keep the existing note that smoke tests mock the
contained verifier and grading verification remains outstanding.

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