← AIバイブコーディングTips 一覧へ Quality / Inventory Counting

在庫を数えるだけのスクリプトが、2回とも同じ方向へ事故った

「まだ公開していない動画は何本か」を数えるだけの、単純な自動判定を書いた。1回目はローカルファイルの照合漏れで、2回目はチャンネルに残っていた無関係な過去動画の混入で——原因はまったく別なのに、どちらも同じ「過大カウント」で事故った実例と、そこから引ける教訓。

この記事で手に入るもの

「在庫を数える」という一見単純な処理が、まったく別の原因で2回連続して同じ方向に事故った実例と、次に集計スクリプトを書くときに使える「境界条件を先に書き出す」チェックリスト。

01 — 1回目の事故

「在庫19本」を信じてスキップしたら、実際は2本だった

FLOW(このブログ)はShorts動画も自動生成している。日次タスクの設計はシンプルで、「まだYouTube上で公開していない動画(在庫)」が7本を切ったら新しい台本を書き、7本以上残っていればその日は何もしない。

2026-07-24、この判定タスクが「在庫は十分」と判断してスキップした。判断の根拠は、ローカルの制作フォルダ数(19本)や、動画一覧データの中の「リモートと未マッチのローカルファイル数」(20件)だった。どちらも閾値の7を大きく超えていたので、迷わずスキップした。

ところが実際にYouTube側のリモート状態を取得し直してみると、本当にまだ公開されていない動画(非公開アップロード+予約投稿)はわずか2本だった。閾値を大きく下回っている。原因は二重だった。

01

そもそも正しいデータが同期されていなかった

動画一覧データにはチャンネルごとのリモート同期情報が入る欄があるが、このときFLOWチャンネル分のリモートデータ自体が同期されておらず、別チャンネル(SURPLUS)分しか入っていなかった。判定タスクは仕方なく、手元にあったローカルのファイル数を代用値として使った。

02

その代用値自体も、取りこぼしで水増しされていた

ローカルファイルとリモート動画を突き合わせる処理は、いくつかの決まったファイル名のメタデータだけを読む設計だった。ところが実際にFLOWの自動化が書き出していたファイル名はそのリストに含まれておらず、すでに公開済みのshortのローカルファイルまで「マッチしなかった=未公開」として計上されていた。


02 — 2回目の事故

「正しい指標」に直したら、今度は無関係な動画が紛れ込んだ

1回目の反省を受けて、ローカルファイルの代用値をやめ、YouTube側のリモート状態だけを直接数える方式に切り替えた。「非公開アップロード」と「予約投稿」の件数だけを見る、これなら取りこぼしようがない——はずだった。

翌2026-07-25、今度は別の落とし穴が見つかった。「非公開」というステータスには、このパイプラインが動き出すよりずっと前からチャンネルに存在していた、無関係な私的動画(2019年・2023年にアップロードされたもの)まで含まれていた。これらも同じ「非公開」というだけで在庫としてカウントされ、再び過大カウントが起きた。

修正は、チャンネルごとに「このパイプラインが動き始めた日付」を明示的な定数として持たせることだった。

# 各チャンネルの自動化パイプライン開始日(在庫としてカウントする境界)
PIPELINE_START_DATE = {
    "flow": "2026-07-12T00:00:00Z",
    "surplus": "2026-07-04T00:00:00Z",
    ...
}

def classify_remote(status, published_at, channel_key):
    if status["privacyStatus"] == "public":
        return "published"
    if status["privacyStatus"] == "private" and status.get("publishAt"):
        return "scheduled"
    cutoff = PIPELINE_START_DATE.get(channel_key)
    if cutoff and published_at and published_at < cutoff:
        return "legacy_private"  # 在庫カウントから除外
    return "uploaded_private"

境界日より前にアップロードされた非公開動画はlegacy_privateという別区分に分類し、在庫カウント(uploaded_privatescheduled)から除外するようにして、ようやく収束した。


03 — なぜ同じ方向に転んだか

「厳しく直した」つもりが、また同じ側に倒れた理由

普通に考えれば、1回目が「多く数えすぎた」ミスなら、2回目の修正はむしろ「絞りすぎて少なく数える」方向に振れてもおかしくない。しかし実際には、どちらも過大カウント側で事故った。

理由は、2回とも「在庫として数える対象の境界」を、実装より先に文章として決めていなかったからだ。1回目はそもそも正しいデータソース(リモートのAPI状態)を見ておらず、境界を厳密に決める以前の問題だった。2回目は正しいデータソースを見てはいたが、「このチャンネルの中で、このパイプラインが作った動画だけを対象にする」という時間的な境界を、誰も明示していなかった。

ここが核心。「数える」処理は、実装より先に「何を含み、何を含めないか」を文章で決めておかないと、ひとつ穴を塞いだ隣に、また別の抜け穴が生まれる。今回はたまたま2回とも過大カウント側に転んだが、フィルタ条件の向きが逆であれば、同じ原因の未定義さで過小カウントに転んでいてもおかしくなかった。


04 — 自分の運用に当てはめる

集計スクリプトを書く前に、境界条件を先に書き出す

この手の「境界条件の未定義」は、在庫管理や進捗集計のような「〇〇はあと何本/何件」を数える自動判定全般に共通する。とくにその判定結果が「スキップするか実行するか」を自動で決めるゲートになっている場合、実測とのズレに誰も気づかないまま運用が続いてしまう。

対象期間の境界を決めているかそのデータソースには、仕組みを導入する前からあった無関係な過去データが混ざっていないか。
代用値に頼っていないか直接測れないときの「近似値」が、本当の値とどれだけズレうるかを一度検証したか。
一度は実測で検算したか自動判定の結果を、最初の数回だけでも手動の実測と突き合わせたか。
過大・過小どちらにも転びうると自覚しているか今回はどちらも過大カウントだったが、フィルタの向きが変われば同じ未定義さが過小カウントを生む。