ビルドが口答えをやめた土曜日
スクリーンショットにならない修正こそが、あなたのランタイムが出荷されるかどうかを決める。RakuAIは1週間を費やして脆さの元を潰し、次の8か月の開発がビルドと言い争わずに済むようにした。
スクリーンショットを生まない類の仕事がある。かつて赤くしていたマシンでもグリーンになるビルドを生む。タイムアウトしなくなるCI実行を生む。原因だったnullポインタが今や境界で捕捉されるため、もう現れなくなるスタックトレースを生む。この土曜日に終わった週は、そういう類の週だった。
Linux上で18個のDLLがグリーンに
ランタイムは18個のネイティブDLLとして出荷される。2週間前まで、これらのDLLはWindowsではきれいにビルドでき、macOSでもきれいにビルドできたが、Linuxではシンボル可視性のエラーで壊れていた。修正は4月11日のPR #1463で着地した。
その物語は地味な種類のものだ。GCCはデフォルトで共有ライブラリ内のすべてのシンボルを可視にする。MSVCはデフォルトで、明示的にエクスポートされない限りすべてのシンボルを不可視にする。ランタイムのコードはMSVCのデフォルトを前提として書かれており、DLL境界を越える必要があるシンボルには__declspec(dllexport)が付けられていた。Linuxではこれらの宣言は何もしないため、すべてのシンボルが可視になってしまい、リンカがDLL内部の正しいバインディングを判別できなくなり、18個のDLLのうち多くがビルドできなくなっていた。
修正はGCCビルド用のコンパイラフラグとしての-fvisibility=hiddenと、エクスポートが必要なシンボルへの明示的な可視性属性だ。差分は小さい。影響範囲は大きい。Linuxビルドは今や第一級のターゲットになった。ランタイムの変更を出荷するエージェントは、今やCIマトリクスによって自動的にLinux上でテストされるようになった。サーバーデプロイ、コンテナイメージ、クラウドビルドファーム、そのすべてが今や手の届く範囲にある。
これは税金控除のような修正だ。プロダクトを前進させることはない。しかし、もはやそれを妨げることもない。それで十分だ。
すべてのLLM呼び出し元へのプロンプト長ガード
書き留める価値のあるもう一つの4月のコミットは、プロンプト長ガードの一掃だ。ランタイムは今、いくつかの場所でClaudeを呼び出している。NPC振る舞いの頭脳、対話モデル、ワールドイベントのナレーター、手続き型コンテンツのシーダー。これらの呼び出し元のそれぞれは、コンテキスト、指示、現在の状態を連結することでプロンプトを組み立てる。これらの呼び出し元のそれぞれは、コンテキストが十分に大きくなればモデルの入力ウィンドウを超えるプロンプトを生成し得る。
素朴な失敗モードは、LLM呼び出しがエラーを返すというものだ。危険な失敗モードは、LLM呼び出しがトランケートされた応答を返し、呼び出し元がそのトランケーションを検知せず、エンジンが不正な形式の答えに基づいて動作してしまうというものだ。文の途中で止まるNPC。何もないところに分岐するダイアログツリー。間違ったパラメータで発火するワールドイベント。
PR #1465は、すべてのLLM呼び出し元にガードを追加した。呼び出し前にトークン数を見積もり、その数が予算を超えていれば送信を拒否し、呼び出し元が反応できる構造化されたエラーを発する。PR #1466は、すべての呼び出し箇所にこのガードが備わっていることを検証するため、コードベース全体を監査した。PR #1467は、次の呼び出し元がこの規律を知れるよう文書を追加した。
これが重要な理由は、ランタイム内でのAI呼び出しがたまにしか起きないものではないからだ。ピーク時には1フレームあたり何度も発生する。呼び出しごとにプロンプトを数トークンずつ増やすようなプロンプト構築のバグは、100フレームでは見えず、1000フレームでは破滅的になる。このガードは、失敗を静かで無制限なものから、大きく境界付きのものへと変える。
APIの名前空間の安定化
3つ目のコミットは3つの中で最も静かだが、おそらく最も重大なものだ。ランタイムは、外部ツール、ダッシュボード、そしてSDKが話しかけるためのREST APIを公開している。このAPIは、それより前にあった内部専用の/api/v1/を引き継いだため、過去6か月間/api/v2/の下で生きてきた。
/api/v2/は間違った名前だ。それはv1が公開されていて置き換えられたことを示唆するが、そうではなかった。それはまた、v3が近く来ることを示唆するが、それもない。PR #1464は、コードベース内のすべての参照と、ドキュメント内のすべての参照を辿り、名前空間を/api/v2/から/api/raku/にリネームした。この名前は今や安定している。後で交渉すべきバージョン番号は存在しない。この名前空間内での後方互換性が契約だ。
これは、正確に一度だけ起こるべきで、外部の開発者がその名前空間に対してコードを書く前に起こるべき類のリネームだ。私たちは間に合って捕まえた。次にチーム外の誰かがそのAPIを叩くスクリプトを書くとき、彼らが目にするのは/api/raku/であり、6か月後にやり直す必要はない。
一掃としてのnullポインタ安全性
ランタイムにおける最新のコミット、2日前の4月30日の58c3a806は「fix(build): resolve all build errors, warnings, and null-pointer safety」だ。このタイトルの中で「null-pointer safety」というフレーズが多くの仕事をしている。その下にある変更は、ポインタがnullになり得てチェックなしに参照外しされていたすべての場所を、コードベース全体にわたって一掃するものだ。
この一掃を引き起こしたパターンは、サンプルアプリケーションの一つからのクラッシュレポートだった。テクスチャローダーがアセットマニフェスト用にnullポインタを渡され、それを参照外しし、クラッシュした。修正はエントリポイントでのチェックだった。監査の問いは、コードベース内の他にいくつの場所が同じパターンを持っているか、というものだった。
答えは数十か所だった。そのすべてが悪用可能というわけではなかった。多くは、呼び出し側のコードがたまたまnullを渡したことがなかったために、一度もヒットしたことのないコードパスだった。それは防御ではない。防御とは、境界でのチェックのことだ。
この一掃はチェックを追加した。ハッピーパスでの振る舞いは変えなかった。失敗パスに対しては、戻り値(RAKU_ERR_INVALID_PARAM)とログ行を追加した。ランタイムは今、かつては静かなクラッシュだったケースにおいて、より騒々しくなっている。騒々しい方が静かよりも良い。ARランタイムにおける静かなクラッシュは、最悪の種類のバグだ。
なぜこの種の週こそ持つべき週なのか
週末サイクルでエンジンプロジェクトをエージェントの部隊とともに運用しているとき、誘惑は新機能を出荷し続けることだ。毎週土曜日の朝、キューは再び満たされる。毎週土曜日の夜、差分は前の週より大きくなっている。このサイクルは前進を報いる。
このサイクルはまた、技術的負債を許容する。過去8か月にマージされたすべてのPRは、ビルド環境、API表面、LLMの入力ウィンドウ、あるいはnull安全性の契約について、何らかの小さな仮定を置いていた。それらの仮定はどれ一つとして、それ単体では間違っていなかった。しかし合わさると、それらはいずれひび割れることになる脆さのリストだった。
前進を止めて脆さの元を修正する1週間は、失われた時間ではない。それは、その後の8か月の前進がビルド、API、LLMの入力、nullポインタと言い争うことなく進められるようになる週だ。時間を無駄にするのはその言い争いだ。このクリーンアップが終わらせたのは、その言い争いだ。
次に来るもの
エージェントたちはこの週末、再びキューに戻っている。次のラウンドの課題は、この土曜日の朝に私が起票しているものだ。それらはもうクリーンアップの課題ではない。機能の課題だ。ビルドはそれらに口答えしないだろう。
それこそが、クリーンなビルドが買ってくれるものだ。スクリーンショットではない。出荷する許可だ。
地味な部分で信頼を勝ち取るランタイム
あらゆるターゲットでグリーン、安定したAPI、静かなクラッシュではなく大きな音の失敗。RakuAIは本番環境が求める規律とともに構築された空間ランタイムだ。パートナーがなぜこの上に構築するのかを見てほしい。