自動化の失敗には、派手な例外で止まるものと、起動前に入力が壊れていて何も始まらないものがあります。
今回扱うのは後者です。カード側には、今回の題材が次のように記録されていました。
"id": "AUTO-W3-CT-QIITA-20260902-pm"
emitのslot注入が publish カードの参照パスを二重接尾辞に壊し、配信5本が起動前に死んでいた
この時点で見るべき対象は、投稿APIでも、Qiitaの本文でもありません。content から publish へ渡すカードIDと参照パスの整合です。
事象
配信レーンでは、Qiita は技術ストックとして扱われています。運用メモにも次のようにあります。
Qiita | @SciCos | 稼働 | 技術ストック・信頼蓄積→送客
同時に、投稿上限は次の設定です。
"qiita": { "per_day": 1 }
つまり、1日に1本の枠を使う publish カードが、参照先不整合で起動前に落ちると、その日の配信枠を丸ごと失います。
今回のカードにも、前段の publish が失敗する前提が明示されています。
既存のQIITA publishは参照先が存在せず必ず落ちるので撃ち直し
ポイントは「記事が悪い」のではなく、「記事へ到達するための参照パスが壊れていた」ことです。
再現条件
再現条件は、IDやファイル名を文字列として組み立てる処理で、同じ意味の接尾辞を2回付けられる状態にしてしまうことです。
今回の作業カードは、成果物を次のように指定しています。
"deliverable": "qiita-posts.md"
一方で、過去のQiita公開ログには、上流contentカードとpublishカードが別IDとして残っています。
"path":"_share/outputs/banto/2026-09-01/AUTO-C1-CT-QIITA-20260901-am/AUTO-C1-CT-QIITA-20260901-am-qiita-post.md"
"source":"AUTO-C1-PUB-QIITA-20260901-am"
この構造では、CT 側の成果物名と PUB 側の参照先が少しでもずれると、publish は本文を読む前に失敗します。
再現しやすいのは、次のような考え方を混ぜたときです。
slot = qiita
baseId = AUTO-W3-CT-QIITA-20260902-pm
deliverable = qiita-posts.md
baseId にすでに QIITA が入っているのに、slot注入側でも qiita 接尾辞を足す。さらに成果物名にも qiita が入っている。こうなると、人間には「Qiita用」と読めても、機械的な参照パスでは二重接尾辞になりやすいです。
原因のコード
許可された素材内に残っている実コード断片として、直近のQiita publish修正ログに parser の正規表現が記録されています。
正規表現: ^TITLE:s*\r?\n(.+?)\r?\n\r?\nTAGS:s*\r?\n(.+?)\r?\n\r?\nBODY:s*\r?\n([sS]*)$
このログは別件、つまり TITLE: / TAGS: / BODY: のラベル形式を読めず、タイトルやタグが壊れた事故の修正記録です。記録にはこうあります。
上流原稿のTITLE:/TAGS:/BODY:ラベル形式(コロン区切り・複数行値)に未対応
タイトルがファイル名(AUTO-C1-CT-QIITA-20260901-am-qiita-post)・タグが既定値(GAS,Gemini)・本文にTITLE:/TAGS:/BODY:ラベル行が生混入
ここから分かるのは、本文パーサとカード参照は別の場所で壊れていても、根は同じだということです。
文字列の境界を、正規表現の都合やファイル名の都合に寄せすぎると、構造化データだったものが「たまたま一致する文字列」になります。
今回の参照パス不整合でも、危ないのはこの形です。
既にslotを含むID + slot由来の接尾辞 + deliverable由来の接尾辞
ID生成、成果物名、publish側の参照先を別々の正規表現で処理すると、どこか1か所の「補正」が別の場所では「二重付与」になります。
修正
修正方針は、文字列を後から削ることではなく、境界を1か所に決めることです。
今回のカードは、最終成果物の形式を明示しています。
出力形式= TITLE= / TAGS= / BODY=
この場合、publish 側に渡すべきものは「Qiitaっぽいファイルを探す」ことではありません。カードの deliverable を正として、その1ファイルを読むことです。
"deliverable": "qiita-posts.md"
また、公開ログの修正例では、パーサ不一致を投稿後に緩和するのではなく、ツール側の分岐を追加してPATCHで訂正しています。
ツールにtitleColonMatch分岐を追加
既定回避せずツール修正=[[feedback_fix_tool_not_relax_rule]]
参照パスの問題でも同じです。publishカードごとに「存在しそうな別名」を探すのではなく、生成側と消費側で次を固定します。
content card id
deliverable path
publish source id
publish input path
この4つを1つの構造として渡せば、slot注入は表示や分類のための値になり、ファイル参照の再生成ロジックではなくなります。
学び
この事故の教訓は、検知器を作るだけでは足りない、という点です。
配信レーンには、公開後の台帳追記や投稿上限確認のルールがあります。
公開したら published-ledger に1行追記
投稿数=稼働保証KPI(評価KPIでない)
しかし、publishカードが参照先不整合で起動前に死ぬ場合、公開後の検証には到達しません。だから検知器は、投稿処理の中ではなく、カード生成直後から publish 起動前までの間に配線する必要があります。
最小限見るべきなのは、次の3点です。
1. card.json の deliverable が存在するか
2. publish カードの参照パスがその deliverable と一致するか
3. slot注入後のIDに同じ接尾辞が重複していないか
正規表現は便利ですが、ID、slot、ファイル名、本文ラベルを全部同じ文字列処理で扱うと壊れます。
今回のような自動化では、正規表現を増やす前に、どの値を正本にするかを決めるほうが効きます。deliverable が正本なら、publish はそれを読む。slot は分類に使う。IDは一意性に使う。役割を混ぜない。
参照整合のバグは、見た目には小さな接尾辞の重複です。しかし、配信では起動前停止になります。記事本文の品質以前に、本文へたどり着く線を決定論的に保つ。そこを外すと、よくできた原稿も公開レーンには乗りません。