Skip to content

lab: technique-aware fingering, oracle stage — does hand attribution explain the tapping slice? - #200

Open
PhysShell wants to merge 7 commits into
claude/lab-fingering-tie-breakfrom
claude/lab-technique-aware
Open

PhysShell wants to merge 7 commits into
claude/lab-fingering-tie-breakfrom
claude/lab-technique-aware

Conversation

@PhysShell

@PhysShell PhysShell commented Sep 17, 2026

Copy link
Copy Markdown
Owner

Question

Does correct hand attribution explain the tapping failure slice? On the whole corpus, no line with tapped notes had its human path in the v1-fit optimum set. That does not mean the players play badly; it means the model reads every note as the fretting hand's position, and tapping breaks exactly that.

This is the oracle stage: tap labels come from the tab (NoteMark::Tap); no technique inference yet.

Stacked on #199 (base claude/lab-fingering-tie-break, itself stacked on #197). Retarget as the base PRs merge.

Research tooling only (lab/); no production code changes.

Built (red → green)

  • TabLine::tapped — one tap flag per note.
  • technique::tap_aware_costv1 per-note costs and string changes, fretting-hand travel from the previous untapped note (the anchor carries across taps), picking-hand travel from the previous tapped note at tap_shift. Equal to v1_cost without taps.
  • technique::tap_aware_chain — the same objective as a ties::Chain (state = note candidate × the other hand's last candidate), so lab: fingering tie-break — exact optimum sets and a learned lexicographic secondary #199's exact optimum-set DPs apply. Brute-force-checked over position assignments for every tap mask.
  • fingering_gap taps — tap-blind vs tap-aware under the same weights (so costs share units), plus an untapped baseline reweighted to the same line lengths.

Re-measured after #201 (GPIF tapping import)

Rerun of this PR's lab code on a local, unpushed merge of main (full numbers in the audit's "Re-measurement after #201" section). Whole corpus: the slice grows to 242 lines (+87 GP6/7 lines from 30 files; the original 155 lines are unchanged line by line). The old length-matched baseline had contained that unlabelled GP6/7 tapping.

v1-fit, whole corpus human path in optimum set excess per note agreement ceiling
all 242, tap-blind 0.0% 3.17 38.3% 43.8%
all 242, tap-aware 1.7% 1.60 43.8% 52.2%
length-matched untapped (corrected pool) 20.9% 1.17 45.3% 54.6%
original 155, tap-aware 1.3% 1.27 44.3% 52.9%
its baseline: before → after correction 21.0% → 21.2% 1.26 → 1.17 44.9% → 45.3% 54.1% → 54.6%
  • Revised: attribution closes most, not all, of the excess gap to the baseline: 94% on the original lines (99% against the old pool), 66% on the added GP6/7 lines against their own format, 78% overall.
  • Stands: exactness is out of reach in both formats (1–2% of tapped lines against 15–24% for every length-matched baseline); tap_shift = 0 is wrong; residual still 75% fretting-hand travel under the current decomposition.
  • Concentrated: two transcriptions of one song carry 23% of the slice's residual, in one-string tapping figures with open-string pull-offs. Without them the added lines match their format's baseline on excess (1.38 vs 1.38) but not on exactness (2.4% vs 24.4%).
  • Stage 2 starts from this baseline (242 lines, 8,803-line untapped pool), reported per format and with a per-song concentration check.

Original results, as first run (whole corpus: 155 lines, 14,480 notes, 3,132 tapped; holdout slice has only 15 lines and is not interpreted)

v1-fit weights:

model human path in optimum set excess per note agreement on tapped notes ceiling
tap-blind 0.0% 2.94 39.4% 30.7% 45.7%
tap-aware (tap_shift = 1) 1.3% 1.27 44.3% 44.6% 52.9%
length-matched untapped lines 21.0% 1.26 44.9% 54.1%
  • (Superseded in part, see above.) Attribution explains the slice's excess, not its exactness. Every continuous measure reaches the length-matched baseline (production v1 weights agree), yet human paths land in the optimum set in 1.3% of tapped lines against 21.0%. 153 of 155 lines keep a positive residual.
  • Where the residual is: 75% fretting-hand travel, 12.5% picking-hand travel, 12.5% open-string penalty. Tapping figures live on one string (the authors put a tap on the preceding fretted note's string 1,023 times, the model's optimum 525), and open strings are natural pull-off targets. With string changes free under v1-fit, the model scatters the figure to save fret travel.
  • tap_shift = 0 is wrong: a free picking hand flattens the objective (no unique optima) and worsens tapped-note choices (23.1%).
  • Control: 0 of 8,890 untapped lines differ between the two objectives.

Next: legato continuity (hammer-on / pull-off / tap binding notes to one string, from GP TechniqueSpans) before any hidden technique inference.

Full write-up: docs/audit/2026-09-fingering-tap-attribution.md.

Checks

  • cd lab && cargo test: 103 tests green (6 technique, 31 fingering, 17 ties, 13 optir, 24 oracle spike, 12 propagate).
  • cargo fmt --check clean; clippy clean at the crate's deny level.

🤖 Generated with Claude Code

…e tab)

The v1 objective reads every note's position as the fretting hand's
position. Tapping breaks that: a tapped note is played by the picking hand
while the fretting hand stays put. On the whole corpus, no line with tapped
notes had its human path in the model's optimum set (against 27% of lines
without taps), so tapping is a sharp failure slice of the model — not of
the players.

Oracle stage only: technique labels come from the tab. Pins:
- TabLine::tapped — one NoteMark::Tap flag per note;
- technique::tap_aware_cost — v1 unary costs and string changes between
  neighbours, fretting-hand travel from the previous untapped note (the
  anchor carries across taps), picking-hand travel at tap_shift from the
  previous tapped note; equal to v1_cost without taps; hand-computed on the
  5 → 8 → tap 12 → 8 → 5 figure (14 tap-blind, 6 tap-aware);
- technique::tap_aware_chain — the same objective as a ties::Chain whose
  states pair a note's candidate with the other hand's last candidate:
  brute force over position assignments gives the same optimum and
  optimal-assignment count for every tap mask, every admissible state path
  scores its assignment exactly, and without taps it is the v1 chain
  (optimum, count, production path);
- LabError::LabelLength for labels that do not cover the line;
  Chain::from_parts for objectives built outside `ties`.

tapped is all-false and the technique functions are todo!() here (7 red).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tab_lines records NoteMark::Tap per note alongside the hand anchor.
tap_aware_cost walks the line with one travel anchor per hand: untapped
notes pay position_shift from the last untapped note, tapped notes pay
tap_shift from the last tapped note, and string changes are charged
between neighbours as in v1.

tap_aware_chain builds the same objective as a ties::Chain: a note's
states pair its candidate with the other hand's last candidate (or none
yet); a transition keeps that carried candidate when the hand repeats and
takes over note i − 1 when the hand switches, and anything else costs an
inadmissible sentinel. State paths and position assignments correspond
one to one, so optimum_set and lexicographic_path apply unchanged; with no
taps the chain is the v1 chain.

Suite green (6 technique + 31 fingering + 17 ties + 49 spike/optir);
clippy clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fingering_gap taps selects the lines with tapped notes and, for v1-fit and
production v1 weights, compares the tap-blind objective with the tap-aware
one (tap_shift = position_shift, so costs share units, and tap_shift = 0)
on the whole corpus and on holdout songs: lines whose human path lies in
the model's optimum set, human excess (per line and per note), unique
optima, production-order agreement overall / on tapped / on fretted notes,
and the ceiling. An untapped baseline reweighted to the tapped slice's line
lengths gives the fair reference, and a control asserts the two objectives
agree on every untapped line (0 of 8,890 differ).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Records the tap-attribution experiment (aggregates only, whole corpus;
the 15-line holdout slice is reported but not interpreted): with tap labels
from the tab and unchanged weights, the tapped slice reaches the
length-matched untapped baseline on excess per note (2.94 -> 1.27 vs 1.26),
agreement (39.4% -> 44.3% vs 44.9%) and ceiling (45.7% -> 52.9% vs 54.1%),
but its human paths almost never land in the optimum set (1.3% vs 21.0%).
75% of the residual is fretting-hand travel spent keeping tapping figures
on one string, and open strings used as pull-off targets make up another
12.5%; legato continuity is the missing term, ahead of any hidden
technique inference. Uses "human path in the model's optimum set" rather
than "human optimal" throughout.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: e32e33fa-6970-41ea-b366-8faafe68a23b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

The audit read "75% of the residual is fretting-hand travel" as if it
located the problem. It is a decomposition under the current objective:
adding a continuity cost would move the optimum and redistribute the
residual across the travel and open-string terms. The section now says so,
states string continuity (H1) and conditional open strings (H2) as
hypotheses for the next oracle stage, and the conclusion and decision
record follow suit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Measured while preparing the legato stage: the guitarpro 0.4.2 GPIF import
never reads the Tapped note property (the beat's tap effect stays a
placeholder), so no GP6/GP7 tapping reaches griff, although 31 of the
corpus's 145 GPIF files contain it (1,006 Tapped note definitions, 45
LeftHandTapped). The audit now states that the 155-line tap slice is
GP3-5 material only and that GP6/7 tapping sits unlabelled inside the
"untapped" lines and the length-matched baseline.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
#201 imports GP6/7 tapping. Rerun on this PR's lab code merged locally
with main: the slice grows from 155 to 242 lines, and the length-matched
baseline had contained that unlabelled tapping (1.26 -> 1.17 excess per
note for the original lines).

Hand attribution closes most, not all, of the slice's excess (94% on the
original lines against the corrected pool, 66% on the added GP6/7 lines
against their format, 78% overall); exactness stays out of reach in both
formats. The remaining excess concentrates in one-string tapping figures
with open-string pull-offs. Stage 2 starts from this re-measured baseline,
per format and with a per-song concentration check.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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