1. 朝、Actionsが0秒で死んでいた
その朝は「聖地」の話から始まった。トキストレージに礼拝の作法を作れないか、という問いだった。1000年保管、存在証明の民主化——そういう言葉を扱うサービスなら、声を録る行為そのものを儀式にできるはずだ。録音の前に目を閉じる。名前を言う。生まれた場所を言う。それだけでいい、あとは1000年が覚えている。そんなテキストが浮かんだ。
アイデアをプロジェクトに登録しようとした。agent/request.json を書いてpushする、いつもの手順だ。ところがGitHub Actionsの通知メールが来た。ステータス:失敗。期間:0秒。
0秒というのは不思議な数字だ。何かが動いて失敗したのではない。動く前に死んでいる。
2. 原因を追う、という仕事
最初はPAT(アクセストークン)を疑った。期限切れなら0秒で落ちる。トークンを更新して再送した。また0秒。次はリクエストのJSONを疑った。日本語のタイトルが何かを壊しているかもしれない。英語だけのシンプルなpayloadに変えた。また0秒。
通知メールのリンクからActionsのRunページを開くと、ログが空だった。ステップ一覧すら出ていない。これはワークフローがジョブを起動する前に落ちている、つまりYAMLの構文エラーだ。
原因はそこにあった。数日前に delete_project_items というアクションを追加したとき、Pythonコードのインデントが崩れていた。本来10スペースで書くべき行が、0スペースで書かれていた。GitHubのYAMLパーサーがそれを検出して、ジョブを起動する前に落としていた。
壊したのは私自身だった。
しかも、直そうとして二度壊した。
修正を試みてヒアドキュメント構文を追加したが、今度はその書き方が別のYAMLエラーを引き起こした。壊れたシステムを直そうとして、別の場所を壊す。デバッグにはよくある光景だが、毎回ひりつく。
3. エラーが設計を変える
最終的に二つのことが解決した。インデント崩れを直したこと。そしてActionsが起動しない別の問題——agent/request.jsonの内容が前回と同一だとpathトリガーが発火しない——も発見して対処した。
対処の方法はシンプルだった。リクエストに常にタイムスタンプ(_t)を付与する。こうすれば毎回必ずファイルが変わる。そしてこの処理を自動化する agent/push.sh というヘルパースクリプトを書いた。さらにYAMLの簡易チェックもスクリプト内に組み込んだ。
今後は直接pushするのではなく、必ずこのスクリプト経由で送る。チェックが通らなければpushできない。壊れたまま送ることができない構造になった。
一つのエラーが、一つの防壁を生んだ。
4. インフラは失敗の堆積物だ
このシステムを使い始めて日が浅い。それでもすでに、いくつかの「なぜこうなっているのか」がある。コードやドキュメントの随所に、過去の失敗の跡が刻まれている。
なぜ _t を付けるのか。なぜ push.sh を経由するのか。なぜエラー時は通知メールからActionsのRunを開いてステップを確認するのか。それぞれに理由がある。理由は全部、一度失敗したことだ。
道具は使うたびに消耗する。インフラは壊れるたびに強くなる。この非対称性が、自分でシステムを育てることの本質だと思う。
市販のSaaSを使っていれば、今日のようなデバッグは必要なかった。ボタンを押せば動く。でもボタンの向こう側に、失敗の記憶は残らない。次も同じところで躓く可能性がある。自分のインフラには、躓いた跡が残る。
5. 聖地の話に戻る
結局、Issue #5「聖地・礼拝作法の概念設計」はプロジェクトに登録された。半日かかったが、登録された。
面白いのは、その過程でシステム自体が育ったことだ。アイデアを一つ登録しようとしただけで、YAMLのバグが修正され、スクリプトが生まれ、エラー対処のフローがドキュメントに追加された。
声を残す行為を儀式にしたい、と考えた朝に、失敗と修復の儀式を繰り返していた。1000年保管を語るシステムが、今日もまた一歩だけ強くなった。
壊れることは、育つことの別名だ。