無人PCへ配った監視が「一台も入っていない」ことに14日気づけなかった — 失敗が見えない3つの穴と塞ぎ方
複数の無人端末へ常駐監視を中央配布する構成で、コードが正しくても14日間1台も導入されない状態が起きた。原因はキューの枠が1つで入れ忘れが無言で止まること、-Dry が本番実行すること、実行結果の要約がキーワード依存で空欄になることの3つ。冪等な監視をキュー外の自己修復へ移す線引き、-Dry の副作用棚卸しと回帰テスト、未導入を毎日名指しする日次チェックまでを手順化した。
約4.1万トークンの節約 (API料金換算で約62円分)。 要件定義・技術調査・試行錯誤ぶんのトークンがまるごと不要になります。※ 出品者申告とレビューに基づく推定値。モデル・タスク内容により変動します。
この巻物について
「無人PCへ配った監視が「一台も入っていない」ことに14日気づけなかった — 失敗が見えない3つの穴と塞ぎ方」は、業務自動化カテゴリのAI指示書(MDファイル)です。複数の無人端末へ常駐監視を中央配布する構成で、コードが正しくても14日間1台も導入されない状態が起きた。原因はキューの枠が1つで入れ忘れが無言で止まること、-Dry が本番実行すること、実行結果の要約がキーワード依存で空欄になることの3つ。冪等な監視をキュー外の自己修復へ移す線引き、-Dry の副作用棚卸しと回帰テスト、未導入を毎日名指しする日次チェックまでを手順化した。この巻物をAIに読み込ませると、ゼロから設計・調査する場合に比べて 約4.1万トークン(API料金換算で約62円)・85%のトークンを節約できます。
- カテゴリ
- 業務自動化
- 対応AI
- claude-code、cursor、codex-cli
- ライセンス
- 商用利用可 (再販不可)
- 価格
- 無料
- ゼロから開発時
- 約4.8万トークン
- この巻物使用時
- 約7,000トークン
- 節約量
- 約4.1万トークン (約62円)
- 更新日
- 2026-09-16
使い方 (AIに渡す3つの方法)
いちばん簡単なのはワンライナー。Claude Code のターミナルに貼るだけです。
claude "https://makimono-md.vercel.app/api/v1/files/md-ad798ca6/raw を読み込んで、この指示書どおりに実装して"
中身
無人PCへ配った監視が「一台も入っていない」ことに14日間気づけなかった — 失敗が見えない3つの穴と塞ぎ方
この指示書が解く問題
複数台の無人PC(遠隔操作専用機・サーバ・店舗端末など)へ、常駐する監視ツールを中央から配布する構成を作ったとする。コードはレビューを通り、マージされ、テストも通っている。それでも14日間、1台も導入されていなかった。
エラーは1件も出ていない。ログも赤くない。誰も気づかない。
原因は監視ツール本体ではなく、配布と報告の経路に「失敗が見えない穴」が3つ空いていたことだった。この3つはどんな中央配布の仕組みでも起こりうるので、設計時に先回りして塞ぐ。
前提となる典型構成
各PCのスケジューラ(毎日 03:15)
└→ ポーラー(中央の命令ファイルを取りに行く)
├ A) 日次の自己ヘルスチェック → チャットへ報告
└ B) 中央キュー(JSON)に積まれた承認済みタスクを1つ実行 → チャットへ報告
中央キューはこの形が多い。
{
"runId": "<毎回ユニークな文字列>",
"task": "<ホワイトリストにあるタスク名>",
"targets": "all"
}
各PCは runId 単位で1回だけ処理し、処理済みIDをローカルに記録して二重実行を防ぐ。任意コマンドは実行せず、ホワイトリストのタスク名だけを受け付ける(これはRCEにしないために必須。ここは正しい設計)。
穴1: キューの枠が1つしかなく、入れ忘れが無言で止まる
何が起きたか
上の JSON を見ると task は1つしか書けない。新しいタスクを配るには、前のタスクが全台に行き渡ってから書き換える必要がある。
監視ツールをマージした時、task を切り替える作業が漏れた。キューは別のタスクを保持したまま。各PCは毎晩律儀にポーリングし、前のタスクは処理済みなので何もせず終了する。
- エラーは出ない(正常動作なので)
- 「未導入です」と言う主体がどこにもいない
- 監視ツール自体は完璧に動く。ただしどこにも入っていない
「マージした=配られた」と思い込み、誰も確かめなかった。
塞ぎ方: 状態を保証したいものはキューから外し、自己修復にする
キューは「1回だけ実行したい命令」には向くが、「この状態であってほしい」を保証するには向かない。冪等で読み取り専用のものは、キューを経由せずポーラー本体で毎日自己修復させる。
# 毎日03:15に無条件で走る。タスクが無ければ入れる。あれば何もしない。
try {
if ($repo -and -not (Get-ScheduledTask -TaskName 'MyMonitorTask' -ErrorAction SilentlyContinue)) {
$installer = Join-Path $repo 'tools\my-monitor.ps1'
if (Test-Path $installer) {
& powershell -NoProfile -ExecutionPolicy Bypass -File $installer -Install *> $null
if ($LASTEXITCODE -ne 0) { throw "my-monitor.ps1 -Install exit $LASTEXITCODE" }
}
}
} catch {
# 黙って失敗させない。ログに残す
$log = Join-Path $env:USERPROFILE '.myapp\logs\poller.log'
New-Item -ItemType Directory -Path (Split-Path $log) -Force | Out-Null
$line = '{0} WARN monitor-self-repair-failed reason={1}' -f (Get-Date).ToString('yyyy-MM-ddTHH:mm:ssK'), ($_.Exception.Message -replace "[\r\n]+", ' ')
[IO.File]::AppendAllText($log, $line + [Environment]::NewLine, (New-Object Text.UTF8Encoding($false)))
}
これで:
- キューを別用途に戻しても監視は生き続ける
- 誰かがタスクを消しても翌日復活する
- 枠の取り合いが起きない
線引き: 何を自己修復に載せてよいか
| 種類 | 自己修復 | 理由 |
|---|---|---|
| 読み取り専用の監視・計測 | ✅ 載せる | 冪等で、入っていて困る人がいない |
| ログ収集・ヘルスチェック | ✅ 載せる | 同上 |
| 設定変更(省電力・上限値・レジストリ) | ❌ キュー経由 | 挙動が変わる。明示的な判断を人に残す |
| データ削除・移行 | ❌ キュー経由 | 不可逆 |
自動化の範囲を「観測は自動・変更は承認」で切ると、安全性と手間削減が両立する。
キューの枠を空ける前の必須確認
前のタスクがまだ全台に行き渡っていない状態で task を書き換えると、未処理のPCは永久にそれを受け取れない(次に来た時には別のタスクになっている)。
書き換える前に必ず、前のタスクが自己修復へ移行済みかを実物のコードで確認する。
# ポーラー本体に、前のタスクのキュー外での自己修復が入っているか
grep -n "PreviousTaskName" tools/poller.ps1
移行済みなら枠を空けても取り残しは出ない。移行していないなら、先にそのタスクを自己修復へ移してから枠を明け渡す。
穴2: -Dry / --dry-run が本番実行する
何が起きたか
配布前に安全確認しようとポーラーを -Dry で実行した。ところが -Dry はチャットへの送信だけを抑止する実装だった。
function Post($msg) {
if ($Dry) { Write-Host "[DRY POST] $msg"; return } # ← ここだけガードされている
Invoke-RestMethod -Uri $webhook -Method Post -Body $bytes
}
# ...一方こちらは無防備
if (($done -notcontains $runId) -and $match) {
Add-Content -Path $procF -Value $runId # ← runId を消費する
if ($WL.ContainsKey($task)) {
$res = & $WL[$task] # ← タスク本体が実際に走る
結果、「安全のため試した」つもりが、
- そのPCで本番のタスクが実行され
- runId が消費され(=本番の夜には「処理済み」として飛ばされる)
「安全のために叩くコマンド」が、最も危険な操作になっていた。
塞ぎ方
-Dry は「送信しない」ではなく「副作用を持つ呼び出しを全部止める」と定義し直す。止めた代わりに予定を表示する。
if ($Dry) {
Write-Host "[DRY SKIP] runId=$runId を処理済みとして記録する予定"
Write-Host "[DRY SKIP] タスク『$task』を実行する予定"
} else {
Add-Content -Path $procF -Value $runId
$res = & $WL[$task]
}
副作用の棚卸しは、少なくとも次を対象にする。
- 状態ファイルへの書き込み(処理済みID・実行ガード・最終実行時刻)
- 外部への送信(チャット・API・メール)
- スケジュールタスク/サービスの登録・削除
- 設定ファイルの書き戻し(BOM除去などの「自己修復」も副作用)
- 外部の表・DBへの追記
そして回帰テストで固定する。-Dry を実行した後に、状態ファイルが1バイトも変わらないことを assert する。
test('-Dry は処理済みファイルを変更しない', async () => {
const before = readFileSync(processedFile, 'utf8');
await runPoller(['--dry']);
assert.equal(readFileSync(processedFile, 'utf8'), before);
});
一般則
**-Dry / --dry-run / --check を名乗るものは、「何を抑止しているか」を必ず実測で確かめてから使う。**名前を信用しない。特に他人が書いたスクリプトでは、ドライランが「表示だけ差し替えた本番実行」であることが珍しくない。
穴3: 実行結果の要約がキーワード依存で、成功も失敗も空欄になる
何が起きたか
タスクの実行結果をチャットへ流す部分が、こうなっていた。
# 実行結果から「それらしい行」だけを拾って要約にする
$sum = ((($res -split "`n") | Where-Object { $_ -match '結果:|OK |NG |完了|エラー|error' } | Select-Object -Last 3) -join ' / ')
Post "▶ **[$label]** タスク『$task』実行 (runId=$runId): $sum"
一方、新しく足したタスクの出力はこうだった。
| 出力 | 上の正規表現に拾われるか | |
|---|---|---|
| 成功時 | タスク MyMonitorTask を登録しました | ❌ |
| 失敗時 | タスク登録に失敗しました: <理由> | ❌ |
どちらも該当語を含まない。結果、チャットにはこう届く。
▶ **[PC-01]** タスク『my-monitor』実行 (runId=xxx):
コロンの後ろが空。成功したのか失敗したのか分からない。
塞ぎ方
正規表現を広げるのではなく、出力する側を規約に合わせる。既存の他タスクへの影響がゼロで安全。
Write-Host "完了: タスク $taskName を登録しました (5分ごとに実行)"
# ...
Write-Host ("エラー: タスク登録に失敗しました: " + $_.Exception.Message)
検証は「同じ正規表現に通す」
目視で確認しない。本番と同一の抽出ロジックに実際の出力を流し込み、空でないことを確かめる。
$res = & powershell -File .\my-monitor.ps1 -Install 2>&1 | Out-String
$sum = ((($res -split "`n") | Where-Object { $_ -match '結果:|OK |NG |完了|エラー|error' }) -join ' / ')
if ($sum.Trim()) { "OK: 『$($sum.Trim())』" } else { "NG: 要約が空" }
一般則
**中央キューに新しいタスクを足す時は、その出力が既存の要約抽出に引っかかるかを必ず確認する。**要約が「賢く拾う」設計になっている仕組みは、新しい出力を静かに落とす。
穴を塞いだ後も残る罠: マージした修正は「その日の実行」には効かない
配布の順序を確認する。典型的にはこうなっている。
03:15 ポーラー実行(中央キューを処理)
03:20 ツール同期(リポジトリの最新を各PCへ取り込む)
**ポーラーが先、同期が後。**つまり今日マージした修正は、今日 03:15 のポーラーには入っていない。入るのは翌日以降。
さらに悪いことに、今日の 03:15 で runId が消費されてしまうと、修正が効いた頃にはもうそのタスクのメッセージは出ない。今回まさにこれで、報告を直す修正がロールアウト確認に間に合わなかった。
対策は2つ。
- ロールアウトの前日までに関連修正を全部マージし終える
- 確認は「実行時のメッセージ」ではなく、毎日走る自己チェックの結果で行う
2のほうが本質的に強い。次項。
一番効くのは「未導入を毎日名指しする」経路
「送らない」だけの修正は後退になる。無駄な通知は消えるが、未導入という事実まで見えなくなるからだ。
正しくは、既存の日次ヘルスチェックに検査項目を1つ足す。
{
"checks": [
{
"kind": "scheduled-task",
"name": "MyMonitorTask",
"label": "監視 常駐タスク(5分ごと)",
"repair": "powershell -File <repo>\\tools\\my-monitor.ps1 -Install"
}
]
}
これで未導入のPCは、毎日の設定チェックにこう出る。
⚠ **[PC-03]** 日次設定チェック: OK 34 / NG 1 … NG: task:my-monitor - 監視 常駐タスク(5分ごと) (修復: ...)
- PC名付きで、どの端末が未導入か分かる
- 修復コマンド付きで、見た人がすぐ直せる
- 既存メッセージに1行増えるだけなので通知の総数は増えない
- キューにも実行結果にも依存しないので、上の穴3つが全部塞がっていなくても機能する
「導入できたか」を実行時の成功メッセージで確認しようとすると、メッセージが1回しか出ないので取りこぼす。状態を毎日確認する経路を1本持つほうが確実。
チェックリスト
無人の複数端末へ常駐するものを配る前に、これを上から確認する。
- 中央キューの枠は1つか。1つなら、冪等で読み取り専用のものはキュー外の自己修復へ移す
- 枠を書き換える前に、前のタスクが自己修復へ移行済みか実物のコードで確認した
- 自己修復の失敗はログに残るか(
catch {}で握り潰していないか) -
-Dryは副作用を全部止めるか。状態ファイルが1バイトも変わらないことをテストで固定したか - 新タスクの出力(成功時・失敗時両方)が、本番と同一の要約抽出に実際に通ることを確認したか
- 未導入を毎日名指しする経路があるか(日次ヘルスチェックへの検査項目追加)
- ポーラーと同期の実行順序を確認し、ロールアウト前日までに関連修正をマージし終えたか
- 設定を変えるタスク(挙動が変わるもの)を、観測タスクと同じ自動化レベルに混ぜていないか
この設計の根っこにある考え方
今回の14日間は、どの単体テストでも見つからない。コードは全部正しく動いていた。問題は「動いていないこと」を誰も言わない構造にあった。
無人システムで本当に怖いのは異常そのものではなく、異常が誰にも届かないこと。だから設計の順序はこうなる。
- まず「状態がこうであってほしい」を毎日名指しで検査する経路を作る
- そのうえで自己修復を足す
- 一回限りの命令だけをキューに載せる
この順序を逆にして「キューで配る」から始めると、今回と同じ穴に落ちる。
よくある質問
+「無人PCへ配った監視が「一台も入っていない」ことに14日気づけなかった — 失敗が見えない3つの穴と塞ぎ方」とは何ですか?
複数の無人端末へ常駐監視を中央配布する構成で、コードが正しくても14日間1台も導入されない状態が起きた。原因はキューの枠が1つで入れ忘れが無言で止まること、-Dry が本番実行すること、実行結果の要約がキーワード依存で空欄になることの3つ。冪等な監視をキュー外の自己修復へ移す線引き、-Dry の副作用棚卸しと回帰テスト、未導入を毎日名指しする日次チェックまでを手順化した。
+どれくらいトークン(費用)を節約できますか?
ゼロから開発すると約4.8万トークンかかりますが、この巻物を使えば約7,000トークンで済みます。差し引き約4.1万トークン(API料金換算で約62円)・85%の節約です。
+どうやって使いますか?
無料です。MDファイルを Claude Code などのAIに読み込ませるだけ。ワンライナーをターミナルに貼れば実装が始まります。要件定義や技術調査を省いて実装だけにトークンを使えます。
+どのAIツールに対応していますか?
claude-code、cursor、codex-cli に対応しています。
+商用利用できますか?
ライセンスは「商用利用可 (再販不可)」です。
🤝 自分でAIを動かすのは、まだ不安…という方へ
この巻物の内容を、AIを使うプロに丸ごと任せることもできます。姉妹サービスAI代行堂なら「LINEで頼むだけで、仕事が完成」。
関連する巻物
Google Meet 自動参加&動画配信Bot 開発指示書
指定した時刻に Google Meet へ自動参加し、動画を再生しながら画面共有する Bot を、Claude Code に一発で作らせる開発指示 MD。朝会の定例動画配信・ウェビナーの自動放送に。
受信メール添付を案件フォルダへ自動取込するパイプライン
メールを読むアプリとドライブに書くアプリが別、という現実的な構成で顧客メールの添付を案件フォルダへ無人保存する設計。権限追加を避ける理由、実行時間制限下の予算3本立て、二重の重複防止、base64url/行数上限/変換判定などの実装罠、案件と顧客のマッチング、名寄せは候補提示+人の承認にする型まで。
Gmail 自動仕分け&返信ドラフト生成MD
受信メールを AI が分類 (要返信/情報/営業/スパム) してラベル付けし、要返信メールには返信ドラフトまで自動生成する仕組みを作らせる指示書。DWD (ドメイン全体委任) 設定手順込み。
この巻物、誰かのトークンも救えます
𝕏 で節約レシートをシェア