Skip to content

Story: anatomy of a misread — postmortem two demo samples, file pinned defects #2

Description

@Stan15

Context

The engine reads messy human tab text through a layered evidence pipeline, and when it misreads something, the wrongness always has an address:

  1. Grammar/recovery (parse repo): the raw text → tree, with error-recovery that can glue fragments onto the wrong structures.
  2. Block classification (plugins: src/taxonomy/segment-kind.ts): is this block music/prose/directive? Census-based, honest confidences.
  3. Line roles (plugins: src/taxonomy/line-role.ts): within a block, is each line a voice/pitched/rhythm/annotation line?
  4. Instrument inference (plugins: src/instrument/profile.ts): what instrument is this section? Line-name evidence, strict gates.
  5. Note resolution (plugins: src/pitch/note-sound.ts): glyph → pitch or percussion voice (per-line vocabularies).
  6. Time/export (plugins: src/time/, src/export/): onsets, durations, MIDI/MusicXML.

A recent real example of why addresses matter: spaced-frame drum systems in Painkiller rendered as prose. The obvious suspect (a shape regex in layer 2) was wrong — the real chain was: recovery glued a repeat-marker line onto the drum system (layer 1 debris) → a trapdoor in layer 2 fired before the shape census → and fixing THAT exposed layer 3 reading the glued fragment's digits as notes → which poisoned layer 4 into calling the section a Guitar. Three fixes at three layers; the note count (1495→1829) was the only honest done-signal. Diagnosis-by-vibes at the wrong layer would have shipped a no-op.

The demo samples (demo/samples/*.txt in this repo) are 22 real-world files — full Zeppelin/Priest/Rush transcriptions pulled verbatim, plus classical and bass — and they still contain misreads nobody has cataloged.

The task

Produce misread postmortems for two demo samples, and file the defects.

  1. Pick two samples (suggested: one drum-heavy like moby-dick-solo.txt or hot-for-teacher.txt, one non-drum like satie-gnossienne.txt or anon-se-io.txt).
  2. Open them in the demo (npm run demo) AND run them through the engine CLI (in the plugins repo: npx tsx tools/export.ts <file>). Read the file like a musician; note every place the engine's reading disagrees with yours: mis-tinted sections, skipped glyphs, phantom notes, wrong instrument, weird measure counts.
  3. For each disagreement, find its address: write a small probe script (the plugins repo's testing kit makes this easy — pluginTest from @tab-edit/ast/testing, then read blockKindClaim/lineRoles/instrumentProfile/noteSound for the exact bytes) and walk the layers top-down until you find the first one whose output is wrong. That layer is the defect's address; everything below it is just downstream damage.
  4. File one issue per distinct defect containing: the verbatim bytes with exact spans (e.g. [3158,3635) in painkiller.txt), the wrong layer + its wrong output (probe output pasted), what a musician expects, and — only if it's obvious — a suspected fix direction. Do NOT implement fixes: classification/inference changes ripple, and each fix wants its own fixtures-first PR.
  5. A short postmortem write-up per file (in the issue or a gist): the file's dialect, what reads correctly, the defect list, anything structurally novel about the dialect worth adding to a test corpus later.

Why we care

Defect reports with pinned bytes and a layer address are the highest-leverage artifact in this project — they convert "the demo looks wrong" into work anyone can pick up cold. The census that drove the entire drum-support roadmap ("20,561 glyphs, only 33% resolve") was exactly this kind of audit. And as an onboarding path, nothing teaches the architecture faster: after two postmortems you will know the evidence pipeline better than any doc could teach it, because you'll have watched each layer be wrong.

Definition of done

  • Two samples audited end-to-end; every found misread either filed as an issue (pinned bytes + layer + probe output) or explicitly noted as "correct but surprising".
  • Probe scripts attached to the issues (inline or gist) so any fixer can reproduce in one command.
  • Zero engine changes.

House rules (non-negotiable in this codebase)

  • Verbatim bytes or it didn't happen: every claim of a misread carries exact spans and probe output.
  • First wrong layer wins: report the address, not the downstream symptoms (one issue that says "layer 3 misroles this line" beats four issues about the notes that go missing because of it).
  • Confidence numbers are part of the evidence: two different code paths can emit the same claim at the same confidence — the probe must identify the PATH, not just the score (that's the lesson of the Painkiller example).
  • No fixes in this story — the deliverable is diagnosis.

Working with Claude

This story is deliberately the best "understand what Claude is doing" vehicle in the backlog:

  1. Have Claude explain the six layers from the actual source files (not from this issue) and tell you, for each, what question it answers and what evidence it uses. Quiz it: "if a snare line played guitar pitches, which layers could be at fault?"
  2. Claude writes the probes; YOU read the tab text and decide what's musically correct. Never let the model be both prosecutor and judge — its reading of the engine output is evidence, your reading of the music is the verdict.
  3. When Claude proposes a diagnosis, require the discriminating experiment: "what output would we see if it were layer 2 instead of layer 3?" — then run it. A diagnosis that can't name its discriminator is a guess.
  4. It WILL want to fix things it finds. Hold the line; paste the finding into an issue instead.

What this looks like

A probe (this is the whole tool — the testing kit does the heavy lifting):

// probe.ts — run with: npx tsx probe.ts   (in the plugins repo)
import * as fs from "node:fs";
import { pluginTest } from "@tab-edit/ast/testing";
import { blockKindClaim, instrumentProfile, lineRoles, taxonomyPlugin, instrumentPlugin } from "./src/index.js";

const text = fs.readFileSync("path/to/sample.txt", "utf8");
const t = pluginTest({ text, plugins: [taxonomyPlugin, instrumentPlugin] });
for (const section of t.tree.topNode.getChildren("Section")) {
  for (const block of section.getChildren("Block")) {
    console.log(`[${block.rangeFrom(0)},${block.rangeTo(block.rangeCount - 1)})`,
      t.prop(blockKindClaim, block), t.prop(lineRoles, block));
  }
}

A filed defect, in the format that makes it actionable cold:

Volta line glued to drum system reads voice, poisons inference to Guitar
Bytes [3158,3635) in painkiller.txt. Probe output: block claims music@0.9 but
lineRoles[0] = voice for the fragment "x | 1.| 2." (the tail of
/ Repeat 2x), so lineNames = ["x","C","H","S","t","F","B"] and the every-name-
known percussion gate fails → section infers guitar-6-standard. A musician reads
that line as a repeat/volta annotation. First wrong layer: line-role census
(classifyLine, sounds>0 branch). Downstream damage: phantom Guitar part, +3 phantom
notes.

(That example is a REAL defect from this project's history — found, filed, and fixed
exactly this way.)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    storySelf-contained contributor story

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions