Journal
Sections
Reading settings
Voice
No voice for this language in this browser.
More voices
Voice for English
Code and English terms
Off: the page’s voice reads everything, with no pause between two voices.
No voice for this language in this browser.
More voices
More voices: Microsoft Edge has natural online voices, and Windows (Settings › Time & language › Speech), macOS and Android install others.
Progress
Section titled “Progress”- Lab scaffold: mission, outline of seven prototypes, CI workflow
- P1, voicing space explorer: build step, page, measurements, lesson
- P2, play a chord, see the universe
- P3, album covers
- P4, AI render pass
- P5, 3D-printable bracelets
- P6, full chain
- P7, playability model
What the lab found in GuitarAlchemist/ga while measuring, not while reading. Line numbers point at 66bdd049, the commit the index was exported from. None of these is a bug report yet: they are what the measurements showed.
| Expected | What happens | Where | Measure | Status |
|---|---|---|---|---|
The index and the two FretDiagram renderers agree on which string comes first |
The index writes a diagram high E first; both renderers read it low E first | FretDiagram.cs:14, FretDiagram.tsx:8-11 |
Row 1000 is 1-2-x-5-x-1 for MIDI 65, 61, 55, 41; 30,000 sampled rows read high E first, 0 mismatches |
Fixed upstream by #696, merged 2026-09-23: the React renderer honours the high-E-first order the API sends, with a test; the C# FretDiagram now documents its low-to-high tokens |
The React FretDiagram shows every fretted note |
It drops a dot when the grid starts at the nut and the voicing reaches a fret below the last row | FretDiagram.tsx lines 31 and 104 |
15,853 of 297,910 guitar voicings, 5.32 % | Fixed upstream by #696, merged 2026-09-23: the grid’s fret window is computed so every fretted note has a row, with a test |
OptickIndexReader’s summary describes the vectors it returns |
It says “L2-normalized”; each partition is normalized, then scaled by the square root of its weight | OptickIndexReader.cs |
Squared length 1.15 or 1.05, never 1 | Fixed upstream by #696, merged 2026-09-23: the summary now gives the squared length as the sum of the partition weights, 1.15 or 1.05, and a test checks it on the vectors. The behaviour was right and is unchanged |
| Two voicings that sound and look different are two points | Voicings with the same pitch classes, the same top note and a similar shape collapse onto one vector | OptickIndexWriter.cs |
121,768 of 297,910 guitar vectors are bit-for-bit copies of another, 40.9 %; the largest group holds 12 voicings of F♯ diminished with A on top | Reproduced. A consequence of the embedding rather than an obvious defect — to arbitrate before changing anything |
| The same commit exports the same index | Deduplication keeps the cheapest voicing of each group, generation runs in parallel, and ties go to whichever arrives first | OptickIndexWriter.cs |
Two exports of one build: every shared vector identical, 70 voicings present in one and missing from the other | Fixed upstream by #730, merged 2026-09-24: generation is still parallel, but the voicings come out in fret order, so ties go to the same voicing every run. Two guitar exports of GA’s main hashed 776c849e… and d0a7d1d9… before, 138603e8… both times after |
--export-max N samples the index |
It keeps the first N raw voicings in generation order, which starts high on the neck | OptickIndexWriter.cs |
--export-max 3000 produced 2,641 vectors, 0 of them in the full index |
Changed by #730: the first N voicings are now the same on every run and come from the nut up, in fret order; they are still not a sample of the index. Not re-measured (to verify) |
| A slash chord names its bass with the spelling of the key | Some names use the wrong enharmonic, for example Gmaj7/Gb instead of Gmaj7/F♯ |
index chord names | Seen while reading the largest duplicate groups | Reproduced, not yet counted |
barreRequired is true for a barre chord |
It asks for the same fret on three adjacent strings, so the E-shape F barre 133211 — strings 1, 2 and 6 at fret 1 — is not a barre, while the A-shape x13331 is |
VoicingPhysicalAnalyzer.cs:219-233 |
133211 and the four-string xx3211 both score exactly 4.50 |
Reproduced (P7) |
minimumFingers counts fingers |
It counts distinct frets, capped at 4 | VoicingPhysicalAnalyzer.cs:102-103 |
Am x02210 and Am7 x02010 both score 2.29; same for E against E7 and D against Dsus4 |
Reproduced (P7) |
| The CAGED shape names the shape | It reads a voicing’s first string as string 6, but GA stores string 1 (high E) first; and it gives up as soon as string 6 is muted, before trying the A and C shapes | VoicingPhysicalAnalyzer.cs, DetectCagedShape |
Over GA’s 667,125 generated guitar voicings: 92 tagged E-Shape, all wrong (0-2-2-1-x-x, which is xx1220), no A or C shape, nothing for 133211 or open E. With the fix: 92 E, 46 A, 152 C shapes |
Found while reviewing a fix for P7; fixed upstream by #735, merged 2026-09-25 2026-09-24 |
The Difficulty label and the DifficultyScore agree |
The label’s “Beginner” band asks for a span of at most 64 mm, under two frets at the nut, so the open C chord is “Intermediate”; and 141404, labelled “Advanced”, scores 4.65, the 26th percentile of the index |
VoicingPhysicalAnalyzer.cs:105-112 |
3.50 and “Intermediate” for x32010 |
Reproduced (P7) |
| A voicing nobody can play scores high | 101,967 of 297,883 guitar voicings (34.2 %) have no legal fingering under a four-finger model, and the highest score GA gives one of them is 7.73, inside the range of ordinary chords, so maxDifficulty: 8 returns them |
VoicingFilterService.cs:53-70 |
The search of fingering.mjs over the whole index |
Reproduced (P7). The hand model is mine and has no thumb, so 34.2 % is an upper bound |
ga_chord_to_set('Cdim7') returns GA’s own diminished-7, 0 3 6 9 |
It returns 0 3 6 10, a half-diminished seventh, Forte 4-27; ga_parse_chord('Cdim7') gives quality diminished with component ext:7, so the seventh is added as a minor one |
GA’s running MCP server against CanonicalChordPatternCatalog.cs:66 |
The exchange in results/ga-check.json, 2026-09-17 |
Reproduced (P5), not reported. Fixed upstream by #681, merged 2026-09-23; re-run on 27a1257: 0 3 6 9, 4-28 2026-09-24 |
ga_chord_to_set('Cm7b5') returns GA’s own half-diminished-7, 0 3 6 10 |
It returns 0 3 7 10, a plain minor seventh: the b5 alteration is dropped |
GA’s running MCP server against CanonicalChordPatternCatalog.cs:65 |
The same exchange; get_scale_notes and Cmaj7 agree with the files, these two do not |
Reproduced (P5), not reported. Fixed upstream by #681; re-run on 27a1257: 0 3 6 10, 4-27 2026-09-24 |
Experiments
Section titled “Experiments”Each prototype writes its hypotheses before the measuring script exists, and they are committed first. A refuted hypothesis stays here: it is the result that cost the most to get.
| Question | Hypothesis, written before measuring | Result | Verdict |
|---|---|---|---|
| How many dimensions does the voicing space really need? | Committed in hypotheses.md at 03f2bbb; the file says which one was not blind |
3 components explain 22.2 % of the variance, 10 explain 48.0 %, 32 explain 84.7 %, 64 explain 99.9 % | The first two axes are mostly MORPHOLOGY, the third STRUCTURE, CONTEXT weighs nothing (2026-09-17) |
| Does a 3D view keep the neighbours the exact search finds? | H2 set a recall@10 threshold of 0.15; H3 predicted the full index would do worse than the sample | Recall@10 of 0.168 within the 30,000 sample, 0.348 against all guitar voicings; with 32 components, 0.693 and 0.787 | H2 missed its threshold, H3 was wrong — both refuted (2026-09-17) |
| Is WebGPU faster than WebGL 2 for 300,000 points? | A first series said 3.5 ms against 0.5 ms and looked like a finding | With trackTimestamp, the render pass takes 0.066 ms on WebGPU and 0.071 ms on WebGL 2 at 30,000 points, 0.26 ms for all 297,910, 2.3 ms for 3 million random points |
The first series was a measuring mistake: 3.5 ms is how long onSubmittedWorkDone takes to resolve, not GPU work. The lesson quotes the second series (2026-09-17) |
| Does picking on the CPU hold at full index size? | H6 expected more than 16 ms at 297,910 points | 0.2 ms for 30,000, 1.4 ms for 297,910, 14 ms for 3 million | H6 refuted (2026-09-17) |
| Can the browser hold the whole index? | — | 156 MB loaded and decoded in 341 ms, 180 MB of JavaScript heap, 28 ms to find the ten exact neighbours | It can, and the lesson ships that mode (2026-09-17) |
| Can a chromagram in a page name the chord you play? | Eight figures predicted before any recording | 79.3 % of 624 synthetic strums named exactly — 89.1 % in root position, 50 % for inversions — and 11 of 17 CC0 recordings; estimating the notes before the chromagram was worth 39 points | Seven of the eight figures held; the oracle, the noise conditions and the recorded failures did not (2026-09-17) |
| Can a small model trained on one CPU core beat GA’s hand-written playability cost? | Twelve figures committed in hypotheses.md at c6a9937 |
Gradient boosting reaches a Spearman of 0.964 against the target and a pairwise accuracy of 0.921, where GA’s score reaches 0.596 and 0.715; a least-squares line through the physical span alone reaches 0.794 | Yes, but the finding is that GA’s three extra flags cost it more than they earn (2026-09-17) |
| How many of GA’s guitar voicings can four fingers play? | H11: more than 98 % | 65.8 %: the search finds no legal fingering for 101,967 of 297,883 | Badly refuted, and it split the prototype in two: a ranking task on what can be played, a yes-or-no question on the rest (2026-09-17) |
| Does splitting by voicing instead of by chord flatter the model? | H6: the naive split overstates the boosted trees by 0.005 to 0.05 of Spearman | 0.0005; the split by transposed shape moves it by 0.002 | Refuted. Group splitting only costs you when the group carries information the features do not, and here both sides are read off the same diagram (2026-09-17) |
| Is the fingering search expensive enough to be worth replacing with a model? | H12: the model scores a voicing at least 50 times faster | 3.8 times: 0.0145 ms against 0.0558 ms | Refuted. The premise of the prototype was wrong; what the model buys is 949 KB that ships without the hand model (2026-09-17) |
| Can a pitch-class bracelet become an object a slicer accepts? | Twelve figures committed in hypotheses.md at 8a2ed53 |
30 solids out of 30 watertight, one body, Euler characteristic 0; 5.2473 cm3, bore 64.9913 mm, thinnest wall 1.9954 mm, no face below the 45-degree support threshold; PrusaSlicer 2.9.6 gives 6.42 g and 35 min 40 s | Yes, and four of the twelve predictions were refuted (2026-09-17) |
| Does a mesh written without a boolean union fail its checks? | H2: the fourteen parts concatenated are not watertight | They are watertight, manifold3d accepts them, and they are 14 bodies; volume 2.6 % high, surface 10.8 % high, wall measured at 1.40 mm — and PrusaSlicer slices them to the same 6.42 g with no repair message | Refuted twice: neither the mesh checks nor the slicer notice (2026-09-17) |
| How much of a bracelet needs support if its beads are spheres? | H6: between 3 % and 8 % of the surface | 0.35 %, worst slope 4.87 degrees | Refuted in magnitude, right that supports are needed (2026-09-17) |
| Does the bead prototype 2 draws survive a 2 mm wall? | H4: the bore stays at 64.98 mm or more | A sphere of radius 3.0 mm centred on the outer surface takes the bore to 63.0287 mm, 1.96 mm lost | The spherical variant failed and is published as a failure; the cone ships (2026-09-17) |
2026-09-16 — P1: where the data comes from
Section titled “2026-09-16 — P1: where the data comes from”- GA’s
mainwas at66bdd049(“chore(quality): snapshot 2026-09-16”). The index isn’t in the repository:FretboardVoicingsCLI --export-embeddingsgenerates it intostate/voicings/optick.index. - I didn’t rebuild it. The session measuring GA’s performance had built
FretboardVoicingsCLIin a clean clone at that commit and exported the index between 23:00 and 23:02 local time, in 149 seconds. Its log says “688,351 raw, 313,047 unique”. I copied that file and pinned it by hash: 183,770,270 bytes, SHA-2561e91e4695bab8aa4bb451fdd1f199a4afaa4c4ad913f08acc5163c6ab07caa23, 313,047 voicings (297,910 guitar, 7,795 bass, 7,342 ukulele), 124 dimensions, schema hash0x37cd8ecf, the one my CRC-32 ofEmbeddingSchema.CompactLayoutV4gives. - Row 1000 settles the string order. Its diagram is
1-2-x-5-x-1and its MIDI notes are 65, 61, 55, 41: read string 1 first (high E + 1 = F4, 65), not low E first. On a chord chart, that shape is1x5x21. The build step checks all 30,000 sampled rows the same way: 0 mismatches. - GA disagrees with itself here.
FretDiagram.csand the ReactFretDiagram.tsxtake frets low E first; the index writes them high E first. The GA AI course found the same split in an MCP tool’s example. - The index’s vectors don’t have unit length, although
OptickIndexReader’s summary says “L2-normalized”: each partition is normalized, then scaled by the square root of its weight, so a vector’s squared length is the sum of the weights of its non-empty partitions, 1.15 or 1.05. The dot product is still the weighted partition cosine, which is what the search needs.
2026-09-16 — P1: hypotheses
Section titled “2026-09-16 — P1: hypotheses”- Written in
results/hypotheses.mdand committed in03f2bbb, before the measurement script existed. - One of them isn’t blind, and the file says so: a smoke test of the build step on 2,000 voicings had already printed that three components explain 22.2 % of the variance.
2026-09-17 — P1: measurements
Section titled “2026-09-17 — P1: measurements”- Three principal components explain 22.2 % of the variance, ten 48.0 %, 32 84.7 %, 64 99.9 %. The first two axes are mostly MORPHOLOGY, the third STRUCTURE; CONTEXT weighs nothing.
- Recall@10 of the 3D view against the exact neighbours: 0.168 within the 30,000 sample, 0.348 against all guitar voicings. With 32 components: 0.693 and 0.787. H2 missed its 0.15 threshold; H3, which predicted the full index would do worse, was wrong.
- 121,768 of the 297,910 guitar vectors are bit-for-bit copies of another voicing’s vector. The largest group holds 12 voicings of F♯ diminished with A on top. Voicings with the same pitch classes, the same top note and a similar shape are the same point for OPTIC-K.
- GA’s React
FretDiagramdrops a dot for 15,853 guitar voicings (5.32 %): the grid starts at the nut when the lowest fretted note is on fret 2, and a voicing reaching fret 6 needs a sixth row.
2026-09-17 — P1: two exports, one commit
Section titled “2026-09-17 — P1: two exports, one commit”- The performance session exported the index a second time from the same build. Matched by instrument and diagram, every shared vector is identical, but 70 voicings of one export are missing from the other: deduplication keeps the cheapest voicing of each group, the generator runs in parallel, and ties go to whichever arrives first. The file’s hash pins the data; the commit doesn’t.
- I tried to confirm the provenance by regenerating 3,000 guitar voicings with GA’s CLI (
--export-max 3000, 4.1 s under the heavy-work lock). It found 0 of their 2,641 vectors in the full index. The limit keeps the first 3,000 raw voicings in generation order, which started at fret 19; within that small set, deduplication kept high-neck voicings that lose to cheaper shapes in the full export. A partial export can’t check a full one.
2026-09-17 — P1: frame times, measured twice
Section titled “2026-09-17 — P1: frame times, measured twice”- First series:
render()plusonSubmittedWorkDone, 120 frames at 1280 × 720 in headless Chromium 153 on the RTX 5080. WebGPU came out at 3.5 ms for 30,000 points and WebGL 2 at 0.5 ms. That looked like a finding, and it was a measuring mistake: WebGPU’s number didn’t change between 30,000 and 3 million points. - Second series, with three.js’s
trackTimestampandresolveTimestampsAsync: the render pass takes 0.066 ms on WebGPU and 0.071 ms on WebGL 2 for 30,000 points, 0.26 ms for all 297,910, 2.3 ms for 3 million random points. The 3.5 ms is the timeonSubmittedWorkDonetakes to resolve, not GPU work. renderer.info.render.drawCallsread 434 and then negative values in the probe, because three.js resets it at each animation frame and the probe awaits across frames. The lesson doesn’t quote it.- Picking by projecting every point on the CPU: 0.2 ms for 30,000, 1.4 ms for 297,910, 14 ms for 3 million. H6 expected more than 16 ms at 297,910.
- Local full-index mode: 156 MB loaded and decoded in 341 ms, 180 MB of JavaScript heap, 28 ms to find the ten exact neighbours of a voicing in the browser.
2026-09-17 — P1: publishing
Section titled “2026-09-17 — P1: publishing”- No pipeline for live demos existed on the site yet.
scripts/publish.mjsbuilds the page with Vite (base: './') intopublic/ga-lab/p1/, which Astro copies as is: 3.65 MB, plus a 241 KB screenshot. The lesson shows the screenshot and a button, and loads the page in an iframe only on click, through a smallLazyDemocomponent that prefixes the site’s base path. scripts/check-published.mjsservespublic/under/learn/like GitHub Pages and opens the published page: WebGPU, 30,000 points, no error.
2026-09-17 — P2: play a chord, see the universe
Section titled “2026-09-17 — P2: play a chord, see the universe”- P2 published. The chord recognizer names 79.3 % of 624 synthetic strums exactly (89.1 % in root position, 50 % for inversions) and 11 of 17 CC0 recordings; estimating notes before the chromagram was worth 39 points. Seven of eight predicted figures held; the oracle, the noise conditions and the recorded failures did not. The audio never leaves the page (connect-src ‘none’). Lesson
2026-09-17 — P7: the label that does not exist
Section titled “2026-09-17 — P7: the label that does not exist”- The question is whether a model beats GA’s
DifficultyScore, and GA’s score cannot also be the answer sheet. So P7 builds a target:fingering.mjsenumerates every legal way of putting fingers 1 to 4 on the fretted notes — any finger may barre, a barre may hold part of a fret and pass under higher notes — and keeps the cheapest. The fret and string geometry is GA’s own; every weight is a constant I chose and listed inWEIGHTS. - Three checks on that target, none of them proof: 32 pairwise judgements on canonical shapes in
annotations.json, whose annotator is named in the file (this model, from written rules, not a guitarist); a checklist of what method books say; and GA’s own score. The search agrees with 25 of the 25 high-confidence pairs, GA with 22, the checklist with 19. The search’s perfect score measures internal consistency — the same head wrote the hand model and the protocol — and the file says so. - Reading GA’s five lines before measuring anything found four things, all in the QA table above: the barre flag misses the F barre,
minimumFingerscounts frets, a full barre and a four-string F score identically, and theDifficultylabel contradicts theDifficultyScore.
2026-09-17 — P7: hypotheses
Section titled “2026-09-17 — P7: hypotheses”- Twelve numbered predictions in
results/hypotheses.md, committed inc6a9937beforetrain.mjshad been run on any data, synthetic or real. - H0 is marked “seen, not blind”: the annotation check had already run when the file was written, and its three numbers are recorded as measurements, not predictions. They did not move when the search was later generalised.
2026-09-17 — P7: what the search rejects
Section titled “2026-09-17 — P7: what the search rejects”- The first version of the search let only the index finger barre, and only across every note of the lowest fret. It rejected 61.9 % of the guitar index. Letting any finger barre, over part of a fret, passing under higher notes, brought that to 34.2 % — 101,967 of 297,883 voicings with no legal fingering at all.
- What is left is genuinely unplayable:
x12345needs five fingers on five frets, and103212has two notes at fret 1 on either side of an open string, so neither of the two barres it would need is possible. - Those rows have no rank, so the experiment splits in two: the ranking task drops them, and a second model answers whether a voicing can be fingered at all. Split by chord, gradient boosting reaches an accuracy of 0.903 against a majority baseline of 0.663, and an AUC of 0.973; GA’s score, read as a ranker for the same question, reaches 0.677.
2026-09-17 — P7: measurements
Section titled “2026-09-17 — P7: measurements”- One core of an Intel Core Ultra 9 285K, Node.js v24.12.0, no GPU, no dependencies. 297,883 voicings, 195,916 of them fingerable, split by chord into 118,243 / 38,287 / 39,386. The whole run — three splits, six predictors each, bootstrap intervals over 200 rounds resampled by chord, and the feasibility model — took 302 seconds; building the dataset from the 183 MB index took 7.8 seconds.
- Spearman against the target, held-out rows: gradient boosting 0.964, MLP 0.959, random forest 0.957, ridge 0.906, a line through the physical span alone 0.794, GA’s score 0.596, the rule checklist 0.449, the mean 0. Pairwise accuracy across chords: 0.921 against GA’s 0.715.
- The boosted trees put 0.723 of their gain on
diagonalMm, the widest pair of fretted notes measured across the strings as well as along the neck — not GA’s along-the-neck span, which holds 0.028. GA’s barre flag holds 0.011; the model rebuilds barres frommaxAtMinFret, which counts strings at the lowest fret without asking whether they are adjacent, exactly the test GA’s flag gets wrong. - Sizes: 949 KB for 300 boosted trees, 11.9 MB for 60 deep forest trees, 15.4 KB for the MLP, 1.24 KB for ridge.
- Four predictions wrong (H2, H6, H11, H12), two half right (H9, H10). Lesson
- A bug worth remembering, found by a failing split: the first group hash was plain FNV-1a, whose high bits barely move between
chord0andchord39. A split reads exactly those high bits, so all forty chords landed in the training set and the test set came out empty. A murmur3 finalizer fixed it, and a test now checks the three sides get 60/20/20 of three thousand near-identical keys.
2026-09-17 — P5: hypotheses, then a bangle
Section titled “2026-09-17 — P5: hypotheses, then a bangle”- Twelve predictions in
results/hypotheses.md, committed in8a2ed53before any mesh existed. Four came out wrong: H2, H6, H11’s repair prediction and H7’s premise. - The pitch classes are parsed out of GA at
a826864, fromCanonicalChordPatternCatalog.csandScale.cs, and cross-checked against the catalog prototype 2 already ported. 58 chord patterns, 19 scales, SHA-256 of both blobs recorded. - 31 solids built, unioned with manifold3d and measured in 5.95 s: all watertight, one body, Euler characteristic 0, manifold3d NoError. A 65 mm bangle with cone beads is 5.2473 cm3 and 6.507 g of PLA, its bore 64.9913 mm, its thinnest wall 1.9954 mm, and no face of it below the 45-degree support threshold.
- PrusaSlicer 2.9.6 was downloaded and run in console mode: 6.42 g and 35 min 40 s for a triad, 6.47 g and 39 min 56 s for a seven-note scale, 6.15 g and 26 min 43 s for the bare band. Lesson
2026-09-17 — P5: three things that were not true
Section titled “2026-09-17 — P5: three things that were not true”- “Watertight” is about edges. H2 said the fourteen parts written into one file without a boolean would fail the test. They pass it, and manifold3d accepts them too — fourteen closed solids in one bounding box. What they cost is the volume (2.6 % high), the surface area (10.8 % high) and the wall measurement, which reads 1.40 mm because the ribs’ buried faces are still real surfaces.
- The slicer does not care either. PrusaSlicer sliced the un-unioned mesh to 2,154.16 mm of filament against the union’s 2,154.05, the same 6.42 g, and reported no repair. The case for doing booleans properly has to be made on other grounds than the slicer.
- The keel’s chamfer was 36 degrees, not the 45 the hypotheses claimed.
min_slope_degprinted 36.027 and nothing else would have caught it. Making the ramp a real 45 degrees removed the overhang and left the mark 0.4 mm proud at 1 mm above the bed, half an ordinary rib. Deleting the ramp fixed both: a vertical feature on a vertical wall, printed axis up, overhangs nothing. - The bead from prototype 2 eats the bracelet. A sphere of radius 3.0 mm centred on the outer surface of a 2.0 mm wall reaches 1.0 mm into the bore: the inner diameter drops from 64.99 to 63.03 mm, and 0.35 % of the surface needs support, with a worst slope of 4.87 degrees. H6 had predicted 3 % to 8 %, an order of magnitude out.
2026-09-17 — P5: GA’s server disagrees with GA’s source
Section titled “2026-09-17 — P5: GA’s server disagrees with GA’s source”- Asked through GA’s MCP tools,
ga_chord_to_set('Cdim7')answers C, E flat, F sharp, B flat — Forte 4-27, a half-diminished seventh — whileCanonicalChordPatternCatalog.cshasdiminished-7as 0 3 6 9, Forte 4-28.ga_parse_chord('Cdim7')returns qualitydiminishedwith componentext:7, so the seventh is added as a minor seventh. ga_chord_to_set('Cm7b5')answers C, E flat, G, B flat, a plain minor seventh: theb5alteration is dropped. GA’s file hashalf-diminished-7as 0 3 6 10.get_scale_notesfor C major and A minor, andga_chord_to_set('Cmaj7'), agree with the files. The exchange is recorded inresults/ga-check.json. Two of the thirteen bracelets would have been wrong if they had been built from the running parser instead of the pinned files. For the author to decide whether this becomes an issue on GuitarAlchemist/ga.
2026-09-24 — Upstream fixes checked again
Section titled “2026-09-24 — Upstream fixes checked again”- The two
ga_chord_to_setrows of P5 are fixed upstream by #681, merged 2026-09-23: the F# closure behind the tool now applies the alterations and implies the seventh. Re-run on GA27a1257by music-theory-ga’srecheck/probe.cs, which calls the tool method directly:Cdim7→{C, Eb, F#, A}, 0 3 6 9, 4-28;Cm7b5→{C, Eb, F#, Bb}, 4-27, “Half Diminished Seventh”. GA’s server and GA’s source now agree on both chords. - A blank line before those two rows ended the QA table early, so they rendered as plain text; removed.
- The three rows about
FretDiagramandOptickIndexReaderwere fixed upstream by #696, merged 2026-09-23, each commit with its test:FretDiagram.test.tsxfor the string order,fretWindow.test.tsfor the dropped dots,CompactVectorNormTests.csfor the vector length. - #730, merged 2026-09-24, makes the export reproducible: the parallel generator now emits its windows in fret order, so the export’s tie-break always keeps the same voicing. The two exports compared here, from one build, would no longer differ; the course’s own index, exported from
66bdd049, is unchanged. - A finding outside the lab’s plan, while reviewing a fix for the P7 rows:
DetectCagedShaperead the strings backwards. A probe over GA’s own 667,125 generated voicings found 92 voicings tagged E-Shape, none of them one, and no A or C shape at all, because the check returned as soon as string 6 was muted. #735, merged 2026-09-25, reads the strings in the order the voicing stores them; its test looks the shapes up among the generator’s voicings and fails on 6 of its 7 cases with the old code. The shape feeds the embedding’s shape tag and the CAGED filter, so the course’s index, exported from66bdd049, still carries the old tags.
To verify
Section titled “To verify”- The explorer on macOS Safari and Firefox, with and without WebGPU: tested only in Chromium on Windows.
- Audio on mobile browsers, which may block the
AudioContextuntil a tap. - P7’s target: the 32 pairwise judgements were made by this model from written rules, not by a guitarist, and the hand model’s constants — comfortable spans, barre and mute penalties — are chosen, not measured on a player. A guitarist ranking a few hundred pairs would replace both.
- P7’s 34.2 % of unfingerable voicings is an upper bound: the search has no thumb over the top, no string damped by the fretting hand, and no note held by two fingers.
- P5 has never been printed. Whether 65 mm fits a wrist, whether a 1.2 mm rib can be told from a 2.5 mm keel by touch, whether a 45-degree cone underside droops on a real machine, and whether a 2.0 mm PLA band survives being pushed over a hand are all open.
- P5’s eight degenerate, zero-area triangles left by manifold3d in the cone unions: PrusaSlicer says nothing about them, and no other slicer has been tried.