Skip to content
learn

Journal

  • Course program: .NET 10, built against GA’s GA.Domain.Core at a pinned commit
  • CI: every lesson’s output compared with its expected file on three OSes
  • Lesson 1: notes, pitch classes and the fretboard
  • Lesson 2: scales, modes and 12-bit scale ids
  • Lesson 3: chords, symbols, inversions and voicings
  • Lesson 4: set classes, interval vectors and the Z-relation
  • Diagrams: bracelets, chord grids, fretboard and circles of fifths drawn by the course program, checked by CI
  • Lesson 5: keys, key signatures and the circle of fifths
  • Lesson 6: the chords of a key
  • Lesson 7: cadences, ii–V–I and the key of a progression
  • Lesson 8: the ukulele and the bass
  • Appendix A: every instrument in GA’s catalogue
  • Appendix B: the OPTIC hierarchy
  • Appendix C: a verdict on every DIFF row of lessons 1 to 7
  • Lesson 9: voice leading and common tones
  • Lesson 10: substitutions and modal mixture
  • Lesson 11: modes in depth
  • Lesson 12: symmetry and modes of limited transposition
  • Lesson 13: extended and altered chords
  • Lesson 14: guitar voicings, shells, drop 2 and drop 3
  • Lesson 15: the fretboard, CAGED, fingering and playability
  • Lesson 16: arpeggios, chord–scale theory and improvisation
  • Lesson 17 (see the outline on the mission page)

The forty divergences between this course’s arithmetic and Guitar Alchemist’s, the nineteen defects they collapse into and the verdict on each are in Appendix C; sixteen of them were merged upstream in GA #711, and three stay as documented design limits. This table is what the appendix does not cover: findings outside the executable comparison of lessons 1 to 7. The BraceletNotation row was read rather than run, and says so; the rows of lessons 9 to 12 come from their programs, which compile GA’s files, copy its functions as they are, or rewrite them, and each row says which.

Expected What happens Where Measure Status
getAllInstruments() returns the instruments the YAML holds It returns one, and two tunings, falling back to hard-coded guitar values without an error or a log line InstrumentsYaml/InstrumentsConfig, GA.Business.Config at a826864 The file holds 122 instruments and 280 tunings; the loader returned 1 and 2. Seven of the 280 are not pitches at all: two pedal steel entries begin with their tuning’s name, one with the word Tuning, one with the instrument’s name, one keeps part of its name Fixed upstream by #734, merged 2026-09-24: the file is read as a mapping, 122 instruments and 280 tunings, and a failure now says so on stderr instead of silently answering with the built-in guitar. Nine tunings were restored from Tunings.txt; the two harp guitar entries keep their `
Tuning.BuildPitchArray numbers the strings of any tuning correctly It decides which end is string 1 by comparing the first pitch with the last one only, so a re-entrant tuning comes out backwards Tuning.BuildPitchArray at a826864 GA’s own two 5-string banjo tunings, whose drone string is written first and sounds higher than the last, are numbered the wrong way round — contradicting Str’s own comment that string 1 is the highest Fixed upstream by #734: the direction is decided by counting rising against falling steps between adjacent strings, so both banjo tunings number string 1 as the highest 2026-09-15, 2026-09-24
Fretboard.GetNote’s documented parameter matches its indexing The comment says “Zero-based string index (0 = lowest string)” and the code indexes Tuning[stringIndex + 1], where string 1 is the highest Fretboard.GetNote at a826864 The comment is wrong, the code is right Reproduced, documentation only 2026-09-15
The MCP server’s ga_chord_to_set spells the chord it was given It takes its pitch classes from an F# closure that keeps one interval per extension and drops the alterations, so the spelling and the Forte number are wrong DomainClosures.fs#L85-L98, GA’s MCP server Cm7b5 gives {C, Eb, G, Bb} and 4-26, where 4-27 with G♭ is right; Cdim7 gives 4-27 and “Scale: Major Seventh”, where 4-28 is right Fixed upstream by #681, merged 2026-09-23. Re-run on 27a1257: Cm7b5 gives {C, Eb, F#, Bb} and 4-27, Cdim7 gives 4-28 2026-09-14, 2026-09-24
ga_set_class_subs groups the chords it lists under the right quality Every chord falls under [maj], because the test is c.EndsWith(""), which is true of every string ChordAtonalTool.cs#L187-L190 The lists themselves are right; only the grouping is not Fixed upstream by #681. Re-run on 27a1257: the major triads under [maj], the minor ones under [m] 2026-09-14, 2026-09-24
ga_scale_by_id and ga_scale_by_name answer for a scale GA knows ga_scale_by_id(2741) returns Major with Forte Number: Some(n/a) where 7-35 is expected, and ga_scale_by_name("Dorian") is not found at all GA’s MCP server One wrong field, one missing lookup Fixed upstream by #681. Re-run on 27a1257: Forte Number: 7-35, and Dorian found as id 1709 2026-09-14, 2026-09-24
Every MCP tool that takes a chord or a key answers Several return the string “An error occurred” rather than an MCP error GA’s MCP server get_chord_voicings("C", maxFret 3), get_neighboring_keys and get_diatonic_chords("A minor") all answer that way; ga_search_voicings("C major open chord") ignored the “open” constraint and returned 8-8-x-x-7-x Reproduced; the server’s build commit was never confirmed, so these are not pinned to a826864 2026-09-14. On 27a1257, get_neighboring_keys("Key of C") answers F, C, G; the other three need GaApi running and were not re-run 2026-09-24
ga_key_from_progression and ga_analyze_progression find the key a cadence establishes They scored chord roots against the natural minor scale and preferred the first chord’s root: Dm7 G7 Cmaj7 gave D minor, Am Dm E7 Am gave A major GuitaristProblemTools.cs#L157-L223, DomainClosures.fs#L255-L336 On 27a1257: Dm7 G7 Cmaj7 gives C major, ii V I; Am Dm E7 Am gives A minor, with E7 labelled v and counted outside the key; Am F C G still puts A minor first, tied 4/4 with C major, against the tool’s own description Fixed upstream by #625, merged 2026-09-24, for the two cadences; the numeral and the description remain, not reported 2026-09-15, 2026-09-24
BraceletNotation.tsx places its dots on its own spokes The dots are placed at angle − 90° while the spokes use cos(angle) with no offset, a quarter turn apart; findSymmetryAxes also misses the axes that fall between two notes and counts each axis twice GA’s ReactComponents/ga-react-components Read in the source, never run Reproduced by #733’s test, and fixed by it, merged 2026-09-24 2026-09-15, 2026-09-24
ga_common_tones and the chatbot’s CommonTones skill name every note two chords share Both call the F# closure domain.commonTones, which builds each chord from its quality’s triad plus one interval per extension, from a six-entry table, and drops the alterations DomainClosures.fs#L85-L98, #L488-L531 The server running on 2026-09-30 (version not reported): 5 of 7 pairs lose one note — F from Bm7b5/G7, B from Cmaj9/Em7, A from Cdim7/F7 and from C6/Am7, B♭ from C9/Gm7 Fixed on GA’s main by #681, whose chordTones now feeds domain.commonTones too: read on 5c3a52a, not run against a server built after it 2026-09-30
VoiceLeadingSkill returns the smallest voice leading, as its answer says (“This is the optimal pitch-class assignment”) It makes the chords the same size by always repeating the smaller chord’s root VoiceLeadingSkill.cs#L131, #L185-L194 G → Cmaj7: 5 semitones where 3 exist, with B doubled. 1,260 of the 2,880 triad → seventh pairs miss the minimum, by up to 4 semitones, and as many in the other direction Reproduced by rewriting the rule in the course program; the chatbot itself was not run; not reported 2026-09-30
The skill’s ten example prompts are read by its own pattern “voice leading C major to G major” does not match: the pattern wants a chord symbol right before “to”. The skill is picked by meaning (CanHandle returns false), so it would receive that prompt and answer “Ask about voice leading between two chords” VoiceLeadingSkill.cs#L35-L57 1 example of 10 Reproduced with the pattern copied as it is; the chatbot itself was not run; not reported 2026-09-30
ga_voice_leading_pair pairs the shorter voicing “against its nearest neighbors”, as its comment says It sorts both voicings, pairs them from the bottom and charges 3 semitones per note left over, so an added or dropped bass shifts every voice CompositionTools.cs#L312-L336 Open G → open C: 28 where 10 exists; open G7 → open C: 28 where 8 exists; xx2010 → x32010: 19 where 3 exists. Its live answer for G7 → C ranks first voicings whose extra notes are on top, where pairing from the bottom is right Reproduced with the function copied as it is; not reported 2026-09-30
VoiceLeadingSpace with transposition but without octave equivalence gives the same distance both ways It only tries upward transpositions of 0 to 11 semitones VoiceLeadingSpace.cs#L46-L61 C4 major → B3 major 0, B3 major → C4 major 3; C4 major → C5 major 36, C5 major → C4 major 3, where 0 is right for all four. With octave equivalence, the cyclic-shift shortcut is exact on every pair of 3- and 4-note sets Reproduced with GA’s file compiled as it is; only tests call the class at a826864; not reported 2026-09-30
SetClassOpticIndex.Distance between two set classes of the same size is a voice-leading distance, like the article it cites Its comment picks “the nearest-neighbor sum approach (approximation)”: each note goes to the nearest note of the other class, so several voices may share one SetClassOpticIndex.cs#L53-L76, #L167-L224 Over the 144 ordered pairs of the 12 trichord classes: 35 below the one-to-one minimum, 0 above, 70 that change when swapped; (012) → (048) gives 2, (048) → (012) 6, one-to-one 6 Reproduced with GA’s file compiled as it is; its only caller outside the tests, ShapeGraphBuilder, uses it when VoiceLeadingWeight is above 0, and it defaults to 0; not reported 2026-09-30
The citation in SetClassOpticIndex leads to Callender, Quinn and Tymoczko’s article It gives Music Theory Online 14(3) and an mtosmt.org link that redirects to the journal’s error page; the article is in Science 320 (2008), doi:10.1126/science.1153021 SetClassOpticIndex.cs#L15-L18 Link followed on 2026-09-30 Reproduced, documentation only; not reported 2026-09-30
The MCP server’s ga_chord_substitutions ranks a chord’s diatonic substitutes in the key it is given, or in the chord’s own key, as its description shows Without a key, the F# closure takes the root’s letter, drops the accidental and assumes major; the example in the tool’s description is wrong on all three chords DomainClosures.fs#L558, GaDslTool.cs#L212 Am is compared with A major, which comes first; F♯m with F major; E♭ with E major. For Am in C major the description says C ★★★, Em ★★, F ★, where the closure gives C ★★, F ★★, Dm ★, Em ★ Reproduced by rewriting the closure in the course program; the server itself was not run. Unchanged on main (5c3a52a); not reported 2026-10-01
The same closure builds altered and extended chords with all their notes It reads the quality and the extensions only, from the six-entry table of lesson 9’s ga_common_tones row DomainClosures.fs#L85-L98, #L603-L609 Bm7♭5 is B D F♯ A, so Dm shares 2 notes with it instead of 3; G9 and G13 are built without their seventh, so they get no tritone substitute Fixed on GA’s main by #681: the closure takes its notes from chordTones (#L663 at 5c3a52a); read, not run 2026-10-01
ChordSubstitutionSkill calls a chord a secondary dominant when it can be one The test compares the roots only: a fifth apart is enough ChordSubstitutionSkill.cs#L262 Dm → G and F♯ → Bdim are labelled “Secondary Dominant”, though Dm is minor and a diminished triad cannot be tonicized Reproduced with the test copied as it is; the chatbot itself was not run. Unchanged on main; not reported 2026-10-01
The skill’s one-chord list ranks substitutes by harmonic closeness, D♭7 among those of G7, as its description promises Every chord with the same interval-class vector ties at distance 1, because GrothendieckDelta.FromIcVs turns a zero difference into a one, and the stable sort leaves them in 12-bit order: the list is the five smallest numbers of the chord’s set class GrothendieckDelta.cs#L125-L131, ChordSubstitutionSkill.cs#L164-L174 G7: Dm7♭5, A♭7, F7, D7, E♭m7♭5; D♭7 is 19th of 155. Dm7♭5 comes first for all 12 dominant sevenths; Em7 and Am7 are not listed for Cmaj7. The same zero-to-one makes C “ICV Neighbor (L1 = 1)” of itself. GaApi serves this logic as its own ga_chord_substitutions Reproduced with GA’s GrothendieckService compiled as it is and the skill’s selection copied; the chatbot itself was not run. Unchanged on main; not reported 2026-10-01
The skill’s twelve example prompts reach the path they illustrate “Alternative chord for F major in C” compares F with C; “Borrow a chord from parallel minor” has no chord symbol, and the skill answers “Could not identify a chord symbol in your message.” ChordSubstitutionSkill.cs#L35-L55, #L144-L160 2 examples of 12 Reproduced with the pattern copied as it is; the chatbot itself was not run; not reported 2026-10-01
get_borrowed_chords spells and labels borrowed chords as the textbook does It finds the course’s 18 chords for C major, but spells every black-key root with a sharp, writes the numerals without ♭ and credits each chord to the first mode of its loop ContextualChordService.cs#L93-L109, #L137, #L157-L170 D♯ “III”, A♯ “VII”, G♯ “VI”; C♯ and D both “II”; C minor spelled C D♯ G; B♭ and G minor credited to Dorian, the parallel minor left with D diminished Reproduced with the service compiled as it is; neither the MCP tool nor GaApi was run. Unchanged on main; not reported 2026-10-01
ModalInterchange.yaml names the borrowed chords right 4 of its 13 spelled chords and 3 of its 6 chromatic numerals are wrong, and its text reaches YamlKnowledgeLoader, whose entries YamlKnowledgeSyncService would store with embeddings if any host registered it ModalInterchange.yaml#L271-L362, #L616-L619, #L826-L875, YamlKnowledgeLoader.fs#L128-L140, YamlKnowledgeSyncService.cs#L8-L25 VI F major, VII G major and III C major are A minor’s own chords, not borrowed from A major; “bii°, Bb diminished” is not in A Phrygian; ♭VI+, iv+ and melodic minor’s vii°7 are not in their modes. The loader returns the file’s 8 entries with those strings Reproduced, the loader compiled as it is; at a826864 no host registers the sync service and no code reads the collection; not reported 2026-10-01
YamlKnowledgeLoader loads every content YAML of the configuration directory It skips SpecializedTunings.yaml, which does not parse, and says so on standard error only YamlKnowledgeLoader.fs#L116-L118 “While parsing a flow sequence, did not find expected ‘,’ or ‘]’.” Seen in the course program’s run; the file itself was not examined 2026-10-01; GA PR #808 makes it parse, and the loader reads 21 entries from it 2026-10-04
Modes.yaml declares the interval-class vector of each family 9 of the 18 declared vectors are not the vector of the family’s notes, and GetAllModes gives every mode its family’s declared vector Modes.yaml#L201-L202, #L248-L249, ModesConfig.fs#L281 Harmonic minor declares <2 4 3 5 4 3> for <3 3 5 4 4 2>, melodic minor <2 4 5 3 6 2> for <2 5 4 4 4 2>, chromatic <11 11 11 11 11 6> for <12 12 12 12 12 6>; also major pentatonic, whole tone, diminished, blues, augmented triad and major seventh chord Reproduced with ModesConfig compiled as it is 2026-10-01. On main 75959835, 2 of the 18 still differed; GA PR #818, not merged yet, corrects the Augmented Triad family’s and leaves the Major Seventh Chord family’s 2026-10-04
The members of a Modes.yaml family are the rotations of its first mode 60 of the 134 members do not have its vector Modes.yaml, #L242-L247, #L465-L466 Whole families: Major Seventh Chord 7 of 7 (seven kinds of seventh chord), All Interval Tetrachord 15 of 15, Hungarian Major 6 of 6, In Sen 4 of 4. The Diminished Triad’s “first inversion”, C E A, is A minor; the Ultralocrian is spelled like the altered scale, with B♭ for B♭♭ Reproduced 2026-10-01. On main 75959835, 26 of the 134; GA PR #818, not merged yet, gives each triad inversion the notes of its name and leaves 22, without touching the Major Seventh Chord and All Interval Tetrachord families 2026-10-04
ModesConfig returns the names Modes.yaml writes An unquoted name loses everything from “ #“, which YAML reads as the start of a comment Modes.yaml#L631, #L639, #L655, #L663-L681 8 of 165 names. 7 names then belong to more than one mode, and a lookup by name returns the first: “Lydian” is always the major scale’s, never “Lydian #2 #6” or “Lydian #6” Reproduced by a test added after the first run, in 13e7607 2026-10-01; fix proposed in GA PR #818, not merged yet 2026-10-04
TryGetModeByName, behind get_mode_info, finds a mode by its alternate names, as its code tries to It runs on GetAllModes, which sets every mode’s alternate names to None ModesConfig.fs#L284, #L413-L441 0 of 165 modes keep them, where GetModalFamilies keeps 104: “Altered bb7”, “Locrian Natural 6”, “Super Locrian”, “Acoustic Scale” and “Spanish Phrygian” are not found. Three of the names of GA’s own mode classes, “Locrian Natural 2”, “Dorian Sharp 4” and “Lydian Sharp 2”, are not in the file at all, and “Ionian Augmented” finds the Double Harmonic Family’s cut “Ionian Augmented #2”, C D♯ E F G♯ A B Reproduced with ModesConfig compiled as it is; the MCP tool was not run 2026-10-01; fix proposed in GA PR #818, not merged yet 2026-10-04
TryGetModeByIntervalClassVector, behind the “Scale:” line of ga_chord_to_set and the names of ga_icv_neighbors, names the set it is given It returns the first mode whose family declares the vector, so a wrong declaration hides a scale, and a family’s vector always names its first mode ModesConfig.fs#L443-L458, ChordAtonalTool.cs#L112, #L249 Harmonic minor, melodic minor and Cmaj7: nothing. G7 and Bm7♭5: “Major Seventh”. Cm: “Major Triad” Reproduced with ModesConfig compiled as it is; the MCP tools were not run 2026-10-01. GA PR #818, not merged yet, corrects the Augmented Triad family’s vector, so ga_chord_to_set("Caug") gets the line “Augmented Triad”; the lookup itself is unchanged 2026-10-04
IScaleDegreeNaming.ToShortName follows one convention Three: degree numbers for major and harmonic major, triad numerals for the minor scales, and iv#4 and IV7 for the fourth degree of harmonic and melodic minor MajorScaleDegree.cs#L64-L74, HarmonicMinorScaleDegree.cs#L67-L77, MelodicMinorScaleDegree.cs#L64-L74, HarmonicMajorScaleDegree.cs#L67-L77 11 of 35 short names differ from the triad on their degree Reproduced with GA’s degree types; nothing in GA calls the method at a826864; not reported 2026-10-01
PitchClassSet.StepBrightness “measures brightness based on the preponderance of large steps” The steps of a set always add up to 12, so the value is 2/n, a function of the number of notes PitchClassSet.cs#L262-L283, ModalVectorService.cs#L157 0.2857 for the 21 modes of major, melodic minor and harmonic minor; 11 values over the 4096 sets, one per size, 4 notes sharing 0.5 with 0 and 1 Reproduced; it feeds one embedding dimension; not reported 2026-10-01
A brightness measure ranks Lydian above Locrian IcvBrightness, dimension 21 of the structure vector (index 27 of the full embedding), reads the interval-class vector, which all the modes of a scale share TheoryVectorService.cs#L159-L172 0.642857 for every mode of major, 0.595238 for melodic minor, 0.571429 for harmonic minor Reproduced with the method copied as it is; by design, since its comment says it is derived from the vector; not reported 2026-10-01
UnifiedModeService.RankByBrightness ranks the modes of a scale For harmonic minor it ranks 14 sets, harmonic major’s modes included, because a ModalFamily groups sets by vector UnifiedModeService.cs#L228-L242, ModalFamily.cs#L108-L131 Harmonic major’s Lydian augmented ♯2 first, with 41; major and melodic minor as the course Reproduced with the service compiled as it is; only GA’s tests call it at a826864. The cause is Appendix C’s defect 6, a known design 2026-10-01
UnifiedModeService.Describe names Messiaen’s mode of a set that is one IdentifyMessiaenMode compares the prime form’s id with ids that, for modes 4 to 7, are not prime forms; mode 6’s id is a form of mode 4 UnifiedModeService.cs#L136, #L277-L347, UnifiedModeAdvancedTests.cs#L87-L119 Modes 1 to 3 found, 4 to 7 not: GA tests 2535, 2275, 3315 and 3055 where the prime forms are 975, 455, 1495 and 2015. GA’s tests for modes 5 to 7 accept Is.Null Reproduced with the service compiled as it is; not reported 2026-10-01
GA’s three symmetric mode classes list each distinct mode once, as their comments count them Items holds one mode per degree, 6, 8 and 6, for 1, 2 and 2 distinct sets; the comments promise two modes for the whole-tone scale and four for the augmented scale WholeToneScaleMode.cs#L12, AugmentedScaleMode.cs#L12, ModalCharacteristicIntervalService.cs#L114-L148 20 modes, 5 distinct sets. TranspositionCount is right, 2, 3 and 4. ModalCharacteristicIntervalService stores them at overlapping indices, 208 to 215 written twice (read, not run) Reproduced with GA’s classes; not reported 2026-10-01
DiminishedScaleDegree.ToName names the two diminished scales by their first step Scale.Diminished starts with a whole step, and degree 1 is named “Half-whole diminished” DiminishedScaleDegree.cs#L47-L52, Scale.cs#L62 Degrees 1 and 2 swapped; DiminishedScaleMode.HalfWhole is the whole-half scale. Modes.yaml names the same spellings right Reproduced 2026-10-01; fix proposed in GA PR #810, not merged yet 2026-10-04
Scale.Tritone is the tritone scale, the notes of the Petrushka chord It has A where the tritone scale has B♭, and TritoneScaleMode’s comment describes neither Scale.cs#L67, TritoneScaleMode.cs#L11-L14 6-Z50 with 12 transpositions and 6 modes, where 6-30 has 6 and 3; GA’s own IsSymmetricSet says False. The comment’s “alternating half steps and minor thirds” is the augmented scale, and its “two distinct modes” fits neither Reproduced 2026-10-01; fix proposed in GA PR #810, not merged yet 2026-10-04
PitchClassSet.IsMonomodal and the atonal catalog’s IsSymmetric find symmetric sets, as their comments and the embedding dimension “Is Symmetric/Monomodal” say Both test a single-member modal family PitchClassSet.cs#L186-L189, ModalVectorService.cs#L132, Program.cs#L249-L253 IsMonomodal: 29 sets, 12 of them single notes, 59 limited sets missed, the octatonic scale of its own comment among them. IsSymmetric: 6 of 200 families, the single note included, 10 limited families missed Reproduced; not reported 2026-10-01
AtonalChordAnalysisService.IsSymmetricalSet checks “equal divisions of the octave” It compares the gaps between sorted pitch classes without closing the circle AtonalChordAnalysisService.cs#L217-L233, #L151-L183 156 sets, all 66 dyads among them, 140 not limited; C D E True, B♭ C D False; 60 limited sets missed. The naming branch that uses it is unreachable, SetClass.IsModal being true for all 4096 sets Reproduced with the service compiled as it is, the private method called by reflection 2026-10-01; fix proposed in GA PR #810, not merged yet 2026-10-04
ModesSkill lists the modes of limited transposition when asked It lists the six IsSymmetric families with a confidence of 1.0, with ProgrammaticForteCatalog’s ordinals as Forte numbers ModesSkill.cs#L596-L601, #L687-L705, ForteCatalog.cs#L9-L12 A single note listed, the octatonic and hexatonic scales it names left out; “Forte 2-1”, “3-1”, “4-1”, “6-1” for 2-6, 3-12, 4-28, 6-35. The file’s labels agree with ForteCatalog for 4 of 200 families, though its comment forbids surfacing them Reproduced by rebuilding the answer from its format and GA’s loader; the chatbot itself was not run. The labels are known: GA #436 moved the generator to ForteCatalog before a826864 and left regenerating the file for later; the list is not reported 2026-10-01
GA’s mode catalogs name the symmetric classes AtonalModalFamilies.yaml takes names from Modes.yaml families that list a set by mistake; the lookup by vector finds Messiaen’s sixth mode under the octatonic scale’s name Modes.yaml#L328-L329, #L531, #L725-L726 Messiaen’s fourth mode is “Major Bebop Family”, the diminished seventh “Major Seventh Chord Family”; Messiaen’s sixth mode is “Diminished (Half-Whole)”, and the octatonic scale is found nowhere Reproduced by a test added after the first run, in 6b4039a; the MCP tool was not run; not reported 2026-10-01
Chord.FromSymbol reads the chord symbols that textbooks and GA’s own files write Its table knows the extended chords, not the altered chords, sus with an extension, add2, slash chords or most spellings; lower-casing turns CM7 into m7 Chord.cs#L110-L141 14 of 41 symbols right; 28 rejections, the two words among them; CM7 read as C minor seventh Reproduced 2026-10-02. GA PR #820, not merged yet, reads a capital M as major, so CM7 is the major seventh of C; the other spellings are still rejected 2026-10-04
ChordSymbolParser parses the chord names of GaApi’s voicing search and of the chatbot A suffix not in its table falls to a cascade of Contains tests, then to a major triad; English words that start with A to G parse ChordSymbolParser.cs#L98-L140, #L176-L188, ChordSymbolParserTests.cs#L33-L40 17 of 41 right, no rejection; C7alt with a perfect fifth, C7#11 with a major seventh, “Can” C major, “Give” G major. GA’s tests check only that a chord comes back Reproduced with the parser compiled as it is 2026-10-02; fix proposed in GA PR #820, not merged yet: 41 of 41 right, “Can” and “Give” rejected 2026-10-04
ChordFormula.GetSymbolSuffix names the extended chords It writes the quality’s letter then the extension, so the major ninth, eleventh and thirteenth lose their “maj” ChordFormula.cs#L299-L325 8 of 11; BasicChordExtensionsService.GenerateChordName, from the same quality and extension, 11 of 11 Reproduced at a826864 2026-10-02; already fixed upstream by GA PR #680, merged on 2026-09-17 2026-10-04
ChordAlterationService names altered chords Functions come from the semitones modulo 12: ♯9 is read as a third, ♯11 and ♭13 as ♭5 and ♯5; one alteration makes a dominant “7alt”, a semitone makes it a ninth chord ChordAlterationService.cs#L37-L61, #L87-L102, ChordFunctionExtensions.cs#L13-L22 None of GA’s own nine altered chords named back; C7(♯9) has “No alterations”; Cdim, Caug, Cm7♭5 and Cdim7 get a (b5) or (#5) Reproduced with the service compiled as it is; not reported 2026-10-02
ChordTemplateFactory.GenerateAllPossibleChords yields each template once It runs GenerateFromAllModalFamilies, then the same three scale groups again ChordTemplateFactory.cs#L37-L62, #L93-L115 25,900 templates, the second 12,950 a copy of the first Reproduced 2026-10-02; fix proposed in GA PR #823, not merged yet: 12,950 templates, each once 2026-10-04
ga_polychord’s description and AdvancedHarmony.yaml describe polychords B major over C is not the Lydian set, and C major over F♯ major makes six pitch classes, not twelve ChordAtonalTool.cs#L194-L218, AdvancedHarmony.yaml#L101-L104 {C, Eb, E, F#, G, B}, 6-Z19, in Lydian ♯2 only; C/F♯ 6-30, the Petrushka chord, as IconicChords.yaml lists it Reproduced with the tool’s code copied and its F# closure replaced; not reported 2026-10-02
VoicingHarmonicAnalyzer names drop voicings One test: raise the lowest note an octave, and answer “Drop-2” when the notes then fit within an octave, nothing otherwise. Close positions, drop 3s and drop 4s come out “Drop-2”; no voicing is ever “Drop-3” VoicingHarmonicAnalyzer.cs#L46-L58 64 of the course’s 128 voicings “Drop-2”, exactly those with at most one dropped voice; among GA’s 687 four-string seventh chords, “Drop-2” on 15 close, 111 drop-2, 92 drop-3 and 46 drop-4 voicings Reproduced with the analyzer compiled as it is 2026-10-02; fix proposed in GA PR #819, not merged yet 2026-10-04
VoicingCharacteristics.IsRootless and IsOpenVoicing describe a voicing Analyze passes intervalSpread > 12 and false VoicingHarmonicAnalyzer.cs#L38-L40, VoicingCharacteristics.cs#L10-L12 112 of 128 “rootless”, all with their root; 672 of 687; lesson 3’s seven open chords; no voicing open Reproduced; not reported 2026-10-02
VoicingFilters selects drop, rootless, shell, closed and open voicings Drop3 and Drop2And4 look for labels nothing produces; Rootless, ClosedPosition and OpenPosition inherit the two fields above VoicingFilters.cs#L101-L124 On the 687: Drop2 264, Drop3 0, Drop2And4 0, Rootless 672, ClosedPosition 687, OpenPosition 0. VoicingIndexingService applies these filters before indexing Reproduced with the filters compiled as they are 2026-10-02. GA PR #819, not merged yet, fixes the three drop filters: on the 687, Drop2 111, Drop3 92, Drop2And4 143; Rootless, ClosedPosition and OpenPosition are unchanged 2026-10-04
GA recognizes shells, root, third and seventh Three rules, none on the chord tones: three or four strings; “rootless” with three pitch classes; “rootless” with three notes VoicingPhysicalAnalyzer.cs#L126-L131, ChordClassificationEngine.cs#L169-L173, VoicingFilters.cs#L118-L119 The fifth-string shells x323xx, x324xx and x313xx pass only the first; lesson 3’s seven open triads get the engine’s shell-voicing Reproduced 2026-10-02; fix proposed in GA PR #819, not merged yet 2026-10-04
VoicingAnalyzer.AnalyzeEnhanced records a voicing’s tones and guide tones It builds new ToneInventory([], false, [], []) for every voicing VoicingAnalyzer.cs#L155 711 analyses, HasGuideTones never true; AutoTaggingService’s “Shell” depends on it (read, not run) Reproduced; not reported 2026-10-02
The GuitarCompingRecipes fingerings play the chords they name Read low to high on their string sets, as the file’s conventions say, none does; one has five frets for four strings ChordProgressions.yaml#L461-L577 0 of 15; “Cm(maj9)” is a Cmaj7, “Gsus2” a Gsus4. YamlKnowledgeLoader returns all six recipes for retrieval Reproduced through YamlKnowledgeLoader; not reported 2026-10-02
PHYSICAL_PLAYABILITY_IMPROVEMENTS.md quotes the 5-fret spans GA computes Its three spans and its “71.4% larger” match neither GA’s calculator nor the formula; the document presents them as the output of a test, in a file the test project removes from compilation PHYSICAL_PLAYABILITY_IMPROVEMENTS.md#L45-L50, #L155-L161, GA.Business.Core.Tests.csproj#L47 120.11, 85.42 and 70.09 mm against 162.55, 108.49 and 81.27 mm on 648 mm; the low span 100.0% larger than the high one Reproduced; not reported 2026-10-02
ComfortableReachScaleUnits is a reach of four frets It is the distance from the nut to fret 4, compared with the span between fretted notes, which counts one fret space fewer BiomechanicsConstants.cs#L24-L28, PhysicalCostService.cs#L27-L38 Five frets from fret 1, ten from fret 12 Reproduced; not reported 2026-10-02
DetectCagedShape names a voicing’s CAGED shape It returns null when the first position is muted, before the A and C tests that need it muted, so it can only answer “E-Shape”; its comments read the first position as string 6, which GA numbers string 1 VoicingPhysicalAnalyzer.cs#L253-L343, #L27 In GA’s order, none of the 11 shapes; reversed, the E shape at 13 of 13 frets and nothing else. The tag e-shape fails VoicingFilterEngine’s test for “E” Reproduced; not reported 2026-10-02
GA’s playability measures account for barres and for the player’s hand PhysicalCostService has no barre term; VoicingPhysicalAnalyzer sees no barre in F and calls open C “Intermediate”; BiomechanicalAnalyzer sees barres on open strings and rates F “Very Easy”; none reads HandSize or MaxComfortableStretchMm PhysicalCostService.cs#L17-L142, VoicingPhysicalAnalyzer.cs#L105-L112, BiomechanicalAnalyzer.cs#L10-L12, #L153 F costs 0.29, the barre shapes of C 0.21 to 0.24; open C 3.50, “Intermediate”; F 0.92 against 0.82 for open C; the same results for the four hand sizes Reproduced; not reported 2026-10-02
Tunings.toml holds GA’s tunings No C# or F# code reads it; it repeats the two octave errors of Instruments.yaml; the React component that names it ignores its tunings and dataUrls properties Tunings.toml#L364-L366, #L388-L390, HarmonicNavigator3D.tsx#L128-L131 15 of 15 entries as in Instruments.yaml; string 2 lowered by 11 semitones in OpenCMajor, by 10 in OpenAMajor Reproduced, the file read as text; not reported 2026-10-02
ga_easier_voicings offers playable voicings of the chord asked for Two shapes of its table play a note outside their chord; under “no-barre” it offers B7 and B5 for Bm and nothing for B♭; its description’s Fsus2 and C/F are not in the table GuitaristProblemTools.cs#L506-L562, #L574-L577, #L611-L652 Gsus4 310013 plays B♭, C5 x3500x B; neither offer for Bm plays D Reproduced with the table read as text and the course’s reading of the filter; not reported 2026-10-02
ga_arpeggio_suggestions pairs each chord with its arpeggio and a mode that fits it, as its description’s example does It appends the table’s suffix to the whole symbol and takes the mode from the degree of the root alone; any key word but “minor” means major GuitaristProblemTools.cs#L409-L412, #L426-L431, #L448-L485 “Amm7”, “Dm7m7”, “G77”; A7 in A major gets Ionian, with G outside; an A major chord in C major gets “Am7” and Aeolian, with C♯ outside; the key “Am” is A major Reproduced with the tool read as text and the course’s reading of lines 426 to 485; the suffix and the degree fixed upstream by GA #626 on 2026-09-24, the key read as before on main c1416eee (read, not run) 2026-10-07
ImprovisationSkill.InferQuality reads the quality a chord symbol names It lowercases the suffix, so CM7 is a minor seventh, the regression ChordVocabulary’s remarks say they removed; Cmaj7♯5 reads “major 7”, C7♯11, C7♭13 and C7+5 “dominant 7”; Cm6’s arpeggio is named CmMaj7 ImprovisationSkill.cs#L323, #L338-L351, ChordVocabulary.cs#L8-L16 7 of 31 symbols get a first scale without one of their notes; CM7 gets Cm7, then Dorian, Aeolian and Phrygian, none with E or B Reproduced, compiled; InferQuality unchanged on main c1416eee; not reported 2026-10-07
OutsideNotesSkill.Classify calls a note a tension or an avoid note as chord–scale theory does One rule for all twelve notes, a semitone above a chord tone, with no scale: notes outside every scale that fits the chord become tensions, the ♭9 and ♭13 over a dominant avoid notes; its advice calls the major seventh over a dominant an altered tension OutsideNotesSkill.cs#L20-L28, #L141-L189 Over Cmaj7, B♭ “tension: b7” and E♭ “#9”; F♯ over Cm7 and A over Cm7♭5, all outside ImprovisationSkill’s scales for the chord; over A7, D an avoid note but C, a semitone below C♯, a tension Reproduced, compiled; Classify unchanged on main c1416eee; not reported 2026-10-07

Questions where a measurement could have gone the other way. The first is the course’s own safety net: its theory was written from published definitions, not from GA, so agreeing with GA over all 4,096 sets is evidence about both.

Question Hypothesis Result Verdict Where
Does the course’s from-scratch Rahn prime form agree with GA’s minimal-id prime form, over the whole space? Built from Open Music Theory, Wikipedia and OEIS, independently of GA, so agreement was not guaranteed They agree for every one of the 4,096 sets. GA’s 224 set classes and 352 transposition classes match OEIS A000029 and A000031. Forte’s packing differs from Rahn’s on 6 set classes and 17 of the 352 transposition classes Confirmed, with the Forte/Rahn gap quantified separately 2026-09-15
Does climbing GA’s OPTIC ladder one equivalence at a time on a real fingering reach the textbook counts? Written in advance: it should land on 4096, 352 and 224 4096, 352 and 224 Confirmed 2026-09-15
Does giving Diagrams.Fretboard a tuning parameter change the guitar diagrams it already produced? Written in advance: it should not — the guitar is just one tuning among the new ones The fourteen existing SVG files are byte-identical after the change Confirmed 2026-09-15
Does GA’s core find the same common tones as the course on lesson 9’s 13 pairs, Cmaj9, Cdim7 and C6 included? Written in advance: agreement on all 13 13 of 13 Confirmed 2026-09-30, Lesson11.cs#L62-L66
How often does repeating the smaller chord’s root, as VoiceLeadingSkill does, miss the smallest voice leading? Written in advance: G → Cmaj7 gives 5 where 3 exist; more than 10 % of the 2,880 triad → seventh pairs miss, and fewer from seventh to triad G → Cmaj7: 5 and 3. 1,260 pairs (43.8 %) miss in each direction, by up to 4 semitones Confirmed for G → Cmaj7 and the share; refuted for the direction: the triad is the padded chord both ways, and a voice leading costs the same read backwards 2026-09-30, Lesson11.cs#L388-L402
Does each of the skill’s ten example prompts pass its own pattern? Written in advance: exactly one fails, “voice leading C major to G major” That one, and no other Confirmed 2026-09-30, Lesson11.cs#L404-L409
What does ga_voice_leading_pair’s distance give from open G to open C? Written in advance: 28, where leaving out the low G gives 10 28 and 10 Confirmed 2026-09-30, Lesson11.cs#L411-L424
Does ProgressionVoiceLeadingAnalyzer agree with the textbook rule, string by string? Written in advance: F → G barre, 3 fifths and 4 octaves; E5 → G5, 1 and 1; open C → G and G7 → C, one octave each on strings 5 and 2; Am → Dm, none As written, 5 of 5 Confirmed 2026-09-30, Lesson11.cs#L426-L438
Is VoiceLeadingSpace’s cyclic-shift shortcut exact, and what does it give without octave equivalence? Written in advance: no disagreement over every ordered pair of 3- and 4-note sets, OP and OPT; without O, C4 major → B3 major 0 and B3 major → C4 major 3 0 of 48,400 and 0 of 245,025, with and without T; 0 and 3 Confirmed 2026-09-30, Lesson11.cs#L450-L471
Does SetClassOpticIndex fall below the one-to-one minimum and change when its arguments are swapped? Written in advance: never above, below and asymmetric on some of the 144 trichord pairs; (012) → (048) 2, back 6, one-to-one 6 0 above, 35 below, 70 asymmetric; 2, 6 and 6 Confirmed 2026-09-30, Lesson11.cs#L473-L486
How does the MCP server’s ga_chord_substitutions rank A minor’s substitutes in C major, and which key does it use when given none? Written in advance: C and F with 2 shared notes, then Dm and Em with 1, against the description’s C ★★★, Em ★★, F ★; without a key, A major for Am and B major for B♭7; Bm7♭5 built as B D F♯ A C 2, F 2, Dm 1, Em 1; A major and B major; Dm and Bdim share 2 notes with Bm7♭5 Confirmed, on a rewrite of the closure 2026-10-01, Lesson12.cs#L529-L549
Which chords get the closure’s tritone line? Written in advance: G7 gets D♭7; G9 and G13 get none As written Confirmed, on the rewrite 2026-10-01, Lesson12.cs#L551-L558
Which path does each of ChordSubstitutionSkill’s twelve example prompts take? Written in advance: ten list substitutes; “Alternative chord for F major in C” compares F with C; “Borrow a chord from parallel minor” finds no chord As written Confirmed 2026-10-01, Lesson12.cs#L560-L568
What are the skill’s five substitutes for one chord? Written in advance: every chord with the same vector at cost 0.60 and distance 1, in 12-bit order; for G7 Dm7♭5, A♭7, F7, D7, E♭m7♭5; for Am Cm, C, A♭, D♭m, Fm; for C Cm, A♭, D♭m, Fm, D♭; for Cmaj7 D♭maj7, A♭maj7, Fmaj7, Dmaj7, Amaj7 The four lists as written; 23, 23, 23 and 11 chords tie, as many as the course counts with the same vector Confirmed 2026-10-01, Lesson12.cs#L570-L610
Which labels does the skill give G7 → D♭7, Dm → G, C → C and B♭7 → C? Written in advance: Tritone Substitution, Set-Class Equivalent and ICV Neighbor (L1 = 1); Secondary Dominant; Set-Class Equivalent and ICV Neighbor (L1 = 1); Backdoor Dominant alone As written; Dm → G also gets the two set-class labels Confirmed 2026-10-01, Lesson12.cs#L612-L616
What does get_borrowed_chords return for C major? Written in advance: 18 chords credited to the first mode that has them, roots spelled with sharps, numerals without accidentals, each listed by mode The 18, in that order, with those numerals Confirmed 2026-10-01, Lesson12.cs#L618-L641
How much of ModalInterchange.yaml is right, and does any GA code read it? Written in advance: 9 of 13 spelled chords right, 3 of 6 chromatic numerals wrong; no code reads the file 9 and 4; 3 of 6; YamlKnowledgeLoader reads it with the other content YAML files, and YamlKnowledgeSyncService would store the entries with embeddings, though no host registers it Confirmed for the chords and numerals; refuted for the readers 2026-10-01, Lesson12.cs#L643-L675
Do GA’s ModeFormulas for the seven modes of melodic minor match the course’s? Written in advance: 7 of 7, GA’s scale spelled from A 7 of 7, and the 7 spellings Confirmed 2026-10-01, Lesson13.cs#L136
And for harmonic minor, double flat included? Written in advance: 7 of 7; Lydian ♯2 measured against Ionian, and the seventh of the altered ♭♭7 a diminished seventh 7 of 7, with >♯2 and >bb7 Confirmed 2026-10-01, Lesson13.cs#L137
How many of the 35 short names of five degree types differ from the triad on their degree? Written in advance: 11; 4 for major, 5 for harmonic major, 1 each for harmonic and melodic minor, none for natural minor 11, degree for degree Confirmed 2026-10-01, Lesson13.cs#L139-L155
Does StepBrightness tell modes apart? Written in advance: no; 0.2857 for all 21 modes, and 11 values over the 4096 sets As written Confirmed 2026-10-01, Lesson13.cs#L157-L184
Does IcvBrightness tell modes apart? Written in advance: no; 0.642857, 0.595238 and 0.571429, one value per scale As written Confirmed 2026-10-01, Lesson13.cs#L157-L172
Does RankByBrightness rank the modes of a scale as the course’s sum does? Written in advance: yes for major and melodic minor; for harmonic minor 14 sets, Lydian augmented ♯2 first with 41, and three ties in 12-bit order As written Confirmed 2026-10-01, Lesson13.cs#L186-L216
How many of the vectors Modes.yaml declares are right, and how many members have their family’s vector? Written in advance: 31 families, 165 modes, 9 of 18 declared vectors wrong, 60 of 134 members without it; the Ultralocrian spelled like the altered scale As written. GA and the course compute the same vector for the notes of 164 of the 165 modes, the chromatic scale aside Confirmed 2026-10-01, Lesson13.cs#L218-L259
What do ModesConfig’s two lookups find? Written in advance: by vector, Ionian for C major, nothing for harmonic minor, melodic minor and Cmaj7, “Major Seventh” for G7 and Bm7♭5, “Major Triad” for C and Cm; by name, 8 of GA’s 14 names and no alternate name The vectors as written. 9 names: “Ionian Augmented” is found in the Double Harmonic Family, through a name cut by a YAML comment. No alternate name Confirmed for the vectors and the alternate names; refuted for the number of names found 2026-10-01, Lesson13.cs#L261-L295
How many sets have limited transpositions, and does IsSymmetricSet find them? Written in advance: 76 sets in 17 transposition classes, 15 without the empty set and the chromatic scale; Messiaen’s transpositions and modes as his list gives them; agreement on all 4096 sets 76, 17 and 15, in 14 set classes; 7 of 7 modes; 4096 of 4096 Confirmed 2026-10-01, Lesson14.cs#L158-L194
How many distinct modes do GA’s three symmetric mode classes hold? Written in advance: 6, 8 and 6 modes for 1, 2 and 2 sets on C, with the transpositions right, 2, 3 and 4 As written Confirmed 2026-10-01, Lesson14.cs#L216-L218
Are the names of the two diminished scales the right way round? Written in advance: swapped, degree 1 being the whole-half scale Swapped Confirmed 2026-10-01, Lesson14.cs#L220-L226
Is Scale.Tritone the tritone scale? Written in advance: no; 6-Z50 with 12 transpositions and 6 modes, where the tritone scale is 6-30 with 6 and 3 As written; GA’s IsSymmetricSet False for GA’s scale, True for the tritone scale Confirmed 2026-10-01, Lesson14.cs#L228-L242
Which of Messiaen’s modes does IdentifyMessiaenMode name? Written in advance: modes 1 to 3 only Modes 1 to 3 only Confirmed 2026-10-01, Lesson14.cs#L196-L212
How many sets does IsMonomodal flag, and how many have limited transpositions? Written in advance: 29, 17 of them limited, the octatonic scale not among them As written Confirmed 2026-10-01, Lesson14.cs#L246-L262
What do AtonalModalFamilies.yaml and ModesSkill call symmetric, and do the file’s Forte labels agree with ForteCatalog? Written in advance: 6 of 200 families; Forte 2-1, 3-1, 4-1 and 6-1 in the answer; no agreement for 2, 3, 4, 8, 9 and 10 notes, agreement for 1-1, 11-1 and 12-1 As written; one hexachord family agrees too, 4 of 200 in all Confirmed 2026-10-01, Lesson14.cs#L266-L302
How many sets does IsSymmetricalSet flag? Written in advance: 156, 16 of them limited, 60 limited sets missed; C D E flagged, B♭ C D not As written Confirmed 2026-10-01, Lesson14.cs#L246-L263
Does IsInversionallySymmetric agree with the course’s mirror axes, and do the limited classes have as many axes as fixing transpositions? Written in advance: 352 classes, 96 symmetric; 13 of the 15 limited classes mirror-symmetric, 6, 4 and 3 axes for the whole-tone, octatonic and augmented scales As written, and no set anywhere with axes in another number Confirmed 2026-10-01, Lesson14.cs#L306-L330
What do GA’s two parsers make of 41 chord symbols? Written in advance: FromSymbol 14 right, 26 chords rejected, CM7 a minor seventh; ChordSymbolParser 17 right, nothing rejected, each misreading named, “Can” C major and “Give” G major As written Confirmed 2026-10-02, Lesson15.cs#L313-L336
Do GA’s three ways of naming read the extended chords back? Written in advance: TryFindExact nothing for C13, Cmaj13, Cm13 and Cmaj11; GetSymbolSuffix without the “maj” of maj9, maj11 and maj13; GenerateChordName all eleven; C13, Fmaj13 and Gm13 one set As written Confirmed 2026-10-02, Lesson15.cs#L273-L301
What does ChordAlterationService call GA’s nine altered chords? Written in advance: C9(b9,b5,#5), C9(b9), C7, four C7alt, C9(b9,b5), Cmaj7(b5); Cdim(b5), Caug(#5), Cdim7(b5), C6(b5) As written Confirmed 2026-10-02, Lesson15.cs#L340-L363
Which alterations do ChordTemplateFactory’s templates carry? Written in advance: every template twice; ♭13 never; ♯9 only in Sus2 templates, ♯11 only in Sus4 templates As written: 80 and 176 templates Confirmed 2026-10-02, Lesson15.cs#L367-L382
What does ga_polychord print, and which modes contain each polychord? Written in advance: {C, Eb, E, F#, G, B} for B over C; Bm/C in Lydian only, B/C in Lydian ♯2 only; C over F♯ six notes, 6-30, in none of the 28 modes and in the half-whole octatonic; D over E♭ in Lydian ♯2 only; C over D A C D E F♯ G As written Confirmed 2026-10-02, Lesson15.cs#L406-L426
What does YamlKnowledgeLoader return from ExtendedChords.yaml and AdvancedHarmony.yaml? Written in advance: 3 and 9 entries; the 13th chord 1 3 5 ♭7 9 13; “Polychords” with twelve chromatic pitches As written Confirmed 2026-10-02, Lesson15.cs#L430-L449
Which triads over C7 avoid F and B, and does GA’s catalog name them? Written in advance: 6 major and 6 minor, Wikipedia’s nine upper structures plus C, Gm and Am; no pattern in the catalog As written; none of the 62 patterns is named upper structure or polychord Confirmed 2026-10-02, Lesson15.cs#L386-L402
What does DetectDropVoicing call the 128 drop voicings of the four seventh chords on C, and Wikipedia’s examples? Written in advance: “Drop-2” for the 64 close, drop-2, drop-3 and drop-4 voicings, nothing for the 64 others; nothing for Wikipedia’s seven examples As written Confirmed 2026-10-02, Lesson16.cs#L173-L215
What do IsRootless and IsOpenVoicing say, and which guitar shapes are “Drop-2”? Written in advance: 112 rootless, none open; lesson 3’s seven open chords rootless, three of them “Drop-2”; x3200x “Drop-2” and not rootless, x3545x “Drop-2” and rootless As written Confirmed 2026-10-02, Lesson16.cs#L219-L232
Which voicings does each of GA’s three shell rules recognize? Written in advance: the string rule the six shells, xx0232 and both Cmaj7 shapes; the engine the three sixth-string shells and the seven open triads; the filter the three sixth-string shells only As written Confirmed 2026-10-02, Lesson16.cs#L236-L252
How many voicings does VoicingGenerator return, and what do GA’s label and filters make of the seventh chords among them? Written in advance: 667,125; “Drop-2” exactly on the close, drop-2, drop-3 and drop-4 voicings; MUS-005’s 48 drop-2 and 32 drop-3 shapes all present; Drop2 the close and single-drop voicings, ClosedPosition all, Rootless those over an octave, the others none As written: 687 voicings, 264 kept by Drop2, 672 by Rootless Confirmed 2026-10-02, Lesson16.cs#L256-L333
Do the ii–V–I rows of TransposeTables show the guide-tone moves, and does GA’s analysis record guide tones? Written in advance: the four moves in all twelve rows, F F E and C B B in C; HasGuideTones false and Tones, OmittedTones empty for every voicing analyzed As written, over 711 analyses Confirmed 2026-10-02, Lesson16.cs#L337-L369
Do the comping recipes reach retrieval, and do their fingerings play their names? Written in advance: the six recipes returned with their fingerings; no fingering with the notes of its name or degree order; the blues recipe’s E7 right As written; 36 entries from the file Confirmed 2026-10-02, Lesson16.cs#L373-L408
Where do GA’s two geometry classes put the frets, and are the spans quoted in PHYSICAL_PLAYABILITY_IMPROVEMENTS.md right? Written in advance: both agree with the course at every fret, fret 12 at 323.85 and 324.00 mm; MUS-009’s five figures; three DIFF rows, 162.55, 108.49 and 81.27 mm, the low span 100.0% larger, not 71.4% As written Confirmed 2026-10-02, Lesson17.cs#L185-L210
How far does GA’s comfortable reach go, and does any measure read the player’s hand? Written in advance: frets 1 to 5 from fret 1, 7 to 13 from fret 7, 12 to 21 from fret 12; the first position at 0.1502; the default profile’s Medium hand and 129 mm, which none of the three analyzers reads As written Confirmed 2026-10-02, Lesson17.cs#L214-L226, Lesson17.cs#L334-L337
Where do the five CAGED shapes of C sit, and does DetectCagedShape name them? Written in advance: frets 0, 3, 5, 8 and 10, in every key the order C A G E D read round from one of its letters; in GA’s string order no shape named, 001220 named “E-Shape”; reversed, the E shape at every fret and nothing else As written: the E shape at all 13 frets Confirmed 2026-10-02, Lesson17.cs#L230-L302
What do GA’s three cost and difficulty measures make of the shapes? Written in advance: no barre term; from −0.63 (Em) to −0.06 (D), F 0.29, the D shape of C 0.59, the E shape at fret 12 0.66; open C “Intermediate”; F “Very Easy” (0.92) above open C (0.82) for BiomechanicalAnalyzer; hand sizes and profile fields without effect; the D shape of C 1.04 under Beginner(), 0.18 under Jazz() As written Confirmed 2026-10-02, Lesson17.cs#L306-L347
Do the guitar tunings of Tunings.toml match Instruments.yaml, and does any GA code read the file? Written in advance: the same 15 entries, string 2 of OpenCMajor and OpenAMajor lowered by 11 and 10 semitones as in Instruments.yaml; OpenEMajorV2 an E minor chord; Wikipedia’s four string orders; no reader in GA’s C# or F# As written; the readers checked by searching the code, not by running it Confirmed 2026-10-02, Lesson17.cs#L351-L372
Are the 41 voicings behind ga_easier_voicings right, and what does it offer for a barre chord? Written in advance: Notes right for all 41; a B♭ in Gsus4, a B in C5, C7 without G; five HasBarre; Fmaj7 for F and F5, B7 and B5 for B and Bm, nothing for B♭; Fsus2 and C/F not in the table As written Confirmed 2026-10-02, Lesson17.cs#L376-L406
Do GA’s arpeggio tables, ArpeggioFor and GA’s modes give the seventh chord and the mode on each degree? Written in advance: all fourteen suffixes and symbols; the modes and intervals of both tables and MajorScaleMode right; ten of the twelve other scales, the two diminished modes DIFF As written Confirmed 2026-10-07, Lesson18.cs#L270-L315
What does ga_arpeggio_suggestions return when the request names its key? Written in advance: “Amm7”, “Dm7m7”, “G77”, “Cmaj7maj7”; the mode from the root’s degree, so Ionian for A7 in A major and Aeolian for an A major chord in C major; a key “Am” read as A major; B♭ in C major chromatic As written; GA #626 had already fixed the suffix and the degree upstream Confirmed 2026-10-07, Lesson18.cs#L317-L341
Does the first scale ImprovisationSkill offers hold the chord, over 31 symbols on C? Written in advance: all but seven, CM7 read as a minor seventh, Cmaj7♯5, C7♯11, C7♭13, C7+5, C7♭9 and C7♯9; 16 of 17 example prompts accepted; F, C and G Ionian over Am F C G; G/A read as G and A As written Confirmed 2026-10-07, Lesson18.cs#L343-L368
Which notes does OutsideNotesSkill.Classify call avoid notes and tensions over Cmaj7, C7, Cm7 and Cm7♭5? Written in advance: the notes a semitone above a chord tone avoid notes, D♭, F and A♭ over Cmaj7; five tensions in none of ImprovisationSkill’s scales; seven of ten example prompts answered As written Confirmed 2026-10-07, Lesson18.cs#L370-L395
Does Classify agree with GAA-003 on A minor pentatonic over Am7 and A7? Written in advance: agreement over Am7; over A7, D an avoid note but C a tension; the three notes that pull a mode away avoid notes; the targets C♯, F♯ and G♯ As written Confirmed 2026-10-07, Lesson18.cs#L404-L419
What do GA’s improvisation entries hold, and are GAA-003’s box 1 and modes right? Written in advance: four entries with an empty category, none pairing a chord with a scale; box 1 A C D E G, GA’s fifth mode of the major pentatonic; the three modes spelled right As written Confirmed 2026-10-07, Lesson18.cs#L421-L448

2026-09-21 — Appendix C reconciled upstream

Section titled “2026-09-21 — Appendix C reconciled upstream”
  • GA #711 reconciled the executable Appendix C findings and was merged as b363c3f.
  • Defects 1–5, 7–11 and 13–18 are fixed with regression coverage. Entries 6, 12 and 19 remain documented design or representation boundaries.
  • The course stays pinned to a826864 so the original 40 DIFF rows remain reproducible; the appendix now links the newer upstream outcome instead of rewriting that historical evidence.

2026-09-18 — Upstream GA fixes: resolving the findings

Section titled “2026-09-18 — Upstream GA fixes: resolving the findings”
  • 16 of the 19 defects resolved upstream in GA:
    • Notes & pitch parsing: Pitch.Flat factories (DFlat, FFlat, GFlat, EFlat, BFlat) corrected; PitchParser anchored regexes with compiled static patterns; Note.Flat.TryParse fixed so natural B parses as B (not B♭) and Unicode ♭ is handled without case mutation; PitchClass.TryParseSetNotation separated from TryParse; SimpleIntervalSize.TryParse and CompoundIntervalSize.TryParse return false on invalid inputs instead of throwing.
    • Chords & voicings: ChordFormula quality and suffix classification updated (dim7 distinguished from 6th/13th, m7b5 and dim7 preserved); spelling-aware interval transposition added to Chord constructor; original Root and Formula preserved across chord inversions; Voicing.HasBarre() open strings (fret 0) excluded.
    • Keys & tonality: KeyTools.GetParallelKey preserves tonic with opposite mode; KeyTools.GetNeighboringKeys uses signature count lookup; Key.GetInterval root-to-note argument order restored; Key.Major.TryParse returns false on invalid root; HarmonicFunction.Subtonic enum member added; Cadences.yaml iii Roman numeral fixed in E Minor.
    • Defect 12 (ICV packing base 12) remains pinned to preserve existing catalog numbering; defect 6 (ModalFamily) and defect 19 (ClosestDiatonicKey tonic ambiguity) documented as design/heuristic limitations.

2026-09-15 — A verdict on every divergence, the ukulele and the bass, three appendices

Section titled “2026-09-15 — A verdict on every divergence, the ukulele and the bass, three appendices”
  • All 40 DIFF rows of lessons 1 to 7 now have a verdict, in Appendix C: 39 are GA’s bug, one (PitchClass.Parse("A")) is a defensible convention with a precedence flaw underneath, and none is an error in the course. They come from 19 distinct defects — one wrong line in Note.Chromatic.ToAccidented accounts for eight rows on its own — and they fall into three families: copy-paste inside a block of near-identical members, a reduced type asked for the information it was built to discard, and TryParse that throws.
  • Nine sentences across lessons 1, 2, 3, 4, 6 and 7 presented a divergence as a matter of taste and now give the verdict. The worst of them was lesson 2’s “Neither answer is wrong” about ModalFamily: a mode is a rotation, a scale has as many modes as it has notes, and Ian Ring’s pages answer 7 and 6 where GA answers 14 and 24.
  • Lesson 8, the ukulele and the bass. Two facts that guitar-shaped code gets wrong: a ukulele is re-entrant (its fourth string is higher than its third), and a bass is tuned entirely in fourths (it has none of the guitar’s one irregular pair). A ukulele’s gaps are 5 4 5, the same as the guitar’s top four strings; a baritone ukulele is those four strings; a bass is the bottom four, an octave down.
  • Tuning.BuildPitchArray decides which end of a tuning is string 1 by comparing the first pitch with the last one only. That works on any tuning that rises or falls all the way. On GA’s own two 5-string banjo tunings, whose short drone string is written first and sounds higher than the last string, it keeps the file’s order and numbers the strings backwards. Str’s comment, “String 1 is the string with the highest pitch”, cannot hold for that instrument: string 1 is D4 and string 5 is G4.
  • Fretboard.GetNote documents its first parameter as “Zero-based string index (0 = lowest string)” and then indexes Tuning[stringIndex + 1], where string 1 is the highest. The comment is wrong, not the code.
  • Appendix A: InstrumentsConfig returns one instrument. The course program now references GA.Business.Config and calls getAllInstruments() on the same run that reads the file itself: the file holds 122 instruments and 280 tunings, the loader returns 1 and 2. InstrumentsYaml expects a document with an Instruments: list of { Name, Tunings }; the file is a mapping of instruments, each a mapping of tunings, with no Instruments key anywhere. Both the null check and the with _ -> handler fall back to defaultData (), two hard-coded guitar tunings. Nothing fails and nothing logs.
  • Seven of the 280 tunings are not pitches: two pedal steel entries begin with their tuning’s name (C6, E9), one begins with the literal word Tuning, one is the instrument’s own name, one is missing an octave, and the two harp guitars use | to separate the sub-bass strings. The same name-as-a-pitch slip produced the ukulele’s five-pitch tunings, which parse silently.
  • Appendix B: the OPTIC hierarchy. One fingering climbed to a set class, one equivalence at a time, with GA’s type at each rung — PitchClassSet after O, P and C, TranspositionClass after T, SetClass after I — and the counts 4096, 352 and 224 agreeing with the course. GA’s OPTIC-K embedding schema splits the same ladder in two: STRUCTURE (dimensions 6-29, weight 0.45) is “pitch-class set invariants (O+P+T+I)”, MORPHOLOGY (30-53, weight 0.25) is “physical fretboard realization”, which is everything the ladder discards. The weights and ranges are read from the skill document, not run: to verify.
  • The appendix also explains Ian Ring’s scale finder as the ladder made clickable — Rotate is T, Reflect is I — and Harmonious as the same material seen from the other end, by key and by chord rather than by set number.
  • The course program grows to ten entry points (l1 to l10), all in check.sh. Diagrams.Fretboard now takes a tuning, so the ukulele and the bass get their own fretboard diagrams; the fourteen existing SVG files are byte-identical after the change.
  • You can hear the lessons now. A ```play code block becomes a player: the pitches on one line sound together, one line after another. The samples are one recorded note per semitone of FluidR3_GM’s nylon acoustic guitar, pinned on one commit of gleitz/midi-js-soundfonts and fetched on the first click — nothing is synthesised, no audio file is stored here, and every note is a sample played at its own pitch. Standard tuning is in lesson 1, C ionian against C dorian in lesson 2, the four voicings in lesson 3, ii–V–I in lesson 7, and the guitar shape against the same fingers on a ukulele in lesson 8. The block is code, so it is identical in the three languages; only the button’s label follows the page.

2026-09-14 — Pinning GA and building against it

Section titled “2026-09-14 — Pinning GA and building against it”
  • GA is pinned on a826864 (chore(quality): snapshot 2026-09-14), the head of main on the day. The author’s local clone of GA is never used or modified: fetch-ga.sh makes its own clone in code/music-theory-ga/.ga, ignored by Git.
  • The clone is blobless (--filter=blob:none) with a non-cone sparse checkout of /Directory.Build.props, GA.Core, GA.Business.Config and GA.Domain.Core: Git downloads only the contents of those files at checkout. GA.Domain.Core references the two others; GA.Business.Config is an F# project, which the .NET SDK builds without extra setup.
  • In Git Bash on Windows, the sparse patterns starting with / were rewritten into Windows paths (C:/Program Files/Git/Common/...) and matched nothing. MSYS_NO_PATHCONV=1 fixes it.
  • A git grep over the blobless clone hung: it fetched every blob of the repository one by one. Reading single files with git show HEAD:<path> fetches only those.
  • The course project references GA with a plain ProjectReference (GaTheory.csproj). Local build of GA’s three projects plus the course: 5 to 13 seconds after the first restore.
  • CI run 34903462624, for the push that contained commit 79d2198: green on the three OSes, 34 s on Linux, 47 s on macOS, 67 s on Windows, clone included.
  • The course’s Theory.cs is written from the textbook definitions only, then compared with GA. Sources: Open Music Theory (chapters listed in each lesson), Wikipedia (MIDI tuning standard, Guitar tunings, Guitar chord, Blues scale, Interval vector, List of set classes), Ian Ring’s scale 2741, OEIS A000029 and A000031.
  • The Open Music Theory site and OEIS answer 403 to plain HTTP clients; the chapters were read in a browser.
  • Checked in bulk on all 4096 sets: the course’s Rahn prime form equals GA’s minimal-id prime form for every set; 224 set classes and 352 transposition classes, as OEIS says. Forte’s and Rahn’s packings differ on 6 set classes, and on 17 of the 352 transposition classes, the figure List of set classes gives (the 17 were computed with the same algorithm outside the program).
  • Bugs found in the course program itself while writing: the empty set crashed the prime-form sort key; the barre rule first counted open strings, copying GA; the voicing x02010 was first named C6/A until the namer tried the bass as the root first.

2026-09-14 — Differences in GA’s code (commit a826864)

Section titled “2026-09-14 — Differences in GA’s code (commit a826864)”

Kept as DIFF lines in the expected outputs, so that CI notices when GA changes. None was reported upstream.

Lesson 1, notes:

  • Pitch.Flat.DFlat(octave), FFlat and GFlat build D, G and A (Pitch.cs#L285-L295).
  • PitchParser’s regex has no anchors and ignores case: Pitch.Sharp.TryParse("Eb2") succeeds with B2.
  • Note.Flat.Parse("B") is B♭: the parser upper-cases, then reads a trailing B as a flat.
  • PitchClass.Parse("A") is 10 (hexadecimal-style digit, on purpose), while the note A is 9.
  • SimpleIntervalSize.TryParse throws instead of returning false.
  • Fretboard.GetPositionsForNote(Note.Sharp.C) finds nothing: positions hold Note.Chromatic, and records of different types are never equal.
  • Minor, no visible effect: ValueObjectUtils.IsValueInRange(..., normalize: true) computes count = max - min and adds 1 after the modulo, unlike EnsureValueRange; with normalization both always end in range (ValueObjectUtils.cs#L69-L92).

Lesson 2, scales:

  • ModalFamily groups the sets containing 0 by interval-class vector, not by rotation: 14 “modes” for harmonic minor (its own 7 and harmonic major’s), 24 for blues (with its inversion and a Z-related class). Not a bug, but the name suggests rotations.
  • The minor scales are spelled from A, so Scale.NaturalMinor has the id of C major (2741). Expected for a pitch-class set, surprising next to the other scales, spelled from C.

Lesson 3, chords:

  • ChordFormula.DetermineQuality never returns Major7, Minor7, HalfDiminished or Diminished7, so GetSymbolSuffix gives 7 for maj7, dim7 for m7♭5 and dim6 for dim7.
  • The Chord constructor spells every non-root note with sharps: Eb → Eb G A#, Gb7 → Gb A# C# E.
  • Chord.AnalyzeChordFormula skips Notes[0] as if it were the root; after ToInversion(1) it is the bass, the third is lost and the quality is Other.
  • Voicing.HasBarre counts open strings: open C with a low E, open E and open G are “barre” chords.
  • GA’s voicing diagrams start at string 1 (high E), the reverse of chord charts.
  • CanonicalChordPatternCatalog lists four interval sets twice (9-sus4/dominant-11, major-6-add-9/6-9, minor-6-add-9/minor-6-9, augmented-7/dominant-7-sharp-5); TryFindExact can never return the second name of each pair.
  • The catalog matches intervals from the lowest note in the course’s usage, so an inversion such as 032010 (C/E) has no name.

Lesson 4, set classes:

  • IntervalClassVectorId packs the counts in base 12; the chromatic scale’s counts of 12 overflow and decode to <1 1 1 1 0 6>.
  • CanonicalForteCatalog describes its data as “the standard Forte-column values”, but stores Rahn’s spellings for the disputed classes (5-20 01568, 6-Z29 023679), like Open Music Theory’s table. The labels are right; only the comment is imprecise.
  • GrothendieckDelta.FromIcVs turns a zero difference into Ic1 = 1 on purpose, so sets with the same vector are reported at distance 1, and a triad’s “neighbours at distance 1” are the other major and minor triads, itself included.

Called from this session through the GA MCP server (the server does not report a version; it may not run commit a826864). The code references are to a826864.

  • ga_set_class_subs("Am") and ("G7"): the right lists (all major and minor triads; all dominant and half-diminished sevenths), but every chord under [maj] (c.EndsWith("") in ChordAtonalTool.cs#L187-L190), and a description saying “Am and C are NOT equivalent”.
  • ga_chord_intervals: Cm7b5 → P1 m3 P5 m7, G7b9 → P1 M3 P5 m7, C9 → P1 M3 P5 M9, Cmaj9 → P1 M3 P5 M9. The F# closure keeps one interval per extension and ignores alterations (DomainClosures.fs#L85-L98). ga_parse_chord("Cm7b5") did parse alt:b5.
  • ga_chord_to_set, which takes its pitch classes from the same closure: Cm7b5 → {C, Eb, G, Bb}, 4-26 (should be 4-27, with G♭); Cdim7 → {C, Eb, F#, Bb}, 4-27, “Scale: Major Seventh” (should be 4-28, with A); C → 3-11, prime 0 3 7, a modal family of 6.
  • ga_icv_neighbors("C", 1): twelve identical lines <0 0 1 1 1 0> Δ=1 Forte:3-11 [Major Triad], explained by the zero-delta rule above.
  • ga_scale_by_id(2741): Major, with Forte Number: Some(n/a) (7-35 expected). ga_scale_by_name("Dorian"): not found.
  • get_tuning(Guitar, Standard): E2 A2 D3 G3 B3 E4, right. get_chord_voicings("C", maxFret 3): “An error occurred”. ga_search_voicings("C major open chord"): the “open” constraint was ignored (diagram 8-8-x-x-7-x, labelled C/E).

2026-09-14 — Notes on the Streeling modules

Section titled “2026-09-14 — Notes on the Streeling modules”

The modules are generated from GuitarAlchemist/Demerzel and were not modified; lessons link them where they help.

  • MUS-006, “Modes as Rotations”: a circular left shift of the id transposes (C major shifted by 2 is D major, 2774); getting D Dorian on C needs the opposite shift (1709). The practice answer “rotate left by 2 semitones → 2nd mode” has the same direction problem.
  • MUS-006 says Forte “identified 224 distinct set classes for cardinalities 3 through 9”; 224 counts all cardinalities from 0 to 12.
  • MUS-002 derives the prime form of the open strings (E A D G B) as [0,2,5,7,9], comparing pitch classes instead of intervals in the tie-break; the prime form is (02479). The same module answers “are major and minor triads different set classes? Yes”, then puts both in 3-11, and writes prime forms in square brackets where Open Music Theory uses parentheses.
  • GTR-002 labels the C shape x 3 2 0 1 0 with “Strings: 5-4-3-2-1”: six symbols for five strings, the x being string 6.
  • The diagrams are SVG files written by the course program (Diagrams.cs) into src/assets/music-theory-ga/, then imported by the .mdx lessons as components, so they are inlined in the page. Their colours are currentColor and Starlight’s CSS variables (--sl-color-accent-high, --sl-color-orange-high, --sl-color-green-high, --sl-color-bg), with a light fallback, and they follow the site’s light or dark theme. They contain note names and numbers only, so the three languages share the same files; the description is in each page’s aria-label and in the paragraph before the figure.
  • check.sh regenerates them in memory and compares them with the committed files; CI fails when a diagram is out of date. Regenerate with dotnet run --project GaTheory -c Release -- svg ../../src/assets/music-theory-ga.
  • GA has React components for the same pictures, which the course reads but does not reuse: BraceletNotation.tsx, FretDiagram.tsx and VexChordDiagram.tsx, under ReactComponents/ga-react-components/src/components. Reading BraceletNotation and its NoteGroup (not run, to verify in a browser): the dots and labels are placed at angle − 90°, but the spokes of NoteGroup use cos(angle) without the offset, so they would be rotated by a quarter turn; and findSymmetryAxes only tests axes through a note, missing axes between two notes, and adds each axis twice.

2026-09-15 — Lessons 5 to 7: differences in GA’s code (commit a826864)

Section titled “2026-09-15 — Lessons 5 to 7: differences in GA’s code (commit a826864)”

Kept as DIFF lines in the expected outputs. None was reported upstream.

Lesson 5, keys:

  • Key.GetInterval(note) returns note.GetInterval(Root), the interval from the note up to the root: Key.Major.C.GetInterval(E) is m6, not M3 (Key.cs#L70-L76).
  • Key.Major.TryParse("H") throws InvalidOperationException instead of returning false, and Key.Minor.TryParse has the same code (Key.cs#L153-L186).
  • The MCP tools get_parallel_key and get_relative_key have the same body: the other mode on the same signature, so the parallel key of C is A minor (KeyTools.cs#L135-L169).
  • get_neighboring_keys passes key.KeySignature.ToString(), the list of accidentals, to a lookup by key name, and fails (KeyTools.cs#L197-L215).

Lesson 6, chords of a key:

  • HarmonicFunctionExtensions.FromDegree(7) is always LeadingTone, also for the subtonic of natural minor; ScaleDegreeFunction.Subtonic exists but is not used there.
  • ga_diatonic_chords names roots from a 12-name table: F♯ major gets Fdim (E♯dim) and G♭ major gets B (C♭) (DomainClosures.fs#L23-L36). The core’s Key.Notes spells both keys right.
  • Code reading only (GA.Domain.Services is not built by the course, to verify): HarmonicFunctionAnalyzer.Parse tests Contains("tonic") before "supertonic" and "mediant" before "submediant", so “Supertonic” would parse as Tonic and “Submediant” as Mediant (HarmonicFunctionAnalyzer.cs#L29-L72). ToPrimaryCategory groups the mediant and submediant with the tonic, where Open Music Theory calls iii and vi weak predominants: a choice of textbook, not a bug.

Lesson 7, cadences and progressions:

  • Cadences.yaml numbers “Chromatic Mediant (Metal)”, Em–Gm in E minor, as i biii, from E major; the Andalusian cadence in the same file is numbered from the notes of E Phrygian. Its “Phrygian Half Cadence” is ♭II–i, where the classical term means iv⁶–V in minor.
  • PitchClassSet.ClosestDiatonicKey breaks ties with “the normal form contains pitch class 3, so minor”. The normal form of a major scale’s notes is 0 1 3 5 6 8 T, so C F G C, G D Em C, Dm7 G7 Cmaj7 and C Am F G7 come out in the relative minor (PitchClassSet.cs#L597-L660).
  • ga_key_from_progression and ga_analyze_progression score keys by chord roots only, against the natural minor scale, and prefer the first chord’s root (GuitaristProblemTools.cs#L157-L223, DomainClosures.fs#L255-L336).

2026-09-15 — GA’s MCP server, keys and progressions

Section titled “2026-09-15 — GA’s MCP server, keys and progressions”

Same caveat as on 2026-09-14: the server does not report its version.

  • get_parallel_key("Key of C") → Key of Am. get_relative_key("Key of Ab") → Key of Fm, right. get_key_signature_info("Key of Gm") → root G, Bb Eb, notes G A Bb C D Eb F, right.
  • get_neighboring_keys("Key of C") and get_diatonic_chords("A minor"): “An error occurred invoking …”.
  • ga_diatonic_chords: F♯ major → F#, G#m, A#m, B, C#, D#m, Fdim; G♭ major → Gb, Abm, Bbm, B, Db, Ebm, Fdim.
  • ga_key_from_progression(["Am","F","C","G"]) → best guess A minor (then C major and D minor, all 4/4), while the tool’s description promises C major; (["Dm7","G7","Cmaj7"]) → D minor (then C major and C minor).
  • ga_analyze_progression("Am Dm E7 Am") → “Key: A major, I IV V I”; ("Dm7 G7 Cmaj7") → “Key: D minor, i iv VII”.

2026-09-15 — Notes on the Streeling modules, continued

Section titled “2026-09-15 — Notes on the Streeling modules, continued”
  • MUS-003, “Guitar Example — D7 to G Voice Movements”: the tab puts C at fret 1 of the high E string and F♯ at fret 1 of the B string. Fret 1 is F on the high E string and C on the B string; open D7 (xx0212) has F♯ at fret 2 of string 1 and C at fret 1 of string 2. The lessons link MUS-003 for its functions, cadences and closely related keys, not for this example.
  • MUS-003 puts iii and vi in the tonic family; lesson 6 gives both readings.

2026-10-02 — The Streeling notes, corrected upstream

Section titled “2026-10-02 — The Streeling notes, corrected upstream”
  • Demerzel #1091, merged as e203e5a, corrected the MUS-006, MUS-002 and GTR-002 points of the 2026-09-14 notes; Demerzel #1098, merged as 98625c3, corrected the MUS-003 tab of the 2026-09-15 notes. Learn #114 syncs both here. At e203e5a:
    • MUS-006 describes a mode as a circular right shift and warns that a left shift transposes (lines 146-148); its practice answers rotate right (lines 194-195); it counts 208 Forte classes of 3 to 9 notes and 224 in all (line 223).
    • MUS-002 gives the prime form of the open strings as (02479) (line 136), writes prime forms in parentheses (line 105), and answers the triad question under each equivalence (line 107).
    • GTR-002 reads its diagrams from string 6 to string 1 (lines 29-33).
    • MUS-003’s D7 puts F♯ at fret 2 of string 1 and C at fret 1 of string 2 (lines 196-197).
  • Lesson 2 quoted MUS-006’s old rule in the present tense; it now quotes it in the past, pinned at Demerzel fbfb2ee, the last commit before #1091.

2026-09-24 — Upstream fixes checked again

Section titled “2026-09-24 — Upstream fixes checked again”
  • GA has merged fixes for the rows of the QA table: #681 for the MCP tools and #682 for the parsers on 2026-09-23, #625 for key detection on 2026-09-24. The course stays pinned to a826864, so the lessons and appendices still show the behaviour they were written against.
  • recheck/probe.cs calls the same tool methods directly, without the MCP transport, on GA 52f7fd5; its output is recheck/results.txt. It prints only the facts each finding is about, and ga-recheck.yml runs it every Monday against GA’s main and fails on any difference from results.txt: a finding that comes back, or a fix that changes an answer, shows up there. It is not part of check.sh, because it needs a full GA clone.
  • Fixed, and re-run: ga_chord_to_set('Cm7b5') → {C, Eb, F#, Bb}, 4-27, and ('Cdim7') → 0 3 6 9, 4-28 (spelled {C, Eb, F#, A}). ga_set_class_subs('Am') → the major triads under [maj], the minor ones under [m]. ga_icv_neighbors('C', 1) → “No other set class within distance 1 of C” instead of twelve identical lines. ga_scale_by_id(2741) → Forte Number: 7-35, and ga_scale_by_name('Dorian') → id 1709. get_neighboring_keys('Key of C') → F, C, G.
  • Key detection now goes through KeyIdentificationService, which adds a cadence weight to the count of diatonic chords (lines 171-237). ga_analyze_progression("Dm7 G7 Cmaj7") → “Key: C major”, ii V I, and ga_key_from_progression agrees; ga_analyze_progression("Am Dm E7 Am") → “Key: A minor”, i iv v i.
  • #729, merged 2026-09-24, fixes what that re-run still got wrong. In a minor key the major dominant now belongs to the key: "Am Dm E7 Am" → i iv V i, confidence 4/4 instead of 3/4. The numeral follows the chord’s quality (Bm7b5 → viiø, Bdim → vii°). A tie between relative keys, which share their chords, goes to the key whose tonic triad opens the progression instead of the first in alphabetical order: [Am, F, C, G] → A minor, [C, G, Am, F] → C major, and ga_analyze_progression and ga_key_from_progression now give the same key. Re-run on 52f7fd5: lines 12-19 of results.txt.
  • Still not right, not reported: E7 is labelled v, because the numeral comes from the natural-minor degree and not from the chord (romanFor); it also counts outside A minor (3/4), by design in #625, which credits a harmonic-minor V7 through the cadence instead. ga_key_from_progression(["Am","F","C","G"]) still puts A minor first, tied 4/4 with C major, and the tool’s description still promises C major (line 168).
  • Not re-run: get_chord_voicings, get_diatonic_chords("A minor") and ga_search_voicings, which need GaApi running.
  • The BraceletNotation row was a code reading; #733, merged 2026-09-24, reproduced it and fixed it. Its test compares the SVG coordinates. On the old code, 3 of its 4 cases fail: C’s spoke ends at x=160 while its dot is at x=100; a diminished seventh gives the axis directions [0, 0, 90] instead of [0, 45, 90, 135]; the major scale draws its one axis twice. The spokes now use the dots’ angleToCoordinates, and the axes are searched in half steps, so the ones between two notes are found. The rendering in a browser is still to verify.
  • #734, merged 2026-09-24, fixes the two rows outside the nineteen defects, each with a test that fails on the old code. getAllInstruments() returns 122 instruments and 280 tunings where it returned 1 and 2. Nine malformed tunings were restored from Tunings.txt, the file the YAML came from: the two pedal steel entries, the Saz, the Huapanguera, the Dulcimer, and the four ukulele tunings that began with C6 or D6. The 5-string banjo tunings now number string 1 from the treble side; tunings that rise or fall all the way are numbered as before. Instrument and tuning names are now the YAML keys, DropD rather than Drop D.
  • One reading in appendix B was wrong. Dulcimer.LydianMode is Bb C4 C4 F3 Bb2, and I took the first Bb for a pitch missing its octave. Tunings.txt reads Ducimer - Lydian Mode Bb C4 C4 F3 Bb2: the Bb is the end of the tuning’s name, Lydian Mode Bb, which the conversion to YAML left in the pitch list. The appendix and the QA row now say so.

2026-09-30 — Lesson 9: predictions before the first run

Section titled “2026-09-30 — Lesson 9: predictions before the first run”

Written from GA’s code at a826864, before the lesson’s program existed; the results entry quotes each one against what the program printed. The MCP tool ga_common_tones was called before this entry was written, so its answers are reported there as observations, not as tests.

  1. Common tones. GA’s core, Chord.FromSymbol then PitchClassSet.Intersect, agrees with the course on the lesson’s 13 pairs of chords, Cmaj9, Cdim7 and C6 included.
  2. The chatbot’s smallest voice leading. VoiceLeadingSkill makes both chords the same size by repeating the root of the smaller one (VoiceLeadingSkill.cs, lines 185-194). For G → Cmaj7 it should answer 5 semitones where 3 exist, with B doubled: G–G, B–B, B–C, D–E. Over every ordered pair between the 48 triads (major, minor, diminished, augmented) and the 60 seventh chords (maj7, 7, m7, m7♭5, dim7), repeating the root should miss the minimum in more than 10 % of the triad → seventh pairs, and less often from seventh to triad.
  3. The chatbot’s chord pattern. Of the skill’s ten example prompts, exactly one should fail its own regular expression: “voice leading C major to G major”, because the pattern wants a chord symbol right before “to”.
  4. ga_voice_leading_pair with voicings of different sizes. The MCP tool pairs the lowest notes of the two voicings and charges 3 semitones for each note left over (CompositionTools.cs, lines 320-336). From open G (320003) to open C (x32010) it should give 28, where leaving out the low G gives 10.
  5. Parallel fifths and octaves. ProgressionVoiceLeadingAnalyzer applies the textbook rule string by string, and the course should agree on every example: F barre → G barre, 3 fifths and 4 octaves; E5 → G5, 1 and 1; open C → open G and open G7 → open C, one octave each, on strings 5 and 2; Am → Dm, none.
  6. VoiceLeadingSpace. With octave and permutation equivalence, its shortcut (the cyclic shifts of the two sorted chords, lines 63-91) should equal the brute-force minimum over every assignment of voices, for every ordered pair of 3-note and of 4-note pitch-class sets, with and without transposition: no disagreement. With transposition but without octave equivalence, it only tries upward shifts of 0 to 11 semitones (lines 46-61): from C4 E4 G4 to B3 D♯4 F♯4 it should give 0, since B3 shifted up by one is C4, and from B3 D♯4 F♯4 to C4 E4 G4 it should give 3.
  7. SetClassOpticIndex. For two set classes of the same size, it adds up, for each note of the first, the distance to the nearest note of the second (lines 167-224), so several voices may land on one note. That can never exceed the one-to-one minimum, and should fall below it, and differ from itself with the arguments swapped, on some of the 144 ordered pairs of the 12 trichord classes: (012) against (048) should give 2 one way and 6 the other, where one-to-one gives 6.

2026-09-30 — Lesson 9: results against the predictions

Section titled “2026-09-30 — Lesson 9: results against the predictions”

The program is Lesson11.cs (entry l11, after the two appendices), its output expected/l11.txt; check.sh passes for lessons 1 to 11 and the 18 diagrams. It compiles four files of GA.Domain.Services as they are (VoiceLeadingSpace, SetClassOpticIndex, ProgressionVoiceLeadingAnalyzer and its report), which fetch-ga.sh now adds to its sparse checkout, and copies the chatbot skill’s pattern and the MCP tool’s distance as they are. The skill’s doubling rule is rewritten, so for that one the course tests its reading of GA’s code, not the running chatbot.

  1. Confirmed: 13 of 13.
  2. Confirmed for G → Cmaj7, 5 semitones where the course finds 3 with B doubled, and for the share: 1,260 of the 2,880 triad → seventh pairs (43.8 %) are above the minimum, by up to 4 semitones, Cdim → Gmaj7 being the first with that gap. Refuted for the direction: seventh → triad misses exactly as often, 1,260 times. I expected the direction to matter because the repeated root belongs to the first chord one way and to the second the other way. But the skill always pads the triad, and a voice leading read backwards costs the same, so the two directions are the same 2,880 problems.
  3. Confirmed: “voice leading C major to G major” is the only example without a match.
  4. Confirmed: 28 and 10. Two more rows have the same cause: open G7 → open C, 28 where 8 exists, and xx2010 → x32010, the same chord with a bass added, 19 where 3 exists.
  5. Confirmed: 5 of 5 changes, pair of strings for pair of strings.
  6. Confirmed: no disagreement over 48,400 ordered pairs of 3-note sets and 245,025 of 4-note sets, with and without transposition. Without octave equivalence, 0 and 3 as written; C4 major → C5 major also gives 36 and the reverse 3, where a distance up to transposition gives 0.
  7. Confirmed: 2 one way and 6 the other, one-to-one 6. Over the 144 ordered pairs: 35 below the one-to-one minimum, none above, 70 that change when swapped.

Observations, not tests, from the MCP server running on this machine on 2026-09-30 (its version is not reported):

  • ga_common_tones loses one note in five of seven pairs: F from Bm7b5/G7, B from Cmaj9/Em7, A from Cdim7/F7 and from C6/Am7, B♭ from C9/Gm7. The cause is the six-entry extension table that also broke ga_chord_to_set (DomainClosures.fs, lines 85-98 at a826864). On GA’s main (5c3a52a), domain.commonTones takes its notes from chordTones, which #681 introduced in its commit 82bc7ee6; read, not run.
  • ga_parse_chord("Bm7b5") keeps the alteration ("components":["ext:7","alt:b5"]): the notes are lost after parsing.
  • ga_voice_leading_pair("G7", "C", limit 3, guitar) evaluated 225 pairs and ranked first two G7/B shells at distance 5, then one at 6. Their extra notes are on top, where pairing from the bottom is right, and the copied function gives the same 5 for the first pair.

2026-10-01 — Lesson 10: predictions before the first run

Section titled “2026-10-01 — Lesson 10: predictions before the first run”

Written from GA’s code at a826864, before the lesson’s program existed; the results entry quotes each one against what the program printed. No MCP tool and no chatbot was called for this lesson.

  1. The MCP server’s ga_chord_substitutions. It calls the F# closure domain.chordSubstitutions (DomainClosures.fs, lines 535-621), which keeps the diatonic triads of the key that share a note with the chord and sorts them by the number of shared notes, ties in scale order. For Am in C major it should rank C and F first with 2 shared notes, then Dm and Em with 1, so the example in the tool’s own description, “C (★★★, relative major), Em (★★, shared E/B), F (★, shared A)” (GaDslTool.cs, line 212), is wrong on all three chords. Without a key, the key is the chord’s letter, in major: Am is compared with the chords of A major, and B♭7 with those of B major. At a826864, Bm7♭5 in C major is built as B D F♯ A, so Dm and Bdim share 2 notes with it instead of 3.
  2. Its tritone line. It is added when the chord has a major third and a minor seventh above its root (lines 603-609): G7 gets D♭7; G9 and G13 get none, because at a826864 “9” and “13” add only their own degree.
  3. The chatbot’s ChordSubstitutionSkill: which path each example takes. The chatbot picks the skill by meaning; the skill then reads chord symbols with its own pattern (ChordSubstitutionSkill.cs, lines 69-71) and compares the chords when it finds two (lines 147-155). Of its twelve example prompts, ten should take the one-chord path; “Alternative chord for F major in C” should compare F with C instead of listing alternatives; “Borrow a chord from parallel minor” should find no chord symbol at all.
  4. The skill’s one-chord list. It keeps the sets of the same size within ICV distance 3 and sorts them by cost (lines 168-174). GrothendieckDelta.FromIcVs turns a zero difference into 1 (GrothendieckDelta.cs, lines 125-131), and two sets of the same size can only be an even distance apart, so every chord with the same interval-class vector, here the chords of the same set class, should come out at cost 0.60 and distance 1, ahead of everything else, in the order of their 12-bit numbers. For G7: Dm7♭5, A♭7, F7, D7, E♭m7♭5, without D♭7; for Am: Cm, C, A♭, D♭m, Fm, without E7; for C: Cm, A♭, D♭m, Fm, D♭, without B♭7; for Cmaj7: D♭maj7, A♭maj7, Fmaj7, Dmaj7, Amaj7.
  5. The skill’s two-chord labels. G7 → D♭7 should get “Tritone Substitution”, “Set-Class Equivalent” and “ICV Neighbor (L1 = 1)”; Dm → G “Secondary Dominant”, because the test looks only at the roots (line 262); C → C “Set-Class Equivalent” and “ICV Neighbor (L1 = 1)”; B♭7 → C “Backdoor Dominant” alone.
  6. get_borrowed_chords. The MCP tool forwards to GaApi’s ContextualChordService.GetBorrowedChordsAsync (lines 75-119), which the program compiles as it is. For C major it should list 18 chords, each credited to the first parallel mode that has it, in the order Dorian, Phrygian, Lydian, Mixolydian, Aeolian, Locrian: Dorian Cm, D♯, Gm, A°, A♯; Phrygian C♯, Fm, G°, G♯, A♯m; Lydian D, F♯°, Bm; Mixolydian E°; Aeolian D°; Locrian C°, D♯m, F♯. The black-key roots are spelled with sharps (line 137) and the numerals carry no accidental (lines 157-170): G♯, the A♭ chord, is “VI”, A♯ “VII”, D♯ “III”, F♯, the G♭ chord, “V”, and C♯ and D are both “II”.
  7. ModalInterchange.yaml. Of the 13 borrowed chords it spells out in C major or A minor, 9 should be right and 4 wrong: VI F major, VII G major and III C major are A minor’s own chords, not chords borrowed from A major, and “bii°, Bb diminished” is not in A Phrygian, whose second degree is B♭ major. Of the six numerals of its harmonic and melodic minor examples, three should be wrong: ♭VI+ (the augmented chord of harmonic minor is ♭III+), iv+ (the fourth degree of melodic minor is a major chord, and its augmented chord is also ♭III+) and the vii°7 of melodic minor, which is half-diminished. No GA code should read the file: it is copied to the output directory and watched for changes, and a change only logs “Unknown configuration file”.

2026-10-01 — Lesson 10: results against the predictions

Section titled “2026-10-01 — Lesson 10: results against the predictions”

The program is Lesson12.cs (entry l12), its output expected/l12.txt; check.sh passes for lessons 1 to 12 and the 18 diagrams. It compiles seven more of GA’s files as they are, which fetch-ga.sh now adds to its sparse checkout: GrothendieckDelta, GrothendieckService and its interface, ChordTemplateFactory and the global usings of GA.Domain.Services, and GaApi’s ContextualChordService with its models. It copies ChordSubstitutionSkill’s pattern, tables, selection and labels as they are. The MCP closure domain.chordSubstitutions is F# in a project the program does not build, so it is rewritten: for predictions 1 and 2 the course tests its reading of GA’s code, not the running server. The test of YamlKnowledgeLoader, which the program already compiled with GA.Business.Config, was not in the first run: I added it afterwards, in its own commit, aab1f4f, once I had found the loader.

  1. Confirmed, on the rewrite: C 2, F 2, Dm 1, Em 1 for Am in C major, so the description’s example is wrong on all three chords; Em shares only E with Am. Without a key, A major for Am and B major for B♭7, and two more of the same kind: F♯m against F major, E♭ against E major. With B D F♯ A, Bm7♭5 gets Dm 2, G 2, Bdim 2, where the course, with B D F A, puts Dm first with 3.
  2. Confirmed, on the rewrite: G7, D7, E7 and C7 get D♭7, A♭7, B♭7 and G♭7; G9 and G13 get none, and neither do Gm7 and Cmaj7, rightly.
  3. Confirmed: ten prompts list substitutes, “Alternative chord for F major in C” compares F with C, and “Borrow a chord from parallel minor” gets “Could not identify a chord symbol in your message.”
  4. Confirmed: the four lists as written, every chord at cost 0.60 and “Δ L1=1”. 23 chords tie for G7, Am and C, 11 for Cmaj7: exactly the other chords of each set class. D♭7 is number 19 of the 155 chords listed for G7; for Am, the textbook’s C is number 2 and F number 8 of 95. Dm7, which I had not predicted, gives Fm7, F♯m7, Am7, E♭m7, Cm7; its textbook substitute Fmaj7 is 4 away, beyond the cut-off of 3, and so are Em7 and Am7 from Cmaj7. Over the twelve roots of each chord type, the lists hold at most 6 different chords, and Dm7♭5 comes first for all 12 dominant sevenths.
  5. Confirmed for the four pairs. Dm → G also gets “Set-Class Equivalent” and “ICV Neighbor (L1 = 1)”, which I did not write down. Two pairs I had not predicted show the same two tests: F♯ → Bdim is a “Secondary Dominant”, and C → Am an “ICV Neighbor (L1 = 1)” where the distance is 0. G7 → C, D7 → G and B♭7 → C agree with the course.
  6. Confirmed: the 18 chords in the predicted order, with the predicted modes and numerals. One detail I had not written down: the notes are spelled with sharps even when the chord’s name has none: C minor is C D♯ G, A diminished A C D♯.
  7. Confirmed for the chords and the numerals: 9 of 13 right, the 4 predicted ones wrong; ♭VI+, iv+ and melodic minor’s vii°7 wrong, the other three right. Refuted for the readers. YamlKnowledgeLoader lists every *.yaml file of the configuration directory, except five that have their own loaders, and flattens each entry into text; YamlKnowledgeSyncService would store those texts in the yaml_knowledge collection with embeddings, “for semantic retrieval by the chatbot agents”, but no host registers it: AddSyncServices, which would, is never called, GaCLI has the call commented out with a TODO, and nothing else reads the collection. The program’s test returns the file’s 8 entries, and the four wrong strings are in their text. I had searched GA for the file’s name, which finds the watcher and the build’s copy, but cannot find a reader that lists a directory.

Observations, not tests:

  • On the way, the loader skips SpecializedTunings.yaml with “While parsing a flow sequence, did not find expected ‘,’ or ‘]’.” on standard error, and nothing else. The file itself was not examined.
  • MUS-003, which the lesson links, has a mistake in its table of secondary dominants in G major: V7/IV is G7, G B D F, whose F is outside the key, not D7, which is V7 itself. Its note “V7/IV looks identical to V7” should say that V/IV, the G triad, looks like I. To fix in Demerzel, where the module is written.
  • The mission page said GA’s partial clone and the build output need under 20 MB. Measured on 2026-10-01: 35.2 MB, the clone 17.5 MB and the build output 17.7 MB. The build output is mostly the libraries GA’s projects depend on (PCRE.NET’s native builds for three systems, FSharp.Core, MathNet.Numerics) and 2.2 MB of GA’s configuration files. The page now says about 35 MB.
  • The lesson 9 entry said that check.sh checks 19 diagrams. It checks 18: its nineteenth ok line is the summary line for the svg command. Corrected there.

2026-10-01 — Lesson 11: predictions before the first run

Section titled “2026-10-01 — Lesson 11: predictions before the first run”

Written from GA’s code at a826864, before the lesson’s program existed; the results entry quotes each one against what the program printed. No MCP tool and no chatbot was called for this lesson.

  1. The modes of melodic minor. MelodicMinorMode rotates the notes of Scale.MelodicMinor, which GA spells from A (A B C D E F♯ G♯, Scale.cs, line 120), so its seven modes start on A, B, C, D, E, F♯ and G♯. Each ModeFormula should equal the formula the course computes from the steps 2 1 2 2 2 2 1, against lesson 2’s reference (Aeolian when the third is minor, Ionian otherwise): melodic minor 1 2 b3 4 5 >♮6 >♮7, Dorian ♭2 1 >b2 b3 4 5 >♮6 b7, Lydian augmented 1 2 3 >♯4 >♯5 6 7, Lydian dominant 1 2 3 >♯4 5 6 >b7, Mixolydian ♭6 1 2 3 4 5 >b6 >b7, Locrian ♮2 1 2 b3 4 >b5 b6 b7, altered 1 >b2 b3 >b4 >b5 b6 b7: 7 of 7.
  2. The modes of harmonic minor. The same for HarmonicMinorMode, from A B C D E F G♯ (line 119) and the steps 2 1 2 2 1 3 1: harmonic minor 1 2 b3 4 5 b6 >♮7, Locrian ♮6 1 >b2 b3 4 >b5 >♮6 b7, Ionian ♯5 1 2 3 4 >♯5 6 7, Dorian ♯4 1 2 b3 >♯4 5 >♮6 b7, Phrygian dominant 1 >b2 3 4 5 >b6 >b7, Lydian ♯2 1 >♯2 3 >♯4 5 6 7, altered 𝄫7 1 >b2 b3 >b4 >b5 b6 >bb7: 7 of 7. Lydian ♯2 is measured against Ionian, because an augmented second is not a minor third, and the altered 𝄫7’s seventh, G♯ to F, is a diminished seventh.
  3. The short names. The five degree types implement the same IScaleDegreeNaming.ToShortName in three ways: Roman numerals without quality for major and harmonic major, the triad on each degree for the three minor scales, except iv#4 for harmonic minor and IV7 for melodic minor. Against the triad on each degree, 11 of the 35 should differ: 4 for major, 5 for harmonic major, 1 each for harmonic and melodic minor, none for natural minor. No GA code calls ToShortName.
  4. PitchClassSet.StepBrightness. It averages the steps between consecutive notes and divides by 6 (PitchClassSet.cs, lines 265-283). The steps of a set of two notes or more add up to 12, so the value is 2/n for n notes: 0.2857 for each of the 21 modes of major, melodic minor and harmonic minor. Over the 4096 sets it should take 11 values, since sets of 0, 1 and 4 notes all give 0.5.
  5. IcvBrightness. The brightness of the chatbot’s embeddings (TheoryVectorService.cs, lines 163-172) reads only the interval-class vector, which all the modes of a scale share: 0.642857 for the seven modes of major, 0.595238 for melodic minor, 0.571429 for harmonic minor. By this measure Lydian and Locrian are equally bright.
  6. UnifiedModeService.RankByBrightness. It adds up the pitch classes of each member of the set’s ModalFamily (UnifiedModeService.cs, lines 228-242). Every member contains C, so the sum is the course’s brightness of the mode on C: the semitones of its seven degrees above the tonic, added up. For major it should give the course’s order, Lydian 39 down to Locrian 33. For melodic minor too: Lydian augmented 40, Lydian dominant 38, melodic minor 37, Mixolydian ♭6 36, Dorian ♭2 35, Locrian ♮2 34, altered 32. For harmonic minor it should rank 14 sets, because the family also holds the seven modes of harmonic major (lesson 2). First comes harmonic major’s Lydian augmented ♯2 with 41, ahead of Lydian ♯2 with 40. Three ties, at 37, 36 and 35, each pair a mode of harmonic minor with one of harmonic major, in the order of their 12-bit numbers: Dorian ♯4 before harmonic major, Mixolydian ♭2 before harmonic minor, Phrygian dominant before Dorian ♭5.
  7. Modes.yaml. The file that the MCP server’s mode tools and the chatbot’s ModesSkill read holds 31 families and 165 modes. 18 families declare an interval-class vector. For 9 of them it is not the vector GA’s PitchClassSet computes from the notes of the family’s first mode: harmonic minor, melodic minor, major pentatonic, whole tone, diminished, blues, chromatic, augmented triad and major seventh chord. Of the other 134 modes, 60 do not have the vector of their family’s first mode. Among them is the harmonic minor family’s Ultralocrian, spelled C D♭ E♭ F♭ G♭ A♭ B♭ like the altered scale, where its seventh is B𝄫.
  8. ModesConfig’s lookups. TryGetModeByIntervalClassVector gives the “Scale:” line of ga_chord_to_set (ChordAtonalTool.cs, line 112). With the vectors GA computes, C major should find Ionian; harmonic minor, melodic minor and Cmaj7 nothing; G7 and Bm7♭5, both 4-27, “Major Seventh”; C and Cm “Major Triad”. TryGetModeByName is behind get_mode_info. Of the 14 names of GA’s two mode classes, 8 should be found and 6 not: Locrian Natural 2, Locrian Natural 6, Ionian Augmented, Dorian Sharp 4, Lydian Sharp 2 and Altered bb7. No alternate name should be found, “Super Locrian” and “Acoustic Scale” included, because GetAllModes sets every mode’s alternate names to None (ModesConfig.fs, line 284). GetModalFamilies, which ModesSkill reads, keeps them.

2026-10-01 — Lesson 11: results against the predictions

Section titled “2026-10-01 — Lesson 11: results against the predictions”

The program is Lesson13.cs (entry l13), its output expected/l13.txt; check.sh passes for lessons 1 to 13 and the 18 diagrams. It compiles one more of GA’s files as it is, UnifiedModeService, which fetch-ga.sh now adds to its sparse checkout, and copies TheoryVectorService.IcvBrightness, which is internal to GA.Business.ML, as it is. ModesConfig comes with GA.Business.Config, which the program already compiled. The test of the names Modes.yaml loses was not in the first run: I added it afterwards, in its own commit, 13e7607, once the first run had shown one of them.

  1. Confirmed: 7 of 7 formulas, and the 7 spellings. GA’s names are the textbook’s in ASCII, “Dorian b2” to “Locrian Natural 2”.
  2. Confirmed: 7 of 7, notes included. GA calls the seventh mode “Altered bb7” and the third “Ionian Augmented”.
  3. Confirmed: 11 of 35, degree for degree.
  4. Confirmed: 0.2857 for all 21 modes, and 11 values over the 4096 sets, one per size, with sets of 0, 1 and 4 notes sharing 0.5.
  5. Confirmed: 0.642857, 0.595238 and 0.571429, the same for every mode of each scale.
  6. Confirmed: major and melodic minor in the course’s order. For harmonic minor, 14 sets, harmonic major’s Lydian augmented ♯2 first with 41, and the three ties in the predicted order.
  7. Confirmed: 31 families, 165 modes, 18 declared vectors, the 9 predicted ones wrong; 60 of the 134 other modes without their family’s vector; the Ultralocrian with melodic minor’s vector, <2 5 4 4 4 2>.
  8. Confirmed for the vectors, as written. Refuted in part for the names: 9 of the 14 are found, not 8. “Ionian Augmented” is found, but in the Double Harmonic Family: the mode the file writes “Ionian Augmented #2” (Modes.yaml, line 655) comes back without “ #2”, which YAML reads as a comment. I had read the harmonic minor family, where “Ionian Augmented” is an alternate name, and not the rest of the file. The other five names are not found, and neither are “Super Locrian”, “Acoustic Scale” and “Spanish Phrygian”, all alternate names; GetModalFamilies keeps 104 alternate names, GetAllModes none.

The test added after the first run, in 13e7607: 8 of the 165 names lose their alterations to a YAML comment, at lines 631, 639, 655, 663, 667, 671, 679 and 681, and 7 names come back for more than one mode. Two of the seven are not cut names: the Minor Second and Major Second families call their inversions “Major Seventh” and “Minor Seventh”, like two chords of the Major Seventh Chord family.

Observations, not tests:

  • The chromatic scale is the one mode whose vector GA’s PitchClassSet and the course compute differently: GA <1 1 1 1 0 6>, the course <12 12 12 12 12 6>. IntervalClassVectorId documents it as a “KNOWN LIMITATION” of its base-12 packing (IntervalClassVectorId.cs, lines 3-15). The file declares <11 11 11 11 11 6> for its family, wrong either way.
  • HarmonicMajorScaleMode’s comment links to Ian Ring’s scale 2773 (HarmonicMajorScaleMode.cs, line 14), which is Lydian, C D E F♯ G A B; harmonic major is 2485. The links of MelodicMinorMode (2733) and HarmonicMinorMode (2477) are right.
  • get_mode_info prints the family’s declared vector on a line called “Intervals” and ends every answer with “Alternate Names: None” (ModeTool.cs, lines 27-33). Read, not run.

2026-10-01 — Lesson 12: predictions before the first run

Section titled “2026-10-01 — Lesson 12: predictions before the first run”

Written from GA’s code at a826864, before the lesson’s program existed; the results entry quotes each one against what the program printed. No MCP tool and no chatbot was called for this lesson.

  1. Limited transposition. A set has limited transpositions when a transposition other than T0 maps it onto itself. If d of the twelve transpositions fix it, it has 12/d distinct transpositions and, with n notes, n/d distinct modes. Over the 4096 sets, 76 should qualify, the empty set and the chromatic scale included, in 17 transposition classes. Without those two, they are the 15 “generalized Messiaen modes” Wikipedia counts. They repeat every 2 semitones (1 class, the whole-tone scale), 3 (2 classes: the diminished seventh and the octatonic scale), 4 (3 classes: the augmented triad, the augmented scale and Messiaen’s third mode) or 6 (9 classes). Messiaen’s seven modes should have 2, 3, 4, 6, 6, 6 and 6 transpositions and 1, 2, 3, 4, 3, 4 and 5 modes, as his list gives. GA’s UnifiedModeService.IsSymmetricSet tests the same thing, Transpose(k) for k from 1 to 11 (UnifiedModeService.cs, lines 349-362): it should agree with the course on all 4096 sets.
  2. GA’s three symmetric mode classes. WholeToneScaleMode, DiminishedScaleMode and AugmentedScaleMode hard-code 2, 3 and 4 transpositions: the course’s counts. But their degree types run from 1 to 6, 8 and 6, so Items gives 20 modes, which make 1, 2 and 2 different sets once each is moved to C. The class comments promise two, two and four distinct modes (line 12 of each file): the whole-tone and augmented counts are wrong. “Augmented dominant” and “Augmented mode 5” should be the same set as “Augmented”, C E♭ E G A♭ B, and “Augmented Lydian” and “Augmented mode 6” the same as “Augmented inversed”.
  3. The two diminished scales. DiminishedScaleMode rotates Scale.Diminished, C D E♭ F G♭ A♭ A B (Scale.cs, line 62), which starts with a whole step. So degree 1, which GA names “Half-whole diminished”, should be the whole-half scale, and degree 2, “Whole-half diminished”, the half-whole one: the names are swapped. Modes.yaml names the same two spellings the right way round (lines 331-342).
  4. The tritone scale. Scale.Tritone is C D♭ E F♯ G A (line 67). The tritone scale, the notes of the Petrushka chord, is C D♭ E G♭ G B♭, 0 1 4 6 7 10: GA has A for B♭. GA’s notes should form 6-Z50 (014679), the vector <2 2 4 2 3 2> of lesson 4’s exercise, with 12 transpositions and 6 modes; the tritone scale is 6-30 (013679), with 6 transpositions and 3 modes. TritoneScaleMode’s comment calls it “a symmetrical scale built from alternating half steps and minor thirds” with “only two distinct modes” (lines 11-14). GA’s steps are 1 3 2 1 2 3, the tritone scale’s 1 3 2 1 3 2, and half steps alternating with minor thirds make the augmented scale.
  5. IdentifyMessiaenMode. It compares the id of the set’s prime form, the smallest of its 24 forms (lesson 4), with fixed ids (UnifiedModeService.cs, lines 290-347). Of Messiaen’s seven modes, it should name the first three. For modes 4 to 7 the prime forms are 975, 455, 1495 and 2015, but GA tests 2535, 2275, 3315 and 3055, ids of other transpositions, so MessiaenModeIndex should stay empty. Both sets its comment gives for mode 6 are forms of mode 4, and the comment gives mode 3 three transpositions instead of four.
  6. IsMonomodal. It is true when a set’s modal family, the sets containing 0 with its interval-class vector, has one member (PitchClassSet.cs, line 189). It fills the dimension of the embeddings called “Is Symmetric/Monomodal” (ModalVectorService.cs, line 132; EmbeddingSchema.cs, lines 881-882). It should be true for 29 sets: the 12 single notes and the empty set, which share the vector <0 0 0 0 0 0>, 6 tritones, 4 augmented triads, 3 diminished sevenths, 2 whole-tone scales and the chromatic scale. That is 17 of the 76 sets with limited transpositions. The octatonic scale, which the property’s comment gives as an example (line 187), is not among them, nor are the augmented scale and Messiaen’s modes 3 to 7.
  7. AtonalModalFamilies.yaml. Its IsSymmetric is true for 6 of its 200 families, the ones with a single distinct mode: single notes, tritone, augmented triad, diminished seventh, whole-tone and chromatic scales. ModesSkill answers “modes of limited transposition” with these six (ModesSkill.cs, lines 596-601 and 687-705), then names the “octatonic (3), and hexatonic (4)”, which its list leaves out; the single note it lists has 12 transpositions. The file’s Forte labels come from ProgrammaticForteCatalog, which numbers each cardinality in increasing order of the vector’s id (ProgrammaticForteCatalog.cs, lines 123-133), the reverse of Forte’s order for small sets. The listing should print 2-1, 3-1, 4-1 and 6-1 for 2-6, 3-12, 4-28 and 6-35. Against GA’s own ForteCatalog (lesson 4), none of the 2-, 3-, 4-, 8-, 9- and 10-note families should agree, while 1-1, 11-1 and 12-1 should.
  8. AtonalChordAnalysisService.IsSymmetricalSet. It sorts the pitch classes and asks whether every step between neighbours is the same, leaving out the step from the last back to the first (AtonalChordAnalysisService.cs, lines 220-233). It should flag 156 of the 4096 sets: all 66 two-note sets and every evenly spaced run that fits between 0 and 11. 16 of them have limited transpositions: 6 tritones, 4 augmented triads, 3 diminished sevenths, 2 whole-tone scales and the chromatic scale. The other 60 sets with limited transpositions, the octatonic scales among them, should not be flagged. C D E should be flagged, and its transposition B♭ C D not.
  9. Mirror symmetry. TranspositionClass.IsInversionallySymmetric compares a class’s prime form with the transposition prime form of its inversion (TranspositionClass.cs, lines 52-53). It should agree with the course on all 352 transposition classes, 96 of them symmetric: 352 − 224 = 128 set classes split into two transposition classes, and the other 96 do not. Of the 15 classes with limited transpositions, all but the two forms of 6-30 should be mirror-symmetric, each with as many mirror axes as transpositions that fix it: 6 for the whole-tone scale, 4 for the octatonic scale, 3 for the augmented scale.

2026-10-01 — Lesson 12: results against the predictions

Section titled “2026-10-01 — Lesson 12: results against the predictions”

The program is Lesson14.cs (entry l14), its output expected/l14.txt; check.sh passes for lessons 1 to 14 and the 20 diagrams. It compiles one more of GA’s files as it is, AtonalChordAnalysisService, which fetch-ga.sh now adds to its sparse checkout, and calls its private IsSymmetricalSet by reflection. UnifiedModeService, compiled since lesson 11, gives IsSymmetricSet and MessiaenModeIndex; AtonalModalFamiliesConfig and ModesConfig come with GA.Business.Config. ModesSkill belongs to GA.Business.ML, which the course does not compile, so the program rebuilds its answer from its format and the same data. The test of what GA’s two mode catalogs call the fifteen classes was not in the first run: I added it afterwards, in its own commit, 6b4039a, once the first run had shown “Major Bebop Family”.

  1. Confirmed: 76 sets in 17 transposition classes, 15 without the empty set and the chromatic scale, in 14 set classes; 1, 2, 3 and 9 classes repeating every 2, 3, 4 and 6 semitones; Messiaen’s seven modes as his list gives them; IsSymmetricSet agrees on all 4096 sets.
  2. Confirmed: 2, 3 and 4 transpositions; 6, 8 and 6 modes, which make the sets 1365; 2925 and 1755; 2457 and 819 on C. “Augmented dominant” and “Augmented mode 5” are 2457 like “Augmented”, “Augmented Lydian” and “Augmented mode 6” 819 like “Augmented inversed”.
  3. Confirmed: degree 1, “Half-whole diminished”, has the steps 2 1 2 1 2 1 2 1, and degree 2 the steps 1 2 1 2 1 2 1 2.
  4. Confirmed: GA’s scale is 6-Z50, <2 2 4 2 3 2>, with 12 transpositions and 6 modes; the tritone scale 6-30, <2 2 4 2 2 3>, with 6 and 3. GA’s own IsSymmetricSet says False and True. TritoneScaleMode.Items gives 6 modes, 6 different sets on C.
  5. Confirmed: modes 1 to 3 named, 4 to 7 not. The program lists the eight ids the method tests: 2925, 2535, 2275, 3315 and 3055 are not prime forms, and 3315 is a form of mode 4.
  6. Confirmed: 29 sets, 1, 12, 6, 4, 3, 2 and 1 of 0, 1, 2, 3, 4, 6 and 12 notes, 17 of them limited; the octatonic scale not flagged.
  7. Confirmed: 200 families, 6 with IsSymmetric, 15 with limited transpositions; the answer’s six families with Forte 1-1, 2-1, 3-1, 4-1, 6-1 and 12-1. No label agrees for 2, 3, 4, 8, 9 and 10 notes, and 1-1, 11-1 and 12-1 agree. The prediction said nothing of 5, 6 and 7 notes: one hexachord family agrees, which makes 4 of 200. The labels are stale output, not an unknown error: GA #436 had moved the generator to ForteCatalog before a826864 (Demos/AtonalModalFamiliesGenerator/Program.cs, line 183) and left regenerating the file for later; the file has not changed since 7a4308a, which generated it.
  8. Confirmed: 156 sets, 16 limited, 60 limited sets not flagged; C D E True, B♭ C D False.
  9. Confirmed: 352 transposition classes, 96 mirror-symmetric, all agreeing; 13 of the 15 limited classes mirror-symmetric, with 6, 4 and 3 axes for the whole-tone, octatonic and augmented scales; the two forms of 6-30 with none. No set has mirror axes in a number other than its fixing transpositions.

The test added after the first run, in 6b4039a: AtonalModalFamilies.yaml names six of the fifteen classes after Modes.yaml, two of them wrongly. Messiaen’s fourth mode is “Major Bebop Family”, because Modes.yaml’s “Major Bebop Mode 8”, C D♭ D F G♭ G A♭ B (lines 725-726), is Messiaen’s fourth mode, not a rotation of the bebop scale; the diminished seventh is “Major Seventh Chord Family”, from the “Diminished Seventh” of that family (line 531). TryGetModeByIntervalClassVector names one class only, Messiaen’s sixth mode, “Diminished (Half-Whole)”: the Diminished Family declares <4 6 4 6 4 4> (lines 328-329), the sixth mode’s vector. The octatonic scale is found nowhere. Twenty modes of Modes.yaml belong to the fifteen classes, ten of them to the octatonic scale.

Observations, not tests:

  • ModalCharacteristicIntervalService stores each symmetric mode at a base plus its degree minus one, with bases 206, 208 and 212 (ModalCharacteristicIntervalService.cs, lines 114-118 and 135): the 8 diminished modes overlap the 6 whole-tone modes at 208 to 211 and the 6 augmented modes at 212 to 215, so indices 208 to 215 are written twice. Read, not run.
  • GenericSymmetricScaleMode’s constructor defaults to hasLimitedTranspositions = true and transpositionCount = 12 (GenericSymmetricScaleMode.cs, lines 23-27), two values that contradict each other.
  • GA’s tests for Messiaen’s modes 5 to 7 assert Is.Null.Or.EqualTo(...), and the one for mode 6 builds 3315, a form of mode 4 (UnifiedModeAdvancedTests.cs, lines 87-119).
  • ProgrammaticForteCatalog’s comment calls its differences from Forte’s numbering “minor” (lines 16-18); ForteCatalog’s says they diverge “for most set classes” and must not be surfaced (lines 9-12).
  • MUS-006 says that symmetric scales are not chiral. The tritone scale has limited transpositions and no mirror axis.

2026-10-01 — Lesson 13: predictions before the first run

Section titled “2026-10-01 — Lesson 13: predictions before the first run”

Written from GA’s code at a826864, before the lesson’s program existed; the results entry quotes each one against what the program printed. No MCP tool and no chatbot was called for this lesson. The course reads 41 chord symbols on C: 14 extended chords, 11 altered chords, 12 spellings of familiar chords taken from Open Music Theory and Wikipedia, two slash chords and two English words.

  1. Two chord-symbol parsers. GA’s C# has two: Chord.FromSymbol in GA.Domain.Core (Chord.cs, lines 75-140) and ChordSymbolParser in GA.Domain.Services (ChordSymbolParser.cs, lines 56-189), which GaApi’s VoicingFilterService and the chatbot’s SpectralRagOrchestrator call. Both lower-case the suffix and look it up in a table.
    • FromSymbol should read 14 of the 41 right: the 11 extended chords of its table (9, maj9, m9, 11, maj11, m11, 13, maj13, m13, 6/9, add9), Cø7, and the two words, which it rejects. It should throw on the other 26 chords: every altered chord, C7sus, C9sus4, Cadd2, both slash chords, and the textbooks’ own spellings Cmi, Cmi7, Cma7, Co7, C∆7 (Open Music Theory’s ∆, U+2206; GA accepts △, U+25B3), CΔ7 (Wikipedia’s Greek Δ), Cm(b5), Cm7♭5 (with the sign ♭), C+7 and Cm(maj7). CM7, a major seventh, should come back as C minor seventh: it is m7 once lower-cased.
    • ChordSymbolParser should throw on none of them and read 17 right. Past its table, it tests Contains("alt"), then b5, #5, b9, #9, #11 and b13, and returns a major triad when nothing matches (lines 98-140). So C7b9, C7#9, C7b13, Cmaj7#11 and the pitch classes of C/E come out right. C7b5 and C7#5 lose their seventh (C E G♭, C E G♯); C7#11 and C9#11 get a major seventh (C E G B F♯); C7alt gets a perfect fifth and no altered fifth (C E G B♭ D♭ D♯); C7b9#11 and C7#9b13 lose their second alteration; Cm(b5) comes out C E G♭; CM7 is a minor seventh again. Cmi, Cmi7, Cma7, Co7, C∆7, CΔ7, Cm7♭5, C+7, Cm(maj7), C7sus, C9sus4, Cadd2 and C/F# all come out as a C major triad, and so do the words: “Can” is C major, “Give” G major. GA’s tests list C7b5, C7#5, Galt and C/E among the valid symbols and check only that a chord comes back (ChordSymbolParserTests.cs, lines 11-18 and 33-40).
  2. Extended chords, read back. FromSymbol builds every 11 and 13 as the full stack of thirds, with the compound intervals 14, 17 and 21 (Chord.cs, lines 131-136). So C13 has seven notes, the F major collection, the same set as Fmaj13 and Gm13. GA’s CanonicalChordPatternCatalog stores the voicings players use, the 13 without its 11 and the major 11 without its third (CanonicalChordPatternCatalog.cs, lines 82-91): TryFindExact should find nothing for C13, Cmaj13, Cm13 and Cmaj11, and name the other seven. ChordFormula.GetSymbolSuffix should drop the “maj” of maj9, maj11 and maj13, as it drops the “maj” of maj7 (lesson 3), while BasicChordExtensionsService.GenerateChordName (lines 164-178), from the same quality and extension, should name all eleven right.
  3. Alterations, named by ChordAlterationService. A chord interval carries a Function, but every builder the course found fills it from the semitones modulo 12 (ChordFunctionExtensions.FromSemitones, lines 13-22): 3 is a third, 6 and 8 are fifths, and 1 falls to Root. DetectAlterationFromSemitones (lines 87-102) then reads 1 as ♭9, 6 as ♭5 and 8 as ♯5, and 3 never as ♯9, and GenerateNameWithAlterations (lines 37-61) calls a dominant seventh with any alteration “7alt”. GA’s own nine “common altered chords” (lines 186-202), built from their descriptions and passed the way GA builds an analytical template (ChordTemplate.Analytical.FromPitchClassSet), should all come back under another name: C7alt as C9(b9,b5,#5), C7(♭9) as C9(b9), C7(♯9) as C7 with no alteration, C7(♭5), C7(♯5), C7(♯11) and C7(♭13) all four as C7alt, C7(♭9,♯11) as C9(b9,b5), and Cmaj7(♯11) as Cmaj7(b5). The fifths that define a chord should count as alterations too: Cdim(b5), Caug(#5), Cdim7(b5) for Cm7♭5 and C6(b5) for Cdim7.
  4. Every template of ChordTemplateFactory. GenerateAllPossibleChords yields GenerateFromAllModalFamilies, which already runs the traditional, symmetrical and pentatonic scales, then runs the three again (ChordTemplateFactory.cs, lines 37-62 and 93-115): every template should come twice, the second half repeating the first. Over the templates, ChordAlterationService should never find ♭13, and should find ♯9 only in Sus2 templates and ♯11 only in Sus4 templates, the one place where the factory sets the function by hand (line 495).
  5. Polychords. ga_polychord merges the pitch classes of two chords (ChordAtonalTool.cs, lines 194-218). Its description promises “B triad over C triad → Lydian mode set {C,D,E,F#,G,B}” (line 198), but a B major triad is B D♯ F♯: the tool’s own code, read with domain.chordIntervals (DomainClosures.fs, lines 85-92 and 199-227), should print {C, Eb, E, F#, G, B}. {C, D, E, F♯, G, B} is B minor over C. Of the 28 modes of the major, melodic minor, harmonic minor and harmonic major scales built on the lower chord’s root, only Lydian should contain Bm/C, and only Lydian ♯2, the sixth mode of harmonic minor, B/C. AdvancedHarmony.yaml says that C major over F♯ major “Creates all 12 chromatic pitches” (line 104): it makes six, C D♭ E F♯ G B♭, the Petrushka chord and lesson 12’s tritone scale, 6-30, which none of the 28 modes contains and the half-whole octatonic scale does. The pending polychord skill gives D over E♭ a “Lydian-augmented colour” (DRAFT.md, line 47): on E♭, only Lydian ♯2 should contain it, not Lydian augmented. Its reading of C over D, A C D E F♯ G, should be right.
  6. The YAML the chatbot retrieves. No typed loader reads ExtendedChords.yaml, but YamlKnowledgeLoader flattens every YAML file without one into knowledge entries for retrieval (YamlKnowledgeLoader.fs, lines 25-31 and 131-140). It should return 3 entries from ExtendedChords.yaml, whose 13th chord, 1 3 5 ♭7 9 13, is the catalog’s and not the parsers’, and 9 from AdvancedHarmony.yaml, among them “Polychords” with its 12 chromatic pitches.
  7. Upper structures. A major or minor triad over C7 adds tensions. The course keeps the triads with no natural eleventh (F, a minor ninth above the third) and no major seventh (B, against the minor seventh). That should leave 6 major and 6 minor triads: Wikipedia’s nine upper structures (US II, ♭III, ♭V, ♭VI, VI, i, ♭ii, ♭iii and ♯iv) and three more, C major, the chord itself, G minor (5, ♭7, 9) and A minor (13, 1, 3). GA’s catalog has no upper-structure or polychord pattern, though its comment reserves priorities 90 to 99 for “polychord fragments” (CanonicalChordPatternCatalog.cs, line 26).

2026-10-02 — Lesson 13: results against the predictions

Section titled “2026-10-02 — Lesson 13: results against the predictions”

The program is Lesson15.cs (entry l15), its output expected/l15.txt; check.sh passes for lessons 1 to 15 and the 21 diagrams. It compiles three more of GA’s files as they are, ChordSymbolParser, BasicChordExtensionsService and ChordAlterationService, which fetch-ga.sh now adds to its sparse checkout. ga_polychord belongs to the MCP server and reads an F# closure of GA.Business.DSL, neither of which the course compiles: the program copies the tool’s code and replaces the closure with the triad intervals it returns. The test of MUS-005’s D♭/C was not in the first run: I added it afterwards, in its own commit, ac620ce, while writing the lesson.

  1. Confirmed: FromSymbol reads 14 of 41 right and rejects 28 symbols, the 26 chords and the two words; CM7 comes back as 0 3 7 T. ChordSymbolParser reads 17 right and rejects none; each misreading is as written, “Can” 0 4 7 and “Give” 2 7 E.
  2. Confirmed: TryFindExact names 7 of 11, nothing for C13, Cmaj13, Cm13 and Cmaj11; GetSymbolSuffix gives back 8, with C9, C11 and C13 for the three major chords; GenerateChordName 11. C13, Fmaj13, Gm13 and the F major scale are one set.
  3. Confirmed: the nine names and the four chords defined by their fifth, as written. The prediction did not say that SuggestedNotation calls the F♯ of Cmaj7(♯11) “Flat fifth (tritone substitution)”, nor that the catalog names seven of the nine.
  4. Confirmed: 25,900 templates, the second half repeating the first; ♭13 in none, ♯9 in 80 Sus2 templates, ♯11 in 176 Sus4 templates.
  5. Confirmed, with one citation corrected: the five merged sets and the modes that contain them are as written, but the draft’s “Lydian-augmented colour” is on line 46 of DRAFT.md, not 47. Not predicted: the card prints “24 modes” for 6-Z19, lesson 2’s wide family, and “mode 0” for D over E♭, whose set has no C, so that ModeIndex is −1.
  6. Confirmed: 3 and 9 entries, the 13th chord without its eleventh, and the Polychords entry with “Creates all 12 chromatic pitches”. Not predicted: the same loader returns IconicChords.yaml’s Petrushka chord with its six pitch classes.
  7. Confirmed: 12 triads, Wikipedia’s nine with C, Gm and Am; no catalog pattern named for an upper structure or a polychord, 0 of 62.

The test added after the first run, in ac620ce: D♭/C is 0 1 5 8, 1 ♭9 11 ♭13 over C, contained in Phrygian, Locrian, Phrygian dominant and Locrian ♭♭7; D/C is 0 2 6 9, in six modes with Lydian first; Cmaj7♯11 in Lydian and Lydian ♯2. MUS-005’s “Cmaj7#11 sound” is D/C, not D♭/C.

Observations, not tests:

  • The three guitar voicings of the figure, which leave out the fifth, match no pattern of TryFindExact.
  • While loading, YamlKnowledgeLoader prints on standard error “Skipping SpecializedTunings.yaml: While parsing a flow sequence, did not find expected ‘,’ or ‘]’.”: one of GA’s content files does not parse, and its entries never reach retrieval.
  • SpectralRagOrchestrator’s progression handler keeps every token ChordSymbolParser accepts, minus a short list of words that leaves out “Can” (SpectralRagOrchestrator.cs, lines 217-226). Read, not run.
  • MUS-005’s table of upper structures gives B♭ major over C7, whose F is the eleventh, as “the sus/11 sound”; it is a sus chord only once the third is gone.

2026-10-02 — Lesson 14: predictions before the first run

Section titled “2026-10-02 — Lesson 14: predictions before the first run”

Written from GA’s code at a826864, before the lesson’s program existed; the results entry quotes each one against what the program printed. No MCP tool and no chatbot was called for this lesson. The course takes Wikipedia’s definition of drop voicings: the voices of a close-position chord, all within an octave, are numbered from the top, and a drop-n voicing lowers voice n by an octave.

  1. Drop voicings, by definition. For the four seventh chords on C (maj7, 7, m7, m7♭5), each of their four close positions (one per top note) and each choice of dropped voices among voices 2, 3 and 4 (none, one, two or all three), the course builds 128 voicings and names each one back. GA’s VoicingHarmonicAnalyzer raises the lowest note an octave and answers “Drop-2” when the notes then fit within an octave, nothing otherwise (VoicingHarmonicAnalyzer.cs, lines 46-58). With four notes of four different pitch classes, that is “Drop-2” exactly when at most one voice is dropped: GA should call the 64 close, drop-2, drop-3 and drop-4 voicings “Drop-2” and leave the 64 others unnamed. It answers nothing else, so VoicingFilters’ Drop3 and Drop2And4, which look for “Drop-3” and “Drop-2+4” (VoicingFilters.cs, lines 114-116), should match none of them. The test needs four notes: Wikipedia’s three drop-2 voicings of the C major triad and its four drop-2-and-4 voicings of G7, which the course should rebuild as written (C E G, G C E, E G C; G D F B, B F G D, D G B F, F B D G, reading down from the top), should get nothing.
  2. Rootless and open. VoicingCharacteristics has IsRootless, DropVoicing and IsOpenVoicing in that order (VoicingCharacteristics.cs, lines 10-12), and Analyze passes intervalSpread > 12, the drop label and false (VoicingHarmonicAnalyzer.cs, lines 38-40). So in GA a voicing is “rootless” when it spans more than an octave, and no voicing is open. Of the 128 voicings, which all contain their root, the 112 with a dropped voice should be rootless and none open. ChordClassificationEngine then tags every voicing closed-voicing and every “Drop-2” drop-2-voicings, close ones included (ChordClassificationEngine.cs, lines 158-167). On guitar shapes, all seven open chords of lesson 3 should be rootless, and three of them “Drop-2”: x32010 (C), x02210 (Am) and xx0232 (D), where the notes fit within an octave once the bass is raised one; 032010, 022100, 320003 and 133211 get nothing. MUS-005’s “Drop-2 Cmaj7 (string set 5-4-3-2)”, x3200x, is C E G B within an octave, a close position: GA should call it “Drop-2”, like the page, and not rootless. The drop-2 voicing with C in the bass on those strings, x3545x (C G B E), should be “Drop-2” and rootless.
  3. Shells. The course plays root, third and seventh on MUS-005’s Freddie Green string sets: G7, Gmaj7 and Gm7 with the root on the sixth string (3x34xx, 3x44xx, 3x33xx, strings 6-4-3, the seventh below the third), C7, Cmaj7 and Cm7 with the root on the fifth (x323xx, x324xx, x313xx, strings 5-4-3, the third below the seventh). The first three span 15 or 16 semitones, the others 10 or 11: GA should call the first three rootless, though their root is the bass, and not the others. Three GA rules decide what a shell is, and none of them looks at the chord tones:
    • VoicingPhysicalAnalyzer gives the ShellFamily “3-4 note shell”, and the tag shell-voicing, to any voicing on three or four strings (VoicingPhysicalAnalyzer.cs, lines 126-131 and 204): all six shells, xx0232, x3200x and every drop voicing on four strings.
    • ChordClassificationEngine tags shell-voicing a rootless voicing of three pitch classes (lines 169-173): the three sixth-string shells, not the fifth-string ones, and all seven of lesson 3’s open triads.
    • VoicingFilters’ ShellVoicings keeps a rootless voicing of three distinct MIDI notes (lines 118-119): the three sixth-string shells and nothing else here; the open triads have four to six notes.
  4. Every voicing GA generates. VoicingGenerator slides a five-fret window along the 24-fret neck and keeps every combination of muted strings and frets in the window that plays at least two notes (VoicingGenerator.cs, lines 18-144 and 156-292). With six choices per string, a window gives 6⁶ − 31 = 46,625 voicings, and two neighbouring windows share 5⁶ − 25 = 15,600: run in sequence, it should return 21 × 46,625 − 20 × 15,600 = 667,125 voicings. The course keeps those that play the four notes of a seventh chord on C on four strings. GA’s label should be “Drop-2” on exactly those the course finds close, drop-2, drop-3 or drop-4. All 48 drop-2 shapes MUS-005 counts (4 qualities × 4 inversions × 3 sets of adjacent strings) should be among them, since none spans more than four frets, GA’s limit, and so should the 32 drop-3 shapes on its string sets 6-4-3-2 and 5-3-2-1. Over these voicings, VoicingFilters should keep with Drop2 the close voicings along with the drop-2, drop-3 and drop-4 ones; with ClosedPosition, all of them; with OpenPosition, Drop3, Drop2And4 and ShellVoicings, none; with Rootless, those spread over more than an octave.
  5. Guide tones. In a ii–V–I, the third of ii is the seventh of V and the third of V the seventh of I, while the sevenths of ii and V fall a half step to the next third. GA’s TransposeTables give the major ii–V–I in twelve keys (ChordProgressions.yaml, lines 581-618): the course should find these four moves in every row, and in C the two lines F F E and C B B behind MUS-005’s “C-B, F-E”. VoicingAnalyzer.AnalyzeEnhanced builds every ToneInventory as new ToneInventory([], false, [], []) (VoicingAnalyzer.cs, line 155): HasGuideTones should be false, and Tones and OmittedTones empty, for every voicing the course analyzes, the shells included.
  6. GA’s comping recipes. ChordProgressions.yaml lists six GuitarCompingRecipes with fifteen fingerings on named string sets (lines 461-577). Its typed loader reads only the ChordProgressions list (ChordProgressionsConfigLoader.cs, lines 41-44), but YamlKnowledgeLoader, which skips five files and not this one (YamlKnowledgeLoader.fs, lines 25-31), should return the six recipes for retrieval, fingerings included. Read low to high on their string sets in standard tuning, as the file’s conventions say (line 464), no fingering should give the notes of its name or of its degree order:
    • The major ii–V–I, a “Rootless Drop-2 set” on strings 6-4-3-2, a drop-3 string set: “Dm9: x-5-5-5” is G C E; “G7(13,#9): x-5-4-6” is G B F, the shell of G7 with its root; “Cmaj9: 8-x-9-9-10” has five frets for four strings and, read from the sixth string up, is C B E A.
    • The minor iiø–V–i, on 5-4-3-2: “Dm7b5(11): 5-6-5-6” is D A♭ C F, a drop-2 Dm7♭5 with its root and no eleventh; “G7(b9,#9): 5-4-5-5” is D F♯ C E, D9 without its fifth, with none of the four notes its degree order lists (B F A♭ A♯); “Cm(maj9): 3-5-4-5” is C G B E, a drop-2 Cmaj7.
    • The pop loop, on 5-4-3-2: “G(add9): 10-12-12-10” is G D G A, without B; “D(add9)/A: 12-11-11-12” is A C♯ F♯ B, without D; “Em(add9): 7-9-9-7” is E B E F♯, without G; “C(add9)/G: 10-10-9-10” is G C E A, without D.
    • The rock recipe, on 6-5-4-3: “A5(add2): 5-7-7-7” is A E A D, a fourth instead of a second; “Gsus2: 3-5-5-5” is G D G C, a Gsus4; “Dsus2/F#: 2-5-7-7” is F♯ D A D, D major over its third.
    • The “So What” vamp’s “Stacked 4ths (no 3rd)”: “Dm11: 5-7-9-10” is D A E A and “Ebm11: 6-8-10-11” is E♭ B♭ F B♭, two fifths and a fourth.
    • The blues recipe gives no frets; its E7, “7th on D string, 3rd on G string”, is right: D is the open fourth string and G♯ the first fret of the third.

2026-10-02 — Lesson 14: results against the predictions

Section titled “2026-10-02 — Lesson 14: results against the predictions”

The program is Lesson16.cs (entry l16), its output expected/l16.txt; check.sh passes for lessons 1 to 16 and the 22 diagrams. It compiles, as they are, twelve more files of GA.Domain.Services, the voicing generator, analyzers and filters with the chord recognizer and fret distances they call, and thirteen files of GA.Business.Core, the twelve analysis records they return and a class of constants; fetch-ga.sh now adds them to its sparse checkout. The build reports one warning, CS8604, in GA’s own VoicingAnalyzer.cs (line 198), where a ModeInfo that can be null goes to a parameter that does not accept null. No MCP tool and no chatbot was called, and no test was added after the first run.

  1. Confirmed: 64 “Drop-2”, 64 nothing, no other label; “Drop-2” exactly when at most one voice is dropped. Wikipedia’s seven examples are rebuilt as written, and GA names none.
  2. Confirmed: 112 “rootless”, none open, all 128 with their root; every voicing tagged closed-voicing, every “Drop-2” drop-2-voicings, the close ones included. On the shapes, lesson 3’s seven open chords are “rootless”, and x32010, x02210 and xx0232 “Drop-2”; x3200x is “Drop-2” and not rootless, x3545x “Drop-2” and rootless.
  3. Confirmed: the string rule on the six shells, xx0232, x3200x and x3545x; the engine on the three sixth-string shells and the seven open triads; the filter on the three sixth-string shells and nothing else.
  4. Confirmed: 667,125 voicings; “Drop-2” exactly on the course’s close, drop-2, drop-3 and drop-4 voicings; 48 of 48 drop-2 shapes and 32 of 32 drop-3 shapes, none wider than four frets; Drop2 264, ClosedPosition 687, Rootless 672, exactly those spread over more than an octave, OpenPosition, Drop3, Drop2And4 and ShellVoicings none. Not predicted: 687 voicings in all, 141 of them beyond drop-n; drop 2s on seven string sets, the four missing from MUS-005’s table each skipping a string; the widest shapes reach the four-fret limit exactly.
  5. Confirmed: 12 rows of 12 with the four moves, F F E and C B B in C; over 711 analyses, HasGuideTones never true, Tones and OmittedTones always empty.
  6. Confirmed: an entry for each of the six recipes, holding all its fingerings; the fifteen fingerings give the notes written in advance, none those of its name or degree order; the blues recipe’s E7 is right. Not predicted: the loader returns 36 entries from ChordProgressions.yaml.

Observations, not tests:

  • InterpretationService, whose only caller, GaCLI’s IndexVoicingsCommand, is excluded from the build (GaCLI.csproj, line 18), looks for “drop 2” and “drop 3” with a space (InterpretationService.cs, lines 41-46), while DropVoicing holds “Drop-2”: its drop tags never fire. Read, not run.
  • MUS-005, as it read at Demerzel 6aef19b until Demerzel #1098 corrected it, called x3200x, a close position, a “Drop-2 Cmaj7”; it said “Drop-2 voicings exist on four string sets” above a table of three rows; its shell diagrams showed one note per chord.
  • The section on drop voicings of Wikipedia’s “Voicing (music)” is flagged “This section does not cite any sources”.

2026-10-02 — Lesson 15: predictions before the first run

Section titled “2026-10-02 — Lesson 15: predictions before the first run”

Written from GA’s code at a826864 after the lesson’s program was built and before it first ran; the figures were worked out by hand from GA’s formulas, and the results entry quotes each one against what the program printed. No MCP tool and no chatbot was called for this lesson. The course places fret n at d(n) = L(1 − 2^(−n/12)) from the nut, reads every shape from string 6 in standard tuning, and takes the CAGED system from Wikipedia’s “Bar chord”.

  1. Where the frets go. FretboardGeometry.CalculateSpanMm(0, n) multiplies 2^0 − 2^(−n/12) by 647.7 mm (FretboardGeometry.cs, lines 17-32), and PhysicalFretboardCalculator.CalculateFretPositionMm(n) computes 648 · (1 − 2^(−n/12)) (PhysicalFretboardCalculator.cs, lines 35-43). Both should agree with the course at every fret, on two scale lengths 0.3 mm apart: fret 12 at 323.85 and 324.00 mm. The calculator should give MUS-009’s 36.4, 162.5, 215.5, 324 and 486 mm. PHYSICAL_PLAYABILITY_IMPROVEMENTS.md quotes 120.11, 85.42 and 70.09 mm for frets 0-5, 7-12 and 12-17, the low span “71.4% larger” than the high one. The formula gives 162.55, 108.49 and 81.27 mm, and since every distance halves an octave higher, the low span is exactly twice the high one: three DIFF rows, and 100.0% larger.
  2. How far a hand reaches. ComfortableReachScaleUnits is 0.21, commented “(1 - 2^(-4/12)) approx 0.206”, the distance from the nut to fret 4 (BiomechanicsConstants.cs, lines 25-28). PhysicalCostService compares it with the span of the fretted notes only (PhysicalCostService.cs, lines 25-38, and FretboardGeometry.cs, lines 38-57): open strings never count, so the lowest fret it measures from is 1. From fret 1 the reach should cover frets 1 to 5; from fret 7, frets 7 to 13; from fret 12, frets 12 to 21, ten frets. The first position, index finger on fret 1 and little finger on fret 4, measures 0.1502, well within it. The default PlayerProfile has a HandSize of Medium and a MaxComfortableStretchMm of 129 mm (PlayerProfile.cs, lines 14-17), and none of the three analyzers reads either.
  3. CAGED. The five open shapes should give GTR-002’s degrees exactly. Raised until their root is C, they should sit at frets 0, 3, 5, 8 and 10, as Wikipedia says, each holding three or four frets and playing only C, E and G. In every key, the order up the neck should be C A G E D read round from one of its letters; in G, G E D C A at frets 0, 3, 5, 7 and 10, Wikipedia’s sequence. GTR-002’s third exercise asks for C on string 2 “in each of the five CAGED positions”: only the C shape (fret 1) and the D shape (fret 13) have it there; the A, G and E shapes play E, E and G on that string. GA’s DetectCagedShape returns null at once when index 0 is muted (VoicingPhysicalAnalyzer.cs, lines 264-265), and its A and C tests both need index 0 muted (lines 297 and 323), so it can only ever answer “E-Shape”. Its comments read index 0 as string 6 (line 263), while ExtractPhysicalLayout numbers index 0 as string 1 (line 27), the order of GA’s voicings. In that order no E shape should be recognised; the pattern that is, 001220 from string 6, plays E A D♯ A C♯ E. Reversed, the E shape should be recognised at every fret, and nothing else. The tag it writes is e-shape (line 203), which contains neither “CAGED-E” nor “E shape”, the strings VoicingFilterEngine looks for (VoicingFilterEngine.cs, lines 61-66; read, not run).
  4. The effort of a shape. PhysicalCostService.CalculateStaticCost has no term for a barre (PhysicalCostService.cs, lines 17-142). On these shapes it should come down to the tension of the fretted strings, a register term when the average fret is below 3 or above 9, and a bonus for each open string: the open chords below zero, from −0.63 (Em) to −0.06 (D); F 0.29; the A, G and E shapes of C 0.21, 0.24 and 0.24; the D shape of C 0.59, and the E shape at fret 12 the highest, 0.66. VoicingPhysicalAnalyzer should call open C “Intermediate”, because its fretted span, 66.7 mm on 648 mm, is over 0.8 × 80 mm, and Am, Em, E, G and D “Beginner”; it finds a barre only on three adjacent strings at the same fret, so in x35553 and 875558, and not in F or the E shapes. BiomechanicalAnalyzer finds a barre whenever three notes share a fret, open strings included (BiomechanicalAnalyzer.cs, line 153): Em, E and G should get one, along with F and the barre shapes, while Am, C, D and the D shape of C do not. It should rate F “Very Easy” (0.92), with Em and E, above open C (0.82, “Easy”). Its HandSize is never read, so the four hand sizes should give the same analyses; nor does PhysicalCostService read the profile’s HandSize or MaxComfortableStretchMm. Under PlayerProfile.Beginner() the shapes above fret 5 should cost more, the D shape of C 1.04, and under Jazz() that shape should cost 0.18.
  5. Open tunings. Tunings.toml should hold the same 15 guitar entries as Instruments.yaml, where MUS-009 found string 2 of Open C and Open A an octave low. As changes from standard, OpenCMajor should lower string 2 by 11 semitones and OpenAMajor by 10, while the nine other six-string tunings move no string by more than 2. Their open strings should sound the chord their names give, except OpenEMajorV2, “Open E - 2 Minor”, an E minor chord. Wikipedia’s string orders for open C, D, E and G should match. No C# or F# code of GA reads the file; a React component lists it as an optional data URL and ignores the property.
  6. The table behind ga_easier_voicings. The tool’s 41 voicings (GuitaristProblemTools.cs, lines 506-562) should all list in Notes the notes of their symbol. Read in standard tuning, Gsus4 310013 should play a B♭ and C5 x3500x a B, outside their chords, and C7 x32310 should leave out its fifth, G. Five are marked HasBarre, F, B♭, B, Bm and F5; four have more than four fretted notes, while F5 has three. Under the default constraint “no-barre” the tool offers only voicings of the table on the same root (lines 611-652): Fmaj7 for F and F5, B7 and B5 for B and Bm, neither with the D of B minor, and nothing for B♭. The description’s Fsus2 (133011) and C/F are not in the table.

2026-10-02 — Lesson 15: results against the predictions

Section titled “2026-10-02 — Lesson 15: results against the predictions”

The program is Lesson17.cs (entry l17), its output expected/l17.txt; check.sh passes for lessons 1 to 17 and the 23 diagrams. It compiles, as they are, seven more files of GA.Domain.Services, the fretboard geometry, the cost service with the position record and the ranker interface it takes, and the biomechanical analyzer with the records it returns, and PlayerProfile from GA.Business.Core; it reads Tunings.toml and GuitaristProblemTools.cs, a file of GA’s MCP server, as text. fetch-ga.sh now adds them to its sparse checkout. The build reports no new warning. No MCP tool and no chatbot was called, and no test was added after the first run. After an independent review, ce3a9ed relabels the two Wikipedia tables, which printed the course’s header over Wikipedia’s figure, and the tunings table, which said GA reads Instruments.yaml, and marks the reach from fret 15 as stopping at the 24th fret; no number changed.

  1. Confirmed: every fret right on 647.7 and 648 mm, fret 12 at 323.85 and 324.00 mm; the ratio 1.059463 at all 24 frets; MUS-009’s five figures; three DIFF rows, 162.55, 108.49 and 81.27 mm, the low span 100.0% larger.
  2. Confirmed: 0.21 against 0.2063; frets 1 to 5 from fret 1, 7 to 13 from fret 7, 12 to 21 from fret 12; the first position at 0.1502; the default profile Medium and 129.0 mm, which no measure reads. Not predicted: the other starting frets, 2, 3, 5 and 9, which reach frets 6, 7, 10 and 16; from fret 15 the reach runs past the 24th fret, the last of the neck; and the reach in millimetres, from 112.4 to 131.3 mm where the neck does not stop it.
  3. Confirmed: GTR-002’s degrees for all five shapes; C at frets 0, 3, 5, 8 and 10, three or four frets each, C, E and G only; every order a rotation of C A G E D, and G’s Wikipedia’s; C on string 2 only in the C shape (fret 1) and the D shape (fret 13), E, E and G in the A, G and E shapes. In GA’s order no shape recognized, and 001220, E2 A2 D♯3 A3 C♯4 E4, recognized as “E-Shape”; reversed, the E shape at all 13 frets and nothing else; the tag e-shape, which fails VoicingFilterEngine’s test.
  4. Confirmed: no barre term; the open chords from −0.63 (Em) to −0.06 (D), F 0.29, the A, G and E shapes of C 0.21, 0.24 and 0.24, the D shape 0.59, the E shape at fret 12 0.66; open C “Intermediate”, Am, Em, E, G and D “Beginner”; VoicingPhysicalAnalyzer’s barre in x35553 and 875558 only; BiomechanicalAnalyzer’s in Em, E, G, F and the barre shapes, not in Am, C, D or the D shape of C; F, Em and E “Very Easy” at 0.92, open C “Easy” at 0.82; the same results for the four hand sizes, and for a profile’s HandSize and MaxComfortableStretchMm; the D shape of C at 1.04 under Beginner() and 0.18 under Jazz(). Not predicted: the scores not quoted above, such as 6.90 for 875558 and 0.72 for the D shape of C.
  5. Confirmed: the 15 entries as in Instruments.yaml; string 2 lowered by 11 semitones in OpenCMajor and by 10 in OpenAMajor, no string moved by more than 2 in the nine others; OpenEMajorV2 an E minor chord; Wikipedia’s four string orders. That GA’s C# and F# never read the file was checked by searching the code at a826864, not by running it.
  6. Confirmed: Notes right for 41 of 41; Gsus4’s B♭, C5’s B, C7 without G; HasBarre on F, B♭, B, Bm and F5, more than four fretted notes on all but F5; Fmaj7 for F and F5, B7 and B5 for B and Bm, nothing for B♭; Fsus2 and C/F not in the table. Not predicted: B7 holds all of B’s notes and B5 does not; 133011 has five fretted notes.

Observations, not tests:

  • PHYSICAL_PLAYABILITY_IMPROVEMENTS.md presents its spans as the output of FretDistance_SameFretSpan_DifferentPositions_ShouldHaveDifferentPhysicalDistances, a test in PhysicalFretboardCalculatorTests.cs, which GA’s test project removes from compilation (GA.Business.Core.Tests.csproj, line 47). The same project removes BiomechanicalAnalyzerTests.cs (line 49), whose hand-size tests call BiomechanicalAnalyzer.CreateForHandSize, a method the class does not have. Read, not run.
  • MusicalEmbeddingGenerator appends “-shape” to the lower-cased CagedShape (MusicalEmbeddingGenerator.cs, lines 110-113), which would give e-shape-shape. Read, not run.
  • GTR-002’s figure “for key of E major” draws the shapes on a scale of frets 0, 3, 5, 7, 8, 10 and 12; the program finds them at 0, 2, 4, 7 and 9.
  • The Guitar Alchemist Lab’s P7 found open C “Intermediate” and no barre in 133211 at GA’s commit 66bdd04, as this lesson did at a826864.

2026-10-02 — Lesson 16: predictions before the first run

Section titled “2026-10-02 — Lesson 16: predictions before the first run”

Written from GA’s code at a826864 after the lesson’s program was built and before it first ran; the results entry quotes each one against what the program printed. No MCP tool and no chatbot was called for this lesson: the program compiles GA’s two chatbot skills and calls them directly, with stand-ins for the logger and the query extractor they take, and it reads the MCP tool as text and applies it. The course builds the seventh chord on each degree by stacking thirds within the scale, and takes the chord-scale pairs from Wikipedia’s “Chord-scale system” and “Jazz improvisation” and the avoid note from its “Avoid note”.

  1. Arpeggios and modes. The seventh chords of C major are Cmaj7, Dm7, Em7, Fmaj7, G7, Am7 and Bm7♭5, and those of A minor are the same seven from Am7. The arpeggio suffixes of the tool’s two tables (GuitaristProblemTools.cs, lines 386-406) should match all fourteen, and ImprovisationSkill.ArpeggioFor should give each chord’s own symbol back (ImprovisationSkill.cs, lines 266-284). The tables’ Mode and Notes columns should match the mode on each degree, and so should GA’s MajorScaleMode. Of the twelve other scales the tool and the skills name, GA’s mode in the same place of its class should match ten. The two exceptions are GA’s diminished modes, as in lesson 12: the first, which GA names “Half-whole diminished”, begins with a whole step, and the second is the reverse, so both rows should read DIFF.
  2. ga_arpeggio_suggestions. The tool finds the degree from the chord’s root alone (line 463) and returns chord + arpeggioSuffix (line 482): the whole symbol, followed by the table’s suffix. For its own example, Am F C G in C major, it should return “Amm7”, “Fmaj7”, “Cmaj7” and “G7”, not the “Am7” of its description (line 412). For a ii–V–I written with sevenths it should return “Dm7m7”, “G77” and “Cmaj7maj7”. The mode comes from the degree, not from the chord. In A major, A7, E7 and D7 should get Ionian, Mixolydian and Lydian, which puts A7’s G and D7’s C outside the mode, where Wikipedia’s chord-scale system pairs all three with Mixolydian. An A major chord in C major should get Aeolian, whose C clashes with the chord’s C♯; ImprovisationSkill’s remarks describe this case (lines 23-32). E7 in A minor should get Phrygian, which lacks the chord’s G♯. The key’s second word names the mode, and any word but “minor” means major (lines 428-430 and 450). A key written “Am” should therefore read “Am major”, that is A major: Am gets “Ammaj7” and Ionian, with C outside, and Dm gets “Dmmaj7” and Lydian, with F outside. B♭ in C major is chromatic: it should get “Bb (chromatic — outside key)”, “depends on context” and the notes “R, M2, M3, P5” (lines 464-475).
  3. ImprovisationSkill. InferQuality lowercases the symbol before matching it (line 323), so CM7 should read “minor 7”: arpeggio Cm7, Dorian first, and E and B outside all three of its scales. Over the 31 symbols on C, the first scale offered should hold every note of the chord except for seven symbols. They are CM7; Cmaj7♯5, read “major 7” because line 340 comes before line 343, with G♯ outside both Ionian and Lydian; C7♯11, whose Mixolydian lacks the F♯ that Lydian Dominant has; C7♭13 and C7+5, read “dominant 7”, whose Mixolydian lacks the A♭ or G♯ that Mixolydian ♭6 has; and C7♭9 and C7♯9, read “altered dominant” with the arpeggio “C7alt”, where the Altered scale lacks the chord’s G and Half-Whole Diminished holds all five notes. Cadd9, Csus2, Csus4 and C5 should read “unknown” and get the major scale of the root. C6 and C69 should read “major triad”, arpeggio C; Cm6 “minor major 7”, arpeggio CmMaj7; C7♯5 “augmented”, arpeggio Caug. CanHandle should accept 16 of its 17 example prompts. It should turn down “what scales fit over the progression Cmaj7 A7 Dm7 G7” (line 74), which holds none of its keywords as a whole word: there “what scale” is followed by an s (ChordIntentMatching.cs, lines 22-35). On a run of chords the skill reads each symbol on its own (lines 196-261). Am F C G should give A Aeolian, then F, C and G Ionian; F Ionian has a B♭ and G Ionian an F♯, notes none of the four chords has. A7 E7 D7 should give Mixolydian three times, Wikipedia’s answer. GAA-003’s Dorian vamp, Am7 D7, should give A Dorian and D Mixolydian. Its Mixolydian vamp, A7 G/A, should read as three chords, A7, G and A, with A Ionian for the last.
  4. OutsideNotesSkill. Classify calls a note outside the chord an avoid note when it lies a semitone above a chord tone, and a tension otherwise (lines 141-189). The avoid notes should be D♭, F and A♭ over Cmaj7, with D, E♭, F♯, A and B♭ as tensions; D♭, F, A♭ and B, the major seventh, over C7; D♭, E, A♭ and B over Cm7; D♭, E, G and B over Cm7♭5. The comment on dominant chords names “the b9/#9/#11/b13 half-step clashes” (line 164), but over C7 D♯ (♯9) and F♯ (♯11) should be tensions, and F and B avoid notes. Some tensions are in none of the scales ImprovisationSkill offers for the same chord: E♭ and B♭ over Cmaj7, E♭ over C7, F♯ over Cm7 and A over Cm7♭5. The skill should answer seven of its ten example prompts (lines 48-63): F an avoid note over Cmaj7, three times; A and F♯ tensions over Cmaj7; F the minor seventh of G7, though CanHandle turns that prompt down for lack of a keyword; and B♭ over C major “an available tension — the b7 (minor seventh)”, measured against a C major triad. “why does that note clash over the chord” and “why does the b9 sound so tense over C7” should pass CanHandle and get no answer, since no capital note comes before “over”; the first passes only because the chord pattern ignores case (line 292) and reads “the chord” as a C chord. “why does the note clash over this chord” should fail both.
  5. GAA-003 through Classify. The module’s answer to its first self-check question says that over Am7 no note of A minor pentatonic lies a half step from a chord tone, and that over A7 C lies a half step below the chord’s C♯ and D a half step above it. Over Am7, Classify should agree: A, C, E and G are chord tones, and D is a tension (the 11). Over A7 it should see only half of that rub: D is an avoid note (the 11), but C is a tension (♯9). The notes the module says pull each mode away should all be avoid notes: F in A Dorian over Am7, the ♭13; G♯ in Mixolydian over A7, the major seventh; D in Lydian over Amaj7, the 11. The thirds of A7, D7 and E7 from ChordVocabulary.GetFormula("dominant 7") (ChordVocabulary.cs, line 121) should be C♯, F♯ and G♯, the module’s targets.
  6. GA’s improvisation entries and box 1. ImprovisationConcepts.yaml holds four concepts (lines 6-14), each with a name and a Concept line and none with a Category. YamlKnowledgeLoader should return four entries, with an empty category and the file name as their only tag (YamlKnowledgeLoader.fs, lines 65-91). Their content should be the name followed by “Concept: …”, and none of them pairs a scale with a chord. GAA-003’s box 1 is frets 5 and 8 on strings 6, 2 and 1 and frets 5 and 7 on strings 5, 4 and 3. It should play A, C, D, E and G, the notes of GA’s fifth mode of the major pentatonic, and the module’s three modes on A should match the course’s spelling.

2026-10-04 — Fixes proposed to Guitar Alchemist

Section titled “2026-10-04 — Fixes proposed to Guitar Alchemist”

Three rows of the QA table went back to GA in PR #810, and one in PR #808, opened from a separate clone at main 75959835. Neither is merged yet: GA leaves the review and the merge to someone other than the author.

  • Scale.Tritone becomes C D♭ E G♭ G B♭, the notes of the Petrushka chord: 6 transpositions, where the old set, C D♭ E F♯ G A, had 12. Scales.yaml and ExtendedScales.yaml follow, so the catalogs give the name “Tritone” to binary ID 1235 instead of 723.

  • DiminishedScaleDegree names degree 1 “Whole-half diminished” and degree 2 “Half-whole diminished”, after the first step of each mode of Scale.Diminished.

  • IsSymmetricalSet also compares the step from the last pitch class back to the first: it is true for 16 of the 4096 sets instead of 156, which included all 66 dyads and C D E.

  • In #808, SpecializedTunings.yaml is valid YAML. YamlKnowledgeLoader.LoadAllKnowledgeEntries(), called from a file-based app that references Common/GA.Business.Config/GA.Business.Config.fsproj, counted by source file:

    main 75959835
    [YamlKnowledgeLoader] Skipping SpecializedTunings.yaml: While parsing a flow sequence, did not find expected ',' or ']'.
    entries: 192
    #808 (e4ce3cc6)
    entries: 213
    SpecializedTunings: 21

    The 15 other files give the same counts on both. ChordProgressions.yaml and GuitarTechniques.yaml, which GA’s typed C# loaders can’t read on main, give 36 and 10 entries there: this loader reads any valid YAML into plain objects.

The row on ChordFormula.GetSymbolSuffix (lesson 13) was already fixed upstream when the course measured it. The course reads GA at a826864, a commit of 2026-09-14, and PR #680, merged on 2026-09-17, adds the “maj”. "C" + Chord.FromSymbol(symbol).Formula.GetSymbolSuffix() gives C9, C11 and C13 for Cmaj9, Cmaj11 and Cmaj13 at a826864, and the symbol itself at #680’s commit 86f81ab2 and at main 75959835.

Eleven more rows went back the same day in four pull requests, opened from the same clone and measured before and after on main 75959835. When this entry was written, GA’s CI had passed on #818, #819 and #820’s first commit, and was running on #820’s second commit and on #823. None is merged yet.

PR QA rows Before → after
#818 Modes.yaml’s vectors and rotations, the names YAML cut as comments, the alternate names; the “Scale:” line in part Names cut: 8 of 165 → 0; modes that keep their alternate names: 0 → 79; declared vectors that differ from their family’s notes: 2 of 18 → 1; members without their first mode’s vector: 26 of 134 → 22
#819 Drop voicings, the drop filters, shells Over the 313,047 voicings of GA’s voicing audit, DropVoicing follows Wikipedia’s definition on 225,338 → all; on lesson 14’s 687 seventh chords on C: Drop2 264 → 111, Drop3 0 → 92, Drop2And4 0 → 143; shell-voicing on 57,981 voicings → on the 877 shells
#820 ChordSymbolParser; CM7 in Chord.FromSymbol Lesson 13’s symbols right: 17 of 41 → 41; “Can” C major, “Give” G major → rejected
#823 GenerateAllPossibleChords 25,900 templates → 12,950, the first half, in the same order
  • The course reads GA at a826864, and on main some rows had already moved: 2 of the 18 declared vectors differed, not 9, and 26 members lacked their first mode’s vector, not 60.
  • Codex reviewed #818: the augmented triad’s inversions were spelled C E G♯, like the root position. They are now C E A♭ and C F♭ A♭, spelled by their intervals above the bass, as the other inversions are.
  • #820 rejects what the parser can’t read. The chatbot, which tries it on a whole message, now finds no chord in “Cmaj7 jazz voicings”, where it found C major.
  • Codex’s review of #820 found that the parser read a slash chord’s bass, then dropped it: C/E had C as its Bass. A second commit puts the bass note first, so C/E is E G C. GaApi’s voicing search still ignores it, since it passes only the pitch classes.
  • #823’s first commit also removed NaturalMinorMode, whose seven modes are the major scale’s, under the same names: its 1,666 templates have the same notes and pitch classes as the major modes’. Codex pointed out that their mode’s parent scale differs (A B C D E F G, not C D E F G A B) and that CreateTraditionalChordLibrary promises those modes, so a second commit puts them back.
  • The ChordAlterationService row went to no PR: only ChordTemplateNamingService and HybridChordNamingService call it, and no app, MCP tool or chatbot path calls those at 75959835.

2026-10-07 — Lesson 16: results against the predictions

Section titled “2026-10-07 — Lesson 16: results against the predictions”

The program is Lesson18.cs (entry l18), its output expected/l18.txt; check.sh passes for lessons 1 to 18 and the 23 diagrams. Codex wrote the program and the predictions on 2026-10-02, before Program.cs had an entry for it; this session took the work over, added the entry and ran the program for the first time on 2026-10-07. It compiles, as they are, five files of GA.Business.ML, the two skills, the keyword matcher, the chord vocabulary and the skill interface, and the attribute of GuitarAlchemist.Registry they carry; StandIns.cs replaces the four types they take from code with packages the course does not build: the logger, the response record, the LLM query extractor and its query. It reads GuitaristProblemTools.cs as text, as lesson 15 did. fetch-ga.sh now adds the six files to its sparse checkout. The build reports no new warning. No MCP tool and no chatbot was called. After the first run, three tables were realigned, with no value changed, and one block was added under a title that says so: OutsideNotesSkill’s whole answer for B over C7, and for E and B over CM7.

  1. Confirmed: the fourteen suffixes and the fourteen symbols of ArpeggioFor; the Mode and Notes columns of both tables and MajorScaleMode; ten of the twelve other scales, and the two diminished modes DIFF.
  2. Confirmed: “Amm7”, “Fmaj7”, “Cmaj7” and “G7”; “Dm7m7”, “G77” and “Cmaj7maj7”; Ionian, Mixolydian and Lydian for A7, E7 and D7, with G and C outside; Aeolian for A in C major, against its C♯; Phrygian for E7 in A minor, without its G♯; “Am major”, with “Ammaj7” and C outside, “Dmmaj7” and F outside; B♭ chromatic, with the three fixed strings. Not predicted: the arpeggios “Am7” for the A major chord and “E7m7” for E7. Found after the run: GA #626, merged on 2026-09-24, fixed the suffix and the degree upstream; on main c1416eee the key is read as before. Read, not run.
  3. Confirmed: CM7 “minor 7”, arpeggio Cm7, Dorian first, E and B outside all three scales; seven symbols whose first scale lacks a note: CM7, Cmaj7♯5 (G♯, in neither of its scales), C7♯11 (Lydian Dominant holds it), C7♭13 and C7+5 (Mixolydian ♭6), C7♭9 and C7♯9 (Half-Whole Diminished); Cadd9, Csus2, Csus4 and C5 “unknown”; C6 and C69 “major triad”, arpeggio C; Cm6 “minor major 7”, arpeggio CmMaj7; C7♯5 “augmented”, arpeggio Caug; 16 of 17 example prompts, the progression prompt turned down; A Aeolian, then F, C and G Ionian; Mixolydian three times; A Dorian and D Mixolydian; A7, G and A, with A Ionian. Not predicted: the remark “careful on the IV chord” under C and G as under F. Found reading the code after the run: ChordVocabulary’s remarks describe the CM7 regression.
  4. Confirmed: the avoid notes and tensions of all four columns; the five tensions in none of the skill’s scales; seven answers, F over G7 answered though CanHandle turns it down, B♭ measured against a C major triad; two prompts that pass CanHandle and get the help text, one that fails both. Not predicted, added after the run: the advice for B over C7, “exactly the kind of altered tension players reach for (major 7th on the V)”, and CM7 read as a major seventh by this skill.
  5. Confirmed: over Am7, four chord tones and D the 11; over A7, D an avoid note and C the ♯9; F, G♯ and D avoid notes, the ♭13, the major seventh and the 11; C♯, F♯ and G♯.
  6. Confirmed: four entries with an empty category, the file name as their only tag, the name then “Concept: …”; box 1 A C D E G, GA’s “Minor pentatonic”; the three modes. Not predicted: 192 entries from 15 files, SpecializedTunings.yaml skipped as on 2026-10-04.

Observations, not tests:

  • Since GA #626, ga_arpeggio_suggestions on main names arpeggios through ImprovisationSkill.InferQuality, so the tool would now read CM7 as a minor seventh too. Read, not run.
  • The lesson cites GAA-003 at Demerzel 0f7a5fd, the commit of the site’s last Streeling sync.
  • Whether GA’s full chord recognizer (not TryFindExact alone) names inversions such as 032010; the MCP voicing search suggests it does (C/E), but the course did not call it.
  • The MCP server’s commit: the answers above were not reproduced against a server built from a826864.
  • HarmonicFunctionAnalyzer.Parse on “Supertonic” and “Submediant”, by a test in GA.
  • The spokes and symmetry axes of GA’s BraceletNotation, rendered in a browser.
  • domain.commonTones on a server built after #681: the fix was read on 5c3a52a, not run.
  • The chatbot’s own answers to “voice leading C major to G major” and “voice leading from G to Cmaj7”, rather than the course’s rewrite of its rule.
  • Whether a later GA registers YamlKnowledgeSyncService and lets an agent search the yaml_knowledge collection, so that the wrong lines of ModalInterchange.yaml can reach an answer.
  • Both live ga_chord_substitutions tools, the MCP server’s closure and GaApi’s tool of the same name, against lesson 10’s results.
  • get_borrowed_chords through the MCP server and through GaApi, rather than the service compiled alone.
  • get_mode_info and get_available_modes through the running MCP server, rather than ModesConfig alone: get_mode_info("Ionian Augmented") should answer with C D♯ E F G♯ A B.
  • What the chatbot’s ModesSkill, which reads GetModalFamilies and so keeps the alternate names, answers for “Ionian Augmented”, “Altered bb7” and “Lydian”.
  • The “Scale:” line of the live ga_chord_to_set for Cmaj7, G7 and Cm.
  • ModesSkill’s live answer to “modes of limited transposition”, rather than the course’s rebuild of its format.
  • The “Scale:” line of the live ga_chord_to_set for Messiaen’s sixth mode, C D E F G♭ A♭ B♭ B: the lookup says “Diminished (Half-Whole)”.
  • The chords GenerateFromSymmetricalScales builds from the 20 modes, and which modes ModalCharacteristicIntervalService loses to its overlapping indices, by running them.
  • VoicingFilterService on “Cmi7” and the chatbot on “Can you analyze this progression: Dm7 G7 Cmaj7”, run rather than read.
  • The live ga_polychord on B over C and C over F♯, against the course’s copy of its code.
  • GA’s full chord recognizer, not TryFindExact alone, on the voicings x3233x, x32335 and x3234x.
  • The live ga_search_voicings on “drop2”, “shell” and “rootless”: which voicings the index returns for each tag.
  • AutoTaggingService’s “Shell” tag on the indexed corpus, run rather than read, and whether a later GA builds IndexVoicingsCommand again, which would make InterpretationService’s drop tags reachable.
  • What the chatbot answers when asked for drop-3 voicings, since GA labels none.
  • The live ga_easier_voicings on F, Bm and B♭, rather than the course’s reading of its filter, and the substitution hints it adds from domain.chordSubstitutions.
  • Whether a later GA reads Tunings.toml, or lets HarmonicNavigator3D use its tunings and dataUrls properties.
  • ga_arpeggio_suggestions, ImprovisationSkill and OutsideNotesSkill live, on lesson 16’s requests, rather than the tool read as text and the skills compiled alone; and on main, whether the tool, which now calls InferQuality, reads CM7 as a minor seventh.