Kristopher Baker iOSを土台に · プロダクトシステム · AI支援ワークフロー
← Sparra

shipped · 2026.07.14 · 約4分

見ていなくてもSparraを信頼して動かせるように

ヘッドレスのコンダクターとHTTPブリッジを出した後、その日の残りの時間は、実際に無人で動かしてみて初めて見えてくる隙間を埋めることに使った。まず、Sparraはソースコードを直接いじる以外に、動いている最中に自分の側から何かを呼び出す手段がなかった。そこでscriptHooksを追加した。フェーズの開始・終了、実行の開始・終了、ユニットの開始・終了、そして判断待ちで一時停止する瞬間、この7つのライフサイクルイベントそれぞれに、設定でシェルコマンドを紐づけられる。開始イベントに登録したフックはゲートで、失敗やタイムアウトがあればそのフェーズ・実行・ユニットはそこで中断し、悪い前提のまま突き進むことはない。終了イベントのフックはベストエフォートにしてあるので、通知用のスクリプトが壊れていても、すでに成功した実行まで巻き添えにはならない。環境変数の名前空間は呼び出しごとにクリアされるので、あるフックへの回答が次のフックに漏れることもなく、判断待ちで一時停止したときに発火する境界は、スクリプトフックとブリッジのダッシュボードイベントの両方を同時に鳴らすようにしたので、結局同じ出来事のために通知の経路を二つ作らずに済んだ。

次に手を付けたのは、ブリッジのダッシュボードそのものだった。実際に想定していた使い方、つまりノートパソコンから流した実行、電車でスマホからトリガーした別の実行、そのどちらも開始していないブラウザのタブでダッシュボードを見る、という状況になった途端に立ち行かなくなった。ダッシュボードがジョブの存在を知る手段は、そのジョブ自身のステータスを毎ティック個別にポーリングすることだけで、動いているジョブの数だけリクエストが増え、どこか他所で始まったジョブはそもそも見えていなかった。これを、ブリッジ側に追記専用のイベントログを持たせGET /events?since=<cursor>で置き換えた。1ティックにつき1リクエストで、全ジョブ分の変化がまとめて返ってくる。今どのジョブを開いているかに応じたログのストリーミングだけは引き続き個別のエンドポイントを使っているが、ジョブの発見とステータス表示は共有のフィードに乗せた。ポーリングのループはバッチを適用する瞬間に最新のカーソル値を読み直すようにしてあるので、遅れて届いた古いレスポンスのせいで画面上のジョブの状態が巻き戻ることもない。

最後の隙間はもっと小さいが、見落としやすいものだった。生成したコードを契約と照らして採点するクロスモデルの評価役が、たまに自分自身のターン数や予算の上限に達して途中で強制終了させられ、パース可能なJSONを一度も返せずに終わることがあった。ループ側には、これを本当にレビューで落ちた場合と区別する手段がなく、どちらも一律で失格扱いにしていた。そこでwriterロールですでに使っていたのと同じ回復処理を評価役にも入れた。もう一ターンだけ、テキストのみで、二回続けて時間切れになれないくらいきつく上限を絞り、ループが期待する形の判定だけを求め直す。それでもパースできない答えが返ってきたときにはじめて、本当の失格として扱う。この3つはどれも、自分が端末を見ながら手で動かしている間は問題にならなかった。実際にSparraを、自分がその場にいなくても信頼して動かせるようにしてみて、初めて表に出てきたものばかりで、それこそがヘッドレスのコンダクターを作った理由そのものだった。