Journal
Sections
Reading settings
No voice for this language in this browser.
More voices
Code and English terms
Off: the page’s voice reads everything, with no pause between two voices.
No voice for this language in this browser.
More voices
More voices: Microsoft Edge has natural online voices, and Windows (Settings › Time & language › Speech), macOS and Android install others.
Progress
Section titled “Progress”- Course program: .NET 10, referencing
GA.Business.ML,FretboardVoicingsCLIandGaChatbot.Apiat 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 |
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 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 |
2026-09-14 — Why this course
Section titled “2026-09-14 — Why this course”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.shmakes a blobless, sparse clone incode/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 truebefore the checkout fixes it. As in the music theory course,MSYS_NO_PATHCONV=1stops Git Bash from rewriting the sparse patterns. - The course project references
GaChatbot.Apidirectly and starts it withWebApplicationFactory<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 generatedProgramclass 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 ownMemoryStoreandChatTranscriptStoreon a fresh folder next to the program;~/.gawas 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.
2026-09-14 — CI
Section titled “2026-09-14 — CI”- 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:
IntentEmbeddingWarmupServicestarts the embedding of the intents in the background when the host starts, and its warnings land in whichever request is running. Fix in commit8e335d7: 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):
VoicingDocumentFactorysetsRootPitchClassandMidiBassNotefromMidiNotes[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 usesMidiNotes.Min(), the real bass. Shown byl2, “From a chord shape to GA’s voicing document” (open C: root E, inversion 1), and byl3’s Cmaj7 search.- 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, getsInversion = -1. Reproduced with a separate program referencingGA.Business.ML, not part of the course’s CI:PitchClass.Parse("E") = 11, and022000 Em ChordId.RootPitchClass="E" doc.RootPitchClass=4 inversion=-1. The music theory course found the same parser behaviour. VoicingHarmonicAnalyzerpassesintervalSpread > 12intoIsRootlessandfalseintoIsOpenVoicingof the positional recordVoicingCharacteristics(lines 31-43). Every voicing spanning more than an octave is “rootless”. Shown byl2, “VoicingCharacteristics and PerceptualQualities”.VoicingAnalyzerbuildsnew PerceptualQualities(curVoiceChars.Consonance, 0, 0, "Neutral", "Medium")for a record declared(Brightness, ConsonanceScore, Roughness, ...)(line 164).ConsonanceScoreis always 0, the document’sConsonancetoo, and CONTEXT’s tension1.0 - doc.Consonanceis always 1. This is the constant dimension of issue #616, whose analysis blames only the literals next to it. Same section ofl2, and the CONTEXT row ofl3’s “Dimensions that never vary”.EmbeddingSchema.AtonalModalDimis 17 while the registry gives ATONAL_MODAL 64 slots;ModalVectorServiceallocates 17 (line 116) andWriteIntodoesn’t check lengths: 47 slots are always zero (l3).HierarchyDimis 8 against 15 (l2, “Loose constants”). Both partitions have weight 0, so search is not affected.- STRUCTURE is not invariant to transposition (221 of 222 set classes,
l2), as GA’s own sweep andTheoryVectorService’s comment say;RootVectorService’s summary still claims “genuinely O+P+T+I-invariant” (lines 3-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. - Versions in comments and documents: the newest schema document is v1.4.1,
OPTIC-K_Embedding_Schema.mddescribes v1.3.1 (109 dimensions),MusicalEmbeddingGeneratorsays 228 (line 13) and 216 (line 55),CompactDimension’s comment says 112,OptickIndexWriterandMusicalQueryEncodersay 112,ExtensionsEnd’s comment says it equalsTotalDimension. The code computes 240 and 124.
Index and search (lesson 3):
- 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.
ModalVectorServicelooks 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). - 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. ApplyFiltersmatches the quality as a substring of the stored name: theAm7filter acceptsGbm7(shell)/A, and the empty quality ofCaccepts any name with C in the bass, includingC + E (Major 3rd)andAm/C(lines 296-329).OptickSearchStrategy.FindSimilarVoicingsAsyncalways returns an empty list (known, commented at lines 75-92).MusicalQueryEncoderleaves the interval-class vector out of queries (known, commented).
Diagrams and MCP tools:
- Two orders for the same diagram string:
VoicingGenerator, the index and the MCP tool’s parser read string 1 (high E) first;PlayableNotationFormatterreads the low E first (lines 31-53). The chatbot’s tablature for3-0-x-2-3-x(Cmaj7) is6/3 5/0 3/2 2/3, the notes G A A D (l4). 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.ga_get_embedding_schemalists ten partitions, without ROOT, with the looseHierarchyDimandAtonalModalDim(lines 69-91). Read in the code, not run by the course.
Chatbot (lessons 1 and 4):
- Without an embedding server, every message that no guard catches ends in HTTP 500.
SemanticRouter.RouteAsyncdoesn’t catch the exception ofEnsureEmbeddingsInitializedAsync(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 althoughskill.relativekeyanswers it with confidence 1.00 (l4). - The legacy
CanHandlepath sends that same question toScaleInfo, not to the relative-key skill (l4). skill.fretspanhas no example prompt, andSemanticIntentRouterskips intents without examples: it can never be routed to (l1).ProductionOrchestrator.AnswerStreamingAsync(from line 154) has the voicing guard but not the algebra fast path ofAnswerAsync. Read in the code, not run.IntentEmbeddingWarmupServicestarts its work fire-and-forget; fine for a server, but a host’s logs then depend on timing (the CI failure above).
Documents:
docs/architecture/chat-surfaces.mdsays in its 2026-05-13 status thatGaChatbot.Apiis 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).- GA’s
CLAUDE.mdsays ix producesoptick.index; in GA’s code and in itsoptic-k-rebuildskill,FretboardVoicingsCLIwrites it.CLAUDE.mdnamesGA.Business.Core.HarmonyandGA.Business.Core.Fretboardas layer 3; the voicing generator and analyzer live inGA.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).
2026-09-24 — Upstream fixes
Section titled “2026-09-24 — Upstream fixes”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 readsEmbeddingSchema), 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.csis identical ata826864and on GA’smainat8cd5042(git diff --statbetween 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
consumeSseStreamand ga-client’sparseSseBuffer. 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/chatreturns CRLF line breaks on Windows. The first run ofl6printed “no” where the text events and/chatdiffered 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.
ChatStreamAsyncawaits 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 grepfinds no caller ofsendChatMessageStreamorstreamChatinApps/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.MLnext to the pinned assembly instead, in the projectGaFix749, and reaches theirImprovisationSkillthrough the extern aliasfix749.fetch-ga.shtakes the files out of the blobless clone withgit show, and fetches the commit when an older clone lacks it. - #749 changed two files of
GA.Business.ML,InvalidChordNames.cs, new, andImprovisationSkill.cs; the third file the lesson compiles,ChordIntentMatching.cs, is the same at both commits. #749’s other changes are inProductionOrchestrator.csand in tests, so the skill and the guard run as they do onmain; 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 leftF#read as F: the plain ASCII sharp root is misread too, by the final\bof the skill’s expression. The corpus gained rows 10 to 12, withC+andBø7, the other symbols that end where\bcan’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.shrestores the file once.ga-fix/SHAis 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.
2026-09-29 — Reported upstream
Section titled “2026-09-29 — Reported upstream”- 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
mainatfc76ad6(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.shcopies it withgit showinto.ga-files/. - The course’s host had been running without GA’s
skills/folder since lesson 4.SkillMdPluginlooks for it at the root of the git repository that holds the running program, here the course’s repository.fetch-ga.shnow checks it out, and the host setsSKILLMD_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 gotAnthropicUnauthorizedException. The host now setsAnthropic:ApiKeyto 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 ofskills/, 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
mainat53b7253, outside the course:GA.Core,GA.Domain.Core,GA.Business.ConfigandIntervalNaming.cstaken withgit archiveinto 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.fshas changed on GA’smainat53b7253, so the code the lesson calls was compared function by function betweena826864andmain: the two arrays of names,preferFlat,transposeChord,diatonicChords,conventionalKeyNameand the line wherecommonTonesnames a note are identical.ChordParser.fshas changed there for forms the lesson doesn’t send.Lesson9.cs’s textbook helpers becameinternalso 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,
KeyIdentificationSkilland the SKILL.md throughga_key_identify, runKeyIdentificationService; the course calls its steps directly, and the model isn’t run. - Key detection has changed on GA’s
mainsince the pin (#625, #729), after the music-theory-ga course tested GA’s MCP server tools, which take the chords alone. The lesson compilesmain’s service at6baf32eagainst the pinned domain (projectGaKeysMain, extern aliaskeysmain) and asks the chatbot’s question, a sentence. Onmainat5c3a52a, the service is unchanged since6baf32e. - The pinned answers are checked against
ga_key_identifyitself, on all 99 questions. The service’s parser and the skill’sBuildPromptare 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.mdis blocked on a tool, at the pin and onmain. The lesson covers the two skills that take a progression,ProgressionMoodSkillandProgressionCompletionSkill. The DSL’sdomain.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
BuildPromptby reflection; formain, its lines are applied to the service at6baf32e(projectGaKeysMainof lesson 11), and for the pin they are checked againstBuildPrompton 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.analyzeProgressionfor later: the chatbot’s progression-analysis skill is a draft blocked on a tool, and the closure it would need already answers throughga_dsl_eval. - The pin’s closure is called through
DslEvalMcpTools.EvalClosure, as a skill would call it.main’s is compiled fromDomainClosures.fsat6baf32e, unchanged, in an F# project,GaDslMain, against the pinnedGA.Business.DSLand the service ofGaKeysMain;fetch-ga.shputs the file in.ga-dsl/, and the program reaches the assembly through the extern aliasdslmain. main’s chord parser changed after the pin, in #686 and3707b72, 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.chordsubstitutionanswers without a model, so each of its answers can be checked against a textbook; its two MCP tools and the DSL’sdomain.chordSubstitutionsanswer 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 throughDslEvalMcpTools.EvalClosureand onmainthroughGaDslMain, as in lesson 13. The course’s projects andfetch-ga.share 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.chordinfoanswers the most basic question a guitarist asks it without a model, so each answer can be checked against a textbook;ga_chord_infoanswers 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:andNotes:, and reads its 19 formulas for lists of notes by reflection: a change in either would show in the output. The course’s projects andfetch-ga.share 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
ChordVoicingsSkillthe 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
mainafter 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 atf4f5b4a, whichfetch-ga.shfetches.check.shbuilds and runs it after the other lessons, so the CI clones and builds GA twice. Both programs compile the same questions, fromcode/ga-ai/Shared/ChordVoicingsProbe.cs. GaMainbuilds the skill and lesson 3’s index withmain’s classes, as the host’s wiring does; the host itself isn’t run onmain.- The pin’s wrong voicings, from the substring filter of difference 11 and the diagram order of difference 13, are fixed on
main: every voicingmain’s skill returns plays the chord it read. - The solutions of the four exercises were checked by running
GaMain’s skill and the pinnedCanHandleoutside 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.
CapoSkillandAlternateTuningsSkillwere 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,CapoSkillonly marks its refusalDeclined, so the lesson asks it at the pin only.AlternateTuningsSkill’s name expressions changed, soGaMaingains a model17, which asks the tuning questions onmain. Both programs compile them fromcode/ga-ai/Shared/TuningsProbe.cs, andcheck.shrunsGaMain l17afterGaMain l16. GaMainbuilds 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.
2026-10-01 — Lesson 18: voice leading
Section titled “2026-10-01 — Lesson 18: voice leading”- The request, on 2026-10-01: more lessons that help GA’s chatbot.
VoiceLeadingSkillwas 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.csis the same onmain, so the lesson asks it at the pin only, andGaMaingains 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.SkillOfis nowinternal, so thatLesson18reads 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 inGaMcpServer, which agents call; the chatbot doesn’t call it, and its parked voice-leading skill would. GaMcpServeris outside the projects the course builds.fetch-ga.shaddsGaMcpServer/Tools/CompositionTools.csto both sparse clones, both programs compile it, andShared/VoicingSearchTool.csstands in for the class whose private fieldStrategythe tool reads by reflection.CompositionTools.csis the same onmain, but the search it calls isn’t. SoGaMaingains a mode,l19, that writes lesson 3’s index withmain’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: onmain, 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.Corpusis nowinternal, so thatLesson19counts 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_progressionfor lesson 20, the companion of lesson 19’s tool inCompositionTools.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.yamlline by line, from the pinned clone. GaMaingains a mode,l20, that only stitches: the tool and its parser are unchanged onmain, 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.
2026-10-02 — Lesson 21: the voicing search
Section titled “2026-10-02 — Lesson 21: the voicing search”- The request, on 2026-10-02:
ga_search_voicingsfor lesson 21, the tool the note ofga_generate_progressionnames for each chord. - The programs of lessons 19 and 20 now compile GA’s
VoicingSearchTool.csin place of the course’s stand-in, with the same output.Shared/SearchIndex.cspoints it at the course’s index throughGA_OPTICK_INDEX_PATHand switches its telemetry off throughGA_VOICING_NO_TELEMETRY, so the course writes nothing under GA’sstate/. - 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.
2026-10-03 — Lesson 22: similar chords
Section titled “2026-10-03 — Lesson 22: similar chords”- The request, on 2026-10-03:
IcvNeighborsSkillfor lesson 22, the chatbot’s similar-chords skill, with the parked skill forga_icv_neighbors. - The program calls
ExecuteAsyncand reads the answer’s header, its vector line and its rows; the table of chords callsTryBuildPcSet, 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:
GrothendieckDeltaSkillandIcvShortestPathSkillfor 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
auggieCLI withgpt-6-soland 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:
SetTheoryEquivalenceSkillandGrothendieckParseSkillfor 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
auggieCLI withgpt-6-soland read-only tools, read the relation pattern, the parser’sws1forms, the grouping of line 426, the order that makesP(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:
TransposeSkillfor lesson 25. Lesson 10 had called its closure, so this lesson asks which questions reach it. GaMainnow builds and startsmain’s chatbot host:fetch-ga.shadds the host’s projects andskills/to the second clone, andMainChatHostsets the host up asChatHostdoes, with lesson 3’s index built withmain’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,
TransposeSkillanswers withSkillMdDrivenSkill’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:
ModesSkillfor 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:
OutsideNotesSkillfor 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
Classifyby 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.
To verify
Section titled “To verify”- 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
IntervalSkillcan’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
Identifyanswers 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 throughga_dsl_evalfor 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_substitutionsandga_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_pairreturns on the production index, with more voicings than lesson 3’s corpus. - Which templates and roots agents pass to
ga_generate_progressionin practice: the course tries the roots a guitarist could write, not logged calls. - How
ga_search_voicingsanswers 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
IcvNeighborsSkillwith 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.
Open questions
Section titled “Open questions”- 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: errorfield or atypein the JSON, and end with[DONE]or its own terminal event? Ata826864, 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, asScaleInfoSkilldoes, rather than with arrays of twelve names? And shouldtransposeChordtake 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.chordSubstitutionsdoes in a key, and keep the ICV distance for set-class questions? And shouldGrothendieckDeltareturn 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? Ata826864, the improvisation, substitution and chord-info skills andga_chord_infoeach 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
ChordVoicingsSkillsay 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
CapoSkillspell its answers as key signatures do and answer where to put the capo? And should both skills accept, inCanHandle, the questions they can read, so that the offline fallback reaches them? - Should GA’s
VoiceLeadingSkillchoose 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 takesM7,Δ,øand a final accidental, spell the notes from the chord’s root, and accept, inCanHandle, the questions it can read? - Should GA’s
ga_voice_leading_paircheck 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_progressionspell from the key signature with seven letters, take a key with its mode, boundlength, 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_voicingsencode 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 forga_search_voicings? - Should GA’s
IcvNeighborsSkillread 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 forga_icv_neighborsas it is? - Should GA’s
GrothendieckDeltaSkillreport a zero delta as zero,GrothendieckDelta.Explainlose its garbled arrow, andIcvShortestPathSkillrefuse 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
SetTheoryEquivalenceSkillread 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? ShouldGrothendieckOperationsParserdrop thews1afterstr, try its alternatives withattempt, and read the usual chord symbols, and shouldGrothendieckParseSkillsay 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.yamlagainst 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? ShouldModesSkillsay when it doesn’t recognize a question rather than list the major scale’s modes? - Should
OutsideNotesSkillclassify 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 withNormalizeQuality’s own vocabulary, and spell the chord from its letters?