Skip to content
learn

Journal

  • Course program: .NET 10, referencing GA.Business.ML, FretboardVoicingsCLI and GaChatbot.Api at a pinned commit
  • CI: every lesson’s output compared with its expected file on three OSes, with no model, no API key and no GPU
  • Lesson 1: the map
  • Lesson 2: OPTIC-K embeddings
  • Lesson 3: the index and search
  • Lesson 4: the chatbot and its agents
  • Lesson 5: the improvisation skill and chord–scale theory
  • Lesson 6: the chatbot’s answer on the wire
  • Lesson 7: chord names the chatbot can’t read
  • Lesson 8: the chatbot’s own exam
  • Lesson 9: every interval, every key
  • Lesson 10: what the model is told to trust
  • Lesson 11: the chords the key skill reads
  • Lesson 12: what the progression skills answer
  • Lesson 13: what the progression analysis answers
  • Lesson 14: what the substitution skill answers
  • Lesson 15: the notes of a chord
  • Lesson 16: the voicings of a chord
  • Lesson 17: the capo and the tunings
  • Lesson 18: voice leading
  • Lesson 19: voice-leading pairs
  • Lesson 20: generated progressions
  • Lesson 21: the voicing search
  • Lesson 22: similar chords
  • Lesson 23: harmonic distance and path
  • Lesson 24: set classes and the Grothendieck parser
  • Lesson 25: what reaches the transpose skill
  • Lesson 26: the modes catalog
  • Lesson 27: notes that sound outside
  • Run the chatbot with Ollama and capture what the agents answer (dated, outside CI)

Findings 1 to 22, from lessons 1 to 4, are numbered in the entry of 2026-09-14, with their upstream status in the entry of 2026-09-24. This table starts at lesson 5. ImprovisationSkill.cs is identical at a826864 and on GA’s main at 8cd5042 (2026-09-28), so rows 23 to 28 also describe the deployed skill. Rows 29 to 32, from lesson 6, give their state on main in the last column. Rows 33 to 38, from lesson 7, also hold on main at f4f4d30: the files they cite are unchanged there since #749. Rows 39 to 45, from lesson 8, cite files that are unchanged on main at fc76ad6 (2026-09-29), except the corpus, which has grown from 68 prompts to 75 without changing the entries they cite. Rows 46 to 53, from lesson 9, cite files that are unchanged on main at 53b7253, except ScaleInfoSkill.cs and RelativeKeySkill.cs, which have changed there only in CanHandle and in a Declined flag on a refusal none of the rows goes through. Rows 54 to 57, from lesson 10, hold on main at 53b7253: the SKILL.md files they cite are unchanged, and so are the functions of DomainClosures.fs they cite. Rows 58 to 64, from lesson 11, give main’s behavior with the service at 6baf32e, unchanged on main at 5c3a52a, where the SKILL.md is unchanged and the tool and the skill differ from the pin only by a using line and a Declined flag. Rows 65 to 72, from lesson 12, hold on main at 5c3a52a, where ProgressionMoodSkill.cs, the two SKILL.md files and the draft are unchanged and ProgressionCompletionSkill.cs differs from the pin only by a using line and a Declined flag; rows 67 and 68 give main’s behavior with the service at 6baf32e. Rows 73 to 78, from lesson 13, give main’s behavior with DomainClosures.fs and the service at 6baf32e, both unchanged on main at 5c3a52a; rows 73 and 74 describe the pin, fixed since. Rows 79 to 86, from lesson 14, hold on main at 5c3a52a, where the skill, its MCP tools, its SKILL.md, GrothendieckDelta.cs, DslEvalMcpTools.cs and GaDslTool.cs are unchanged and FindNearby differs only in the test that keeps the source out of its list; row 85’s closure was run at the pin and at 6baf32e, the same as on main. Rows 87 to 95, from lesson 15, hold on main at 5c3a52a, where ChordInfoSkill.cs, ChordVocabulary.cs, ChordSpelling.cs, ChordMcpTools.cs and the SKILL.md are unchanged. Rows 96 to 100, from lesson 16, hold on main at f4f5b4a: rows 96 to 98 come from TypedMusicalQueryExtractor.cs, ChordPitchClasses and the skill’s CanHandle, identical at the pin, and rows 99 and 100 were run with main’s voicing pipeline, in GaMain. Rows 101 to 107, from lesson 17, hold on main at f4f5b4a: on main, CapoSkill only marks its refusal Declined, and rows 106 and 107 were also run on main, in GaMain. Rows 108 to 113, from lesson 18, hold on main at f4f5b4a, where VoiceLeadingSkill.cs is unchanged. Rows 114 to 117, from lesson 19, hold on main at f4f5b4a, where CompositionTools.cs and the parked skill drafts are unchanged; rows 114 to 116 were also run on main, in GaMain. Rows 118 to 122, from lesson 20, hold on main at f4f5b4a, where CompositionTools.cs, ChordPitchClasses, ChordProgressions.yaml and the parked skill drafts are unchanged; row 121 was also run on main, in GaMain. Rows 123 to 127, from lesson 21, hold on main at f4f5b4a, where VoicingSearchTool.cs and VoicingVocabularyTool.cs differ only in a comment and a description, the voicing-search skill in one line about another tool, and the reader, the tag registry and the parked draft are unchanged; rows 123, 124 and 126 were also run on main, in GaMain. Rows 128 to 131, from lesson 22, hold on main at f4f5b4a, where IcvNeighborsSkill and IntervalClassVectorSkill only mark their refusal Declined, FindNearby compares the source by value, and DefaultRoutingHintProvider and the parked draft are unchanged; rows 128 to 130 were also run on main, in GaMain. Rows 132 to 135, from lesson 23, hold on main at f4f5b4a, where GrothendieckDeltaSkill and IcvShortestPathSkill only mark their refusal Declined, FindNearby compares the source by value, and GrothendieckDelta and DefaultRoutingHintProvider are unchanged; the four rows were also run on main, in GaMain. Rows 136 to 140, from lesson 24, hold on main at f4f5b4a, where SetTheoryEquivalenceSkill and GrothendieckParseSkill only mark their refusal Declined and the Grothendieck parser is unchanged; the five rows were also run on main, in GaMain. Rows 141 to 144, from lesson 25, were run at the pin and on main at f4f5b4a, in GaMain, which now starts main‘s chatbot host; TransposeSkill, its SKILL.md, the hint rules and SkillMdPlugin are unchanged there, so rows 141 to 143 hold on main as written, and row 144 gives both commits’ answers. Rows 145 to 149, from lesson 26, hold on main at f4f5b4a, where ModesSkill.cs, Modes.yaml and ModesConfig.fs are the same files; they were run at the pin only. Rows 150 to 154, from lesson 27, hold on main at f4f5b4a, where OutsideNotesSkill.cs only marks its refusal Declined and ChordVocabulary.cs is the same file; they were run at the pin only.

Expected What happens Where Measure Status
23. A run of chord symbols is read as written Cmaj7#5 is read as Cmaj7; C7sus4 and CmMaj7 are dropped without a word, though mMaj7 is the skill’s own arpeggio label ImprovisationSkill.cs lines 453-462 l5: 15 of 17 chords read, 1 of them cut short Reproduced; reported as #757
24. Cm7#5 is a minor chord Classified augmented: arpeggio Caug, an E against the chord’s E♭; neither scale on offer holds the E♭ line 343 l5, one-chord table Reproduced; reported as #758
25. The arpeggio of Cm6 spells C E♭ G A CmMaj7, which plays B instead of A; the scale, melodic minor, is fine line 346 l5, one-chord table Reproduced; reported as #758
26. C7#11 leads with a scale that holds F♯ Mixolydian, with F natural; Lydian dominant is the second choice lines 360-436 l5, one-chord table Reproduced; reported as #758
27. Suspended and power chords keep their own tones “unknown”: the arpeggio label is the bare root, a major triad, E against the sus4’s F and the sus2’s D line 283 l5, one-chord table Reproduced; reported as #758
28. A progression’s lead scales stay in its key, or say the key is ambiguous Ionian on major triads, Aeolian on minor triads, whatever their degree: B♭ on F and F♯ on G over Am F C G, F♯ G♯ on A in C A Dm G, F♯ C♯ D♯ on E in Am Dm E; C G isn’t flagged as ambiguous lines 212-261 l5: of 23 chords in 7 progressions (the borrowed chord left out), 11 same, 10 DIFF, 2 right in one of two keys Reported as #744
29. A client tells a failed request from an answer The stream answers HTTP 200 and sends {"error": …} as an ordinary event, with no [DONE]. GaChatbot.Api’s page renders that JSON as the assistant’s answer and stores it in the conversation history, which the next request sends back index.html lines 740-809, line 984 l6, failure table, through the course’s port of the page Reproduced in the port, not in a browser; reported as #760
30. Streamed chunks join back into the answer SseChunker splits on (?<=[.!?])\s+, which removes the whitespace between sentences: the improvisation answer loses 6 of its 8 line breaks and its list renders as one bullet SseChunker.cs lines 19-29 l6, sentences: 552 of 558 characters Fixed upstream by #743, merged on 2026-09-28; the course runs the pinned version and a copy of the fix
31. Every line of text survives GaApi’s stream data: {chunk} with no prefix on the chunk’s other lines: a blank line inside a chunk ends the event, and the line after it is lost for every reader GaApi ChatbotController.cs lines 318-322 l6, pairs table: 1 of 6 lines and 8 line breaks missing, both splits Reported upstream: #746, open; unchanged on main at 8621c3e. Latent: no ga-client component reads that stream
32. ga-client’s SSE client keeps every line of an event parseSseBuffer keeps the first data: line of each event and trims it, so it loses a line and every line break even from GaChatbot.Api’s writer chatService.ts lines 41-48 l6, pairs table Client half of #746’s acceptance; no caller at a826864
The oracle gives a taught scale for every chord For a borrowed chord, F minor in C, its rule gives F G A♭ B C D E, not a taught scale; the usual answer is F Dorian code/ga-ai/GaAi/Lesson5.cs, Textbook l5, C Fm G C A limit of the course’s oracle, not of GA; documented in lesson 5
33. A request with lowercase chord names is declined, or the answer says it can’t read them which arpeggio fits hm q7 passes #749’s guard, whose roots are uppercase H to Z, and goes on to the model InvalidChordNames.cs line 97 l7, row 5 Reported as #759
34. An unknown quality on a valid root is flagged Cq7 passes the guard and goes on to the model InvalidChordNames.cs lines 41-59 l7, row 6 Out of scope in #749’s description; reported as #759
35. An invalid chord next to valid ones is named in the answer Am F Q7 G is answered for Am, F and G, and Q7 is dropped without a word; C and Q7 goes on to the model InvalidChordNames.cs lines 41-59, ExtractChordRun l7, rows 13 and 14 By design in #749 (“keeps its route”); reported as #759, and the silent drop as #757
36. Flat and sharp signs are read B♭ E♭ F is answered as B, E and F: B Ionian for B♭. The skill’s expressions allow [#b] only; GA’s capo and alternate-tuning skills list ♭ and ♯, but often drop them (rows 103 and 106) ImprovisationSkill.cs lines 447-462 l7, row 9 Reported as #757
37. A symbol that ends on #, + or ° is read whole F# is read as F and C+ as C: ChordTokenRegex ends with \b, and no word boundary follows those characters, so the match backtracks to the bare root line 461 l7, rows 10 and 11 Reported as #757
38. Bø7 is read Left out of the run without a word: the list has °7 but no ø7, and ø is a letter, so no boundary separates it from the 7 line 461 l7, row 12 Reported as #757
The course’s recognizer reads every word of a message right It reads the interjection that opens “Hm, which mode is brightest?” as a broken chord symbol; GA’s guard skips it, which is right code/ga-ai/GaAi/Lesson7.cs, Read l7, row 15 A limit of the course’s recognizer, not of GA; documented in lesson 7
39. A wrong answer fails the corpus’s check #26, “modes of melodi minor”, gets the major scale’s modes and passes: its contains_any is satisfied by the suggestion line every family answer ends with prompts.yaml lines 270-276, ModesSkill.cs line 449 l8, #26 Reported as #763
40. A misspelled family is answered for, or the answer says it wasn’t recognized “melodi minor” falls to the default branch, which lists the major scale’s modes without a word ModesSkill.cs lines 202-207 l8, #26 Reported as #765
41. An error message fails the corpus’s check #31 passes on “I encountered an error processing your request. Please try again.”: its contains_any holds G, which is in “processing” prompts.yaml lines 316-322, PromptCorpusTests.cs lines 367-379 l8, #31 Reported as #763
42. A skill that can’t reach its model is flagged as a degraded backend A Path B skill without a model answers with SkillMdDrivenSkill’s error text, which isn’t one of BackendDegradedMarkers: six failures and one pass instead of seven times “no signal” SkillMdDrivenSkill.cs lines 204-226, PromptCorpusTests.cs lines 54-75 l8, #10 and the first table Reported as #764; GA’s daily runs to verify
43. A skill’s log says what happened DiatonicChordsSkill logs that the model “produced an answer without invoking ga_dsl_eval” when no model was reached SkillMdDrivenWrapperBase.cs lines 107-136 l8, #10 Reported as #764
44. The corpus’s checks tell an answer from the answer to another question Case-insensitive substrings, 84 of the 224 one character long: the passing prompts accept 169 of the other answers, and 6 of 15 answers moved a semitone higher, among them Drop C# for drop C PromptCorpusTests.cs lines 367-379 l8, first semitone table Reported as #763; the course’s stricter reading accepts 69 and 4
45. A scale’s formula spells its notes Formulas are computed by position against C major: #6 for the whole-tone scale’s B♭, and 1 b2 b3 b4 b5 bb6 bb7 bb8 for C D♭ E♭ E F♯ G A B♭ ModesSkill.cs lines 779-811 l8, #38 and #39 Reported as #765
The course’s semitone test makes every answer wrong Only for a prompt that names a pitch; for the others the moved answer is still right, and the table says n/a. The course recognizes a note name in the question only when it is capitalized, so #30, “notes in c major”, also says n/a code/ga-ai/GaAi/Lesson8.cs, Transpose l8 A limit of the course’s test, not of GA; documented in lesson 8
46. The interval skill reads a sharp on the second note The final \b of its pattern gives the # back: E to G# is a minor third, C to F# a perfect fourth; 147 of 441 questions IntervalSkill.cs lines 62-64 l9, the grid’s # columns Reported as #767; the same cause as #757, in another pattern
47. The interval skill reads ♯ and ♭ C to F♯ is a perfect fourth; B♭ to D can’t be parsed IntervalSkill.cs lines 62-64 l9, “written another way” Reported as #767
48. A lowered unison is a diminished unison C# to C is a perfect unison of 0 semitones: the semitones are taken modulo 12, and a difference the switch doesn’t cover falls to its default arm; 21 unisons of 441, and 5 more intervals with Cb, Fb, E# or B#. The same on main’s domain NoteExtensions.cs line 110, lines 13-56 l9, “the note names read right” Reported as #768
49. Every interval quality is written out Doubly augmented and doubly diminished print as AA and dd: 27 of 441 answers IntervalNaming.cs lines 56-64 l9, the grid’s a cells Reported as #767
50. Two skills give the same relative key ScaleInfoSkill takes the first key with the same pitch classes: B major → A♭ minor, G♯ minor → C♭ major, 6 of 30 keys; RelativeKeySkill, fixed for this in May, gives G♯ minor and B major KeyNaming.cs lines 55-65, RelativeKeySkill.cs lines 119-125 l9, the key table Reported as #769
51. A key’s signature is right, or the key is refused D♯, G♯ and A♯ major and D♭ and G♭ minor have “no sharps or flats”; the parallel minor of G♯ major has “3 flats” RelativeKeySkill.cs lines 208-212 l9, “keys past seven” Reported as #769
52. A key read in one question is read in the others C♭ major is read for its relative minor and refused for its signature, with a message asking for a letter followed by # or b RelativeKeySkill.cs lines 72-81, lines 245-251 l9, after the key table Reported as #769
53. A skill answers its own example prompts IntervalSkill can’t read 8 of its 13, five definitions and three questions such as “Minor third up from D”; ScaleInfoSkill 3 of its 24 IntervalSkill.cs lines 27-52, ScaleInfoSkill.cs lines 23-91 l9, last section IntervalSkill’s reported as #767, ScaleInfoSkill’s not reported; what the chatbot serves for them to verify
54. A key’s diatonic chords are spelled one letter per degree domain.diatonicChords names each chord from an array of twelve sharp or flat names, chosen by the tonic: D, G and C minor get A♯, D♯ and G♯, and C♭, F♭, E♯ and B♯ come out B, E, F and C; 11 of 30 keys DomainClosures.fs lines 22-36, line 187 l10, the key table Reported as #770
55. The diatonic-chords SKILL.md describes what the closure returns It promises correct spelling in “Gb major, F# minor” and says G♭ major “returns Cm”; the closure returns B for G♭ major’s C♭, and Bdim where the SKILL.md shows B° skills/diatonic-chords/SKILL.md line 26, DiatonicChordsSkill.cs lines 27-32 l10, “the SKILL.md’s examples” Reported as #770
56. A transposed chord is spelled by the interval domain.transposeChord takes semitones and names the result from the source root’s array: C up a minor third is D♯, “Transpose G7 to Eb” gives D♯7, B♭ up a whole step and back is A♯; 70 of 252 on another letter, even C♭ moved by 0 semitones, and 30 of them where the textbook needs a double accidental DomainClosures.fs lines 159-162, skills/transpose/SKILL.md line 42 l10, the transposition grid Reported as #770
57. Common tones are spelled as the chords spell them domain.commonTones names each shared note by its pitch class alone: A and C♯m share “Db”; 144 of the 630 triad pairs of the 30 keys DomainClosures.fs lines 263-266, line 523 l10, the common-tones table Reported as #770
58. The key skill reads every chord of the question At the pin, ExtractChords skips every symbol with a digit right after a letter, because its final \b fails between two word characters: the 30 questions of lesson 11 with seventh chords lose them all. On main, CM7, CΔ7 and Cø7 are still skipped KeyIdentificationService.cs line 211 l11, the reading table and the grid Fixed on main by #625 for the symbols with digits; CM7, CΔ7 and Cø7 reported as #771
59. A sharp at the end of a chord symbol is kept The final \b fails between # and a space: F# and C# are read as F and C in both versions. Besides, B♭ and F♯ are read as B and F, C° as a C major triad, and C/E as two chords KeyIdentificationService.cs line 211, line 325 on main l11: 16 of 90 questions read other chords on main Reported as #771; the same cause as #757 and #767, in another pattern
60. Keys with the same pitch classes are told apart by the question’s spelling Such keys tie and the name decides: for D♭ G♭ A♭ D♭, A♯ minor comes first at the pin and C♯ major on main, where 13 of 90 questions get the key’s enharmonic twin first KeyIdentificationService.cs line 178, lines 224-227 on main l11, the ~ of main’s grid Reported as #772
61. A repeated chord keeps its place in the progression ExtractChords drops repeated chords, so main’s cadence weight never sees the return to I and hears D G as V–I of G major: C D G C is read C D G and answered G major; in the 11 keys of I II V I whose chords are read, the key of the dominant, or its enharmonic twin, comes first line 299 on main, lines 231-246 l11, the x of the grid Reported as #771
62. The top matches are the keys ranked first The tool and the skill keep the keys whose count equals the first key’s, while main ranks by count plus cadence: C♯m7b5 F♯7 Bm gets a top of six keys at 1/3, with G♯ minor at 2/3 among the partial matches; given C D G C, the top leaves out G major (3/3) and the partial matches repeat two keys of the top KeyIdentificationMcpTools.cs lines 66-75, KeyIdentificationSkill.cs lines 62-63 l11: 2 of 90 questions on main, and C D G C as a list Reported as #772
63. A key’s relative key is spelled from the key The service takes the first key of the other mode with the same pitch classes: B major → A♭ minor, G♯ minor → C♭ major; 6 of 30 keys, in both versions KeyIdentificationService.cs lines 93-101 l11, the relative keys Reported as #772; the same lookup as ScaleInfoSkill’s in #769
64. The key-identification SKILL.md’s examples describe what the tool returns Its example of a single top candidate is “The progression Dm G C is in C major (3/3 chords diatonic)”; the tool returns C major and A minor, tied, in both versions. Its line 81 says the tool “doesn’t distinguish a Cmaj7 from a C”; at the pin, it doesn’t read Cmaj7 at all skills/key-identification/SKILL.md line 60 l11, the example prompts Reported as #772
65. The mood skill answers the mood the prompt asks for 6 of its 8 example prompts that ask to brighten get the five ways to darken: the test looks for “brighter”, “uplift” and “happier”, and misses “Brighten”, “brightness” and “lift the mood”. They are the six brighten prompts added on 2026-05-12 to fix misroutes ProgressionMoodSkill.cs line 61 l12, the mood table Reported as #773
66. The mood skill’s text writes chord symbols, as its SKILL.md does C bA G and C bB F: bA and bB are neither chord symbols nor Roman numerals. The SKILL.md writes Ab and Bb, asks for its technique list verbatim, and differs from the C# text in the second technique of each branch lines 74 and 77, SKILL.md line 38 l12, the progressions of the two texts Reported as #773
67. The chords the completion skill offers belong to the question’s key The list comes from the first key, and at the pin ties are broken by name: G Em C gets C major’s chords, without D; 6 of the 15 major keys for I vi IV ProgressionCompletionSkill.cs lines 93-107, KeyIdentificationService.cs line 178 l12, the grid: x 6 at the pin, 0 on main Fixed on main by #729
68. The list is spelled like the question D♭ B♭m G♭ gets A#m, B#dim, C#, D#m, E#m, F#, G# at the pin and C♯ major’s chords on main ProgressionCompletionSkill.cs line 107 l12, the grid: e 4 at the pin, 5 on main Reported as #774; the same cause as #772
69. A minor key’s list lets the model build an authentic cadence The list is the natural minor’s: the major V is in none of the 15 minor keys’ lists. The prompt allows V7 “even if only V appears”, which never happens in minor; the SKILL.md allows V7 and forbids any chord outside the list lines 117-119, SKILL.md lines 71 and 96 l12, the dominant table Reported as #774
70. The prompt’s example follows the prompt’s rules It suggests E7 as V7–i of A minor, outside A minor’s list, and G as a half cadence, “bVII-i”, though a half cadence ends on V and G is the seventh degree lines 130-131 l12, the prompt and the dominant table Reported as #774
71. The progression-completion SKILL.md’s example describes what the tool returns It says C major first, “also tied with A minor”, with B°; at the pin the tool puts A minor first, ties G major and E minor too, and writes Bdim. Its answer suggests Dm → G7, against its own hard constraint. It calls a tie with another scale rare: 13 of the 25 questions read as written have one SKILL.md lines 56, 86, 92 and 96 l12, the examples and the grid Reported as #774
72. A skill’s keyword test accepts its own example prompts ProgressionCompletionSkill.CanHandle rejects “What chord comes next after C G Am?” and “Help me end Am F G”; ProgressionMoodSkill.CanHandle returns false. On main, the router falls back on these tests when it can’t embed a question; at the pin, nothing calls them ProgressionCompletionSkill.cs lines 35-45, SemanticIntentRouter.cs line 321 on main l12, the keyword table Reported as #773 and #774
73. The key of a progression depends on its chords’ qualities At the pin, only the roots count: Am Dm E Am is A major, Dm7 G7 Cmaj7 D minor, F G C F major; 90 of the 120 textbook questions get another key, among them all 60 in a minor key DomainClosures.fs lines 268-270, lines 308-316 l13, the grid: x 90 at the pin, 0 on main Fixed on main by #625
74. A Roman numeral follows the chord’s quality At the pin, the numeral is the degree’s in the key’s table, whatever the chord: D and Dm are both ii in C major, and Fm Bbm C7 Fm is I IV V I in F major lines 257-260, lines 272-276 l13, the draft’s four examples: 2 right at the pin, 4 on main Fixed on main by 6baf32e
75. The key is named as the question spells it At the pin, the name comes from the pitch class: C♯, G♭ and C♭ major come back as D♭, F♯ and B major. On main, it’s the name Identify puts first, with keys of the same pitch classes ordered by name: C♭, G♭ and D♭ major come back as B, F♯ and C♯ major, E♭, B♭ and G♯ minor as D♯, A♯ and A♭ minor lines 262-266, KeyIdentificationService.cs lines 224-227 at 6baf32e l13, the grid: e 6 at the pin, 21 on main Not reported; the same cause as #772
76. A minor key’s vii° gets a Roman numeral On main, romanFor looks for the root among the natural scale’s seven notes: vii°7 gets ? in all 15 minor keys, as bIII and bVI do in C major DomainClosures.fs lines 353-374 at 6baf32e l13, the grid: n 15 on main; the borrowed chords Reported as #775
77. Borrowed chords leave the key on the chord a progression opens and closes on On main, Identify counts the diatonic chords before it looks at the opening chord, then prefers the major key, then the name: C Bb F C is F major, V IV I V, and C Ab Bb C E♭ major, VI IV V VI, a key in which the C major chord isn’t diatonic; only the confidence, 2/4, shows it KeyIdentificationService.cs lines 224-227 at 6baf32e l13, the borrowed chords Reported as #775
78. The confidence counts a minor key’s ii and vii° as its own On main, NormalizeChord cuts Bm7b5 to Bm, a minor triad: Bm7b5 E7 Am is 2/3, and iiø7 V7 i is below the chord count in all 15 minor keys. G♯dim7 isn’t in the natural minor, but F##dim7 is read as F♯ major, VII of G♯ minor, and counts KeyIdentificationService.cs line 319 at 6baf32e, line 132, lines 145-150 l13, the grid: below the chord count for iiø7 V7 i in 15 keys, for i iv vii°7 i in 12 Reported as #775
79. A list of substitutes depends on the chord FindNearby puts every chord of the source’s set class at the same, smallest cost, and the skill keeps the five with the smallest bitmasks: the lists of the 12 major triads name six chords, Cm C Ab Dbm Fm Db; G7’s list has no D♭7; major and minor 7ths get a textbook substitute on none of the 12 roots; 29 of the 60 chords listed for major triads share no note with their source ChordSubstitutionSkill.cs lines 164-174, GrothendieckService.cs lines 55-81 l14, one chord: 108 chords; ga_chord_substitutions the same for all 108 Reported as #777
80. Equal interval-class vectors are at distance 0 GrothendieckDelta.FromIcVs writes 1 for a zero difference: C and C, C and Am, G7 and D♭7 are at L1 1; every listed substitute costs 0.60, and every pair of one set class is an “ICV Neighbor (L1 = 1)”. The SKILL.md’s examples, costs of 0.50 and 0.83 and an L1 of 0, can’t occur GrothendieckDelta.cs lines 125-131, SKILL.md lines 100-108 l14, the four ComputeDelta lines Reported as #776
81. The comparison names what a textbook names A minor v is a “Secondary Dominant”, from the roots alone. Am and C, which share two notes, F♯m and C, which share none, and C and itself get the same labels, “Set-Class Equivalent” and “ICV Neighbor (L1 = 1)”; so do Em7b5 and C7, which share three ChordSubstitutionSkill.cs lines 261-264, lines 279-290 l14, two chords, on the 12 roots; ga_chord_compare the same Reported as #777
82. The answer follows the relation the question names One chord gets the list whatever the question asks: “Tritone substitution for G7”, “What’s the secondary dominant of Am?” and “Show me a backdoor dominant for C major” get no D♭7, E7 or B♭7. A key name and an article are read as chords: “Alternative chord for F major in C” compares F with C, “A substitute for G7?” A with G7 ChordSubstitutionSkill.cs lines 146-160, lines 69-71 l14, the example prompts; what the skill reads Reported as #777
83. The skill reads the symbols its SKILL.md lists Neither the skill nor ga_chord_substitutions reads Cmin7, while the SKILL.md lists min7; B♭7 is read as B major ChordSubstitutionSkill.cs lines 69-71, ChordSubstitutionMcpTools.cs lines 230-232, SKILL.md line 92 l14, what the skill reads Reported as #777; B♭ read as B is the same as #757
84. CanHandle accepts the skill’s own example prompts substitut\b and reharmoni\b match no word, and “secondary dominant”, “backdoor”, “replace” and “modal interchange” have no trigger: 4 of 12 accepted. On main, the router falls back on CanHandle when it can’t embed a question ChordSubstitutionSkill.cs lines 59-75, SemanticIntentRouter.cs line 321 on main l14, the example prompts: 4 of 12 Reported as #777
85. ga_dsl_eval runs a closure without the inputs its schema calls optional domain.chordSubstitutions declares key and scale as string? with defaults, but ga_dsl_eval requires every input: missing-required-arg. Five inputs of two closures are marked optional. Without a key, the closure takes the chord’s root in major: Am gets the chords of A major DslEvalMcpTools.cs lines 257-270, DomainClosures.fs lines 557-559 l14, the closure Reported as #778
86. A tool’s description matches its answer GaMcpServer’s GaChordSubstitutions promises, for Am in C major, “C (★★★, relative major), Em (★★, shared E/B), F (★, shared A)”; the closure gives C and F two stars, two shared notes each, and Dm and Em one GaDslTool.cs line 212, DomainClosures.fs line 614 l14, the closure Reported as #778
87. Only a chord’s name is read as its root The article “a” before “chord” is the root A: “what makes a chord a major seventh”, “what makes a chord diminished” and “what makes a chord a dominant seventh”, three example prompts, get “A major chord contains A, C#, and E”. The symbol expression ignores case, so “am” is A minor in “I am learning Cmaj7, what notes are in it?” and “Am I right that G7 has an F?”; the note expression reads the article of “Which chord has a C, an E and a G?”: A minor 7 ChordInfoSkill.cs lines 302-303, line 315, line 325 l15, the example prompts: 3 of 3; beyond the vocabulary; lists of notes Reported as #782
88. The skill reads its own example prompts “anatomy of a D7sus4 chord”, “what is a C add 9 chord” and “tones in a Bb diminished seventh” get “Could not parse a chord name from your question.”: the symbol expression has no 7sus4 and no “add 9” with a space, and the third prompt has neither “chord” nor a symbol. CanHandle also rejects “Tell me the tones in F#m7b5” and “break down Gmaj13 for me”, which the skill answers; on main, the router falls back on CanHandle when it can’t embed a question ChordInfoSkill.cs lines 100-110, lines 267-282, SemanticIntentRouter.cs line 321 on main l15, the example prompts: 26 of 29 right, CanHandle 27 of 32 Reported as #782
89. The skill reads the suffixes its vocabulary knows 22 of 82 aren’t read, or are read as another quality: the symbols +, 5, no3, dom7, ma7, Δ7, -7, °7, min7b5, ø and ø7, 7+5 read as a dominant 7th, and ten words, “power”, “dominant” and “diminished 7” among them. The power chord’s formula can’t be reached. The symbol expression’s comment counts + and “power 5/no3” among the forms it reads ChordInfoSkill.cs lines 302-316, ChordVocabulary.cs lines 59-104 l15, every suffix: 60 of 82 Reported as #782
90. The vocabulary’s Δ7 is a major 7th NormalizeQuality lowercases a suffix before its second switch, and ToLowerInvariant turns Δ into δ: the arm "Δ7" can’t match, and δ7, which GetFormula doesn’t know, gets a major triad, as every unknown quality does. Neither of the skill’s expressions reads Δ ChordVocabulary.cs line 66, line 82, line 140 l15, every suffix: NormalizeQuality("Δ7") returns δ7 Reported as #783
91. A symbol read in part doesn’t get a smaller chord’s answer After the suffix, the symbol expression accepts #, (, / and +: Cmaj7#11 gets Cmaj7’s answer, C7#11 and C7(b9) C7’s, Cm(maj7) Cm’s, C6/9 C6’s, C7#5#9 C7#5’s and C7+5 C7’s, and the answer doesn’t say what it dropped. Lesson 5’s improvisation skill does the same, row 23 ChordInfoSkill.cs line 315 l15, beyond the vocabulary: 6 symbols; every suffix: 7+5 Reported as #782
92. The skill names the chords it spells Given back its own spellings, it names all 293 with single accidentals and none of the 106 with a double or triple one: the note expression drops Bbb and reads C## as C♯, so C E♭ G♭ B♭♭, its Cdim7, is C diminished and F♯ A♯ C♯♯ is F♯ major. ChordVocabulary.PitchClasses has no name with two accidentals either. B♭ is read as B: C E G B♭ is C major 7 ChordInfoSkill.cs lines 325-326, ChordVocabulary.cs lines 23-32 l15, the round trip: 0 of 106 named back Reported as #782; B♭ read as B is the same as #757
93. The reading of notes knows the chords the skill spells 7b9 and 7#9 have five notes, within the limit, but aren’t among its 19 formulas: “What chord is C E G Bb Db” is refused, though “What is C7b9” spells those notes and GA’s corpus asks both kinds of question. “What chord is C E G# B”, the example of the list’s own comment, is refused: Cmaj7♯5 isn’t in the list ChordInfoSkill.cs lines 236-265, lines 261-262 l15, the round trip: 0 of 42; lists of notes Reported as #782
94. A note is spelled as the interval that names it The altered dominant’s formula puts its “flat fifth” three letters above the root: F♯ for C and F♯♯ for C♯, where the flat fifth is G♭ and G ChordVocabulary.cs line 130 l15, spelling: the 21 chords that differ, all 7alt Reported as #783
95. ga_chord_info reads the symbols its SKILL.md lists The tool’s own expression reads 14 of the vocabulary’s 51 symbols and refuses Cdom7, which the SKILL.md’s table lists. The SKILL.md has the model decline suspended and added-note chords, 9ths, 11ths, 13ths, altered dominants and lists of notes, which the C# skill answers ChordMcpTools.cs lines 81-82, SKILL.md line 68, lines 95-102 l15, the tool: 15 of 16; every suffix: 14 of 51 Reported as #783
96. A spelled-out quality is read as that quality “minor” is read as a mode and the chord as a major triad: “voicings for A minor” is read as A major with the mode minor, and on main the example “open chord shape for E minor” gets eight voicings of E major. In “voicings for C …”, all 31 spelled-out qualities of lesson 15 are read as C major, and only “major” is right. The index then drops the mode (row 100) TypedMusicalQueryExtractor.cs lines 90-102, lines 104-111, lines 217-226 l16, the words: 1 of 31; l16 (main), “open chord shape for E minor”: other 8 Reported as #785
97. CanHandle accepts the chords the skill answers It rejects its own example “quartal voicings in C”, every bare major chord (“voicings for C”, “G chord shapes”, “Bb voicings please”), the slash chord “voicings for C/G” and, in exercise 3, “voicings for C-7”: a root alone, / and - match neither of its expressions. It accepts 13 of the 31 spelled-out qualities, those that start with “major” or “minor”: dim and aug must end a word. On main, the router falls back on CanHandle when it can’t embed a question ChordVoicingsSkill.cs lines 57-73, lines 178-185, SemanticIntentRouter.cs line 321 on main l16, the examples: 11 of 12; other phrasings: 4 of 10; the words: 13 of 31 Reported as #785
98. The chatbot’s two readers of chord symbols agree ChordPitchClasses, which reads the chord of a voicing request, reads 47 of the 51 symbols of lesson 15 as a textbook spells them: Cdom7, Cma7 and C7+5 read no chord, and C-7 is read as C7, because the residue check takes - for a separator. It reads five symbols differently from ChordVocabulary, which ChordInfoSkill uses: dom7, ma7, Δ7, -7 and 7+5 MusicalQueryEncoder.cs lines 240-336, line 335, ChordVocabulary.cs lines 81-87 l16, the symbols: 47 of 51; read differently: 5 Reported as #785
99. The index names a chord so that the name filter finds it On main, the index stores the name GA’s analysis gives each voicing, and the filter compares its root and quality with the request’s. C E G A is named Am7, never C6; the 6/9 C D E G A is named C6; D G A is named Gsus2; an augmented triad takes one of its three roots’ names, Faug for D♭ F A. The filter cuts a stored name at its first parenthesis, so Dbaug(maj7)/C passes as D♭ augmented. Of a grid of 144 chords, the 12 sixth chords, 8 augmented and 6 sus4 triads get no voicing of their own, though lesson 3’s corpus holds them all OptickSearchStrategy.cs lines 307-319 on main, lines 348-349 l16 (main), the names of five chords; the grid, roots without an exact voicing: 12 of 12 for 6, 8 for aug, 6 for sus4 Reported as #784
100. A technique the prompt names is honoured, or the answer says it isn’t On main, the index path drops Tags and ModeName by design (ADR-0002), and VoicingAgent logs the dropped filters; ChordVoicingsSkill logs nothing, and its answer says nothing. Of the 48 voicings answered for the six example prompts that name a technique, 5 are that technique, 2 drop2 and 3 barre voicings; none is a shell, a rootless A7, a quartal voicing or an open E minor, though lesson 3’s corpus holds 19, 45, 1 and 60 of them OptickSearchStrategy.cs lines 101-130 on main, VoicingAgent.cs lines 110-126, 0002-voicing-filter-parity-cpu-gpu-only.md l16 (main), the techniques: 5 of 48 Reported as #784
101. A skill that needs no model answers when the router can’t embed the question CapoSkill and AlternateTuningsSkill answer without a model, but their CanHandle returns false, “semantic-routing only”, at the pin and on main. On main, the router’s keyword fallback, added so that the deterministic skills answer when the embeddings are down, never reaches them: it gets none of their 22 example prompts. At the pin’s CanHandle, two go to other skills: the one that names Em to skill.chordinfo, “What chord shape for B major with capo 4” to skill.chordvoicings CapoSkill.cs line 47, AlternateTuningsSkill.cs line 50, SemanticIntentRouter.cs line 321 on main l17, without embeddings: 0 of 22 Reported as #787 and #788
102. CapoSkill answers the question asked The direction is read from the phrasing: unless the question names a shape, a key after “in” is the sounding key, and the answer is a shape. Its own example “What’s the sounding key if I play in G with capo on 5” gets the shape D instead of the sound C, and “Playing in D with capo 2, what key does it sound in?” the shape C. The expressions ignore case, so “I’m in a band, capo 2, song is in G” reads the article as the key A. Row 82 reads an article as a chord the same way CapoSkill.cs lines 52-64, lines 89-104 l17, the examples and other phrasings: wrong direction 2; “in a band”: wrong chord Reported as #787
103. CapoSkill reads a key with its accidental The key ends with \b, which fails after #, ♯ and ♭, so the expression keeps the letter: “Capo on 2, song in F#” gets the shape D♯ instead of E, “song in C#, capo 4” G♯ instead of A, and “song in B♭, capo 1” A♯ instead of A. With “major” or “minor” after the key, the reading is right. Row 106 is the same boundary in the tunings skill CapoSkill.cs lines 52-56 l17, other phrasings: wrong chord for F#, C# and B♭ Reported as #787
104. CapoSkill reads the keys and shapes a guitarist writes It declines its own example “What chord shape for B major with capo 4” and “what shape for F with capo 3”: a key must follow “in” or “key”. It declines “song in Em”: a minor key must be a word. It declines C♭ major, missing from its table of roots, and the C7, Cmaj7 and Dsus4 shapes: a shape is a letter, an accidental and an optional m. It declines “Where should I put the capo to play in with open chords?” for the 12 keys: both expressions need a fret number CapoSkill.cs lines 52-64, lines 67-76 l17, the examples: declined 1; the grid: declined 11 (Cb); other phrasings: declined 5; where to put the capo: declined 12 Reported as #787
105. CapoSkill spells a key as a key signature writes it It writes sharps unless the key or shape it read has a flat, or, for a shape, the question says “flat”. 27 shapes of the major grid, and 3 of the 11 answers for each major open shape, are A♯, D♯ or G♯ major: a C shape with the capo on 3 sounds as D♯, where the class’s comment says E♭. Minor keys written with flats get D♭m and G♭m shapes. Row 56 is the same spelling in domain.transposeChord CapoSkill.cs lines 178-191, line 13 l17, the grid: theoretical spelling 27 major, 6 minor; the open shapes: 3 of 11 for each major shape Reported as #787
106. AlternateTuningsSkill reads a tuning given by its notes The expression for six notes ends with \b, so #, ♯ and ♭ are dropped. Open D’s notes are read as D A D F A D and get no name, in every notation; E♭ A♭ D♭ G♭ B♭ E♭ is read as E A D G B E; E♭ standard written in sharps, D♯ G♯ C♯ F♯ A♯ D♯, is named Whole-step down (D standard), a semitone lower on every string. NotesMatch compares spellings, so G♭ for F♯ gets no name either (exercise 3). Unchanged on main AlternateTuningsSkill.cs lines 203-211, lines 228-238 l17 and l17 (main), by the notes: 35 of 44, another named tuning 4 Reported as #788
107. AlternateTuningsSkill answers the tuning named, or declines At the pin, “open D minor” gets open D and “open G minor” open G, a major third for a minor one; “drop C♯” gets drop C and “drop D♭” drop D. On main, the two drop questions are declined, and the two minor ones still get the major tunings. The class’s comment promises a caveat for “What does an Em shape look like in drop-D”; the answer is the drop D table, which doesn’t name Em AlternateTuningsSkill.cs lines 53-74, line 15, lines 57-67 on main l17, outside the table: another tuning 4; l17 (main): another tuning 2; the answer names Em: no Reported as #788
108. A skill that needs no model answers when the router can’t embed the question VoiceLeadingSkill answers without a model, but its CanHandle returns false, “semantic-routing only”, at the pin and on main. On main, the router’s keyword fallback never reaches it: it gets none of its 10 example prompts. At the pin’s CanHandle, the two that say “voicing” go to skill.chordvoicings, which answers fingerings for one chord. Row 101 is the same for the capo and tuning skills VoiceLeadingSkill.cs line 49, SemanticIntentRouter.cs line 321 on main l18, without embeddings: 0 of 10 Reported as #789
109. VoiceLeadingSkill’s total is the least motion, as its answer claims Between two chords of the same size, it always is. When the sizes differ, the skill doubles the smaller chord’s root, and 1186 of 3072 answers move more than with another doubling, up to 7 semitones more: Csus2 to Em9 gets 11 semitones where 4 are enough, and its own example “smoothest voicing from Em to A7” 5 where 4 are enough. Every answer closes on “every other voicing of … requires more total semitone movement”, which is also wrong in the 607 answers where another pairing moves just as little VoiceLeadingSkill.cs lines 101-110, line 131, lines 185-194 l18, the grid: more than the least 1186 of 3072, several best pairings 607; the examples: Em → A7 5, least 4 Reported as #789
110. VoiceLeadingSkill reads the chord symbols its table lists 24 of 46 symbols are read right as the first chord, and 24 as the last. Δ, ø, +, -, o, “major7” and “dom7” are declined, except + and - as the last chord, which are read as the letter alone. CM7 is read as C minor seventh: the quality is lowercased before the table, which lists M7. A final ° is dropped, words after the last chord are ignored (“voice leading F to C minor” gets C major), and chords written as words before “to” are declined, its own example “voice leading C major to G major” among them. add9, 7sus4, 7♭9, 7♯9, mMaj7, 11 and 13 are declined on purpose VoiceLeadingSkill.cs lines 51-57, lines 224-252 l18, the symbols: right 24 of 46 as the first chord, 24 as the last; the examples: declined 1 Reported as #789
111. VoiceLeadingSkill reads a root with its accidental The expression ends with \b, which fails after #, ♯ and ♭, so a last chord keeps its letter: “voice leading from C to F#” gets C → F. With m after the accidental, or b written in ASCII, the reading is right. Rows 103 and 106 are the same boundary in the capo and tuning skills VoiceLeadingSkill.cs lines 55-57 l18, accidentals: right as the last chord 0 of 5 for #, ♯ and ♭ Reported as #789
112. VoiceLeadingSkill spells the notes as their chords do It writes sharps unless a symbol has a flat or the question says “flat”. TokenHasFlats looks for b ignoring case, so it finds the root of “Bb” at position 0 and doesn’t count it: “F7 to Bbmaj7” gives A♯ and D♯. Without a flat in the question, D7 to Gm gives A♯ for B♭. With one name per pitch class, E♯, B♯ and the double-sharp F are written F, C and G. Row 105 is the same spelling in the capo skill VoiceLeadingSkill.cs lines 85-86, lines 261-266 l18, spelling: as spelled 22 of 36; accidentals: “Bb” first spelled A# Reported as #789
113. VoiceLeadingSkill reads the chords of the question, or declines The expression ignores case, so “how do I get from G7 to a C chord” reads the article as the chord A. “voice leading Dm7 to G7 to Cmaj7” gets only its first move, and the answer doesn’t say so. “-” and “into” aren’t read as “to”. Row 102 reads an article as a key the same way VoiceLeadingSkill.cs lines 51-57 l18, other phrasings: wrong chord 4, declined 3, two of three chords 1 Reported as #789
114. ga_voice_leading_pair checks the voicings it pairs The tool ranks the pairs by motion only, and its search names no chord, so nothing compares a voicing with the chord asked. With 50 candidates per chord, which its description calls “more exhaustive”, the first pairs get worse: on main, the first pairs of 8 of lesson 18’s 10 examples play both chords, where 15 candidates give 10, and G7 → Cmaj7 and D → A trade the chord for 2 semitones instead of 3. At the pin, none does, and C → Am pairs three Es with themselves at 0 semitones CompositionTools.cs line 195, lines 224-235, lines 278-298 l19 (main), 50 candidates: right on both sides 8 of 10; l19, 50 candidates: 0 Reported as #791
115. VoiceLeadingDistance pairs a smaller voicing with its nearest notes, as its comment says It pairs the lowest notes of the larger voicing. On main, 340 of the 1360 pairs of different sizes the tool weighs for the ten examples get the least motion, and the gap reaches 39 semitones: Em x-x-2-x-0-3 to A7 0-0-2-x-2-3 weighs 47 where 8 are enough. Between voicings of the same size, the sorted matching is always the least, though the description calls it “not a formal Hungarian-optimal assignment”. For these ten examples, the smallest distance doesn’t change CompositionTools.cs lines 180-185, lines 312-336 l19 (main): different sizes 340 of 1360, largest gap 39; same size 890 of 890 Reported as #791
116. ga_voice_leading_pair returns its diagrams in chart order It returns the index’s string, high E first: on main, the first pair for C → F comes back as 0-1-0-x-x-x → 1-1-2-x-x-x, which a chart writes x-x-x-0-1-0 → x-x-x-2-1-1. On main, ChordVoicingsSkill turns the same strings into chart order; the tool’s answer doesn’t say which string comes first CompositionTools.cs lines 239-254, ChordVoicingsSkill.cs line 136 on main l19 (main), the first pair for C → F as the tool returns it Reported as #791
117. The parked voice-leading skill describes the tool it would call skills-dev/_pending-tools/voice-leading/DRAFT.md expects an argument optimize and an answer with VoiceMovements, TotalSemitones and CommonTones; ga_voice_leading_pair takes no optimize and returns pairs of voicings with a distance. The draft promises “a Plücker-line / minimum-displacement solution” and points to HarmonyMcpTools.cs, which GA doesn’t have, and its example answer announces 2 semitones from Dm7 to G7 for moves of 0, 0, 2 and 1. Its README parks it because the chatbot’s own tools lack ga_voice_leading_pair, which GaMcpServer has DRAFT.md lines 29-44, lines 54-60, line 74, README.md lines 19-29 l19 (main), Dm7 → G7: 3 semitones; the draft, read at both commits Reported as #791
118. ga_generate_progression spells each chord as its key does Every chord of the nine templates, in the 15 keys of each mode, has the right pitch classes, and 712 of 750 are spelled as a textbook spells them. PrefersFlats wants a b in the root or the root F, so D, G and C minor get sharps: the andalusian in C minor gets A♯ and G♯ for B♭ and A♭. With one name per pitch class, C♭ major’s I and IV are written B and E, G♭ major’s IV B, C♯ major’s iii Fm, A♯ minor’s V F7, and the ♭VI of E♭ and A♭ minor B and E. GA’s ChordProgressions.yaml writes the same B for the IV of G♭. Row 54 is the same choice in domain.diatonicChords CompositionTools.cs lines 344-380, ChordProgressions.yaml line 685 l20, the templates: spelled right 712 of 750, pitch classes right 750; the YAML’s tables: same chords in every row Reported as #792
119. ga_generate_progression reads the key it is given The root goes through ChordPitchClasses.TryParse, a reader of chord symbols. “D minor” and “Dm” are read as D, so I-V-vi-IV in “D minor” gets D major’s chords, without a warning. PrefersFlats compares the whole string with “F”, so “F major” gets sharps, F C Dm A#. “b” is read as B and turns B major to flats, B Gb Abm E, and any b in the string decides the spelling: “C7b9” writes the andalusian in C minor with flats, which “C” doesn’t. ♭ is refused CompositionTools.cs line 139, line 379, MusicalQueryEncoder.cs lines 166-171 l20, the roots: “D minor” → D A Bm G, “b” → B Gb Abm E Reported as #792
120. ga_generate_progression bounds the length of its answer length loops the template without a limit, where limit and candidatesPerChord in the same file are clamped: a length of 100000 returns 100000 chords, 17,300,216 characters of JSON. 0 and a negative length give the template’s own length, without a warning CompositionTools.cs lines 148-151, lines 202-203 l20, the length: 100000 chords, 17300216 characters Reported as #792
121. Stitching ga_voice_leading_pair over a generated progression gives a path of voicings, as the tool’s note suggests The note says “stitch with ga_voice_leading_pair”. One first pair per move, in C major or A minor: on main, they meet at 17 of 32 places, where the next pair starts on the voicing the last one ends on; elsewhere the hand jumps between two voicings of a chord. In 7 of the 9 templates, a joined path through the same candidates moves as little as the first pairs added up; in rhythm-changes-a and 12-bar-blues, any joined path moves more, 25 semitones for 21 and 28 for 24 CompositionTools.cs lines 174-175, lines 224-235 l20 (main): first pairs meet 17 of 32; l20: 6 of 32 Reported as #792
122. The parked progression skill describes the tool it would call skills-dev/_pending-tools/progression-generator/DRAFT.md expects mood, key, length with a default of 4, style and complexity, and an answer with Chords, RomanNumerals, Style and Rationale; the tool takes root, template and length and returns chords. Its keys “D minor” and “F major” are misread as roots (row 119), its example Dm – B♭maj7 – Gm – A7 is no template, and it points to ProgressionMcpTools.cs, which GA doesn’t have. Row 117 is the same for the voice-leading draft DRAFT.md lines 32-45, lines 49-63, line 77 l20, the roots; the draft, read at both commits Reported as #792
123. ga_search_voicings searches by mode The tool’s description promises “mode names (Lydian, Dorian)”, and the skill calls MODAL “mode flavor”. The reader finds the 23 modes, but MusicalQueryEncoder.Encode never reads the mode: after C, Am and G7, 19 of the 23 modes answer as the chord alone, 18 after Dm7, and the others read a tag. A mode alone gives every voicing a score of 0 and returns the index’s first ten, for HasIntent counts the mode and the “No chord, mode, or known tag recognized” answer never comes. “lydian dominant” and “phrygian dominant” are read as Lydian and Phrygian and the tag “dominant”. “something jazzy in F minor”, the vocabulary’s own example, reads the chord F and gets F major voicings VoicingSearchTool.cs line 95, lines 238-242, MusicalQueryEncoder.cs lines 43-108, TypedMusicalQueryExtractor.cs lines 104-121 l21, the modes: C 19 of 23 the same; modes alone 18 of 23 at 0.0000, the index’s first ten; l21 (main): the same Reported as #795
124. A tag gets the voicings it names The 188 tags set 12 bits of SYMBOLIC, worth 0.10 of the score, and the tags of a bit answer alike: jazz as rock-guitar, shell-voicing as rootless. Cmaj7 gets 7 different answers from the 188 tags at the pin, 5 on main, and the tags of bits 3, 4, 5 and 10 change nothing. After ten seventh chords, “rootless” returns no rootless voicing in 100, at the pin and on main, where the index holds between 5 and 63 for each chord: the query’s STRUCTURE holds the whole chord, root included SymbolicVectorService.cs lines 13-32, SymbolicTagRegistry.cs lines 46-111, SemanticNomenclature.yaml lines 129-140 l21, the tags: Cmaj7 7 different answers, 12 of 12 bits alike; “rootless”: voicings answered 100, rootless 0; l21 (main): Cmaj7 5, rootless 0 Reported as #795
125. The reader takes as tags the words of the vocabulary GetBitIndex falls back to a match by substring, both ways: “for” reads normal-form, “the” pitch-axis-theory, “what” so-what-chord, “end” beginner-friendly. The reader skips “chord”, “voicing” and words under three letters, not these. A second bit lowers the weight of the first: “G7 shell for” scores 0.4932, “G7 shell” 0.5078. The skill’s example “something jazzy in F minor for a ballad” and the vocabulary’s “shell voicing for G7” read “for”. Two listed tags can’t be typed: g5 is under three letters, and schoenberg-op.16-chord splits at its . SymbolicTagRegistry.cs lines 196-220, TypedMusicalQueryExtractor.cs lines 132-147 l21, GA’s example queries: for → normal-form; the course’s: the → pitch-axis-theory, what → so-what-chord, end → beginner-friendly Reported as #795
126. The voicing-search skill describes the scores and the tags as the tool has them The skill’s weights “approach 1.0” and its example scores 0.9831, but a query leaves MORPHOLOGY and CONTEXT at zero: no query can score above 0.70, and the 828 chord queries peak at 0.5250 at the pin, 0.6000 on main. “drop2 does NOT match”: it reads drop2voicings, the registry’s copy without hyphens. A style tag “currently lowers the score”: “Cmaj7 jazz” scores 0.5085, “Cmaj7” 0.4585, on the index the pinned code writes. Its uses “Lydian on guitar”, “jazz voicings” and “shell voicings” name no chord and score 0, 0.0577 and 0.0707; “something warm and dreamy”, given as a query the reader gets nothing from, reads dreamy SKILL.md line 19, line 26, line 39, line 47, line 81, lines 94-96 l21, the scores: the highest a query can reach 0.70, chord queries 828, the highest top score 0.5250; l21 (main): 0.6000 Reported as #795
127. The parked voicing-search skill describes the tool it would call skills-dev/_pending-tools/voicing-search/DRAFT.md calls ga_search_voicings_by_query, which is in VoicingEmbeddingTool.cs and needs GaApi and Ollama; the draft says it is not implemented and points to VoicingMcpTools.cs, which GA has at neither commit. It expects topK, instrument and an answer of Results with contextTags and sourceCorpus; the tool takes query and limit. Its “mellow Cm9”, x-3-5-3-3-3, has no minor third, its chart calls the G string’s third fret E♭ and the high E’s D where they play B♭ and G, and none of its three tags is in the vocabulary. Rows 117 and 122 are the same for the two other drafts DRAFT.md lines 22-26, lines 35-44, lines 57-68, line 83, VoicingEmbeddingTool.cs lines 93-132 l21, the draft: x-3-5-3-3-3 plays C G A# D G, leaves out D#; its tags in the vocabulary: no Reported as #795
128. IcvNeighborsSkill answers its example prompts The 2026-06-16 curation reworded the skill’s ten routing anchors with “resemble”, “similar” and “most like”, and dropped “close”, “nearby” and “ICV”, but the two patterns that read the question still need “neighbors”, “nearby”, “close to”, “near”, “adjacent” or “harmonically close”: the skill declines all ten anchors, the questions the router compares with, and its hint rule boosts none of them. Two of the four phrasings of its doc comment, “Nearby pitch-class sets to C major” and “Closest chord to G7 in ICV space”, are declined too, and its refusal’s own example, “ICV neighbors of Cmaj7”, is boosted toward skill.intervalclassvector as well. IntervalClassVectorSkill, curated in the same pass, answers 8 of its 10 anchors. On main, the refusal is only marked Declined IcvNeighborsSkill.cs lines 51-89, lines 254-260, DefaultRoutingHintProvider.cs lines 124-139 l22, GA’s phrasings: anchors 10: answered 0, declined 10, hinted toward skill.icvneighbors 0; IntervalClassVectorSkill’s anchors: answered 8; l22 (main): the same Reported as #798
129. IcvNeighborsSkill builds the chord it is asked about Of 27 common chords after “ICV neighbors of”, the skill builds 10 right. It lowercases the quality before reading its table, so the "M7" of line 221 never matches and CM7 is built as C minor seventh. A quality it doesn’t know gives a major triad, without a warning: Cdom7, Cadd9, C7b9, C7#9, Cm11, C13 and C5 answer as C E G. ° and + aren’t letters, so the chord ends before them: C° and C+ become C, and C°7 the triad C°; Cø7, C7sus4 and CmMaj7 are declined. “C minor” and “A minor” are read as major triads, and the pattern ignores case, so “a” is A major: the parked draft’s “What’s close to a half-diminished chord?” gets the neighbors of A IcvNeighborsSkill.cs lines 82-89, lines 196-232 l22, the chords: 27, built right 10; CM7 built C D# G A#; “a Cmaj7” read as a, C# E A; l22 (main): the same Reported as #798
130. IcvNeighborsSkill lists the closest sets, “top 8 by harmonic cost” The skill keeps the sets at L1 1 or 2 and sorts them by L1, but a chord of three notes or more has none at 1: every row is at L1 2, cost 1.20, and the eight rows are the first eight by bitmask, in FindNearby’s order. The rows depend on the vector only: the 192 chords of 12 roots and 16 qualities get 10 different answers, and every major or minor triad gets the same eight sets, five of them two-note intervals. Cdim’s and Caug’s rows are two-note sets only. Cdim7 gets none, with “Try a wider radius”, but the radius is the constant DefaultMaxDistance. Rows are pitch-class numbers and vectors, without a name or a Forte number, and hold several transpositions of one set class: C’s eight rows hold five set classes. On main, ga_icv_neighbors lists each set class once since 51ac3f68; the skill doesn’t IcvNeighborsSkill.cs lines 76-77, lines 130-175, GrothendieckService.cs lines 45-90 l22, the neighbors: every row 2, 1.20, the first by id: yes; chords 192: different answers 10, different vectors 10; Cdim7: no pitch-class sets within L1 = 2; l22 (main): the same Reported as #798
131. The parked icv-neighbors skill describes the tool it would call skills-dev/_pending-tools/icv-neighbors/DRAFT.md says ga_icv_neighbors is not implemented and points to Common/GA.Business.ML/Agents/Mcp/AtonalMcpTools.cs, which GA doesn’t have; the tool is in GaMcpServer/Tools/ChordAtonalTool.cs. It expects chord, topK and metric, and an answer of Neighbours; the tool takes symbol and maxDistance and returns text. Its example puts Am7 at distance 0.0 with Cmaj7’s vector, but Am7 is 4-26, at L1 4, and Cm9 without its root at 0.6, but it is E♭ major seventh, 4-20, at L1 0. The chatbot’s skill declines two of the draft’s three phrasings and reads A major in the third. Rows 117, 122 and 127 are the same for the three other drafts DRAFT.md line 18, lines 31-58, line 72, ChordAtonalTool.cs lines 220-254 l22, the draft: Am7 4-26, L1 4; Cm9 without its root 4-20, L1 0 Reported as #798
132. GrothendieckDeltaSkill and IcvShortestPathSkill answer their example prompts The delta skill answers 8 of its 10 example prompts: “how harmonically far is Am from D7”, also the first phrasing of its doc comment, has “from” between the chords, and “how different are C major and F major harmonically” a word after the first chord. The path skill answers 7 of its 10: “shortest path from C major to F major”, also in its doc comment, “shortest chord path from Dm7 to Gmaj7” and “harmonic stepping stones from Cmaj7 to Fmaj7” are declined. The delta skill answers 9 of the path skill’s 10, so a path question worded like them and routed to it gets a distance, not a refusal; “harmonic cost to move from C to G” boosts skill.transpose. Both build their chords with a copy of lesson 22’s table: CM7 is C minor seventh, “C to A minor”, a path prompt, asks for A major, and “a” is A major GrothendieckDeltaSkill.cs lines 46-67, lines 153-173, IcvShortestPathSkill.cs lines 55-93, DefaultRoutingHintProvider.cs lines 257-259 l23, GA’s phrasings: GrothendieckDeltaSkill: anchors 10, answered 8; IcvShortestPathSkill: anchors 10, answered 7; the other skill answers 9 of them; chord tokens 36: built alike 36; l23 (main): the same Reported as #802
133. GrothendieckDeltaSkill gives the distance between two chords GrothendieckDelta.FromIcVs turns a zero delta into Ic1 = 1, and the skill prints it: C to G, Am to Em, Cmaj7 to Fmaj7 and C to C get [+1, 0, 0, 0, 0, 0], an L1 of 1, a cost of 0.60 and “more chromatic color”, as do all 4320 ordered pairs of the 192 chords that share a vector. IcvNeighborsSkill recomputes the delta to avoid it; the heuristic is the one GA issue #776 reports. The interpretation is the first rule that matches, so Cmaj7 to G7 and G7 to Cmaj7 both get “more chromatic color”, as 1440 of the 16272 pairs with different vectors do. The arrow before it prints as →, the bytes of → read as Windows-1252 and saved as UTF-8, at the pin and on main GrothendieckDelta.cs lines 113-134, line 217, lines 226-258, GrothendieckDeltaSkill.cs lines 97-131 l23, the deltas: with the same vector 4320, answered L1 1 4320; with different vectors 32544, L1 right 32544; “more chromatic color” both ways 1440; l23 (main): the same Reported as #802
134. IcvShortestPathSkill finds a path between two chords The search only moves to sets of the same size, so chords of different sizes are never joined: Am to D7 and Gmaj7 to Em, two of the skill’s example prompts, and “How do I get from Am to D7 harmonically”, from its doc comment, get “No path found within 5 steps”. Before saying so, it expands 168 of the 220 sets of three notes, or 462 of the 495 sets of four, each a scan of the 4096 sets whose vectors are recomputed at each read, unless a FindNearby cache of 256 entries, oldest out first, kept its answer. Cdim7 and Caug reach only their own transpositions, 3 and 4 sets GrothendieckService.cs lines 117-163, line 21, PitchClassSet.cs line 104, IcvShortestPathSkill.cs line 137 l23, the paths: Am → D7 and Gmaj7 → Em, no path; sets expanded 168 of the 220 of 3 notes, 462 of the 495 of 4 notes; l23 (main): the same Reported as #802
135. IcvShortestPathSkill describes the path it finds The answer counts sets as steps: Cmaj7 to G7 is “4 steps total (3 intermediate moves)” for three moves through two sets, C to G “2 steps total (1 intermediate move)” for one move through none, C to C “1 step total”. A move between two sets of one vector, at an L1 of 0, counts as a step: C to G, C to F, C to Cm and C to A. The answer gives the sets in between as numbers without a name, C D D# G and C C# E F# for Cmaj7 to G7, whose moves keep 2 notes, then 1, then none, where the answer promises “common-tone bridges”. The skill also keeps two curation comments, the second written over the first IcvShortestPathSkill.cs lines 55-66, lines 127-162 l23, the paths: Cmaj7 → G7, 4 steps total (3 intermediate moves), moves 3, common notes 2,1,0; C → C, 1 step total; l23 (main): the same Reported as #802
136. SetTheoryEquivalenceSkill and GrothendieckParseSkill answer their example prompts The set skill answers 4 of its 5 example prompts: “Are pitch classes 0,2,4 and 1,3,5 transpositionally equivalent” has a word between the second set and “equivalent”. The parse skill answers 5 of its 10: “parse Cmaj7 + Gmaj7 + Fmaj7”, “explain power(Cmaj7)” and “parse Transpose(Cmaj7)”, the form its own failure message suggests, hold none of the symbols its surface pattern looks for, and “what is Hom(Cmaj7, Gmaj7)” has no anchor word; the parser rejects “Transpose(C ⊗ G)” and “equalizer Transpose Invert”. No hint rule names the set skill, and two of its prompts boost skill.transpose SetTheoryEquivalenceSkill.cs lines 35-54, GrothendieckParseSkill.cs lines 51-82, line 177, DefaultRoutingHintProvider.cs lines 257-259 l24, GA’s phrasings: SetTheoryEquivalenceSkill: anchors 5, answered 4; GrothendieckParseSkill: anchors 10, answered 5; l24 (main): the same Reported as #806
137. SetTheoryEquivalenceSkill reads the relation a question names In “equivalent under transposition” and “equivalent under inversion”, the wording of its example prompts, \s* takes the space that the optional \s+under group needs, so the skill reads no relation and answers for the set class: {0,1,3} and {0,2,3} “under transposition”, its second example prompt, get “Yes”, though only an inversion relates them. That wording is wrong for 2016 of the 48400 ordered pairs of three-note sets and 4032 of the 245025 of four-note sets. The relation is read only after “set class”, “set” or “class”, where every verdict is right, so none of the example prompts gets the verdict for transposition alone written after a review on 2026-05-13. A pair related by transposition and asked “under inversion” is said to reduce to the same prime form “via either operation”, which is false for 2016 of the 2608 pairs of three notes and 4032 of the 5841 of four it is said of SetTheoryEquivalenceSkill.cs line 48, lines 69-80, lines 97-107, lines 137-148 l24, the relation: equivalent under transposition, 0,1,3 | 0,2,3: yes, TI, wrong; every pair: 3 notes wrong 2016, 4 notes wrong 4032; “via either operation”: 2016 of 2608, 4032 of 5841; l24 (main): the same Reported as #806
138. SetTheoryEquivalenceSkill reads the sets The first set must be followed by “ and”, so it starts after the last dash: “0-1-4” is asked as {4}, and the Forte name “3-3” as {3}, and the answer compares them without a warning. 10 and 11 written as “t”, “T”, “A” or “0te” are declined, as are parentheses and angle brackets. The prime forms hold: 224, the same for the 24 transpositions and inversions of every set, each the set GA’s table of Forte’s catalog gives for its label, with 23 pairs that share a vector, and the answers about Z-related sets are right SetTheoryEquivalenceSkill.cs lines 47-54, lines 218-241, PitchClassSetId.cs lines 123-156 l24, the sets: 0-1-4, answered, 4 {0}; 3-3, answered, 3 {0}; prime forms 224, stable 4096, differing from the table 0, missing from the table 0, Z pairs 23; Z-related pairs said 1152 of 1152; l24 (main): the same Reported as #806
139. GA’s Grothendieck parser reads the forms its comments name 25 of the 50 forms parse, 21 into the case their comment promises. str reads the spaces after its word, so every form that then asks for ws1 fails: “limit of”, “lim”, “colimit of”, “colim”, “define functor”, “natural transformation”, “define nat_trans”, “define sheaf”, “truth_value of”, “subobjects of”, “glue”, and “transpose” with a number; “restrict S to U” fails sooner, since the parser wants the sheaf’s name first. A first alternative that reads an object hides the word forms, such as “C tensor G”. “P(C)”, “power(C)”, “Ω(C)” and “eta(C)” are functor applications. None of the 50 forms reaches 9 of the 22 cases, three of which a form written another way reaches: “equalizer” needs two parentheses, and no space may follow their comma or their “|”; two operators in a row fail. Only the quality names in its list, such as min and dom7, are read: Am, Cm, G7, C7, Dm7, CM7, C°, C+ and Cm7b5 fail GrothendieckOperationsParser.fs line 18, lines 53-85, lines 406-431, lines 625-633 l24, the expressions: forms 50, parsed 25, as their comment says 21; cases no form reaches 9; written without spaces, eq(invert,reflect) Equalizer; the chords: Am, G7, Dm7 fail; l24 (main): the same Reported as #806
140. GrothendieckParseSkill answers a question Its surface pattern lets through ⊗, ⊕, ×, ∘ and a few words, but not “+”, “^” followed by a space, Ω, “=>” or a call such as “Invert(C)”, so it answers for 12 of the 50 forms. It removes only a leading anchor followed by a space and a final “mean” or “do”: “Parse:”, “please parse”, “can you parse” and a final “please” reach the parser and fail, and “explain the tensor product” is parsed as an expression. The answer names the case and gives a gloss written for the case: “parse C ⊗ G” and “parse F# ⊗ Bb” get the same answer apart from the expression GrothendieckParseSkill.cs lines 74-109, lines 146-163, lines 244-276 l24, the expressions: “parse ” answered for 12; the questions: Parse: C ⊗ G, can’t parse; explain the tensor product, can’t parse; the same answer apart from the expression: yes; l24 (main): the same Reported as #806
141. The transpose hint fires on transposition questions It fires on 16 of 26 phrasings. It misses 3 of TransposeSkill’s 13 example prompts, “What’s Dm7 up a whole step?”, “raise the key by two semitones” and “lower the key by a half step”, the SKILL.md’s “Cmaj7 in the key of G”, and “Shift the song to the key of E”: it needs a word that starts with “transpos”, or “shift”, “bring” or “move” followed by “up”, “down”, or “to” and a capital letter. Two example prompts that name an interval boost skill.interval as much; “Raise Bb7 by a minor third” and “Lower G by a tritone” boost skill.interval alone, and “Play this song a whole step down” boosts skill.alternatetunings alone DefaultRoutingHintProvider.cs lines 257-259, lines 74-76, lines 159-161, TransposeSkill.cs lines 41-60 l25, transpose phrasings: the transpose hint 16 of 26, another intent’s 6, 3 of them without the transpose hint, no hint 7; l25 (main): the same Reported as #809
142. The transpose hint and transpose’s SKILL.md triggers stay on transpositions The hint fires on 10 of the 407 example prompts of the other intents: “transposition” and “transpositionally” in two set-theory prompts and in “What are modes of limited transposition”, the functor “Transpose” in five parse prompts, and “move from G7 to C” and “move from C to G”. The triggers, read as substrings, are in 8: “up a” in “what notes make up a G7 chord” and “Brighten up a minor key tune”, “in the key of” in “All chords in the key of D”, and “transpose” in the five parse prompts DefaultRoutingHintProvider.cs lines 257-259, skills/transpose/SKILL.md lines 4-11, SkillMdDrivenSkill.cs lines 54-65 l25, the other intents: 407 example prompts of 34 intents, the transpose hint 10, a trigger 8; l25 (main): 412 of 35, the same 13 rows Reported as #809
143. The skill whose triggers read transpositions can be routed to Two skills are named Transpose: TransposeSkill, behind skill.transpose, whose CanHandle accepts none of the 26 phrasings, and the SkillMdDrivenSkill built from its SKILL.md, whose triggers accept 18 of them but which is behind no intent, like all 18 skills SkillMdPlugin builds SkillMdDrivenWrapperBase.cs line 85, SkillMdPlugin.cs lines 83-92 l25, the skills: TransposeSkill 0 of 26, SkillMdDrivenSkill 18 of 26, built from a SKILL.md 18, behind an intent 0; l25 (main): the same Reported as #809
144. Without embeddings, a transposition gets a transposition or a message that it can’t be answered At the pin, the intent router routes none of the 26 phrasings and the chat endpoint answers HTTP 500 to all. On main, the intent router’s keyword fallback can’t pick skill.transpose, whose CanHandle says no: it sends 2 to ChordInfoSkill, which gives the notes of the chord before the transposition, and “Lower G by a tritone” to ChordSubstitutionSkill, all three labelled orchestrator-skill-semantic. The agent router’s keywords send 16 to the voicing agent, which has no keyword list and scores 0.1: “Shift Am7 up a fifth” gets Am7 voicings. 7 get “Our reasoning service is currently unavailable” SemanticIntentRouter.cs lines 115-127, SemanticRouter.cs lines 70-73; on main, SemanticIntentRouter.cs lines 313-335, OrchestratorSkillIntent.cs line 29, line 54, SemanticRouter.cs lines 341-365 l25, without embeddings: the router picks none 26, HTTP 500 26; l25 (main): the router picks none 23, skill.chordinfo 2, skill.chordsubstitution 1; the chat endpoint answers with voicing (keyword) 16, fallback-direct 7 Reported as #809
145. Each mode of ModesSkill’s catalog is its family’s first mode played from the mode’s degree Mode 1 is a textbook’s scale in all 26 families. Of the 129 modes, 93 are mode 1 from their degree, 2 from another degree, the major pentatonic’s “Blues Minor” and “Minor Pentatonic”, and 34 from no degree: Ultralocrian has the altered scale’s notes, three seventh modes begin C Dbb, and the Neapolitan major’s modes 4 to 7, 8 bebop modes, 3 Prometheus, 5 enigmatic, 6 Hungarian major and 4 In Sen modes are other scales Modes.yaml lines 242-243, lines 659-674 l26, the families: modes 129, from their own degree 93, from another degree 2, from no degree 34, notes out of order 3 Reported as #813
146. ModesSkill leaves out only Modes.yaml’s chord and triad families It also leaves out “All Interval Tetrachord Family”, whose name contains “chord Family” when case is ignored; its comment counts four families ModesSkill.cs lines 515-516, lines 530-532 l26: families 26, left out 5 Reported as #813
147. ModesSkill’s formulas give each note its degree They are a textbook’s for 65 of the 129 modes: 60 of the 63 seven-note modes, the other three having their notes out of order, 3 of the 14 six-note and 2 of the 10 two-note modes, and none of the 15 five-note, 26 eight-note and one twelve-note modes. A two-note scale can get the wrong note: 1 2 for C F. Reading every number from the letter would give the degrees of 52 of the 63 seven-note modes ModesSkill.cs lines 779-811 l26, the formulas: the skill’s 65 of 129; the letters’ 52 of the 63 seven-note modes Reported as #765, with lesson 26’s counts in a comment
148. What is <mode name>? gets that mode 119 of the 129 mode names get their mode, and all 68 alternate names. Eight names are unquoted and lose everything from “ #“, “Lydian #6” becoming “Lydian”, and then repeat a name that comes first in the catalog; “Minor Pentatonic” and “Whole-Half Diminished” are twice in the catalog. CanHandle accepts every mode name but 7 of the 68 alternate names Modes.yaml lines 631-681, ModesSkill.cs lines 244-255, lines 137-144 l26, the names: written 165, read otherwise 8; mode names with their mode 119 of 129, alternate names 68 of 68; CanHandle 129 and 7 Reported as #813
149. ModesSkill’s example prompts get the answer they ask for “List unnamed modal families” gets the major scale’s modes, the default answer, because the atonal path looks for “unnamed famil”; “What modes have a major 7th” gets them too, and “Mixolydian versus Ionian differences” gets Mixolydian alone. CanHandle accepts 11 of the 37 ModesSkill.cs line 606, lines 202-207 l26, the example prompts: 37, CanHandle 11 Reported as #813
150. OutsideNotesSkill classifies a note as a textbook does Over five seventh chords, 42 of 60 notes get the textbook’s verdict, and 12 more the same advice. #9 and b7 over Cmaj7, #11 over Cm7 and 13 over Cm7b5, which no usual chord scale holds, are “an available tension”, “Safe to sustain as an extension”. The b9 and b13 of C7 are avoid notes: the b9’s headline says so, and its explanation the opposite OutsideNotesSkill.cs lines 141-189 l27, the rule: cells 60, the same 42, avoid notes no usual scale holds 12, tensions no usual scale holds 4, avoid notes the textbook makes tensions 2 Reported as #814
151. The 11 over an extended chord gets the verdict it gets over the seventh chord F over C11, C13, Cmaj11 and Cmaj13 is “a chord tone — the perfect eleventh”, while over C7, C9, Cmaj7 and Cmaj9 it is an avoid note, a semitone above E, which the larger chords still hold: ChordVocabulary stacks thirds up to the 11 ChordVocabulary.cs lines 134-138 l27, F over C and its extended chords Reported as #814
152. OutsideNotesSkill reads the note and the chord a question names The 14 notes written with # or b are read right, the 14 written with ♯ or ♭ are read as naturals, and the 7 lowercase notes get the refusal. 27 of the 40 chords ChordVocabulary has are read right: the chord’s run stops at “-”, “o”, “(”, ♭, ♯ and the second letter of ma7 and mi7, so Cma7 is Cm; °, Δ and Δ7 (as in #783) become C E G, like 7sus4, mMaj7, 7#11 and maj7#11, which the vocabulary lacks. “over a minor chord” is A major, “over a dominant chord” D major OutsideNotesSkill.cs lines 289-299, ChordVocabulary.cs lines 81-84, line 140 l27, the notes: natural, # and b 7 of 7 each, ♯, ♭ and lowercase 0; the chords: 27 of 40, and 0 of the 5 the vocabulary lacks Reported as #814
153. The skill’s evidence spells the chord’s notes It names them from two arrays of twelve names: right on 12 roots for 11 major, 8 minor, 9 dominant 7, 11 major 7, 8 minor 7, 6 half-diminished, 3 diminished 7 and 6 augmented chords. C7 is C E G A#, F# major Gb Bb Db, Cdim7 C Eb Gb A OutsideNotesSkill.cs lines 226-241 l27, the chord’s notes Reported as #814
154. OutsideNotesSkill’s example prompts get an answer “why does that note clash over the chord”, “why does the note clash over this chord” and “why does the b9 sound so tense over C7” get the refusal. CanHandle accepts 8 of the 10, not “what is F over G7”, which the skill answers OutsideNotesSkill.cs lines 31-34, lines 48-86 l27, the example prompts: 10, CanHandle 8, refused 3 Reported as #814

The request, on 2026-09-14: a course on the OPTIC-K index, on all of GA’s machine learning and agent code, and on what the chatbot is trying to accomplish. The course sits under Machine Learning, next to IX: it is about building and using embeddings, a vector index and a classifier-style router inside a product, not about using coding agents, which is what AI-assisted development covers.

2026-09-14 — Pinning GA and building against the chatbot host

Section titled “2026-09-14 — Pinning GA and building against the chatbot host”
  • GA is pinned on a826864, the commit the music theory course uses. The author’s local clone is never used: fetch-ga.sh makes a blobless, sparse clone in code/ga-ai/.ga, ignored by Git, with 17 project folders and GA’s research PDFs excluded.
  • On Windows, the first checkout failed on paths longer than 260 characters; git config core.longpaths true before the checkout fixes it. As in the music theory course, MSYS_NO_PATHCONV=1 stops Git Bash from rewriting the sparse patterns.
  • The course project references GaChatbot.Api directly and starts it with WebApplicationFactory<Program>. Two details were needed: an assembly attribute, WebApplicationFactoryContentRootAttribute, pointing at the host’s folder inside the clone (GaAi.csproj); and no top-level statements in the course program, whose generated Program class would clash with the host’s.
  • The author’s machine runs Ollama. Left alone, the host would have used it, and the outputs would have differed from CI’s. The course sets the Ollama address to http://127.0.0.1:9, closed on the author’s machine and on the runners.
  • GA’s chat memory defaults to files under ~/.ga. The course registers its own MemoryStore and ChatTranscriptStore on a fresh folder next to the program; ~/.ga was checked to be unchanged after a run.
  • The first corpus, 5 frets and a window of 4, had 77,140 voicings and took 33 seconds to embed. 3 frets and a window of 3 give 15,360 voicings in about 8 seconds, enough for every point of lesson 3.
  • Exception messages differ between systems (socket errors). Lesson 4 prints only the exception type and the innermost GA frame, file:line, deduplicated and sorted.
  • Local build of GA’s projects and the course: about 23 seconds after the first restore; a full check of the four lessons, 40 seconds.
  • Run 34917150100, for commit 43f009d: lessons 1 to 3 passed everywhere, lesson 4 passed on Windows and failed on Linux and macOS. On those two, the warnings of the semantic intent router appeared under the first request as well.
  • Cause: IntentEmbeddingWarmupService starts the embedding of the intents in the background when the host starts, and its warnings land in whichever request is running. Fix in commit 8e335d7: the course waits for the warm-up’s last log line before sending requests. Lesson 4 tells the story.
  • Run 34917602262, for 8e335d7: green on the three systems, 1 min 34 s on Linux, 1 min 50 s on macOS, 3 min 2 s on Windows, clone and build included.

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

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

Each item says where the course program shows it. None was reported upstream.

Embeddings (lesson 2):

  1. VoicingDocumentFactory sets RootPitchClass and MidiBassNote from MidiNotes[0] (lines 37-38). GA builds voicings string 1 (high E) first, so that note is the top note. ROOT, MODAL, MORPHOLOGY’s bass and the inversion are computed from it; the search filter uses MidiNotes.Min(), the real bass. Shown by l2, “From a chord shape to GA’s voicing document” (open C: root E, inversion 1), and by l3’s Cmaj7 search.
  2. The inversion compares that note with the chord’s root name, parsed by PitchClass.Parse (lines 43-44), which reads note names as digits: "A" is 10 and "E" is 11. A root-position E minor, 022000, gets Inversion = -1. Reproduced with a separate program referencing GA.Business.ML, not part of the course’s CI: PitchClass.Parse("E") = 11, and 022000 Em ChordId.RootPitchClass="E" doc.RootPitchClass=4 inversion=-1. The music theory course found the same parser behaviour.
  3. VoicingHarmonicAnalyzer passes intervalSpread > 12 into IsRootless and false into IsOpenVoicing of the positional record VoicingCharacteristics (lines 31-43). Every voicing spanning more than an octave is “rootless”. Shown by l2, “VoicingCharacteristics and PerceptualQualities”.
  4. VoicingAnalyzer builds new PerceptualQualities(curVoiceChars.Consonance, 0, 0, "Neutral", "Medium") for a record declared (Brightness, ConsonanceScore, Roughness, ...) (line 164). ConsonanceScore is always 0, the document’s Consonance too, and CONTEXT’s tension 1.0 - doc.Consonance is always 1. This is the constant dimension of issue #616, whose analysis blames only the literals next to it. Same section of l2, and the CONTEXT row of l3’s “Dimensions that never vary”.
  5. EmbeddingSchema.AtonalModalDim is 17 while the registry gives ATONAL_MODAL 64 slots; ModalVectorService allocates 17 (line 116) and WriteInto doesn’t check lengths: 47 slots are always zero (l3). HierarchyDim is 8 against 15 (l2, “Loose constants”). Both partitions have weight 0, so search is not affected.
  6. STRUCTURE is not invariant to transposition (221 of 222 set classes, l2), as GA’s own sweep and TheoryVectorService’s comment say; RootVectorService’s summary still claims “genuinely O+P+T+I-invariant” (lines 3-7).
  7. The similarity weights add up to 1.15; a vector scores 1.15 against itself (l2). Not a bug, but “cosine” is misleading in logs and thresholds.
  8. Versions in comments and documents: the newest schema document is v1.4.1, OPTIC-K_Embedding_Schema.md describes v1.3.1 (109 dimensions), MusicalEmbeddingGenerator says 228 (line 13) and 216 (line 55), CompactDimension’s comment says 112, OptickIndexWriter and MusicalQueryEncoder say 112, ExtensionsEnd’s comment says it equals TotalDimension. The code computes 240 and 124.

Index and search (lesson 3):

  1. Nine of the 33 named MODAL slots are never set on the 15,360-voicing corpus: LocrianNatural6, DorianSharp4, LydianSharp2, AlteredDoubleFlat7, DorianFlat2, LydianAugmented, MixolydianFlat6, LocrianNatural2, Diminished. ModalVectorService looks modes up by display names such as "Locrian ♮6" and returns silently when nothing is found; a name mismatch is the likely cause (to verify).
  2. 37 of the 124 searched dimensions are always zero and 4 constant on that corpus (l3); #616 reports 40 dead dimensions on the live index.
  3. ApplyFilters matches the quality as a substring of the stored name: the Am7 filter accepts Gbm7(shell)/A, and the empty quality of C accepts any name with C in the bass, including C + E (Major 3rd) and Am/C (lines 296-329).
  4. OptickSearchStrategy.FindSimilarVoicingsAsync always returns an empty list (known, commented at lines 75-92). MusicalQueryEncoder leaves the interval-class vector out of queries (known, commented).

Diagrams and MCP tools:

  1. Two orders for the same diagram string: VoicingGenerator, the index and the MCP tool’s parser read string 1 (high E) first; PlayableNotationFormatter reads the low E first (lines 31-53). The chatbot’s tablature for 3-0-x-2-3-x (Cmaj7) is 6/3 5/0 3/2 2/3, the notes G A A D (l4).
  2. ga_generate_voicing_embedding’s description says “228-dim” and gives 'x-3-2-0-1-0' for Cmaj7 (lines 42-44); the tool’s parser reads it as Dsus2/E (l2), and read low E first it would be C, not Cmaj7.
  3. ga_get_embedding_schema lists ten partitions, without ROOT, with the loose HierarchyDim and AtonalModalDim (lines 69-91). Read in the code, not run by the course.

Chatbot (lessons 1 and 4):

  1. Without an embedding server, every message that no guard catches ends in HTTP 500. SemanticRouter.RouteAsync doesn’t catch the exception of EnsureEmbeddingsInitializedAsync (lines 68-104), so its keyword fallback is unreachable; the host’s fallback to a direct model call throws too. “What is the relative minor of C major?” fails although skill.relativekey answers it with confidence 1.00 (l4).
  2. The legacy CanHandle path sends that same question to ScaleInfo, not to the relative-key skill (l4).
  3. skill.fretspan has no example prompt, and SemanticIntentRouter skips intents without examples: it can never be routed to (l1).
  4. ProductionOrchestrator.AnswerStreamingAsync (from line 154) has the voicing guard but not the algebra fast path of AnswerAsync. Read in the code, not run.
  5. IntentEmbeddingWarmupService starts its work fire-and-forget; fine for a server, but a host’s logs then depend on timing (the CI failure above).

Documents:

  1. docs/architecture/chat-surfaces.md says in its 2026-05-13 status that GaChatbot.Api is canonical and serves the public demo (lines 16-21), while later sections say the deployed page calls SignalR on GaApi and treat both as parallel-to-canonical (lines 215-223 and 262).
  2. GA’s CLAUDE.md says ix produces optick.index; in GA’s code and in its optic-k-rebuild skill, FretboardVoicingsCLI writes it. CLAUDE.md names GA.Business.Core.Harmony and GA.Business.Core.Fretboard as layer 3; the voicing generator and analyzer live in GA.Domain.Services.

Leads not checked by the course, noted earlier from reading GA’s documents (to verify): GaChatbotCli may fail to resolve its services; the backlog and the roadmap disagree on some statuses; GA’s documents give several sizes for the live index (161, 168, 175, 176 and 660 MB).

Most of the 22 differences of 2026-09-14 were fixed upstream by #689, merged 2026-09-23, which also carries the commits merged again as #686 and #688 on 2026-09-24. The code fixes come with tests in GA. This course hasn’t re-run them: it stays pinned to a826864 (to verify).

  • Fixed: 1 and 4 (voicing documents built from the chord root, the lowest note and the analyzer’s named fields), 2 (roots read as note names), 3 (named arguments), 5 (HIERARCHY and ATONAL_MODAL constants aligned with the partitions), 6 (RootVectorService’s summary), 8 (the 112/216/228 and v1.3.1 comments), 9 (MODAL slots looked up by the mode catalogue’s names), 11 (the chord filter compares root, quality and optional bass), the query half of 12 (the query’s interval-class vector is encoded), 13 (the diagram string order is explicit), 14 and 15 (the schema tool reads EmbeddingSchema), 16 (keyword fallback, and an “unavailable” answer instead of HTTP 500), 17, 18, 19, 21 and 22.
  • Unchanged: 7, which isn’t a bug; the other half of 12, FindSimilarVoicingsAsync, still returns an empty list, as its comment says; 20, listed by #688 as found and not changed. 10 wasn’t measured again, and fixes 5 and 9 touch dimensions it counted.

2026-09-28 — Lesson 5: the improvisation skill

Section titled “2026-09-28 — Lesson 5: the improvisation skill”
  • The request, on 2026-09-28: more lessons that help GA’s chatbot. The same day, a tracer run against the public chatbot had found that “which arpeggio fits Am F C G” gets F Ionian and G Ionian, filed as GA issue #744. Lesson 5 reproduces that finding offline, in the course’s CI, and extends it to single chords.
  • ImprovisationSkill.cs is identical at a826864 and on GA’s main at 8cd5042 (git diff --stat between the two is empty), so the pinned skill is still the one the chatbot runs. Whether the deployed router sends every prompt of the lesson to this skill is not checked by the course (to verify); the tracer saw it do so for Am F C G.
  • The progression path never calls the skill’s IMusicalQueryExtractor, the one dependency that needs a model. The course passes an extractor that throws, so a change in GA that made the path call a model would fail the run.
  • The oracle’s tables have to cover every scale name the skill can return; a missing name throws. One progression was run once and not kept in the program: Bb Gm Cm F, for the solution of the first exercise.
  • CI run 36425151971, for commit c2ab348: green on the three systems, 1 min 42 s on Linux, 2 min 4 s on macOS, 2 min 59 s on Windows, clone and build included.

2026-09-28 — Lesson 6: the answer on the wire

Section titled “2026-09-28 — Lesson 6: the answer on the wire”
  • The tracer run of 2026-09-28 saw the public page render the improvisation answer’s list as one bullet. #743 fixed the cause, SseChunker, the same day, and #746 recorded a second defect, in GaApi’s writer. Lesson 6 checks the whole wire offline, in the course’s CI: the real host’s stream, read by a reader written from the HTML standard, then two writers and three readers on the pinned and the fixed splits.
  • The program has copies of both writers and ports of two JavaScript readers, the page’s consumeSseStream and ga-client’s parseSseBuffer. The copy of GaChatbot.Api’s writer is checked against the host’s bytes on every run; GaApi’s writer and the two readers are not run in their own runtime, so a port that drifts from its source would go unnoticed.
  • GA builds its answers with StringBuilder.AppendLine, so /chat returns CRLF line breaks on Windows. The first run of l6 printed “no” where the text events and /chat differed only by CR, and counts that changed with the system. The program now compares with LF line breaks. The host’s stream is the same on every system: its writer strips CR.
  • The routing event’s trace holds timings that change on every run; the program prints the event cut at a comma.
  • ChatStreamAsync awaits the whole answer before the first event: the stream renders sentence by sentence, but the first sentence leaves when the last is known.
  • GaApi’s writer (#746) is latent at a826864: git grep finds no caller of sendChatMessageStream or streamChat in Apps/ga-client/src, and the React chat talks AG-UI, whose JSON events escape line breaks.
  • CI run 36509333842, for commit ed723b9: green on the three systems, 1 min 47 s on Linux, 1 min 13 s on macOS, 3 min 6 s on Windows, clone and build included.

2026-09-28 — Lesson 7: chord names the chatbot can’t read

Section titled “2026-09-28 — Lesson 7: chord names the chatbot can’t read”
  • The request: the next lesson, on #745. By the time it was prepared, pull request #749 had merged (2026-09-28, 23:32 UTC) and closed #745. Moving the course’s pin would change every lesson’s output, so lesson 7 compiles #749’s three files of GA.Business.ML next to the pinned assembly instead, in the project GaFix749, and reaches their ImprovisationSkill through the extern alias fix749. fetch-ga.sh takes the files out of the blobless clone with git show, and fetches the commit when an older clone lacks it.
  • #749 changed two files of GA.Business.ML, InvalidChordNames.cs, new, and ImprovisationSkill.cs; the third file the lesson compiles, ChordIntentMatching.cs, is the same at both commits. #749’s other changes are in ProductionOrchestrator.cs and in tests, so the skill and the guard run as they do on main; the orchestrator’s call to the guard is read, not run.
  • The first corpus had one Unicode case, B♭ E♭ F. Checking an exercise, normalizing ♭ and ♯ still left F# read as F: the plain ASCII sharp root is misread too, by the final \b of the skill’s expression. The corpus gained rows 10 to 12, with C+ and Bø7, the other symbols that end where \b can’t follow.
  • The solutions of exercises 1 and 2 were checked by editing the course program and #749’s file for one run; fetch-ga.sh restores the file once .ga-fix/SHA is deleted.
  • CI run 36518749257, for commit 226fbb0: green on the three systems, 1 min 28 s on Linux, 1 min 36 s on macOS, 3 min 20 s on Windows, clone, the fetch of #749’s files and build included.
  • Four GA issues, filed from the rows of lessons 5 to 7, each with the course’s run as its evidence:
    • #757: the skill reads chord symbols as other chords, or drops them without a word (rows 23 and 35 to 38);
    • #758: misclassified qualities, and arpeggios that add a note (rows 24 to 27);
    • #759: the requests #749’s guard lets through (rows 33 to 35);
    • #760: GaChatbot.Api’s page renders the stream’s error event as the answer, and keeps it in the history (row 29).
  • The code they cite is unchanged on main at fc76ad6 (2026-09-29), except for the interjection separators of #749’s guard (2d9c5e3), which don’t change its rows.

2026-09-29 — Lesson 8: the chatbot’s own exam

Section titled “2026-09-29 — Lesson 8: the chatbot’s own exam”
  • The request: more lessons that help GA’s chatbot. GA’s own oracle, the prompt corpus, hadn’t been run by the course. It is outside the sparse checkout, so fetch-ga.sh copies it with git show into .ga-files/.
  • The course’s host had been running without GA’s skills/ folder since lesson 4. SkillMdPlugin looks for it at the root of the git repository that holds the running program, here the course’s repository. fetch-ga.sh now checks it out, and the host sets SKILLMD_SKILLS_PATH; the outputs of lessons 1 to 7 are the same. Before the change, the seven diatonic prompts were flagged as a degraded backend by the wrapper’s text; after it, they fail as ordinary answers, which is row 42.
  • A key in the environment would have reached Anthropic: run with a fake ANTHROPIC_API_KEY, the host got AnthropicUnauthorizedException. The host now sets Anthropic:ApiKey to an empty string, and the output with the fake key is the same as without it.
  • The solutions of exercises 1 and 2 were checked by editing the course program for one run; 3 and 4 were worked by hand.
  • CI run 36584340462, for commit a305edf: green on the three systems, 1 min 58 s on Linux, 2 min 24 s on macOS, 6 min 41 s on Windows, clone, the checkout of skills/, the corpus fetch and build included.
  • Reported upstream the same day: #763 for rows 39, 41 and 44, #764 for rows 42 and 43, #765 for rows 40 and 45.

2026-09-29 — Lesson 9: every interval, every key

Section titled “2026-09-29 — Lesson 9: every interval, every key”
  • The request: more lessons that help GA’s chatbot. After the prompt corpus, the three skills that answer, without a model, questions with one right answer: intervals, scales and relative keys. The course’s oracle is a textbook’s letter arithmetic in Lesson9.cs.
  • The domain’s intervals were checked once against GA’s main at 53b7253, outside the course: GA.Core, GA.Domain.Core, GA.Business.Config and IntervalNaming.cs taken with git archive into a scratch folder and run on the 441 pairs with the notes read right. The 21 unisons and the 5 other wrong intervals come out the same, and so does C to B#, an augmented seventh it calls major.
  • The solution of exercise 1 was checked with .NET’s regex engine, and exercise 4 by asking its two questions for one run; 2 and 3 were worked by hand.
  • CI run 36666224042, for commit 73fd84d: green on the three systems, 1 min 52 s on Linux, 1 min 48 s on macOS, 6 min 38 s on Windows, clone and build included.
  • Reported upstream the next day: #767 for rows 46, 47, 49 and 53, #768 for row 48, #769 for rows 50 to 52.

2026-09-30 — Lesson 10: what the model is told to trust

Section titled “2026-09-30 — Lesson 10: what the model is told to trust”
  • The request: more lessons that help GA’s chatbot. After the skills that spell without a model, the three that hand the question to one and tell it to call a closure through ga_dsl_eval: transpose, diatonic chords and common tones. The course calls the tool itself, DslEvalMcpTools.EvalClosure, with the arguments of each SKILL.md; the model isn’t run.
  • DomainClosures.fs has changed on GA’s main at 53b7253, so the code the lesson calls was compared function by function between a826864 and main: the two arrays of names, preferFlat, transposeChord, diatonicChords, conventionalKeyName and the line where commonTones names a note are identical. ChordParser.fs has changed there for forms the lesson doesn’t send.
  • Lesson9.cs’s textbook helpers became internal so that lesson 10 reuses them; l9’s output is unchanged.
  • The solutions of the exercises were worked by hand from the code.
  • CI run 36668901083, for commit 6f91f88: green on the three systems, 2 min 01 s on Linux, 2 min 19 s on macOS, 6 min 29 s on Windows, clone and build included.
  • Reported upstream the same day: #770 for rows 54 to 57.

2026-09-30 — Lesson 11: the chords the key skill reads

Section titled “2026-09-30 — Lesson 11: the chords the key skill reads”
  • The request: more lessons that help GA’s chatbot. After the skills that call a closure, the one whose answer is computed before the model sees the question: key identification. Both chatbot surfaces, KeyIdentificationSkill and the SKILL.md through ga_key_identify, run KeyIdentificationService; the course calls its steps directly, and the model isn’t run.
  • Key detection has changed on GA’s main since the pin (#625, #729), after the music-theory-ga course tested GA’s MCP server tools, which take the chords alone. The lesson compiles main’s service at 6baf32e against the pinned domain (project GaKeysMain, extern alias keysmain) and asks the chatbot’s question, a sentence. On main at 5c3a52a, the service is unchanged since 6baf32e.
  • The pinned answers are checked against ga_key_identify itself, on all 99 questions. The service’s parser and the skill’s BuildPrompt are private; the program calls them by reflection.
  • The solution of exercise 1 was checked with .NET’s regex engine on the lesson’s 90 questions; the others were worked by hand from the code.
  • CI run 36758614173, for commit 8c2221d: green on the three systems, 1 min 54 s on Linux, 2 min 24 s on macOS, 6 min 41 s on Windows, clone and build included.
  • Reported upstream the same day: #771 for rows 58, 59 and 61, #772 for rows 60, 62, 63 and 64.

2026-09-30 — Lesson 12: what the progression skills answer

Section titled “2026-09-30 — Lesson 12: what the progression skills answer”
  • The request: more lessons that help GA’s chatbot, on progression analysis. The chatbot has no analysis skill: skills-dev/_pending-tools/progression-analysis/DRAFT.md is blocked on a tool, at the pin and on main. The lesson covers the two skills that take a progression, ProgressionMoodSkill and ProgressionCompletionSkill. The DSL’s domain.analyzeProgression, which GaMcpServer calls and no chatbot skill does, is left for a later lesson.
  • The mood skill is run through its intent in GaChatbot.Api, with no model. The completion skill’s prompt is read by calling its private BuildPrompt by reflection; for main, its lines are applied to the service at 6baf32e (project GaKeysMain of lesson 11), and for the pin they are checked against BuildPrompt on all 36 questions.
  • The solutions of the exercises were worked by hand from the code and the output.
  • CI run 36777958134, for commit e002d6d: green on the three systems, 2 min 05 s on Linux, 2 min 16 s on macOS, 7 min 24 s on Windows, clone and build included.
  • Reported upstream the same day: #773 for rows 65, 66 and 72, #774 for rows 68 to 72.

2026-09-30 — Lesson 13: what the progression analysis answers

Section titled “2026-09-30 — Lesson 13: what the progression analysis answers”
  • The request: more lessons that help GA’s chatbot. Lesson 12 left the DSL’s domain.analyzeProgression for later: the chatbot’s progression-analysis skill is a draft blocked on a tool, and the closure it would need already answers through ga_dsl_eval.
  • The pin’s closure is called through DslEvalMcpTools.EvalClosure, as a skill would call it. main’s is compiled from DomainClosures.fs at 6baf32e, unchanged, in an F# project, GaDslMain, against the pinned GA.Business.DSL and the service of GaKeysMain; fetch-ga.sh puts the file in .ga-dsl/, and the program reaches the assembly through the extern alias dslmain.
  • main’s chord parser changed after the pin, in #686 and 3707b72, for symbols the lesson doesn’t use; that comes from reading the diff, not from a run.
  • The solutions of the exercises were worked by hand from the code and the output.
  • CI run 36784699663, for commit 0ace36e: green on the three systems, 1 min 40 s on Linux, 1 min 25 s on macOS, 7 min 15 s on Windows, clone and build included.
  • Reported upstream the same day: #775 for rows 76 to 78.

2026-09-30 — Lesson 14: what the substitution skill answers

Section titled “2026-09-30 — Lesson 14: what the substitution skill answers”
  • The request: more lessons that help GA’s chatbot. skill.chordsubstitution answers without a model, so each of its answers can be checked against a textbook; its two MCP tools and the DSL’s domain.chordSubstitutions answer the same question for the SKILL.md path and for GaMcpServer.
  • The skill is reached as in lesson 12, through the intents of the chatbot’s host, and the tools are built with the host’s IGrothendieckService. The closure is called at the pin through DslEvalMcpTools.EvalClosure and on main through GaDslMain, as in lesson 13. The course’s projects and fetch-ga.sh are unchanged.
  • The program reads the skill’s answers back from their Markdown with regular expressions: a change of format would stop it rather than change a count. The textbook substitutes, two per quality, are the course’s choice, written in Lesson14.cs.
  • The solutions of exercises 1, 2 and 4 were worked by hand; exercise 3’s count was checked with .NET’s regular expressions, outside the course program.
  • CI run 36788871032, for commit b9aec40: green on the three systems, 2 min 11 s on Linux, 1 min 24 s on macOS, 7 min 16 s on Windows, clone and build included.
  • Reported upstream the same day: #777 for rows 79 and 81 to 84, #776 for row 80, #778 for rows 85 and 86.

2026-09-30 — Lesson 15: the notes of a chord

Section titled “2026-09-30 — Lesson 15: the notes of a chord”
  • The request: more lessons that help GA’s chatbot. skill.chordinfo answers the most basic question a guitarist asks it without a model, so each answer can be checked against a textbook; ga_chord_info answers it for the SKILL.md path from the same vocabulary.
  • The skill is reached as in lessons 12 and 14, through the intents of the chatbot’s host. The program reads what the skill understood from its evidence lines, Root:, Quality: and Notes:, and reads its 19 formulas for lists of notes by reflection: a change in either would show in the output. The course’s projects and fetch-ga.sh are unchanged.
  • The textbook spellings come from degrees written in Lesson15.cs. The round trip gives the skill its own spellings back and needs no oracle.
  • The solutions of the four exercises were checked by calling GA’s compiled skill, vocabulary and regular expressions from PowerShell, outside the course program.
  • CI run 36793871229, for commit 829eaea: green on the three systems, 2 min 19 s on Linux, 2 min 42 s on macOS, 7 min 12 s on Windows, clone and build included.
  • Reported upstream the next day: #782 for rows 87 to 89 and 91 to 93, #783 for rows 90, 94 and 95.

2026-10-01 — Lesson 16: the voicings of a chord

Section titled “2026-10-01 — Lesson 16: the voicings of a chord”
  • The request, on 2026-10-01: more lessons that help GA’s chatbot. A comment in GA calls ChordVoicingsSkill the chatbot’s most used skill. It answers without a model, and each voicing it returns can be checked by playing its diagram.
  • Most of the voicing pipeline changed on GA’s main after the pin; the reading of the question didn’t. The lesson therefore runs the skill twice: at the pin, through the chatbot’s host as in lesson 15, and in a new program, GaMain, built from a second blobless clone of GA at f4f5b4a, which fetch-ga.sh fetches. check.sh builds and runs it after the other lessons, so the CI clones and builds GA twice. Both programs compile the same questions, from code/ga-ai/Shared/ChordVoicingsProbe.cs.
  • GaMain builds the skill and lesson 3’s index with main’s classes, as the host’s wiring does; the host itself isn’t run on main.
  • The pin’s wrong voicings, from the substring filter of difference 11 and the diagram order of difference 13, are fixed on main: every voicing main’s skill returns plays the chord it read.
  • The solutions of the four exercises were checked by running GaMain’s skill and the pinned CanHandle outside the course’s expected output.
  • CI run 36870600319, for commit 6d647b1: green on the three systems, 3 min 14 s on Linux, 2 min 56 s on macOS, 8 min 40 s on Windows, clone and build included.
  • Reported upstream the same day: #785 for rows 96 to 98, #784 for rows 99 and 100.

2026-10-01 — Lesson 17: the capo and the tunings

Section titled “2026-10-01 — Lesson 17: the capo and the tunings”
  • The request, on 2026-10-01: more lessons that help GA’s chatbot. CapoSkill and AlternateTuningsSkill were built to close two “dealbreakers” of GA’s backlog. They answer without a model, and every answer can be checked against a textbook: the capo’s by semitone arithmetic and key signatures, the tunings’ by the notes of each string.
  • On main, CapoSkill only marks its refusal Declined, so the lesson asks it at the pin only. AlternateTuningsSkill’s name expressions changed, so GaMain gains a mode l17, which asks the tuning questions on main. Both programs compile them from code/ga-ai/Shared/TuningsProbe.cs, and check.sh runs GaMain l17 after GaMain l16.
  • GaMain builds the tunings skill with a logger alone, as its constructor takes nothing else.
  • A “theoretical spelling” is the course’s test: a major or minor key or chord whose root no key signature writes with that quality.
  • Lesson 7 and row 36 said that the capo and tuning skills accept ♭ and ♯. Their expressions list both signs, but often drop them (rows 103 and 106); the sentence of lesson 7 and row 36 now say so.
  • The solutions of the four exercises were checked by running the pinned skills outside the course’s expected output.
  • CI run 36944575536, for commit 5bbae97: green on the three systems, 2 min 45 s on Linux, 3 min 49 s on macOS, 8 min 34 s on Windows, clone and build included.
  • Reported upstream after the CI run: #787 for rows 101 to 105, #788 for rows 101, 106 and 107.
  • The request, on 2026-10-01: more lessons that help GA’s chatbot. VoiceLeadingSkill was built on 2026-05-14, like the capo and tuning skills, to close a “dealbreaker” of GA’s backlog. It answers without a model, and every answer claims to be optimal, which a slower exhaustive search can check.
  • VoiceLeadingSkill.cs is the same on main, so the lesson asks it at the pin only, and GaMain gains no mode.
  • The “least motion” is the course’s test: as many voices as the larger chord, every note of both chords sounded, and any note of the smaller chord doubled.
  • Lesson17.SkillOf is now internal, so that Lesson18 reads the skill of an intent the same way.
  • The solutions of the four exercises were checked by running the pinned skill outside the course’s expected output.
  • CI run 36957355913, for commit eadae1f: green on the three systems, 2 min 11 s on Linux, 4 min 43 s on macOS, 8 min 32 s on Windows, clone and build included.
  • Reported upstream as GA issue #789, for rows 108 to 113.

2026-10-02 — Lesson 19: voice-leading pairs

Section titled “2026-10-02 — Lesson 19: voice-leading pairs”
  • The request, on 2026-10-01: a lesson on ga_voice_leading_pair, the path that lesson 18 left aside. The tool is in GaMcpServer, which agents call; the chatbot doesn’t call it, and its parked voice-leading skill would.
  • GaMcpServer is outside the projects the course builds. fetch-ga.sh adds GaMcpServer/Tools/CompositionTools.cs to both sparse clones, both programs compile it, and Shared/VoicingSearchTool.cs stands in for the class whose private field Strategy the tool reads by reflection.
  • CompositionTools.cs is the same on main, but the search it calls isn’t. So GaMain gains a mode, l19, that writes lesson 3’s index with main’s code and asks the same questions.
  • At the pin, 80 of the 210 candidates are exact and 3 of the 10 first pairs play both chords; on main, 210 and 10. That gets no row: on main, the search returns only voicings of the chord asked, for these 14 chords.
  • “no fifth” is the course’s verdict for a seventh chord that lacks only its perfect fifth, and counts as right, like “exact”.
  • Lesson16.Corpus is now internal, so that Lesson19 counts the exact voicings of each chord in the corpus.
  • The solution of the fourth exercise was checked by running the pinned tool outside the course’s expected output; the three others read the expected output.
  • CI run 36964238674, for commit e8394cd: green on the three systems, 3 min 17 s on Linux, 2 min 29 s on macOS, 8 min 42 s on Windows, clone and build included.
  • Reported upstream as GA issue #791, for rows 114 to 117.

2026-10-02 — Lesson 20: generated progressions

Section titled “2026-10-02 — Lesson 20: generated progressions”
  • The request, on 2026-10-02: ga_generate_progression for lesson 20, the companion of lesson 19’s tool in CompositionTools.cs, which both programs already compile.
  • The textbook spelling is the course’s: the root’s letter follows the Roman numeral’s degree in the key, for the 15 key signatures of each mode.
  • The program reads the transposition tables of ChordProgressions.yaml line by line, from the pinned clone.
  • GaMain gains a mode, l20, that only stitches: the tool and its parser are unchanged on main, the search isn’t.
  • The joined path is the course’s search over the same 15 candidates per chord, with the tool’s distance: one voicing per chord, chosen by dynamic programming.
  • The solution of the fourth exercise was checked by running the pinned tool outside the course’s expected output; the three others read the expected output and the tool’s code.
  • CI run 37045152172, for commit 2db1270: green on the three systems, 3 min 14 s on Linux, 4 min 23 s on macOS, 9 min 46 s on Windows, clone and build included.
  • Reported upstream as GA issue #792, for rows 118 to 122.
Section titled “2026-10-02 — Lesson 21: the voicing search”
  • The request, on 2026-10-02: ga_search_voicings for lesson 21, the tool the note of ga_generate_progression names for each chord.
  • The programs of lessons 19 and 20 now compile GA’s VoicingSearchTool.cs in place of the course’s stand-in, with the same output. Shared/SearchIndex.cs points it at the course’s index through GA_OPTICK_INDEX_PATH and switches its telemetry off through GA_VOICING_NO_TELEMETRY, so the course writes nothing under GA’s state/.
  • A rootless voicing is the course’s test: three notes or more, all of them the chord’s, none of them its root.
  • “The same answer” compares the ten voicings and their scores; the table of modes compares the voicings only.
  • The solution of the second exercise is arithmetic on the printed scores; the three others read the expected output and the tools’ code.
  • CI run 37070292367, for commit 92e8d13: green on the three systems, 3 min 51 s on Linux, 4 min 23 s on macOS, 9 min 3 s on Windows, clone and build included.
  • Reported upstream as GA issue #795, for rows 123 to 127.
  • The request, on 2026-10-03: IcvNeighborsSkill for lesson 22, the chatbot’s similar-chords skill, with the parked skill for ga_icv_neighbors.
  • The program calls ExecuteAsync and reads the answer’s header, its vector line and its rows; the table of chords calls TryBuildPcSet, which is private, through reflection.
  • The notes of each chord in that table are the course’s, a textbook’s for each symbol, spelled with sharps as GA prints them.
  • The routing hints come from DefaultRoutingHintProvider.GetDeltas; the router itself isn’t run, for it needs the embeddings.
  • The solutions read the expected output and the skill’s code; the second is arithmetic on two printed vectors.
  • CI run 37143011796, for commit 627a3ca: green on the three systems, 3 min 56 s on Linux, 3 min 59 s on macOS, 8 min 54 s on Windows, clone and build included.
  • Reported upstream as GA issue #798, for rows 128 to 131.

2026-10-03 — Lesson 23: harmonic distance and path

Section titled “2026-10-03 — Lesson 23: harmonic distance and path”
  • The request, on 2026-10-03: GrothendieckDeltaSkill and IcvShortestPathSkill for lesson 23, the two skills next to lesson 22’s that take a pair of chords.
  • The program asks each question once and keeps the answer, since a question that finds no path costs the path skill a search of hundreds of sets.
  • The count of sets expanded before “No path” comes from the course’s own search, which follows FindShortestPath’s rule; the skill doesn’t print it, and the program doesn’t time the skills.
  • A second read of the pinned code by another model, Augment’s auggie CLI with gpt-6-sol and read-only tools, estimated the same 168 and 462 expansions and read the step count the same way.
  • The solutions read the expected output and the skills’ code.
  • CI run 37147833299, for commit 12b105a: green on the three systems, 4 min 14 s on Linux, 6 min 9 s on macOS, 12 min 28 s on Windows, clone and build included.
  • Reported upstream as GA issue #802, for rows 132 to 135.

2026-10-03 — Lesson 24: set classes and the Grothendieck parser

Section titled “2026-10-03 — Lesson 24: set classes and the Grothendieck parser”
  • The request, on 2026-10-03: SetTheoryEquivalenceSkill and GrothendieckParseSkill for lesson 24, two of the chatbot skills no lesson had asked yet.
  • The program checks every verdict against its own tests for transposition and inversion, on 12-bit masks, for every ordered pair of three-note and of four-note sets.
  • The prime forms are compared with GA’s own table of Forte’s catalog, not with Forte’s book.
  • A second read of the pinned code by another model, Augment’s auggie CLI with gpt-6-sol and read-only tools, read the relation pattern, the parser’s ws1 forms, the grouping of line 426, the order that makes P(C) a functor application and the chord qualities the same way.
  • The solutions read the expected output and the code.
  • CI run 37166154652, for commit 922ae7e: green on the three systems, 7 min 39 s on Linux, 6 min 7 s on macOS, 14 min 9 s on Windows, clone and build included.
  • Reported upstream as GA issue #806, for rows 136 to 140.

2026-10-04 — Lesson 25: what reaches the transpose skill

Section titled “2026-10-04 — Lesson 25: what reaches the transpose skill”
  • The request, on 2026-10-04: TransposeSkill for lesson 25. Lesson 10 had called its closure, so this lesson asks which questions reach it.
  • GaMain now builds and starts main’s chatbot host: fetch-ga.sh adds the host’s projects and skills/ to the second clone, and MainChatHost sets the host up as ChatHost does, with lesson 3’s index built with main’s code.
  • The phrasings are the skill’s 13 example prompts, one phrasing of its SKILL.md, GA’s four corpus prompts and eight of the course’s own.
  • Called directly, TransposeSkill answers with SkillMdDrivenSkill’s error message, and its evidence calls the answer LLM-only: the behaviour of rows 42 and 43, reported as #764.
  • The solutions read the expected output and the code.
  • Reported upstream as GA issue #809, for rows 141 to 144.
  • CI run 37182332132, for commit 33daa24: green on the three systems, 8 min 22 s on Linux, 8 min 7 s on macOS, 24 min 49 s on Windows, clone and build included.

2026-10-04 — Lesson 26: the modes catalog

Section titled “2026-10-04 — Lesson 26: the modes catalog”
  • The request, on 2026-10-04: ModesSkill for lesson 26. Lesson 8 had met its formulas on two scales, reported as #765.
  • The skill needs no model and no host: the program builds it with a null logger, calls it directly, and reads the catalog through the skill’s own GetTonalModalFamilies.
  • A textbook’s first mode of each family is the course’s, in Lesson26.cs; every other mode is checked against its own family’s first mode.
  • The solutions read the expected output and the code.
  • CI run 37215721850, for commit 16dbeff: green on the three systems, 6 min 4 s on Linux, 8 min 18 s on macOS, 25 min 3 s on Windows, clone and build included.
  • Reported upstream as GA issue #813, for rows 145, 146, 148 and 149; row 147’s counts were added to #765 in a comment.

2026-10-04 — Lesson 27: notes that sound outside

Section titled “2026-10-04 — Lesson 27: notes that sound outside”
  • The request, on 2026-10-04: OutsideNotesSkill for lesson 27.
  • The skill needs no model and no host: the program builds it with a null logger, calls it directly, and calls its private Classify by reflection for the rule.
  • The usual chord scales of the five seventh chords are the course’s, in Lesson27.cs.
  • The solutions read the expected output and the code.
  • Reported upstream as GA issue #814, for rows 150 to 154.
  • CI run 37221169417, for commit 49e7e1a: green on the three systems, 8 min 3 s on Linux, 10 min 6 s on macOS, 23 min 49 s on Windows, clone and build included.
  • Whether the deployed chatbot routes every prompt of lesson 5 to ImprovisationSkill; the tracer of 2026-09-28 saw it do so for Am F C G.
  • The Berklee names of lesson 5, Mixolydian ♭13 for V7/II and Mixolydian ♭9 ♭13 for a dominant in minor, and F Dorian for a borrowed iv, against Nettles and Graf, The Chord Scale Theory & Jazz Harmony, which the course hasn’t read.
  • The solutions of lesson 5’s exercises 2 and 3, not compiled against GA.
  • Whether the public chatbot runs a build that has #749, and what a model answers to lesson 7’s rows 5, 6, 13 and 14.
  • The solution of lesson 7’s exercise 4, not compiled against GA.
  • Whether GA’s daily corpus runs, which have an Ollama endpoint and no Anthropic key, score the seven diatonic prompts as six failures and one pass; the snapshots of 2026-09-25 to 2026-09-29 are marked degraded and don’t list failures by prompt.
  • The solution of lesson 8’s exercise 4, a formula derived from each note’s letter, not compiled against GA.
  • What the chatbot serves for the eight example prompts IntervalSkill can’t read, which needs the intent router and its embedding model.
  • The solutions of lesson 9’s exercises 1 and 3, not compiled against GA, and what exercise 2’s change would print for a diminished unison.
  • What the model sends and answers for lesson 10’s questions, and whether it re-spells a closure’s answer despite its SKILL.md.
  • The solutions of lesson 10’s exercises 1 and 2, not compiled against GA.
  • What the model says for lesson 11’s questions when four keys are “all tied”, or when a sharp has been dropped.
  • What Identify answers for the 16 questions of lesson 11 once their sharps are read, as in the solution of exercise 1.
  • What the model suggests from lesson 12’s lists, and whether it follows the prompt’s V7 rule or the list; what a model does with the mood SKILL.md’s rule for brightening.
  • The solutions of lesson 12’s exercises, not compiled against GA.
  • What a model writes from the three lines of domain.analyzeProgression, and whether it calls the closure through ga_dsl_eval for a question no skill takes.
  • The solutions of lesson 13’s exercises, not compiled against GA.
  • Which intent the semantic router picks for each of lesson 14’s example prompts, and what a model writes from ga_chord_substitutions and ga_chord_compare.
  • The solutions of lesson 14’s exercises, not compiled against GA.
  • Which intent the semantic router picks for lesson 15’s questions, “I am learning Cmaj7” among them, and what a model writes from ga_chord_info.
  • Which voicings the production index returns for lesson 16’s prompts, which intent the router picks for them, and what a model reads in “voicings for am”.
  • Which intent the router picks, with embeddings, for lesson 17’s capo and tuning questions, the phrasings that the skills misread among them.
  • Which intent the router picks, with embeddings, for lesson 18’s voice-leading questions, the two example prompts that say “voicing” among them.
  • Which candidates and pairs ga_voice_leading_pair returns on the production index, with more voicings than lesson 3’s corpus.
  • Which templates and roots agents pass to ga_generate_progression in practice: the course tries the roots a guitarist could write, not logged calls.
  • How ga_search_voicings answers on GA’s production index: the course’s index is lesson 3’s, and the skill’s warning about style tags may hold there.
  • Which questions the router sends IcvNeighborsSkill with the embeddings, and how often users word them like its example prompts rather than like its patterns.
  • How long the path skill takes in the deployed chatbot for two chords of different sizes: the course counts the sets the search expands, not the time.
  • Whether the deployed chatbot’s router sends a question about set equivalence to the set skill or to the transpose skill, which two of its example prompts boost: the course doesn’t run the router.
  • Which skill the deployed chatbot’s router picks, with its embeddings, for the transpositions that get no transpose hint or another intent’s hint: the course doesn’t compute the embeddings.
  • Whether the deployed chatbot’s router sends “Tell me about Hijaz” or “List unnamed modal families” to ModesSkill: the course doesn’t run the router.
  • Whether the deployed chatbot’s router sends “why does F sound outside over a minor chord” or “what is F over G7” to OutsideNotesSkill: the course doesn’t run the router.
  • How GaChatbot.Api’s page shows an error event in a browser: lesson 6 runs a C# port of its reader, not the page. The same for the solution of lesson 6’s exercise 3.
  • Which key-inference rule should GA’s improvisation skill use? #744 leaves it open: the count of chord tones outside each major scale used by lesson 5, or GA’s existing key-identification services.
  • Should GA’s streams mark an error as such, with an event: error field or a type in the JSON, and end with [DONE] or its own terminal event? At a826864, each client has to guess from the JSON’s shape.
  • Should GA’s guard also decline lowercase names and unknown qualities, at the price of the false positives of lesson 7’s exercise 3, or should a request that mixes valid and invalid chords get an answer that names the invalid ones?
  • Should the corpus check a chord’s or a scale’s notes as a set, with a structured invariant, rather than as strings? Lesson 8’s stricter reading of the same strings still accepts 69 foreign answers.
  • Should each skill’s example prompts run as a test against the skill itself? The three skills of lesson 9 can’t answer 11 of their 49.
  • Should GA’s closures spell notes with the domain’s Key, as ScaleInfoSkill does, rather than with arrays of twelve names? And should transposeChord take an interval or a target key instead of a number of semitones?
  • Should the key tool hand the model Identify’s ranking with its score, rather than the keys grouped by count? And should keys with the same pitch classes be told apart by the question’s spelling?
  • Should a minor key’s list hold the harmonic minor’s V and V7, so that the completion skill’s catalog of cadences can be built from it? And should a skill’s fixed text be generated from its SKILL.md, so the two can’t drift apart?
  • Should GA’s key identification weigh the chord a progression opens and closes on before the count of diatonic chords, and should its numerals mark a chromatic root, bVI, bVII or a minor key’s vii°, instead of ??
  • Should GA’s substitution skill rank the substitutes of one chord by common tones and function, as domain.chordSubstitutions does in a key, and keep the ICV distance for set-class questions? And should GrothendieckDelta return 0 for equal vectors, leaving the callers that must tell two sets apart to compare the sets?
  • Should GA’s chord skills share one reader of chord symbols, built on ChordVocabulary, that reads a symbol whole or declines it? At a826864, the improvisation, substitution and chord-info skills and ga_chord_info each have their own expression, and each reads part of what the others read.
  • Should GA’s voicing index store a name the filter can find, one per set of notes and root, or should the filter compare pitch classes, as the course’s check does? And should ChordVoicingsSkill say in its answer which filters the index dropped, or read the techniques from the diagram, as the comfort filters are?
  • Should GA’s capo and tuning skills read a key or a note with one parser that takes the accidental with the letter, rather than each with its own expression? Should CapoSkill spell its answers as key signatures do and answer where to put the capo? And should both skills accept, in CanHandle, the questions they can read, so that the offline fallback reaches them?
  • Should GA’s VoiceLeadingSkill choose the doubling as well as the pairing, or say that it doubles the root, rather than claim that every other voicing moves more? Should it read chord symbols with one parser that takes M7, Δ, ø and a final accidental, spell the notes from the chord’s root, and accept, in CanHandle, the questions it can read?
  • Should GA’s ga_voice_leading_pair check that each voicing plays the chord asked, pair the nearest notes when the sizes differ, and return its diagrams in chart order? Should the parked voice-leading skill be rewritten for the tool that exists, or the tool given the contract the draft expects?
  • Should GA’s ga_generate_progression spell from the key signature with seven letters, take a key with its mode, bound length, and answer a progression of voicings rather than suggest stitching pairs? Should the parked progression skill be rewritten for the templates the tool has?
  • Should GA’s ga_search_voicings encode the mode or stop promising it, count a mode alone as no query, filter on a tag rather than add a bit, and match words whole? Should the voicing-search skill’s scores, gotchas and examples be rewritten from the tool’s output, and the parked voicing-search skill for ga_search_voicings?
  • Should GA’s IcvNeighborsSkill read the words of its own example prompts, build its chords with GA’s chord parser rather than a table that falls back to a major triad, list each set class once with a name, and rank its neighbors by something that differs between them? Should the parked icv-neighbors skill be rewritten for ga_icv_neighbors as it is?
  • Should GA’s GrothendieckDeltaSkill report a zero delta as zero, GrothendieckDelta.Explain lose its garbled arrow, and IcvShortestPathSkill refuse chords of different sizes before searching, count moves rather than sets, and name the sets it passes? Should both skills read the words of their own example prompts and share one chord parser?
  • Should GA’s SetTheoryEquivalenceSkill read the relation in its own example prompts’ wording, refuse a set it can’t read whole, and say “via either operation” only of sets symmetric under inversion? Should GrothendieckOperationsParser drop the ws1 after str, try its alternatives with attempt, and read the usual chord symbols, and should GrothendieckParseSkill say something about the chords it parses?
  • Should GA keep a transpose hint that misses 3 of the skill’s own example prompts and fires on set theory, functors and “move from X to Y”? Without embeddings, should a transposition get a message that it can’t be answered rather than the voicing agent, which wins the agent router’s keywords by default?
  • Should GA check Modes.yaml against the scales it names, mode by mode, quote its names, give each mode one name, and work out a formula by degree for seven notes and by letter otherwise? Should ModesSkill say when it doesn’t recognize a question rather than list the major scale’s modes?
  • Should OutsideNotesSkill classify a note against the chord’s usual scales rather than its tones alone, give the b9 and b13 of a dominant chord a tension’s headline, leave the 11 out of 13th chords, read the chord with NormalizeQuality’s own vocabulary, and spell the chord from its letters?