Skip to content

[DONE] 정리 4단계 PR 18 — MIDI 로 나가는 메시지 벡터 열한 개와 /api/midi 벡터 아홉 개가 가상 패널에서 돈다 - #88

Merged
patternflow-bot merged 3 commits into
devfrom
claude/cleanup-step4-pr18-l11ur3
Oct 15, 2026
Merged

patternflow-bot merged 3 commits into
devfrom
claude/cleanup-step4-pr18-l11ur3

Conversation

@patternflow-bot

@patternflow-bot patternflow-bot commented Oct 15, 2026 •

Copy link
Copy Markdown

dev / https://claude.ai/code/session_01WnWnZzvrtVz8L7czv6JcHg

정리 4단계 계획서 표의 PR 18 이다. 패널이 MIDI 로 내보내는 것(손으로 돌린 노브의 CC 24~27, 버튼의 노트, 패턴이 바뀔 때의 Program Change)을 docs/midi-vectors/out- 벡터 열한 개로, /api/midi 와 status 의 midi 를 docs/rest-api-vectors/midi- 벡터 아홉 개로 적었다. 머지되면 CI 가 PR 마다 MIDI 보내는 쪽과 /api/midi 를 가상 패널에 돌려 보고, midi-spec.md 에서 옮긴 문장이 바뀌면 그 벡터가 빨개진다.

좋아지는 것: 없음

할 일

한 일

REST 벡터는 check:rest-vectors 가 돌린다. 이 스위트는 midi Sender 를 가져오지 않으므로 아홉 개 모두 http 만 쓰고, 세션이 있어야 보이는 값은 타입으로만 본다. rest-api.md 에는 /api/midi 절이 없어서, 경로를 정하는 midi-spec.md 의 Status 와 Settings 에서 문장을 옮겼다. midi-caps 만 audio-caps, mqtt-caps 처럼 rest-api.md 의 conventions 문장이다.

벡터 says 보는 것
midi-caps A feature's endpoints exist only when that feature is composed in … (rest-api.md 613행) caps 가 ["patterns","params","sleep","midi"], GET /api/midi 가 200 과 JSON
midi-status GET /api/status on a build with this feature lists "midi" in caps and carries (68행) 같은 caps, midi 의 열 키를 타입으로
midi-rx-detail Unreleased 1.2 adds rxDetail to both … (81행) status 의 midi.rxDetail 과 GET /api/midi 의 rxDetail, 표의 열두 키
midi-get channel, outDiv, outMul, outMode, host, session state, counters (115행) channel 1(31행), outDiv [1,1,1,1](119행의 기본값), outMode "abs"(116행), 나머지는 타입
midi-out-mode knobs out as a virtual position (default) or as relative steps. (116행) abs 에서 시작해 outMode=rel 로, 폼 본문 outMode=abs 로 돌아온다. GET 과 status 둘 다
midi-out-div outbound sensitivity: detents per outbound step, 1..16, … (119행) 양 끝 16 과 1 을 받는다
midi-out-mul Setting one of outMul/outDiv above 1 resets the other to 1 (117행) outDiv=4 다음 outMul=8 이면 outDiv 가 1, 다시 outDiv=2 면 outMul 이 1
midi-knob Apply outMul/outDiv to one knob; without it, to all four. (118행) outDiv=4&knob=3, outMul=2&knob=2 뒤 [1,1,4,1], [1,2,1,1](72행의 예와 같다). knob 없이 outDiv=2 면 넷 다
midi-host Empty string clears it. (120행) host=192.168.0.23 이 GET 과 status 에 보이고, 폼 본문 host= 로 빈 글

보내는 쪽은 check:midi-vectors 가 _rtpmidi.ts 의 세션으로 돌린다. 손은 hand, 시간은 wait 와 out 의 within 이다. 손으로 돌린 것은 다음 프레임 끝에 나가므로 첫 메시지는 20 ms 안에 본다. 같은 노브의 다음 메시지를 보는 벡터는 그 사이에 100 ms 를 둔다.

벡터 says (midi-spec.md) 보는 것
out-cc-abs the value is a virtual position 0..127 … starts at 64, … clamps at the ends (57행) K1 +3 → CC 24 = 67, K2 −2 → CC 25 = 62. K1 +62 는 127 에서 멈추고 −1 이면 126, K2 −63 은 0 에서 멈추고 +1 이면 1
out-cc-rel 64 ± steps since the previous message (binary offset) (57행) outMode=rel 뒤 K3 −2 → 62, 100 ms 뒤 +5 → 69, K1 +1 → 65
out-cc-div The encoders have 24 detents a turn, so 1 is 24 steps per turn and 4 is 6. (119행) outDiv=4&knob=1 뒤 K1 한 바퀴(24) → 70, K2 한 바퀴 → 88. K1 +3 은 100 ms 동안 아무것도 없고, +1 더하면 71
out-cc-mul outbound sensitivity the other way: steps per detent, 1..8. (117행) outMul=8&knob=4 뒤 K4 +2 → 80, K3 +2 → 66, K4 −1 → 72
out-pacing at most one message per knob every 50 ms … trailing-edge so the final value always arrives (57행) K1 +1 → 65. 20 ms 뒤 +1 과 40 ms 뒤 +1 은 50 ms 까지 나가지 않고, 다음 50 ms 안에 67 하나만
out-abs-same in abs only a position that differs from the last one sent goes out (57행) K2 +1 → 65, 50 ms 안에 +1 과 −1 이면 위치가 65 로 돌아와 100 ms 동안 아무것도 없다. 다음 +1 → 66
out-rel-sum in rel the steps sum between messages and a sum leaving ±63 is sent at once (57행) K4 +1 → 65, 50 ms 안의 +2 와 +3 이 합쳐 69 하나. +1 → 65 뒤 +40, +40 은 합 80 이라 바로 127(63), 50 ms 뒤 나머지 81(17)
out-buttons Encoder button 1–4 pressed (58행) K1 누름 → 노트 60 온 127, 500 ms 쥐고 있는 동안 아무것도 없고 놓으면 노트 오프. K4 는 노트 63
out-program-change The pattern changed (by anyone: … MIDI, a show), for n ≤ 127 (60행) /api/patterns/select?index=2 → PC 2, 호스트의 PC 5 → PC 5
out-no-echo a CC or note the host itself sent is never reflected back (63행) 호스트의 CC 20 = 100, CC 25 = 65, 노트 62 온과 오프가 모두 패널에 닿고(paramActive, input) 아무것도 돌아오지 않는다
out-hand-only Encoder 1–4 turned by a hand. (57행) POST /api/params?p1=900 은 CC 를 내지 않는다. 100 ms 뒤 손 K1 +1 → 65

증거

로컬, web/:

$ git diff --stat origin/dev...HEAD
 docs/midi-vectors/out-abs-same.json       | 12 ++++++++++++
 docs/midi-vectors/out-buttons.json        | 13 +++++++++++++
 docs/midi-vectors/out-cc-abs.json         | 15 +++++++++++++++
 docs/midi-vectors/out-cc-div.json         | 13 +++++++++++++
 docs/midi-vectors/out-cc-mul.json         | 12 ++++++++++++
 docs/midi-vectors/out-cc-rel.json         | 12 ++++++++++++
 docs/midi-vectors/out-hand-only.json      | 10 ++++++++++
 docs/midi-vectors/out-no-echo.json        | 13 +++++++++++++
 docs/midi-vectors/out-pacing.json         | 11 +++++++++++
 docs/midi-vectors/out-program-change.json | 10 ++++++++++
 docs/midi-vectors/out-rel-sum.json        | 17 +++++++++++++++++
 docs/rest-api-vectors/midi-caps.json      |  9 +++++++++
 docs/rest-api-vectors/midi-get.json       | 19 +++++++++++++++++++
 docs/rest-api-vectors/midi-host.json      | 11 +++++++++++
 docs/rest-api-vectors/midi-knob.json      | 12 ++++++++++++
 docs/rest-api-vectors/midi-out-div.json   | 11 +++++++++++
 docs/rest-api-vectors/midi-out-mode.json  | 12 ++++++++++++
 docs/rest-api-vectors/midi-out-mul.json   | 13 +++++++++++++
 docs/rest-api-vectors/midi-rx-detail.json | 37 +++++++++++++++++++++++++++++++++++++
 docs/rest-api-vectors/midi-status.json    | 24 ++++++++++++++++++++++++
 20 files changed, 286 insertions(+)

$ npm run check:midi-vectors
docs/midi-vectors/in-cc-absolute.json: same
… in- 아홉 개 모두 same
docs/midi-vectors/out-abs-same.json: same
docs/midi-vectors/out-buttons.json: same
docs/midi-vectors/out-cc-abs.json: same
docs/midi-vectors/out-cc-div.json: same
docs/midi-vectors/out-cc-mul.json: same
docs/midi-vectors/out-cc-rel.json: same
docs/midi-vectors/out-hand-only.json: same
docs/midi-vectors/out-no-echo.json: same
docs/midi-vectors/out-pacing.json: same
docs/midi-vectors/out-program-change.json: same
docs/midi-vectors/out-rel-sum.json: same
passed   midi-vectors

$ npm run check:rest-vectors | grep midi-
docs/rest-api-vectors/midi-caps.json: same
docs/rest-api-vectors/midi-get.json: same
docs/rest-api-vectors/midi-host.json: same
docs/rest-api-vectors/midi-knob.json: same
docs/rest-api-vectors/midi-out-div.json: same
docs/rest-api-vectors/midi-out-mode.json: same
docs/rest-api-vectors/midi-out-mul.json: same
docs/rest-api-vectors/midi-rx-detail.json: same
docs/rest-api-vectors/midi-status.json: same
passed   rest-vectors

$ for i in $(seq 1 20); do npx tsx scripts/drift.ts midi-vectors; done
midi-vectors passed 20 failed 0
$ for i in $(seq 1 5); do npx tsx scripts/drift.ts rest-vectors; done
rest-vectors passed 5 failed 0

처음 돌렸을 때 out-rel-sum 하나가 drift 였다. 내가 +2 와 +3 을 70 으로 적었고 패널은 69(64 + 5)를 보냈다. 벡터를 69 로 고쳤다.

깨뜨린 한 번: midi-out.ts 를 일곱 군데 바꿨다. 간격 50 ms 를 20 으로 줄이고 시작 위치를 63 으로 옮겼다. 노트 온 벨로시티는 100 이고 PC 번호에는 1 을 더했다. knob= 과 빈 host= 는 무시했다. rel 은 63 에 걸음을 더했다. 백업 파일로 되돌렸다.

out-abs-same: drift        step 01 [176,25,65] ← [176,25,64]
out-buttons: drift         step 01 [144,60,127] ← [144,60,100]
out-cc-abs: drift          step 01 [176,24,67] ← [176,24,66]
out-cc-div: drift          step 03 [176,25,88] ← [176,25,69]
out-cc-mul: drift          step 03 [176,26,66] ← [176,26,79]
out-cc-rel: drift          step 02 [176,26,62] ← [176,26,61]
out-hand-only: drift       step 03 [176,24,65] ← [176,24,64]
out-pacing: drift          step 03 <absent> by 10 ms ← [176,24,66] at 10 ms · step 04 [176,24,67] ← nothing
out-program-change: drift  step 01 [192,2] ← [192,3]
out-rel-sum: drift         step 04 <absent> by 10 ms ← [176,27,68] at 10 ms · step 09 [176,27,127] ← [176,27,126]
failed   midi-vectors
midi-host: drift           step 04 host "" ← "192.168.0.23"
midi-knob: drift           step 03 outDiv[2] 4 ← 1, outMul [1,2,1,1] ← [2,2,2,2]
failed   rest-vectors

(벡터 ← 패널로 줄였다.) out-no-echo 는 이 깨뜨림에 걸리지 않아 두 번째로 깨뜨렸다. midi.ts 가 받은 메시지를 그대로 돌려보내게(MIDI Thru), midi-out.ts 가 버스가 잡은 노브의 손 클릭을 버리게(core_midi.h:403 처럼) 했다. 되돌렸다.

out-hand-only: drift   step 03 [176,24,65] by 20 ms ← nothing
out-no-echo: drift     step 04 <absent> ← [176,20,100], [176,25,65], [144,62,127] at 100 ms · step 06 <absent> ← [128,62,0]
failed   midi-vectors

스펙 문장을 바꾼 한 번: midi-spec.md 57행의 「clamps at the ends」를 「clamps at either end」로 바꾸면 out-cc-abs.json: drift, says docs/midi-spec.md ← says not in docs/midi-spec.md. git checkout 으로 되돌렸다.

$ npm run typecheck                    # exit 0
$ npm run lint -- --max-warnings=0     # exit 0
$ npm run check:drift
passed audio-vectors, bom, bom-schematic, changelog, editions, features, links-absolute, markers, midi-vectors, mqtt-vectors, osc-vectors, presets, rest-vectors, shared, virtual-panel · skipped roadmap(원래 레포를 못 읽는다, CI 는 strict)
$ python3 ../.github/scripts/check_links.py
all 734 relative links resolve
$ npm run check:human-lock
check:human-lock: no locked words changed
$ npm run check:voice
read: 0 added lines in 0 files
check:voice: nothing found              # JSON 의 says 는 스펙을 옮긴 글이라 읽지 않는다. 이 본문은 따로 `-` 로 돌렸다
$ npx tsx scripts/drift.ts markers
passed   markers
$ git fetch --tags && npm run doc-map           # CSV 는 git checkout 으로 되돌렸다
1120 links from 224 files → docs/investigations/2026-10-doc-map.csv
0 do not resolve
1366 Korean/English pairs → docs/investigations/2026-10-doc-parity.csv
7 en-short, 18 ko-short, 1 missing-ko, 1340 ok

GitHub, 이 PR: 66bd642 에서 「Drift checks / check」(116546946688)와 「Docs point at files that exist / check」(116546946548)가 success. 「Web CI」는 web/** 에서만 돌아 이 PR 에서는 돌지 않았다.

실행법

cd web && npm ci
npm run check:midi-vectors     # in- 아홉, out- 열하나
npm run check:rest-vectors     # midi- 아홉 개가 여기 있다

벡터 하나를 고쳐 보려면 JSON 을 고치고 같은 명령을 돌린다. git 에 넣기 전에도 디스크에서 읽는다.

가정

  • 기준 브랜치는 dev(머리말). 표가 PR 18 에 적은 docs/midi-vectors/out-*.json 과 docs/rest-api-vectors/midi-*.json 만 더했다. 두 README, CHANGELOG.md, docs/rfc-cleanup-progress.md, doc-map CSV, 가상 패널은 안 고쳤다. 라벨은 앞 PR 들처럼 달지 않았다.
  • rest-api.md 에 /api/midi 절이 없어 midi- 여덟 개는 midi-spec.md 의 Status 와 Settings 를 contract 로 둔다. _vectors.ts 는 contract 가 가리키는 스펙에서 문장을 찾으므로 그대로 돈다. REST README 첫 문장(「one sentence of rest-api.md」)과는 어긋난다. 아래 「for another k」.
  • 세션이 있어야 보이는 값(rtpPeers 1, rtpPeer, rx)은 벡터를 두지 않았다. PR 17 은 out-*.json 에 두라 했지만, MIDI README 는 out- 을 Device → host 의 한 행이라 하고 GET /api/midi 는 패널이 보내는 것이 아니다. midi-get 이 타입으로만 본다.
  • 한 번에 도는 걸음은 ±63 안이다. 펌웨어와 패널은 한 프레임의 걸음을 ±63 에서 자르는데(core_midi.h 의 steps > 63), 스펙은 그것을 말하지 않는다. 그래서 out-cc-abs 는 62 와 −63 으로 양 끝을 넘긴다.
  • out-rel-sum 의 합 80 은 바로 127(63), 나머지는 50 ms 뒤 81(17)로 본다. 바이너리 오프셋 한 메시지는 ±63 까지만 싣고, 57행이 마지막 값은 늘 닿는다고 한다. 펌웨어(core_midi.h 의 sum > 63)도 그렇게 한다.
  • 노트 오프의 벨로시티는 <integer> 다. 59행은 노트 오프라고만 한다. 펌웨어는 0 을 보낸다. 노트 오프를 벨로시티 0 노트 온으로 보내는 패널은 실패한다(58·59행이 둘을 나눈다).
  • 기다림의 창은 프레임이 손 뒤 20 ms 안에 도는 패널이면 맞도록 잡았다. out-pacing 의 「나가지 않는다」 창은 손 뒤 20~50 ms 로, 첫 메시지가 0 ms 보다 늦게 나간 패널이면 50 ms 규칙이 그 안에 다음 메시지를 막는다.
  • PC 를 노브(SELECT, K4 길게 누름)로 바꾸는 것과 쇼가 바꾸는 것, 127 넘는 번호는 다시 돌리지 않는다. 가상 패널은 쇼를 돌리지 않고 Basics 는 34개다. 레인이 움직인 노브(57행의 Hand motion only)도 check:midi-vectors 에 ws Sender 가 없고 midi 만 넣은 패널에는 레인이 없어 돌리지 않는다. 버스(POST /api/params)는 out-hand-only 가 본다.
  • outDiv=17, outMul=9 같은 범위 밖 값은 벡터가 없다. 스펙은 범위만 말하고, 거절인지 잘림인지는 말하지 않는다(D8).
  • outMul 의 기본값은 <integer> 로 본다. 119행은 outDiv 의 기본값을 적고 117행은 outMul 의 기본값을 적지 않는다.
  • 스크린샷은 없다. 화면이 바뀌는 PR 이 아니다.
  • firmware/ 와 web/src/ 를 건드리지 않아 build.sh all, check:ci, check:panels 는 §2.1(1) 대상이 아니다. 계획서대로 typecheck 와 이 PR 의 check, 깨뜨린 한 번을 적었다.

Changelog

Tools

  • MIDI out and /api/midi have contract vectors. Eleven files in docs/midi-vectors/ start with out-, each one sentence of the device → host table or the settings in docs/midi-spec.md, from three detents on knob 1 arriving as CC 24 at 67 to a host's own CC that never comes back. check:midi-vectors replays them over its RTP-MIDI session. Nine more in docs/rest-api-vectors/ start with midi- and read GET and POST /api/midi and the midi block in /api/status under check:rest-vectors.

for an issue

D9. 벡터는 스펙을 따르고, 펌웨어는 한 곳에서 다르다(PR 16 의 「k18, for an issue」).

  • out-hand-only.json · midi-spec.md 57, "Encoder 1–4 turned by a hand." · core_midi.h:403 은 버스가 그 채널을 잡고 있는 프레임의 델타를 0 으로 둔다(paramAbsoluteActive). 손은 버스가 250 ms 안에 쓴 채널을 되찾지 못하므로(ABSOLUTE_RELEASE_GRACE_MS), POST /api/params?p1=900 100 ms 뒤에 K1 을 돌리면 기기는 CC 를 보내지 않는다. 위 두 번째 깨뜨림이 그 규칙이고, 벡터가 빨개진다.

벡터는 없고 PR 16 이 넘긴 것: core_midi_http.h:107 이 on=0|1 을 읽고, GET /api/midi 가 you, ip, port, outPos 를 싣는다(59~69행). Settings 표는 그중 아무것도 적지 않는다. 스펙이 말하지 않으니 벡터가 없고(D8), 모양은 다른 키를 지나가게 둔다.

for another k

  • k23: midi-spec.md 32행은 패널이 「never echoes a host's messages back」이라 하고, 60행은 PC 가 「by anyone: … MIDI」로 바뀌어도 나간다 한다. out-program-change 는 메시지를 정하는 60행을 따라 호스트의 PC 5 에 PC 5 를 듣는다(D8). PR 16 도 적었다.
  • k23: 117행에 outMul 의 기본값을 넣을지. midi-get 은 지금 네 정수로만 본다.
  • docs/rest-api-vectors/README.md 첫 문장은 「one sentence of rest-api.md」다. midi- 여덟 개는 midi-spec.md 를 옮긴다. 표에 그 README 를 다시 가진 행이 없어, 고친다면 행을 하나 더해야 한다.
  • k22: docs/README.md 의 midi-spec.md 줄에 midi-vectors/ 를(PR 17 이 적은 것과 같다). rest-api-vectors/ 에 midi- 가 있다는 것도.

Upstream

  • What: Add docs/midi-vectors/out-.json, eleven vectors of docs/midi-spec.md's device → host table and its settings, and docs/rest-api-vectors/midi-.json, nine vectors of GET and POST /api/midi and the midi block in /api/status. check:midi-vectors and check:rest-vectors replay them against the virtual panel.
  • Checks: npm run typecheck, npm run lint and npm run check:drift (midi-vectors and rest-vectors included) pass; firmware/ and web/src/ are untouched.
  • Docs map: none.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WnWnZzvrtVz8L7czv6JcHg

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WnWnZzvrtVz8L7czv6JcHg
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WnWnZzvrtVz8L7czv6JcHg
@patternflow-bot patternflow-bot changed the title [WIP] 정리 4단계 PR 18 — MIDI 로 나가는 메시지와 /api/midi 답이 벡터로 적힌다 [DONE] 정리 4단계 PR 18 — MIDI 로 나가는 메시지 벡터 열한 개와 /api/midi 벡터 아홉 개가 가상 패널에서 돈다 Oct 15, 2026
@patternflow-bot
patternflow-bot marked this pull request as ready for review October 15, 2026 16:50
@patternflow-bot
patternflow-bot merged commit c8e877f into dev Oct 15, 2026
2 checks passed
@patternflow-bot
patternflow-bot deleted the claude/cleanup-step4-pr18-l11ur3 branch October 15, 2026 16:54
engmung pushed a commit to engmung/Patternflow that referenced this pull request Oct 15, 2026
engmung pushed a commit to engmung/Patternflow that referenced this pull request Oct 15, 2026
Patternflow-lab#11
Patternflow-lab#12
Patternflow-lab#13
Patternflow-lab#15
Patternflow-lab#17
Patternflow-lab#14
Patternflow-lab#16
Patternflow-lab#19
Patternflow-lab#20
Patternflow-lab#18
Patternflow-lab#21
Patternflow-lab#23
Patternflow-lab#24
Patternflow-lab#25
Patternflow-lab#26
Patternflow-lab#28
Patternflow-lab#29
Patternflow-lab#30
Patternflow-lab#27
Patternflow-lab#31
Patternflow-lab#32
Patternflow-lab#34
Patternflow-lab#35
Patternflow-lab#33
Patternflow-lab#36
Patternflow-lab#37
Patternflow-lab#38
Patternflow-lab#39
Patternflow-lab#40
Patternflow-lab#41
Patternflow-lab#42
Patternflow-lab#43
Patternflow-lab#46
Patternflow-lab#44
Patternflow-lab#47
Patternflow-lab#48
Patternflow-lab#49
Patternflow-lab#45
Patternflow-lab#50
Patternflow-lab#51
Patternflow-lab#52
Patternflow-lab#54
Patternflow-lab#57
Patternflow-lab#55
Patternflow-lab#53
Patternflow-lab#56
Patternflow-lab#59
Patternflow-lab#58
Patternflow-lab#60
Patternflow-lab#61
Patternflow-lab#62
Patternflow-lab#63
Patternflow-lab#64
Patternflow-lab#65
Patternflow-lab#66
Patternflow-lab#67
Patternflow-lab#68
Patternflow-lab#69
Patternflow-lab#70
Patternflow-lab#72
Patternflow-lab#73
Patternflow-lab#71
Patternflow-lab#75
Patternflow-lab#74
Patternflow-lab#77
Patternflow-lab#78
Patternflow-lab#76
Patternflow-lab#79
Patternflow-lab#80
Patternflow-lab#81
Patternflow-lab#82
Patternflow-lab#84
Patternflow-lab#83
Patternflow-lab#85
Patternflow-lab#86
Patternflow-lab#87
Patternflow-lab#88
Patternflow-lab#90
Patternflow-lab#91
Patternflow-lab#89
Patternflow-lab#92
patternflow-bot added a commit that referenced this pull request Oct 16, 2026
… 않는다 (#94)

* Add horizontal desktop printed enclosure and assembly instructions. Switch username to mbchars.

* tools: case-models builds mbchars' horizontal desktop case

mbchars.py puts the remix's printed parts together from its STLs (no
assembled file exists): the three front sections where the joint bars'
36 mm insert pitch puts their lug holes, the covers on the sixteen inserts,
the USB retainer on its boss and the stands on the bottom edge, each step
checked against the README (410.28 x 176.16 x 42 mm), the board's encoder
grid, the 320 x 160 mm panel and boolean overlaps. assemble.mjs learns
ledTurn (the panel lies the long way here) and knobLook (the official knob,
printed white), and lib/looks.mjs pla_orange, the 3MF's own #F46B16. The
other three models rebuild byte-identical; the new one is 380 KB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QGBYm64PnfneJq2ZDJ8Gs7

* web: /build's case switch and 3D preview get a fourth case, mbchars' horizontal desktop one

The card is the remix README's own facts and photos (resized to 1200 px),
the preview shows the device in the case, and the switch is four tabs, two
by two on a phone. A case too wide for the view at the one scale (this one,
41 cm across on a desktop's tall canvas) is drawn smaller by as little as
keeps it clear of the arrows; every case still fits at the one scale on a
phone, and the three upright ones on a desktop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QGBYm64PnfneJq2ZDJ8Gs7

* docs: changelog for mbchars' horizontal desktop case and the fourth preview case

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QGBYm64PnfneJq2ZDJ8Gs7

* web: ZuperZuche's build joins the map - the first print, from Moscow

Found by accident, with the PCBs ordered ten minutes later; the 3D printer came
a month on, bought for the case, and the case was the first thing it printed.
Moscow was given, so the pin is on the city.

The blurb stops at the printer. The maker's note about making the patterns with
AI is left out on purpose, and there are no pronouns for the maker: the message
does not say.

Three photos: a pixel-art Mount Fuji beside a modular synth, a pixel-art frog
pond seen from above, and a cat plush in front of the panel.

groups.test.ts pins the number of countries on the real map; Russia is new, so
11 becomes 12.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* web: the 3D preview names whose case it is, in the build map's label

On the Build tab the name on the case's tab (Official, Besoiobiy,
SimonePDA, mbchars) stands over the device, on the build map's dark
highlight. It follows the pick at once, as the tab does, and reads
Official for a case whose model did not load. The knob hint moves under
it while it is up. Changelog also gets the line for the arrows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* web, docs: the first flash starts in download mode, /flash goes to the guide's Flash chapter, and the README is new from its first screen

The Play guide's 01 Flash (and /ko) puts hold BOOT, tap RST before Flash
Patternflow on every first flash, assumes the Wi-Fi step after
"Installation complete!" usually does not come (RST, Flash Patternflow
again, Connect to Wi-Fi), and ends on the panel's own patternflow-xxxx
network at 192.168.4.1 for when that does not take; the device simulator
draws the NETWORK screen's hotspot. The Build guide's flash card, the
Pattern tab's notes and the breadboard page give the same order. The bare
/flash, which the device's Update page and every release's FLASHING.md
point at, redirects to /guide/play#flash; /flash/manifest.json and
/flash/bin/* are untouched. flash.test.ts pins the card order and anchors.

The README opens with a one-line definition, the hero, the
photosensitivity warning, four doors (Build, Play, Make patterns, Add
sound) and First light; the story, community, facts and editions follow,
shorter. The docs that give the first flash (firmware/README.md,
PATTERN_GUIDE.md, SUPPORT.md, docs/assembly/) say the same order, the
indexes list the guide pages, the three case remixes and the bundles that
exist, and stale wording (the custom slots, "variant", the dead /flash
page) is gone. CONTRIBUTING.md gets a row for the interactive guide's
copy, and the pull request template asks for the row and the link check.

From #7 (caaf2c1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VaFtEmtx2AkdH1wnbQpV3Z

* docs: the README opens with the fixed lines and three ways in, and First light follows the build

The opening is L1 + L2 + L3 from docs/manifesto.md (Paik moves to Where it
began, in L5's own words), with Play one in your browser, Build one and Get
one assembled under the badges. First light moves below the build, gains the
steps it was missing (unplug before seating, Origin as the sign of success,
Format storage on the first Basics install) and states the J4 rule as data
never power on the DevKit's ports. Stale lines are corrected against the
repo: sound and MQTT shipped in editions, MIDI arrived in 3.9.0, 3.10.5
fixed four requests, and the photosensitivity warning is the manifesto's
full text.

From #8 (bfc4b22).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VaFtEmtx2AkdH1wnbQpV3Z

* docs: BUILD_GUIDE flashes in the Play guide's order, and the README's campaign is still open

BUILD_GUIDE §8.1 now goes download mode first (hold BOOT, tap RST),
Flash Patternflow, the Wi-Fi as a second go after RST, the DevKit seated
with the power bank unplugged, then the panel's own patternflow-xxxx
network (K2 shows its name before the hotspot is up), then the Basics
pack: the Play guide's 01 Flash card for card. §7 no longer seats the
DevKit before it has firmware, so it is Wiring, and 7.4 names the step
that powers up. The README's First light points at §8 again, and Crowd
Supply reads as it stands: past its goal, open until 15 October; the
200% figure stays in the dated timeline. Besoiobiy's case README cites
the renumbered steps.

From #9 (16eca1b).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VaFtEmtx2AkdH1wnbQpV3Z

* ## What changed

**New journal post** — `fine` (ko + en), published 2026-10-13. Not highlighted.

Putting the blog off but not past a week, a hand-written paper diary after months, being selected for Anthropic's Claude startup program, reading and music slipping, AI making time feel more pressing, the interview shoot for the introduction video at the startup center studio, the funding ending in two days, the apartment renovation that still hasn't happened, the visually impaired artist enjoying the Patternflow at the Seoul Station teardown, and parts bought for a workshop.

**Two photos**, resized to 2000 px on the long side:
- the interview studio (faces already pixelated in the source), after the interview paragraph; also the cover, since the author prefers not to lead with a face
- the Seoul Station exhibition before the teardown, after the teardown paragraph

## Notes

- The draft pasted the Korean section twice in `_temp/blog.md`; the second copy was clean and is the one used, so the text matches the English paragraph for paragraph (13 each).
- Verified against the dev server: both language versions return 200, both images serve, and the index hero is this post with the interview photo as its thumbnail. Not in the highlighted rows.
- `dev` and `main` were identical before this commit, so the PR carries only the post.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
BODY
journal: new post "fine"

Post (ko+en), not highlighted: the paper diary, the Claude startup
program, the interview shoot for the introduction video, the
funding ending in two days, the apartment still unrenovated, and the
visually impaired artist enjoying the Patternflow at the teardown.

Two photos at 2000 px: the interview studio (faces already
pixelated), also the cover, and the exhibition before the teardown.
The draft pasted the Korean section twice; the clean second copy is
used.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: the cleanup has a baseline

From #11

* docs: cleanup step 1 is planned

From #12

* docs: the v2 build guide moved to `docs/legacy/BUILD_GUIDE_v2.md`

From #13

* docs: the doc map lists the Korean/English pairs

From #15

* docs: the doc map reads the YAML under `.github/`

From #17

* docs: `CHANGELOG.md` is split by release into `docs/releases/`

From #14

* docs: the community files moved to `.github/` and the audio guide to

From #16

* docs: cleanup step 2 is planned

From #19

* docs: cleanup step 3 is planned

From #20

* docs: the project's writing voice is written down

From #18

* docs: cleanup step 4 is planned

From #21

* docs: the docs index and the repository map catch up with step 1 of the

From #23

* docs: cleanup step 5 is planned

From #24

* web: `check:features` fails when /features and the feature folders

From #25

* web: `check:links-absolute` fails on a GitHub link into the repository

From #26

* hardware: `check:bom` and `check:bom-schematic` hold the BOM to the

From #28

* web: `check:editions` fails when the shelf and the bundles disagree

From #29

* web: `check:presets` fails when a web preset cannot reach a panel

From #30

* web: `check:human-lock` fails a pull request that changes a word of the

From #27

* web: `check:changelog` fails when `CHANGELOG.md` and `docs/releases/`

From #31

* web: every pull request runs the drift checks and the text lock

From #32

* web: `check:roadmap` fails when a /roadmap node and its issues disagree

From #34

* docs: the contributor docs say what CI runs on every pull request

From #35

* web: `check:voice` points at the habits of generated prose, line by line

From #33

* docs: `AGENTS.md` carries the writing-voice rule and the step 2 check

From #36

* docs: the doc map is regenerated at the close of step 2 of the cleanup

From #37

* web: five of the site's twelve lint warnings are gone

From #38

* web: the seven `<img>` lint warnings carry a named exception

From #39

* docs: where the panel size lives, line by line

From #40

* web: pattern Lab's July storage keys say who wrote them and when

From #41

* web: the site's redirects and old routes say what links them

From #42

* web: web CI fails on a lint warning

From #43

* firmware: the MIDI page counts 24 detents a turn, as the encoder has

From #46

* web: the community's old formats say what wrote them and when

From #44

* firmware: the vendored HUB75 driver names the upstream commit it came

From #47

* firmware: the vendored HUB75 driver takes upstream's fix for its config

From #48

* web: a new TODO or FIXME turns the drift checks red

From #49

* web: one file holds the values the firmware and the site both carry

From #45

* docs: the any-resolution RFC has a draft

From #50

* docs: the Linux player has a draft RFC

From #51

* firmware: the firmware reads the values it shares with the site from

From #52

* web: the site's lint and test tooling is on its latest releases inside

From #54

* docs: `AGENTS.md` names `shared/`, the HUB75 driver's `UPSTREAM.md` and

From #57

* docs: the two step 5 drafts are listed in `docs/README.md`

From #55

* web: the site reads the panel size and the knob numbers from

From #53

* web: drizzle is on its latest patch releases

From #56

* docs: the repository maps catch up with step 3

From #59

* web: tailwind is on its latest 4.x release

From #58

* web: react Three is on its latest releases inside the current majors

From #60

* web: zustand is on its latest release inside the current major

From #61

* web: mediabunny is on its latest release inside the current major

From #62

* web: changes from #63

From #63

* web: posthog-js is on its latest release inside the current major

From #64

* web: better-auth is on its latest release inside the current major

From #65

* web: react is on its latest release inside the current major

From #66

* web: next is on its latest release inside the current major

From #67

* docs: the doc map is regenerated at the close of step 3 of the cleanup

From #68

* web: the guide's simulated board takes input that no hand made

From #69

* tools: a virtual panel for the contract vectors

From #70

* tools: the virtual panel answers device state over REST

From #72

* tools: the virtual panel answers the pattern routes

From #73

* tools: contract vectors have a format and a replay

From #71

* web: `check:virtual-panel` runs every preset on the virtual panel

From #75

* tools: the virtual panel speaks MQTT

From #74

* tools: the virtual panel answers `/api/mqtt`

From #77

* tools: the virtual panel speaks OSC

From #78

* docs: the REST contract has its first vectors

From #76

* docs: the MQTT contract has its first vectors

From #79

* tools: oSC has contract vectors

From #80

* docs: the REST vectors reach the rest of the core

From #81

* docs: the MQTT vectors reach the channels and the snapshot, and

From #82

* tools: the virtual panel takes the audio WebSocket

From #84

* tools: the virtual panel speaks RTP-MIDI

From #83

* tools: the audio WebSocket has contract vectors

From #85

* tools: the virtual panel sends MIDI back

From #86

* tools: mIDI has contract vectors

From #87

* tools: mIDI out and `/api/midi` have contract vectors

From #88

* docs: the maps of the tree name the virtual panel and the contract

From #90

* docs: the REST and MIDI specs say one thing where they said two

From #91

* docs: `AGENTS.md` names the virtual panel, the contract vectors and

From #89

* docs: the doc map is regenerated at the close of step 4 of the cleanup

From #92

* docs: changelog for 81 workshop fork PRs

#11
#12
#13
#15
#17
#14
#16
#19
#20
#18
#21
#23
#24
#25
#26
#28
#29
#30
#27
#31
#32
#34
#35
#33
#36
#37
#38
#39
#40
#41
#42
#43
#46
#44
#47
#48
#49
#45
#50
#51
#52
#54
#57
#55
#53
#56
#59
#58
#60
#61
#62
#63
#64
#65
#66
#67
#68
#69
#70
#72
#73
#71
#75
#74
#77
#78
#76
#79
#80
#81
#82
#84
#83
#85
#86
#87
#88
#90
#91
#89
#92

* 시작 — 정리 마지막 기록을 원래 레포 dev 위에 다시 짠다

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpjXsYZbwT5PpwzpNCEEqq

* docs: the doc map lists the six links the cleanup's closing entry adds

The progress log's entries for steps 1 to 5 and the gate link
contract-vectors.md, the doc-parity list, the panel-geometry record and
the two RFC drafts. The cleanup RFC's status line links the log. Each of
the six resolves. The log and the RFC come over with the cleanup's
records, so until they land the map is six rows ahead of the tree.

From #93 (2f094fe).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpjXsYZbwT5PpwzpNCEEqq

---------

Co-authored-by: Mykyta Bilous <belousnikita@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Lee Seunghun <122682380+engmung@users.noreply.github.com>
Co-authored-by: pi-hub <pi-hub@localhost>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants