見出し画像

92日ひとりで書いてきた OSS に、初めて外部からコードが届いた — CodeRouter v2.9.4、自分では気づけなかった KV キャッシュ破壊を直してもらった話

TL;DR: 初コミットから 92 日、ひとりで書いてきた CodeRouter に、初めて外部の人からコードが届いた ── PR #75、作者は firelzrd 氏(Linux の BORE CPU スケジューラ作者 Masahito Suzuki 氏)。中身は、自分では気づけていなかった不具合の修正だった。Claude Code CLI は 2.1.154 以降、会話途中に `role:"system"` のリマインダを送る。CodeRouter はこれを毎ターン先頭へ巻き上げていて、プロンプト冒頭が毎回変わり、llama.cpp / LM Studio 系の KV キャッシュが丸ごと無効化されていた(PR 作者の実測で再利用可能プレフィックスは 11.8%)。Opus 1 体で精査し、Medium 1 件を宿題に残してマージ(`91b2687`)。同じ日に作った Launcher デバイス選択ベンチスイープ (beta)v2.9.4 で出荷。全体 1905 passed


あらすじ — 36 話目です

初コミットは 2026-04-19。本記事の作業日は 2026-07-19、初コミットから 92 日目です。前話(第 35 話)は 07-12 の v2.9.1。

  • 第 1〜9 話 (v1.8 → v2.2):「動く」→「壊れない」→「壊れても自分で直る」

  • 第 10〜21 話 (v2.3 → v2.6.0):Plugin SDK / Launcher / 上流の事故をフィルタで受ける

  • 第 22〜26 話 (v2.7.0 → v2.7.6):AI レビュー軍団で 26,656 行を総ざらい / 空応答フォールバックの実測 / MTP 対応

  • 第 27〜31 話 (v2.7.7 → v2.9.0):外部エージェントを backend に / agent_cli を plugin へ切り出し

  • 第 32〜35 話 (〜v2.9.1):サブエージェント E2E / grok の空応答修正 / launcher にモデル自動スワップを自前実装

  • 第 36 話 (本記事・v2.9.4)92 日目にして、初めて外部からコードが届いた

通低音はずっと「机上で動くはず → 実機が裏切る → 直す」でした。今回はそこに初めて自分以外の目が入ります。


通知が来た

GitHub の通知に、見慣れないユーザー名があった。`firelzrd` から Pull Request。CodeRouter は 92 日間、著者が自分と自動化ボットしかいないリポジトリだった。そこへ、外部フォークから PR が 1 本立った。

タイトルは `fix(translation): keep mid-conversation system messages in place`。firelzrd 氏は Linux の BORE CPU スケジューラの作者 Masahito Suzuki 氏で、カスタムカーネル界隈では広く知られた人だ。変更は 2 ファイル、+76 −18、コミット 1 個。中身を読む前に、ひとりで積み上げてきたものに外から手が伸びてきた、その事実のほうが先に効いた。


直されたのは、自分では気づけていなかった穴

Claude Code CLI は 2.1.154 以降、会話の途中に `role:"system"` のメッセージ(system-reminder)を `messages[]` の中へ差し込んでくる。この挙動は vllm や本家 claude-code の issue でも複数報告されている回帰だ。CodeRouter の翻訳層は、この途中の system をトップレベルの `system` フィールドへ巻き上げていた。

問題はここだ。system-reminder は毎ターン中身が変わる。それを先頭へ動かすと、プロンプトの冒頭が毎回変化する。llama.cpp や LM Studio のプレフィックスキャッシュは「最長共通プレフィックスから先だけ再計算する」仕組みなので、冒頭が変われば実質すべてが再処理になる。PR 作者の実測(LM Studio + Qwen3.6-35B、Claude Code 2.1.211)では、再利用できたプレフィックスはわずか 11.8%242,419 文字を毎回計算し直していた。素の Anthropic API 直結なら system は不変 ── 犯人は CodeRouter 側の変換だと作者はすでに切り分けていた。

修正はきれいだった。`normalize_message_roles()` を位置で分岐させ、先頭の system は従来どおり巻き上げ、ターンが出たあとの途中の system はその場で `role:"user"` に変換して動かさない。冒頭さえ動かなければ、キャッシュは守られる。


レビューして、マージして、宿題を 1 つ残した

外から来たコードは鵜呑みにしない。いつもの体制 ── Opus 1 体を壊すつもりで精読させ、base / PR 適用後 / マージ後の 3 状態でテストを回した。

所見は Medium 1 件と Low 2 件。Medium は、`tool_use` と `tool_result` が隣接する並びで途中 system を user 化するとブロック対応が崩れうるエッジケースで、テスト未カバーだった。稀な条件で修正本体を損なうものではないが、テストが無いのは事実だ。だからここは宿題として残すと決めた。

初回コントリビュータの PR は、ブランチ保護でワークフローの実行承認が要る。承認 → CI 緑 → マージという流れを、初めて他人のコードで踏んだ。マージコミットは `91b2687`、マージ後の全体テストは 1905 passed。git の全履歴でも、非所有者・非ボットの著者はこの 1 人だけ ── 外部の人からコードが届いたのは、これが初めてだった。


同じ日、自分の側で作っていたもの

奇遇なことに、この PR をレビューしていたのと同じ日、自分は Launcher のデバイス選択を作っていた。`--list-devices` でデバイスを検出し、VRAM 付きチェックボックスで選ばせ、`--device` と `--tensor-split` を生成する(5090 + 3090 なら比率 0.57, 0.43 を自動提案)。GUI と Web の両方に載せ、共通ロジックは新規モジュール `launcher_devices.py` へ切り出した。何も選ばなければ生成コマンドは従来とバイト単位で同一だ。

実機が、公式ドキュメントに無い顔を見せた。うちの Linux 機(RTX 5090 + RTX 3090)を CUDA と Vulkan の両対応でビルドすると、`--list-devices` が 5 デバイスを返す。CUDA0=5090、CUDA1=3090、Vulkan0=3090、Vulkan1=5090、Vulkan2 が iGPU の Radeon ── 同じ物理 GPU が二重に列挙され、番号の対応まで逆になる。うちの実機での観測だ。Mac (M3 Max) では `BLAS: Accelerate` が 0 MiB で出るので、0 MiB のデバイスは除外した。launcher 関連のテストは 246 → 372 に増えた。


ベンチスイープ (beta) と、CVE 2 連発

もう 1 つ、ベンチスイープ (beta) を載せた。構成リストを、起動 → readiness → 外部ベンチ → 停止、の順に回す仕掛けだ。計測そのものは自作の別ツール llmbench(`zephel01/swe-bench`)に委ね、launcher は回す枠だけを持つ。まだ beta だ。

小ネタを 1 つ。マージ直後の CI で cve-audit が赤くなった。click 8.3.2 の PYSEC-2026-2132(修正 8.3.3)を pip-audit(PyPA DB)が検出。click を 8.4.2 に上げたら、今度は OSV-Scannerjson-repair 0.59.10 の GHSA-xf7x-x43h-rpqh(CVSS 7.5、修正 0.60.1)を拾った ── こちらは pip-audit が素通しした(PyPA に未ミラー)。スキャンを pip-audit と OSV の多層にしていたおかげで、片方が見逃したものをもう片方が捕まえた。json-repair 下限も `>=0.60.1` へ上げ、対応後も 1905 passed


まとめ

92 日、ひとりで書いてきた。レビュー係を「雇う」話(第 22 話)はしてきたが、全部自分の指揮下のエージェントだった。今回は違う ── まったく外の人が、自分では見えていなかった穴をコードで直してくれた。しかもそれが、ローカル LLM のいちばん地味で効くところ ── プロンプトキャッシュの再利用率 ── だった。この v2.9.4 でいちばん記憶に残るのは、間違いなく届いた PR 1 本のほうだ。

ひとりで作っている OSS に初めて外からコードが届いた日を、あなたはどう記録しますか。外部コントリビュータを迎えるとき、レビューの温度感 ── どこまで直させ、どこを宿題に残すか ── を、あなたならどう決めますか。よかったらコメントで聞かせてください。


CodeRouter は MIT ライセンスの OSS です: https://github.com/zephel01/CodeRouter
`pip install coderouter-cli` / `uvx coderouter-cli serve` で動きます。

#CodeRouter #ローカルLLM #llamacpp #LMStudio #OSS #個人開発 #ClaudeCode #KVキャッシュ #プロンプトキャッシュ #OSSコントリビューション

いいなと思ったら応援しよう!

zephel01 サーバー代とコーヒー代になります☕ 役に立ったら応援よろしくお願いします!