Skip to content

feat(editor): keys parts get a playability lint too#356

Merged
ChrisBeWithYou merged 1 commit into
mainfrom
feat/keys-playability-lint
Jul 22, 2026
Merged

feat(editor): keys parts get a playability lint too#356
ChrisBeWithYou merged 1 commit into
mainfrom
feat/keys-playability-lint

Conversation

@ChrisBeWithYou

Copy link
Copy Markdown
Contributor

The gap

Keys were the one instrument with no advisory playability lint at all. The lint pass is fret/anchor-shaped (stretch/overlap/open-bend/legato/finger-conflict), so _lintResults() just bailed to an empty result on any keys arrangement (|| isKeysArr()). A keys chart could ask a physically impossible reach and nothing flagged it. This closes the "keys parts get NO playability lint" gap called out in EDITOR-NEXT-WINS §2 Step C.

What it lints

For keys parts, a piano lint now runs (instead of bailing), over each onset-clustered simultaneity — using the pedagogy seat's thresholds:

  • keys-span — one hand (grouped by the per-note lh/rh assignment) reaching over an octave (a look) / over a 10th (beyond most hands, KEYS_SPAN_ERR). An exact octave is fine.
  • keys-handmore than five notes in one hand at once (a hand has five fingers).
  • keys-muddy-low — the two lowest simultaneous pitches within a major 3rd, below ~E2, where close intervals turn to mud.

Span/count use the authored hand where present — assign hands (the merged hand-arc) to get that feedback; unassigned notes are skipped there. muddy-low is pitch-only, so it runs whether or not hands are assigned. Thresholds are exported named constants so the pedagogy seat can tune them without reading the rules.

Same advisory posture as the fretted lint — a yellow underline on the flagged notes, the count chip, and the click-to-seek popover (labels added for the three keys rules). Never blocks, never auto-fixes.

Scope / deferred

Per the pedagogy spec, two refinements are noted as follow-ups rather than pulled in: a large-hand exception flag on the 10th error, and a hand-leap / impossible-shift rule (a same-hand leap over a 12th faster than ~150ms). Both are additive to the same pass.

Tests

  • tests/keys_lint.test.mjs pins the rules — an exact octave is fine, over it warns, beyond a 10th escalates; span is PER HAND (a wide two-hand voicing is fine); unassigned notes aren't span-linted; >5-in-a-hand; muddy-low's high-register and open-interval negatives, and that it judges the two lowest pitches; onset clustering — and the wiring: _lintResults() now yields keys issues and flags the reached notes (empty on main).
  • npm test green apart from the two known-on-main reds (mixer_meter_teardown, song_fit); pytest untouched (JS-only); lint clean (0 errors).

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@ChrisBeWithYou, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 19 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dd6c167b-5c17-48be-9328-9da54d90c0e5

📥 Commits

Reviewing files that changed from the base of the PR and between e217c91 and d52a3af.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • src/playability-lint.js
  • tests/keys_lint.test.mjs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/keys-playability-lint

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Keys were the one instrument with NO advisory playability lint: the pass
is fret/anchor-shaped, so `_lintResults()` bailed to an empty result on
`isKeysArr()` — a keys chart could ask an impossible reach and nothing
flagged it.

A piano lint now runs for keys parts instead of bailing, over each
onset-clustered simultaneity:
  - keys-span  one HAND (by the per-note lh/rh assignment) reaching over
    an octave (a look) / over a 10th (beyond most hands);
  - keys-hand  more than five notes in one hand at once (five fingers);
  - keys-muddy-low  the two lowest simultaneous pitches within a major
    3rd, below ~E2, where close intervals turn to mud.

Span/count use the authored hand where present (assign hands to get that
feedback — unassigned notes are skipped there); muddy-low is pitch-only,
so it runs regardless. Same advisory posture as the fretted lint (yellow
underline + count chip + popover, never blocks). Thresholds are exported
named constants for the pedagogy seat to tune. Large-hand exception and a
hand-leap/impossible-shift rule are noted as follow-ups.

Tests: tests/keys_lint.test.mjs pins the rules (octave is fine, over it
warns, over a 10th escalates; span is PER HAND so a wide two-hand voicing
is fine; unassigned skipped; >5-in-a-hand; muddy-low high/open negatives;
onset clustering) AND the wiring (_lintResults now yields keys issues +
flags the notes, empty on main). npm test green apart from the two
known-on-main reds; pytest untouched; lint clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017xGPjDBF8NTwTK7VQvizix
Signed-off-by: ChrisBeWithYou <chris@rifflarr.local>
@ChrisBeWithYou
ChrisBeWithYou force-pushed the feat/keys-playability-lint branch from 8a63d6e to d52a3af Compare July 22, 2026 23:00
@ChrisBeWithYou
ChrisBeWithYou merged commit 1799682 into main Jul 22, 2026
4 checks 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