node · 2026-05-27 teach-me documentation
AC declared 2026-06-09

Exit condition (Jake, sync): “a blog post + video about lessons from making a harness.”

blog post + video published  →  DONE  →  archive this node

MIDI Harness

Active project · play a couple chords, have an interesting discussion about music. Code lives in its own repo: ~/Documents/midi-harness.

The MIDI harness is an example of what I want it to be: a really simple system prompt and a hook that captures MIDI events from a JSON-L and then an HTML, so that we're basically just trying to give Claude the power to let me play a couple chords and then have an interesting discussion about music.
Jake, the north star · 2026-06-09
MIDI keyboardWilliams Legato III
Go listenerforever-running
events.jsonlappend-only
conversationabout music
Claude46-word CLAUDE.md
hook on send<played> block → erase file
↻ play more chords — the emptied log IS the next turn's boundary
The one idea: the hook deletes events.jsonl after injecting it — so "since last message" needs no offsets, no time windows, no second clock, no audio.

Runs ~/Documents/midi-harness/bin/listener (builds first if needed). Notes scroll below as you play. Pill polls every 5s, so it also sees listeners started from a terminal or the repo's SessionStart hook. Stop kills the listener no matter who started it. Local-viewer only.

checking…
(no output yet — start the listener, then play)
                        ┌──────────────────────────┐
                        │           USER           │
                        │   (plays + talks at the  │
                        │       same time)         │
                        └────┬────────────────┬────┘
                  hands on   │                │  voice
                  MIDI keys  │                │  (Wispr Flow → text)
                             ▼                ▼
              ┌────────────────────┐   ┌──────────────────┐
              │ listener (Go)      │   │ Claude Code      │
              │ daemon, all session│   │ input box        │
              │ raw note on/off,   │   └────────┬─────────┘
              │ no cleanup         │            │ user hits ⏎
              └─────────┬──────────┘            ▼
                        │ append      ┌─────────────────────────────┐
                        ▼             │ hook (Go, UserPromptSubmit) │
              ┌──────────────────┐    │                             │
              │  events.jsonl    │    │  ① read WHOLE file          │
              │  raw + noisy:    │    │  ② delete file              │
              │  {"t":...,       │───►│  ③ clean up (in memory):    │
              │   "type":"on",   │    │     · cluster press→release │
              │   "note":60}     │    │       into chords           │
              │  one line per    │    │     · collapse repeats → xN │
              │  key event       │    │     · note numbers → names  │
              └──────────────────┘    │  ④ emit <played> block      │
                        ▲             └──────────────┬──────────────┘
                        │                            │
            next note played                         │  e.g. 34 raw JSON lines in →
            recreates the file                       │  3 lines of chords out
            (fresh turn boundary)                    ▼
                                      ┌──────────────────────────────┐
                                      │ Claude                       │
                                      │ prompt = user's text         │
                                      │        + <played> block      │
                                      │ CLAUDE.md ≈46 words:         │
                                      │ "piano practice partner"     │
                                      └──────────────┬───────────────┘
                                                     ▼
                                        talk about the music
                                        (+ ad-hoc HTML finger
                                         diagrams — not a skill,
                                         Claude just writes one)
  

The 34→3 example: a 4-key C-major voicing struck & released 3× (24 on/off events) + a 4-key F voicing (8) + one G4 (2) = 34 JSON lines on disk → C3 G3 C4 E4 x3 / F2 F3 A#3 D4 / G4.

┌─ RECIPE: midi-harness ─────────────────────────────────┐
│                                                        │
│  hardware                                              │
│   · 1 MIDI keyboard (USB)                              │
│                                                        │
│  off-the-shelf                                         │
│   · Claude Code        (the chat + hook runtime)       │
│   · Wispr Flow         (voice → text in the input box) │
│                                                        │
│  written for this                                      │
│   · 1 Go listener      (~90 lines — append raw notes   │
│                         to events.jsonl, forever)      │
│   · 1 Go hook          (~160 lines — read, clean,      │
│                         inject <played>, delete)       │
│   · 1 system prompt    (CLAUDE.md, 46 words)           │
│                                                        │
│  no database · no server · no audio · no clocks        │
└────────────────────────────────────────────────────────┘
  

Plus ~20 lines of glue: settings.json + two shell scripts that auto-start/stop the listener with the Claude session. A hook can be any language — Go is a latency choice (~1ms startup vs 30–170ms interpreter dead air before every message).

Where~/Documents/midi-harness — separate repo, own .claude/, NOT this vault
What's builtcmd/listener + cmd/hook (Go, stdlib, ~0ms hook startup) + 46-word system prompt
Real use~10 practice sessions logged (2026-05-30 → 2026-06-01) in session-drain.md
Uncommitted driftCLAUDE.md edited; future-ideas.md + session-drain.md untracked; repo has 1 commit
Parked on purposetiming/rhythm capture (future-ideas.md) — harmony-read gets solid first
The one next thing

Commit the drift, then run a fresh practice session to keep calibrating the chord-read loop.

Why fresh: you can't tune a system prompt from inside the session it governs (see [[system-prompt-self-edit]]) — the edited CLAUDE.md only takes effect next session.

midi-coach
abandoned predecessor · ~/Documents/midi-coach · github.com/jakesimonds/MCP-Dash-Demo
midi-harness-rebuild
the 2026-05-29 strip-back
midi-harness
this node + the live repo

Both predecessor nodes archived 2026-06-09 (archive/midi-coach, archive/midi-harness-rebuild). This directory also still holds a dormant earlier experiment: a Rust synth + Python listener — see midi-harness.md.