Sparraにはすでに二つの動かし方があった。Claude Codeのセッションからその場で操縦するか、plan → freeze → build → reflect を丸ごと任せて放置しておくかだ。ただ、どちらの方法でも、実際に処理を回しているのは結局自分のノートパソコンだった。今回、その前提を二つの方向から崩した。sparra conduct "<プロンプト>" は新しいヘッドレスのコンダクターで、プロンプトをひとつ渡すとそれをいくつかのユニットに分解し、それぞれを contract-negotiate → generate → cross-model evaluate → decide というループに通す。/sparra-loop で自分が手動でやっているのと同じ流れだが、今度はハンドルを握るのが自分ではない。デフォルトは hybrid ブレインモードで動く。基本のループは決まった手順通りに進み、LLMのコンダクター役が呼ばれるのは、合格ラインぎりぎりの判定や契約がなかなか収束しないときなど、本当に判断が要る数か所だけだ。--auto を渡せば、その判断もすべてブレインに任せられる。
二つ目のピースは、家にいないときにこそ効いてくる。小さなHTTPブリッジをMac上でlaunchdサービスとして動かし、Tailscaleでつながったデバイス、スマホでも別のノートパソコンでも、そこからネットワーク越しに実行をトリガーして様子を見られるようにした。実装で時間をかけたのは「できること」より「絶対にしないこと」の方だ。どんな設定でも公開アドレスにはバインドしない。ヘルスチェック以外の全ルートはbearerトークンを一定時間で検証する。呼び出し元が渡すパスはすべて、許可されたルートの内側に解決されない限りファイルシステムに触れる前に弾かれる。そしてどのエンドポイントも生のロール出力やホールドアウトのテキストを返すことは決してなく、対話型のコンダクターがすでに見ているのと同じ、伏せた要約だけを返す。インストールはコマンド一つで、ランダムなトークンを生成し、ランチエージェントを書き込み、そのトークンを一度だけ表示する。今では判定待ちの状態になっても、自分を待たせているのは端末ではない。スマホから開けるダッシュボードのページだ。
すんなりとはいかなかった。ブリッジがどうにも起動しなくて、しばらく理由が分からなかった。launchdがサービスを起動する環境は、シェルのプロファイルもPATHも普段の対話的なターミナルとは違う、削ぎ落とされたものだと思い出すまでは。だから自分のシェルから手で動かせば普通に動くスクリプトが、バックグラウンドサービスとしては黙って失敗していた。こういうバグは、実際に無人稼働をやらせてみて初めて表に出てくる。そしてそれこそが、今回作ったものの狙いそのものだった。