GA's AI: OPTIC-K, ML, agents and the chatbot — Mission
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.
Why I’m learning this
Section titled “Why I’m learning this”Guitar Alchemist (GA) has a chatbot for guitarists. Behind it sit a 240-number description of every guitar chord shape, called OPTIC-K, an index of 313,047 of those descriptions, a router that decides which piece of code answers a question, and a handful of agents that call a language model. The documentation around it is large and partly out of date, and the code moves every week.
I want to know what actually happens: which numbers a chord gets, why two chords come out as similar, what the index file contains, and what the chatbot does with a question when no model is reachable. The way to find out is to call GA’s own classes from a program, print what they return, and compare it with what the comments and the documents say. Where the two disagree, the difference goes into the journal.
Who this course is for
Section titled “Who this course is for”You write C#. You know float[], LINQ, dependency injection and ASP.NET Core well enough to read a Program.cs. You don’t need any machine learning: the course uses three ideas, each explained where it first appears.
- An embedding is a fixed-length array of numbers that describes an object, so that similar objects get similar arrays.
- Cosine similarity measures how much two such arrays point the same way: 1 for the same direction, 0 for nothing in common.
- Nearest-neighbour search returns the stored arrays closest to a query array.
Three other courses on this site cover the background, and this one links to them instead of repeating them:
- Music theory for Guitar Alchemist: pitch classes, voicings, interval-class vectors and set classes, the vocabulary OPTIC-K encodes;
- Machine learning, as applied in IX: features, distances, nearest neighbours and clustering, written by hand;
- Agentic coding with Claude Code and Codex: the tool loop, hooks, skills and MCP servers, from the side of a developer who uses agents.
By the end of this course, I will be able to
Section titled “By the end of this course, I will be able to”- draw GA’s AI stack: which project computes embeddings, which writes the index, which routes a chat message, and what ix, Demerzel and TARS do around it;
- read an OPTIC-K vector partition by partition, compute the weighted similarity of two voicings by hand, and say what the vector is and isn’t invariant to;
- open an OPTK index file, explain its header, and predict what a search returns and why;
- follow a chat message through GA’s hooks, deterministic guards, intent router and agents, and explain why some questions work without a model and others fail;
- grade a deterministic skill’s musical answers with a small oracle of my own, and say where the oracle stops;
- read a server-sent event stream as the HTML standard does, and check that the text a client rebuilds is the text the server computed;
- run a fix that isn’t in the pinned commit next to the pinned code, and test a guard with input it wasn’t written for;
- test a test suite: apply its checks to answers they weren’t written for and to answers that are wrong on purpose, and read what passes;
- test a skill on every input it can be given, with a textbook’s arithmetic as the oracle, and run a skill’s own example prompts against it;
- tell apart, in GA’s AI, what works today, what is being built, and what is only planned.
Outline
Section titled “Outline”| # | Lesson | In GA | If you write C# |
|---|---|---|---|
| 1 | The map | the five layers, the index pipeline, the chatbot host, the sibling repositories | reading a DI container, WebApplicationFactory |
| 2 | OPTIC-K embeddings | EmbeddingSchema, MusicalEmbeddingGenerator, VoicingAnalyzer |
positional records, TensorPrimitives |
| 3 | The index and search | OptickIndexWriter, OptickIndexReader, OptickSearchStrategy, MusicalQueryEncoder |
binary formats, memory-mapped files, top-k heaps |
| 4 | The chatbot and its agents | ProductionOrchestrator, SemanticIntentRouter, SemanticRouter, skills, hooks |
hosted services, fallbacks, testing a host in process |
| 5 | The improvisation skill | ImprovisationSkill against chord–scale theory, issue #744 |
source-generated regexes, reading AgentResponse.Data, writing a test oracle |
| 6 | The answer on the wire | SseChunker, the two WriteSseLineAsync, the page’s and ga-client’s readers, #743 and #746 |
server-sent events, Regex.Split and lookarounds, porting a client to test it |
| 7 | Chord names it can’t read | InvalidChordNames (#749), the chord expressions of ImprovisationSkill, issue #745 |
extern alias and CS0436, word boundaries in regular expressions |
| 8 | The chatbot’s own exam | prompts.yaml and PromptCorpusTests, DiatonicChordsSkill (Path B), ModesSkill |
YamlDotNet, StringComparison, testing a test suite with wrong answers |
| 9 | Every interval, every key | IntervalSkill, ScaleInfoSkill, RelativeKeySkill, KeyNaming, the domain’s DetermineQuality |
exhaustive tests, \b and lookahead in regular expressions, switch default arms |
| 10 | What the model is told to trust | ga_dsl_eval, the closures domain.diatonicChords, domain.transposeChord and domain.commonTones, the SKILL.md files of the three Path B skills |
calling a model’s tool without the model, one letter per degree, reusing an oracle across lessons |
| 11 | The chords the key skill reads | KeyIdentificationService at the pin and on main, ga_key_identify, KeyIdentificationSkill, the key-identification SKILL.md |
reading symbols out of prose, a ranking and the selection built on it, calling private methods by reflection |
| 12 | What the progression skills answer | ProgressionMoodSkill, ProgressionCompletionSkill, their SKILL.md files, KeyIdentificationService at the pin and on main |
a word test that picks a fixed answer, a model restricted to a computed list, keyword fallback routing |
| 13 | What the progression analysis answers | the closure domain.analyzeProgression at the pin and on main, ga_dsl_list_closures, the progression-analysis draft, KeyIdentificationService.IsChordDiatonic |
compiling another commit’s F# file against the pinned project, a draft’s examples as a test, Roman numerals from the scale and from the chord |
| 14 | What the substitution skill answers | ChordSubstitutionSkill, ga_chord_substitutions and ga_chord_compare, GrothendieckService.FindNearby, GrothendieckDelta, the closure domain.chordSubstitutions |
a distance under which a whole set class ties, a tie broken by storage order, optional inputs a tool requires |
| 15 | The notes of a chord | ChordInfoSkill, ChordVocabulary, ChordSpelling, ga_chord_info and the chord-info SKILL.md |
the leftmost match of a regular expression, ToLowerInvariant beyond ASCII, a round trip as a test |
| 16 | The voicings of a chord | ChordVoicingsSkill, TypedMusicalQueryExtractor, ChordPitchClasses, OptickSearchStrategy and ADR-0002, at the pin and on main |
a second clone of a dependency at another commit, checking an answer by playing it, a filter on names, a filter dropped without a word in the answer |
| 17 | The capo and the tunings | CapoSkill, AlternateTuningsSkill and the offline fallback, at the pin and on main |
checking arithmetic against a textbook on every key and fret, a word boundary after an accidental, a direction read from the phrasing, spelling as part of the answer |
| 18 | Voice leading | VoiceLeadingSkill and the offline fallback |
checking a claim of optimality against an exhaustive search, a doubling chosen without a search, a table of symbols behind a narrower expression, a case-insensitive search for a flat |
| 19 | Voice-leading pairs | ga_voice_leading_pair in GaMcpServer, its search at the pin and on main, and the parked voice-leading skill |
a ranking that never checks what it ranks, more candidates that make the answer worse, a sorted matching that is the least only for equal sizes, a string order that one consumer fixed and another passes on |
| 20 | Generated progressions | ga_generate_progression in GaMcpServer, GA’s twelve-key tables, the stitching with ga_voice_leading_pair at the pin and on main, and the parked progression skill |
spelling from a key signature rather than a name, two sources that agree on a wrong chord, a parameter without a limit, answers for single moves that don’t make a path |
| 21 | The voicing search | ga_search_voicings and ga_voicing_vocabulary in GaMcpServer, GA’s query reader, tag registry and voicing-search skill, at the pin and on main, and the parked voicing-search skill |
a word read and then dropped, many tags on few bits, a tag that isn’t a filter, words matched by substring, a score range out of reach |
| 22 | Similar chords | IcvNeighborsSkill and IntervalClassVectorSkill in GA.Business.ML, GrothendieckService.FindNearby and the routing hints, at the pin and on main, and the parked icv-neighbors skill |
routing anchors the parser doesn’t read, a table that falls back to a major triad, a sort over equal costs, neighbors that depend on the vector only |
| 23 | Harmonic distance and path | GrothendieckDeltaSkill and IcvShortestPathSkill in GA.Business.ML, GrothendieckDelta and GrothendieckService.FindShortestPath, at the pin and on main |
a distance that is never zero, an interpretation that reads one rule, mojibake in an answer, a search that can’t succeed, steps counted as sets |
| 24 | Set classes and the Grothendieck parser | SetTheoryEquivalenceSkill and GrothendieckParseSkill in GA.Business.ML, and GrothendieckOperationsParser in GA.Business.DSL, at the pin and on main |
a relation never read, a set read from its last number, a parser whose spaces are already eaten, an answer about no chord |
| 25 | What reaches the transpose skill | DefaultRoutingHintProvider, TransposeSkill, SkillMdPlugin and the two routers of the chatbot host, at the pin and on main |
a hint that misses the skill’s own examples, a skill no fallback can reach, a default that answers every question nobody claims |
| 26 | The modes catalog | ModesSkill, Modes.yaml and ModesConfig, at the pin and the same on main |
modes that aren’t their family’s scale, names cut by a YAML comment, formulas numbered by position |
| 27 | Notes that sound outside | OutsideNotesSkill and ChordVocabulary, at the pin and the same on main |
tensions no chord scale holds, an 11 inside 13th chords, chord symbols cut short, sharps in flat chords |
| — | Journal |
Prerequisites
Section titled “Prerequisites”- The .NET 10 SDK and Git. On Windows, run the course scripts from Git Bash.
- About 20 MB of disk for GA’s partial clone, and about 1.1 GB once GA’s projects and the course program are built.
- A network connection for the first run only, to clone GA and restore the NuGet packages. After that, everything runs offline.
- No guitar needed, but the chord shapes in lesson 2 are the first ones a guitarist learns.
Resources
Section titled “Resources”- GuitarAlchemist/ga at
a826864, in particular itsCLAUDE.md, the OPTIC-K schema documents and the chatbot roadmap. - Clifton Callender, Ian Quinn and Dmitri Tymoczko, “Generalized Voice-Leading Spaces”, Science 320, 2008: the paper that named the OPTIC equivalences.
- Jared Updike, Harmonious: an exhaustive chord and scale reference for piano and guitar. Its Equivalence Groups page illustrates each OPTIC equivalence with chord diagrams, and adds the K of OPTIC-K, for complementarity.
- Integration tests in ASP.NET Core, for
WebApplicationFactory, and Microsoft.Extensions.AI, the abstractions GA’s chatbot calls models through.