miniに実装を任せた結果 マージまで4秒、壊れたmainが49分残った

決算書類のPDF2本を、Mac miniの常駐エージェントに任せた。仕様書を書いて渡し、実装から修正までminiの仕組みで回した結果をまとめる。
結果から書く。仕様書を入れてから1時間25分で、実装のPRが出た。そのPRは、型エラーとテスト失敗5件を持ったまま、作成から4秒でmainに入った。 直ったのは49分後。ただ、直ったあとのPDFを開いたら、欄の番号がすべて「☒」だった。
任せたもの
次の2本をminiに渡した。どちらも、仕様書を先にaccounting-app/docs/02_detail_design/へ置いてある。
| Issue | 中身 | 仕様書 |
|---|---|---|
| #1801 | 別表十四の値出力PDFと、画面のボタン、API | 25番 |
| #1802 | 勘定科目内訳明細書PDFを表形式にする | 26番 |
仕様書の末尾に、完了条件をコマンドで書いた。
- 追加したテストがすべて通る(
npx vitest runで指定の2ファイル) npx tsc --noEmit -p .で、src/middleware.ts以外に型エラーがないnpx eslintで、追加・変更したファイルに指摘がない- 画面の操作は未確認でよい。確認できていないことをPRに書く
miniの経路は、実装→合図のファイルを1枚置く→ホストの固定スクリプトがpush・PR作成・squashマージ、という形だ。鍵を渡さない作りは前に書いた。
その日の時系列
4秒でmainに入った
14時35分にPR #1815が出た。451行の追加、5ファイル、コミット1つ。PR本文には「マージは人が判断します。 内容を確認してください。」と書いてある。マージされたのは、その4秒後だった。
人が判断する前に入った理由は、ホスト側のスクリプトの作りにある。合図を検知すると、push・PR作成のあとにそのままgh pr merge --squashまで進む。止めるスイッチ(AUTO_MERGE=0)はあるが、既定は有効だ。

支流(PR)の水門が開けっぱなしだと、濁りは、誰も見ないうちに本流(main)へ入る。ホスト側の検査が見ているのも、次の4つだけだった。
- mainへのpushでないか
- ブランチ名の接頭辞が許可の範囲か
- 秘密情報らしいファイルが入っていないか
- 変更が空でないか
コードが動くか(tscとテスト)は、どこでも見ていない。 仕様書に完了条件を書いても、実行する役が誰もいなかった。
mainに入っていたもの
15時13分、mainでtscと指定テストを実行した。#1815の変更ファイル4つに型エラーがあり、テストは5件落ちた。
| 場所 | 起きていたこと |
|---|---|
| PDF生成 | createDocWriter()は非同期で値を返すのに、待たずに使っている |
| PDF生成 | DocWriterに存在しないdrawTableを呼んでいる |
| API | requireCapabilityに渡す引数の型が違う(NextRequestではなくRequest) |
| API | 存在しないプロパティ(capital・companyName)を読んでいる |
| API | Uint8Arrayを、そのままNextResponseへ渡している |
| テスト | 存在しない入力項目(enterpriseTax)を渡している |
| テスト | viとbeforeEachをimportしていない |
2行目のdrawTableは、仕様書に「#1802で追加する」と書いた関数だった。別のIssueで作る予定の関数を、#1801の実装が先に呼んでいた。 仕様書に書いてあった情報が、「この回では使うな」ではなく「あとでできる」としてだけ読まれたことになる。
next.configに型エラーを無視する設定は入れていないので、この状態でデプロイすればnext buildが落ちる。ただ、Vercelの自動デプロイは止めてあって、デプロイはこちらが明示的に起動する。mainは49分間壊れていたが、本番には何も出なかった。 守ってくれたのは、miniの検査ではなく、デプロイが手動だったことだ。
修正も、mini上で回った
直したのは15時25分の#1816(7ファイル、+464/−384)と、15時35分の#1817(#1802の実装、5ファイル、+289/−20)だ。どちらも同じ合図の仕組みを通って、PR作成から数秒でmainに入った。
ただし、直したのはminiのローカルLLM(qwen3-coder:30b)ではない。mini上でCodexが引き継いだ。 ローカルLLMのモデル自体は動いていて、短い応答は返る。一方、OpenClawからのエージェント要求は、会話の圧縮のあとで失敗した。原因は特定できていない。
つまり今回の「修正を任せた」は、正確には「修正を戻す回路(worktree→合図→PR)は使えた。ただし、戻した先は別のモデルだった」だった。
Codexの報告には、通したものと、通していないものが分けて書いてあった。
- 通した: 指定テスト(#1816で11件、#1817で13件)、tsc、ESLint。#1817は、PDFのヘッダーと80明細の複数ページ化まで確認
- 通していない: PDFの目視、画面操作、本番デプロイ
もう1つ、小さな手戻りがあった。最初の合図は、空のファイルだった。ウォッチャーは1行目をコミットメッセージとして読むので、空の合図は破棄される。1行目にコミットメッセージを書いて、置き直した。
テストは通ったが、PDFの欄番号は「☒」だった

Codexが「未実施」と書いた、PDFの目視をこちらでやった。別表十四を実際に出力して開くと、欄の番号(①〜⑥)が「☒」になっていた。
原因は、PDFに埋め込んでいるフォントに丸数字の字形がないことだった。無い文字を描くと、エラーも警告も出ず、「☒」になる。 tscも、vitestも、ESLintも通る。通ったのは、PDFの「見た目」を何も見ていない検査だったからだ。
PDFを作るソース33ファイルの文字列を、フォントの字形と照合した。
| ファイル | 文字 | 出力 |
|---|---|---|
| 別表十四のPDF | ①〜⑥ | 欄の番号が「☒」(今回の出力) |
| 請求書PDF | ※ | 注記の印と凡例が「☒」(既存のコード) |
| 貸借対照表PDF | ※ | 警告の頭が「☒」(既存のコード) |
| 手入力確認PDF 2本 | □ | チェックリストの印が「☒」(既存のコード) |
最初の1個は今回のものだが、残りは前からあるコードだった。 miniに任せたことで見つかったのではなく、出力を開いたことで見つかった。置き換えと再発防止のテスト(pdfGlyphCoverage.test.ts)をPR #1818で入れた。仕組みと対処はWIKIにまとめた。
全体レビューで出た指摘は、どちらの側にもあった
翌日、#1800配下の全体レビューをした。確認できた指摘9件のうち、6件を#1821で直した。
| 区分 | 件数 | 例 |
|---|---|---|
| miniの経路で作った別表十四のAPI(Codexの修正を含む) | 2件 | PDF生成の失敗でHTMLのエラーを返す。標準と異なる税率の登録のまま計算していた |
| 同じ日にClaude Codeで書いた様式転記・概況説明書 | 4件 | PDF画像の取り出しで処理時間が2乗で増える。認証より先に本文をメモリへ読む |
任せたほうだけが穴だらけだった、という結果ではない。 任せなかった側にも、同じくらい出た。
違ったのは、マージの前に人の目が入ったかどうかだ。Claude Codeで書いた#1810〜#1814は、マージの前にCodexの静的レビューを通し、重大な指摘が3件出たので直してから入れた。miniの#1815は、そのレビューの前にmainへ入っていた。それでも、レビューを通した側に4件残った。レビューは穴を減らすが、ゼロにはしない。 だからこそ、通る関門は多いほうがいい。
結論: どこまで任せられたか
| 工程 | 結果 |
|---|---|
| 仕様書つきの小さなIssueから、動く骨格のPRまで | 任せられた(1時間25分。値の組み立ては、修正後に昨年度の提出書類と一致) |
| 失敗の差し戻しと修正のPR化 | 任せられた(検知の12分後と22分後にmainへ。ただし戻し先はCodex) |
| 「完了」の判定 | 任せられなかった(完了条件を実行する役がいない) |
| マージ | 任せられなかった(4秒。人の判断の前に入った) |
| 出力物の検品 | 任せられなかった(通ったのに「☒」) |
実装は任せられる。任せられないのは、「できた」を決める工程だ。
次の手(まだ実装していない)
次のIssueを任せる前に、次の3つを入れる。どれも未実装で、効くかは実測してから書く。
- マージの前に、完了条件を実行する。 合図を受けたホスト側のスクリプトが、worktreeで
tscと指定のテストを実行し、落ちたらPR作成で止める。仕様書に書いた条件を、書いた側ではなく、通す側が実行する - 自動マージを切る。 9/24に直した導入手順を、mini側にまだ入れ直していない。10/4時点のウォッチャーの設定には
AUTO_MERGEが書かれておらず、既定の自動マージのままだ。止める手段は、止まるところまで通して確かめる - 出力物は、人が開く工程を残す。 PDFは生成して目視する。見つかった「☒」は、再発防止のテストへ落とす
仕様書にも1行足す。「ほかのIssueで作る予定の関数は、この回では呼ばない」。 書いていなかったから、先取りされた。
手順と挙動はWIKIに置いた。
- PDFのフォント欠落 — 「☒」の原因、置き換え表、再発防止のテスト
- 鍵を渡さないpush設計 — 合図1枚でmainへ入る経路の作り
この経路が実装を任せる先は、ちょうぼっちを含むこのリポジトリのコードだ。前の記事はAIに鍵を渡さず、合図1枚でmainへ、同じ「できたと言われたら生成物を見る」の話はMCPのツールが見えていなかった日にある。