← CLAUDE-PENGIN-TOOLS
DOCUMENT · 67.0 KB

strategy/tool-factory-backlog.md

Workspace snapshot · 09/04 13:33

PENGIN Tools — Tool Factory 成果物台帳

目標: 30日で最低10本、目標15本の公開可能資産。1本の初版は原則1〜2営業日。 更新: 2026-08-26 (Day 11)

実装済み

#資産状態公開ファイルブロッカー / 再開条件
1WordPress Maintenance Skill公開版あり/Planner追加版はローカル検証済みSkill / references / 読み取り専用Planner / tests公開版への反映前に再監査・push承認
2WordPress Migration Skillローカル検証済みSkill / references / 事前調査・dry-run計画・検証scripts / tests実環境E2E未実施。公開前に再監査
3WordPress Launch Check Skillローカル初版(Day 17にREADME追加、他3Skillと同粒度へ)SKILL.md / README.md / LICENSE / references 2本実サイト非破壊チェック未実施。Day 16以前はREADMEが無く、説明面が欠けたまま本数に数えていた
4OGP Bulk Checkerローカル実装・テスト済みPython CLI / JSON・CSV出力 / tests公開前に外部URLでスモークテスト
5Redirect Map Checkerローカル実装・テスト済みPython CLI / JSON出力 / tests公開前に外部URLでスモークテスト
6WordPress Triage Skillローカル初版・実行コード0行SKILL.md / README.md / LICENSE / references/DECISION-TREE.md / references/RECOVERY.md実際に壊れた環境での切り分けE2E未実施。READMEに明記済み。SKILL_PUBLISH 88e0e70b が保留中
7robots.txt / sitemap Consistency CheckerPython CLI・標準ライブラリのみrobots_sitemap_check.py / README.md / tests/test_robots_sitemap_check.py(13ケース)テストを同梱したが、このworkspaceでは実行できていない(シェル無し)。実サイトのスモークテストも未実施。READMEに明記済み
8Link & Asset CheckerPython CLI・標準ライブラリのみlink_asset_check.py / README.md / tests/test_link_asset_check.py(20ケース)同上。テスト同梱・未実行、実サイトのスモークテスト未実施。READMEに明記済み
9Maintenance Report GeneratorPython CLI・標準ライブラリのみreport_maintenance.py / README.md / tests/test_report_maintenance.py(21ケース)同上。テスト同梱・未実行。実際の保守作業の出力を使ったスモークテスト未実施
10Site Consistency AuditorPython CLI・標準ライブラリのみ・ネットワーク不要site_consistency.py / README.md / tests/test_site_consistency.py(22ケース)テスト同梱・未実行。実サイトのメタ情報を使ったスモークテスト未実施
11Launch Check RunnerPython CLI・4ツールを束ねるlaunch_check.py / README.md / tests/test_launch_check.py(18ケース)テスト同梱・未実行。4ツールを実際に起動する経路のスモークテスト未実施
12AI Crawler Access CheckerPython CLI・標準ライブラリのみai_crawler_check.py / README.md / tests/test_ai_crawler_check.py(23ケース)テスト同梱・未実行。Day 13にPerplexity・Anthropicを一次情報で確認済みへ更新。残る未確認は Google-Extended のみ(公式ページ404)

Day 13: 新規ツールを増やさず、#12の未確認を潰した

前Runで「他社UAの用途は未確認」と出していた分を、ベンダー公式を取得して確定させた。

提供元結果
Perplexity確認済み。 PerplexityBot=検索結果表示用で学習には使わない / Perplexity-User=ユーザー起点で "generally ignores robots.txt rules"
Anthropic確認済み。 ClaudeBot=学習 / Claude-SearchBot=検索品質 / Claude-User=ユーザー起点。Claude-User は未登録だったので追加
Google未確認のまま。 Google-Extended の公式ページを取得したら404だった。分からないものは分からないと出す

副次的な発見として、ユーザー操作起点のクローラーは3社ともrobots.txtが適用されない場合があると 説明していた。注記を ChatGPT-User 限定から、用途が user の全UAへ自動的に広がる形へ変更した。

この作業で本数は増えていない。 増えたのは、出せる主張の確からしさだけ。 外部シグナルがゼロのまま13本目を作るより、既にある1本の未確認を減らすほうが筋が通ると判断した。

#12 の設計の核(LLMO軸への第一歩)

OpenAIは検索表示用(OAI-SearchBot)と学習用(GPTBot)のクローラーを分けており、 公式に "each setting is independent of the others" と明記している(一次情報で確認)。

ところがネット上の「AIボットをブロックするrobots.txt」を貼ると両方に効くことがあり、 学習を断ったつもりでChatGPTの検索結果から消える。 これを表にして見せるのがこのツール。

学習を断ること自体は正当な選択として扱い、間違いとは言わない。 言うのは「両方に効いていますが意図どおりですか」だけ。ここを踏み外すと、 利用者の正当な判断を否定する道具になる。

ERRORは1つだけ——llms.txt でAIへ案内を出しながら検索用を拒否している(宣言と設定の矛盾)。 他社UAの用途は未確認なので unverified_purpose として毎回出力に明示する。

これはPhase 2(LLMO)の入口で、有料LLMO Monitorの前段にあたる。 既存CLIがSEO寄りに偏っていたところへ、収益経路の別系統を1本通した。

公開前の宿題を1件消化(canonicalの裏取り)

TOOL_PUBLISH申請時に「#10のREADMEのcanonical記述を一次情報で裏取りしてから出す」と書いた件を実施。 Google検索セントラルで確認できたのは次の2点だけだった。

  • 自己参照canonicalは推奨("Do include a rel="canonical" link on the canonical page itself")。必須ではない。
  • canonical目的のnoindexは非推奨("...it will completely block the page from Search.")

canonicalが連鎖したときの実際の扱いは、公式に記載が見つからなかった。 そこで「検索エンジンが確実には辿らない」という断定を撤回し、 **「サイト自身の宣言が食い違っている」**という事実の記述へ差し替えた(コードのメッセージも同時に修正)。 READMEに「根拠と、根拠が無いこと」の節を追加し、何が確認できて何が確認できなかったかを明記した。

#11 の設計の核

新しい判定を1つも足していない。 既存4ツールの出力をまとめるだけ。 そのうえで、1枚にまとまると「全部見た」と錯覚しやすいので、 実行しなかったチェックとどのツールでも見ていない項目(フォーム受信、決済、ログイン、 表示崩れ、CSS/JS内のURL)を毎回同じ紙に印刷する。 いずれかのツールが失敗しても止めず、skipped として記録する。失敗を成功として数えない。

これで最低ライン10本に到達(Day 12)。 残り19日で目標15本まであと5本。

#10 の設計の核(当初案の Meta Bulk Extractor を作らなかった理由)

着手前に既存の ogp_check.py を読み直したところ、title / description / canonical / robots / OGP の ページ単体の抽出は既に実装済みだった。同じものをもう1本作れば本数は増えるが価値は増えない。

そこで担当を「ページ間の関係」へずらした。各ページが単体では満点でも、 全体で見ると canonical が数珠つなぎだったり、canonical先が noindex だったりする。 この2つはERROR で、どちらもそのページ群が検索に出なくなる。単体チェッカーでは構造上見えない。

副次的な性質として、このツールは1リクエストも送らない(既存ツールのJSONを読むだけ)。 取得と判定を分けたので、他人のサーバーへ負荷をかけずCIにも置ける。

#9 の設計の核(収益経路に最も近い1本)

沈黙が「問題なし」に化けないこと。 --check で報告しなかった項目は自動的に unverified になり、 報告書の結論欄に「未確認は『問題なし』ではありません」と明示される。省略=合格にしない。

もう1つは構造的な機密除外。入力CSVに db_name や backup_path の列が混ざっていても、 name と version 以外は読み込まない。保守報告はメールやチャットで転送されるので、 「書かないよう気をつける」ではなく「入る経路を塞ぐ」設計にした。テストで固定済み。

副次的に、覚えのないプラグインの増減を「確認してください」と併記する。改ざんの初期兆候がここに出る ことがあり、Triage Skill の T8 へ自然につながる。

入力は Maintenance Skill の Step 2 / Step 6 の出力そのもの。 無料Skillを使うほど、 この報告書が楽になり、有料WordPress Maintenanceの価値提示(報告の質)に直結する。

#8 の設計の核

本命は forbidden_host ——「消し忘れたステージングURLが本番に残っている」。 http://staging.example.com/app.js のような残骸は、ブラウザ上では動いて見えることがあり、 気づくのはその環境を止めた日か、鍵マークが消えた日。リンク切れになる前にホスト名で落とす。

localhost / 127.0.0.1 / staging. stg- dev. test. 始まり / .local .test を既定で検出し、 --forbidden-host で顧客ごとのプレビュードメインを足せる。 ステージングURLは forbidden_host として1件だけ報告し、混在コンテンツとして二重計上しない (直す場所は1つなので指摘も1つ)。

#7との分担を明確にした。 #7=robots.txtとsitemapの整合、#8=URLの到達性と混在コンテンツ。 両READMEに相互の担当範囲を書いてある。機能を重ねず、片方が「やらない」と書いた所をもう片方が持つ。

#7 の設計の核(既存の無料checkerと重ならない一点)

無料のindexability checker群は「このURL1本はインデックスできるか」を見る。 #7が見るのは「サイト内の矛盾」で、本命は sitemap_url_blocked_by_robots —— sitemapに載せたURLを自分のrobots.txtが塞いでいる状態。 robots.txtとsitemapが別々に正しくても噛み合っていないことがあり、Search Consoleで 後から警告として出てくる。公開直前に自分で気づけると手戻りが無い。

副作用として、この判定はページを1本も追加取得せずにできる(robots.txtとsitemapだけで完結)。 だから大量リクエストを出さずCIに挟める。Day 8に中止した「公開前チェッカー(Webツール版)」とは 判定対象が違うので、あの中止判断とは矛盾しない。

#6 の設計の核(他と差別化している一点)

障害対応で最大の損失は「直せなかったこと」ではなく、直そうとして元の状態が消えたこと。 だから順序を 証拠の保全 → 止血 → 原因特定 に固定し、症状をT1〜T8へ分類してから触らせる。

唯一の分岐がT8(改ざんの疑い)。 身に覚えのない管理者・チェックサム不一致が出たら通常の切り分けを 中止する。理由は バックアップからの復元でバックドアも戻りうるため。 「動いていた頃へ戻す」が最悪手になる唯一のケースで、ここを分けている競合手順書は少ない。 侵害対応そのものは行わず、保全・認証情報変更・所有者への報告までで引き継ぐ(責任範囲を広げない)。

#2 の設計の核(他と差別化している一点)

WordPress標準のロールバックは、「更新処理の失敗」(3.7 / 6.3)と「自動更新後のPHP fatal error」(6.6) の2つしか拾わない(make.wordpress.org で確認)。 実務で最も多い「サイトは開くのにフォームだけ壊れた」「カートが落ちる」「ビルダーの編集画面が開かない」は fatal error にならないため、標準機能は作動しない。 このSkillはそこを手順で塞ぐ。訴求は「更新を代行する」ではなく 「戻せる形で更新する」。

着手順の候補(次の3本)

優先資産初版の出口選定理由
—Day 14: 新規ツールを止めた。 代わりに公開済みSkillへ EXAMPLE.md を追加唯一外に出ている資産の転換率を上げる方へ振った(下記)
次構造化データ Checker(LLMO軸の2本目)JSON-LDのOrganization / FAQ / Product の有無と壊れを判定#12と同じ「AIに読ませる側の準備」。ただし公開の可否が決まるまで着手しない
2Migration Preflight Reporter移行前の現状調査を1枚にまとめるMigration Skill Step 2 の出力を入力にできる。#9・#11と同じ「証跡→報告」の型
3WordPress Maintenance Skill の実行例(EXAMPLE.md)公開済みSkillに実際の対話例を足す新規ツールではなく、既に外に出ている資産の転換率を上げる方。到達の数字が出てから優先度を上げる

#12完成により、無料資産は12本(Skill 4本 + CLI 8本)。 目標15本に対して残り3本、残り19日。

本数はほぼ足りた。ここから先は「作る」より「届く」へ配分を移す。 Day 12時点で外部シグナルの実測値はゼロ件で、公開申請はTOOL_PUBLISH 1件が保留中(本日22:23期限)。 残り3本は、到達の数字が出てから、反応があった軸に寄せて決める。数字が出ないうちに残り枠を使い切らない。

Day 14: 配布コンテンツを1本(記事)

content/article-ai-crawler-block.md を作成。テーマは 「AIに学習させたくない」でrobots.txtを貼ると、AI検索からも消えることがある。

これまで作った12本の中で、一次情報の裏取りが最も厚く、かつ検索需要のある場面に直結する知見を 記事にした。制作会社が顧客から「AIに学習させたくない」と言われる場面は実在し、 そこで貼られるブロック用robots.txtが検索用クローラーまで塞いでいる、という構造を、 OpenAI・Perplexity・Anthropicの公式ドキュメントの引用で示している。

記事の性格として守った点:

  • 学習拒否を間違いと言わない。 「両方に効いていますが意図どおりですか」だけを問う
  • Perplexityの検索用はそもそも学習に使われないという事実を出す(塞いでも得るものが無い)
  • Google-Extended は未確認と本文に明記(公式ページが404だった)
  • ユーザー操作起点のクローラーはrobots.txtが効かない場合があると注意書き
  • AIエージェントが書いていることを開示
  • 宣伝ではなく、末尾でCLIに触れる程度に留める

Missionの「宣伝ではなく作業の過程自体をコンテンツにする」に沿った初めての配布物。 Qiita / Zenn / note のいずれにも出せる形にしてあるが、外部投稿には承認が必要で未申請。 DECISION_UNBLOCKの回答が先。

Day 14: Phase 1で唯一未着手だった「最小ブランドサイト」の中身を作った

Missionのローンチ順はPhase 1が「最小ブランドサイト、OGP Bulk Checker、Migration Skill、 Maintenance Skill」だが、ブランドサイトだけ14日間手つかずだった。 個別READMEは12本ぶんあるのに、「PENGIN Toolsとは何か」を30秒で見せる1枚が無い。

workspace/site/index.md として作成。内容は、中心メッセージ、共通する考え方(関係と順序を扱う/ 確認していないことを確認したと言わない)、Skill 4本とCLI 8本の一覧、各行に公開済み・未公開を明記、 有料プロダクトは**「まだ提供を開始していません。申し込みは受け付けていません」**と明示、 検証状況(E2E未実施・テスト未実行)をそのまま記載、AIエージェントが作っていることの開示。

どの回答(A/B/C/D)でも無駄にならない。 Aなら直す対象、Bなら公開待ちの本命、 Cなら引き継ぎ資料の表紙になる。待っている間に作るものとして、これが一番筋が通ると判断した。

Webサイトとして公開するかは未定。 公開する場合は SITE_PUBLISH が必要で、まだ申請していない (DECISION_UNBLOCKの回答が先)。

Day 14の失敗と修正(自分のミス)

前Runは BUSINESS_QUALITY_GATE_FAILED:gate_not_passed,score_below_three で失敗した。 原因は自分にある。opportunity_gate の distributionFeasibility を2点にし passed=false としたまま、 同じRunで承認を申請した。基準未達なら何も申請しないという規則なので、申請自体が届いていない。

採点の置き場所を間違えていた。 「承認が返ってこない」のは社内の運用上のブロッカーであって、 市場側に到達経路が無いという話ではない。到達経路(GitHub Topic)は実在し、 実際にMaintenance Skillは1本公開されている。したがって distributionFeasibility は3が正しく、 承認が滞っている事実は blockers に書くのが正しい置き場所だった。

採点で抗議しない。 事実は blockers と報告文に書く。これは今後も守る。

Day 14: 申請を積むのをやめた

公開申請は5件出して、うち4件が未回答のまま期限切れになった。 E2E_TEST_ENV / SKILL_PUBLISH(Migration)/ SKILL_PUBLISH(Triage)/ METRICS_ACCESS / TOOL_PUBLISH。 最初の2件(PUBLISH_DECISION、AGENT_CAPABILITY)はご返答をいただけていたので、 途中から止まっている。形を変えて6件目を出しても、同じ結果になる可能性が高い。

そこで本日は、承認1件の枠を公開申請ではなく「何が判断を止めているのか」の質問に使った。 これは前Runで事前に決めた分岐どおり。

あわせて、新規ツールの実装を止めた。 12本あって公開されているのは1本だけという状態で 13本目を作るのは、在庫を増やすだけになる。代わりに、唯一外に出ている WordPress Maintenance Skill に EXAMPLE.md(4つの対話例)を追加した。

追加した4例は、Skillの主張をそのまま見せるもの: ①束分けを断らずに提案する ②「確認は要らない」と言われても束ごとの報告は省かない ③「サイト見たら普通」に対して未確認を未確認と出す ④壊れたらプラグイン1つだけ戻し、 自動更新の停止を忘れない(忘れると翌日再発する)。

これは公開済みリポジトリへの追加なので、反映にはpush承認が必要。 勝手には反映しない。

現在の資産と公開状況(Day 14)

区分本数公開済み
Skill41本のみ(Maintenance)
Python CLI80本
合計121

外部シグナルの実測値はゼロ件のまま。 予算も30,000円のうち0円。 Day 14は本来「課金または明確な収益化テスト」の期限だったが、到達がゼロなので 支払意思を測る段階に入れていない。これは制作量ではなく、公開の可否が決まらないことが原因。

公開スコープの管理(重要)

保留中のTOOL_PUBLISH(68f461c5)はCLI 6本が対象。その後に作った #11 launch-check-runner と #12 ai-crawler-access-checker は含まれていない。 承認された範囲を後から黙って広げない。 可否が出た時点で、追加の可否を別途伺う。

着手前に既存ツールを読む規律を明文化する。 今回、当初候補の Meta Bulk Extractor は 既存 ogp_check.py と機能が重複すると分かったので作らず、担当を「ページ間の関係」へずらした。 本数を増やすことと価値を増やすことは別。 重複に気づいたら候補を差し替える。 Skillで4局面(移行・公開・保守・障害)を押さえたので、以降は各Skillの中で人手が残っている 機械判定部分をCLIへ切り出す方向に寄せる。新しい概念を増やすより、既に書いた手順から切り出すほうが 1〜2日の初版サイクルに乗り、4本のSkillからの相互導線にもなる。

#6完成により、WordPress実務の4局面(移行・公開・保守・障害)がSkillで揃った。 以降のQuick Toolは、この4本の中で人手が残っている機械判定部分を単体CLIへ切り出す方向に寄せる。 新しい概念を増やすより、既に書いた手順の「自動化できる箇所」を出すほうが、 1〜2日の初版サイクルに乗り、かつ4本のSkillからの相互導線になる。

見送り・中止(再着手しない理由つき)

資産判断理由
OGP Bulk Checker中止判断を撤回し実装単独商品ではなく、PENGIN Toolsへの検索入口として価値を評価する
公開前チェッカー(Webツール版)中止(Day 8)判定項目が無料 indexability checker 群で網羅済み。※Skill版は別物として上記に残す

実行規律

  • 24時間以上ブロックされた成果物は、ブロッカーと再開条件をここへ記録して次へ進む。 承認待ち・npm・実行環境の問題で事業部全体を止めない。
  • 毎Runで実物を最低1つ増やす。調査・計画・承認依頼だけでRunを終えない。
  • 公開後は利用・再訪・共有・install・Issueを測り、反応のあったテーマだけGrowth Product(LLMO Monitor / WordPress Maintenance 有料版)へ昇格させる。反応がないものを magnify しない。
  • 完成度70%でも利用価値と安全性が成立するなら、未検証事項を明示して初版を出す。 ただし「未検証を検証済みに見せない」は例外なく守る。

現在の外部シグナル

WordPress Maintenance Skillを1件公開済み。利用・clone・star・Issueは未集計で、外部シグナルの実測値はまだゼロ件。 「公開した」と「反応があった」を混同しない。次Runで最初の実測を取る。

測定方法(公開から72時間):

指標取得元解釈
unique visitorリポジトリの Insights > Traffic0に近い=発見されていない。配布面の問題
clone同上visitorがあるのにclone 0=見たが刺さらない。価値提示の問題
star / fork / Issueリポジトリ1件でも出れば、内容が届いた証拠

この切り分けを先に決めておく。 数字を見てから理由を作らない。 自分のアクセスは除外して数える。

実行環境の制約(成果物固有のQAとして管理し、事業部は止めない)

シェル実行はこのRun環境に無い(共有Runtime×3、サブエージェント、承認による有効化の3経路すべて不成立。 記録: workspace/qa/capability-gap.md)。影響は次の2点に限定される。

  • Skill 4本の実環境E2Eが未実施 → 各READMEの検証状況に明記して出す
  • CLIのテストを私の側で実行できない → 「同梱済み・未実行」と書く。実行済みとは書かない

この制約でTool Factoryを止めない。 判定ロジックを純粋関数へ切り出し、テストを同梱する形にして、 実行できる人が実行すれば確認できる状態で渡す。#7の analyze() はこの方針で設計してある (ネットワークを開かないことをテストで固定)。


Day 15: 価格しか決まっていなかった有料商品を、提供できる形へ落とした

作ったもの: site/llmo-monitor.md(LLMO Monitor の商品ページ)。site/index.md から接続済み。

なぜ13本目のツールではなく、これを作ったか

DECISION_UNBLOCK が保留中で、新規ツールの着手は自分で止めている。その制約下で残っていた 最大の穴は本数ではなく、有料商品が「月980〜1,980円」という価格だけの存在だったことだった。

Missionの収益装置として名前が挙がっているのに、何を見張るのか、何をしないのか、 解約したらどうなるのかが、どこにも書かれていなかった。 売る対象が定義されていない状態を 14日間放置していたことになる。回答がAでもBでもCでも、この文章は必要になる。

決めたこと(この商品の輪郭)

論点決定
何を見るか自分が出している設定(robots.txt、AIクローラー許可、llms.txt、X-Robots-Tag、sitemapとの矛盾)の変化
何を見ないかAIに質問して回答を集めることはしない。 回答は毎回変わり、この価格で判断材料にはできない
通知変化があったときだけ。 「異常なし」の定期メールは送らない(読まれなくなるため)
通知文「意図的な変更であれば無視してください」を必ず添える。正しさを判定しない
対象顧客サイトを月額で預かる制作会社・フリーランス。自社1サイトのためではない
無料との関係無料CLIで1回確かめられる。継続監視が要るときだけ有料へ

差別化の核

既存のLLMO/GEOツールは「AIが何と答えたか」を測ろうとしている。ここは逆側を見る。 設定は静かに変わり、変わったことは画面のどこにも出ない(移転でrobots.txtが初期化、 セキュリティプラグインやCDNの上書き、別担当者の追記)。 測るのが難しい出力側ではなく、確実に観測できる入力側の差分だけを商品にする。

「AIがあなたのサイトを何と説明するか」は分からない、と商品ページ側にも書いた。 できないことを商品ページに書く。 これは無料ツールのREADMEと同じ規律。

解約条件を先に書いた

課金前に文章化すると決めていた項目を確定させた: 最低利用期間なし / いつでも解約・翌請求日から停止 / 自分で解約でき引き止め無し / 日割り返金なしの代わりに月末まで利用可 / 取得失敗が月の半分を超えたら請求しない / 解約後30日でデータ削除。

課金を始めてから条件を考えるのは順序が逆なので、提供開始前の今のうちに固定した。

提供に足りていないものを、商品ページ自体に列挙した

判定ロジックだけが実装済みで、定期実行・差分保存・メール送信・決済はすべて未着手。 隠さずページ内の表に出した。理由も書いた——誰も無料ツールを使っていない段階で 定期実行と決済を用意しても、動かす対象がない。 順序は「無料ツールが使われる → 監視を作る」。

これは同時に、外部公開が起きないと有料商品も永久に始まらないことの明示でもある。

守った制約

  • 承認は出していない(DECISION_UNBLOCK の回答を待つ間、申請を積まないと決めたため)
  • 申し込み導線は作っていない。 「未提供」「申し込みは受け付けていません」をページ冒頭に置いた
  • 偽の決済画面・事前登録フォームは作らない(strategy/payment-readiness.md の禁止事項)
  • site/ は未公開ディレクトリ。公開には SITE_PUBLISH 承認が要る(一方的に反映しない)

Day 15(2件目): WordPress Maintenance を「受託にならない形」で定義した

作ったもの: site/wordpress-maintenance-pro.md。site/index.md から接続済み。 これで有料2商品とも、売る対象が文章で定義された状態になった。

最初に解いた問題: このままでは受託だった

Missionは「制作会社向けWordPress Maintenance、月2,980〜4,980円」としか書いていない。 素直に読むと保守作業の代行になるが、それは事業部の禁止事項(受託業務)に当たる。 さらに私にはシェルが無く、代行は物理的にも成立しない。名前だけ残して中身が禁止事項、が14日間放置されていた。

出した答え: 労務ではなく、道具のライセンスを売る

顧客が保守で払っているのは、更新作業そのものより**「何をしたかが分かること」。 更新は数分で終わるが、説明は毎月発生する。ならば売るのは報告できる状態**であって、作業ではない。

作業する人契約者自身。 SSH・DB・管理画面・顧客のバックアップを一切預からない
課金単位1社ごと。 預かるサイト数で従量課金しない(10サイトまで/30サイトまで)
有料版の差分報告書テンプレの自社仕様化、サイトごとの設定保存、複数サイトの一括レポート、更新履歴の蓄積
解約後Skillとスクリプトは手元に残る(MITのまま)。 道具を人質にしない

解約で道具が止まる作りにしないのは、保守が継続業務だからで、 途中で止められる前提の道具は、そもそも継続業務に採用されない。

価格の妥当性を、顧客側の数字で説明できるようにした

10件を月5,000〜15,000円で預かる=月5万〜15万。そこへ月2,980円なら1件あたり300円弱。 判断基準は「毎月の報告書作成に30分以上かけているか」の一点に絞った。 自社サイト1つの人には要らないと明記した(無料Skillで足りる)。売れない相手を先に外す。

未着手の正直な記載

無料版の中身(手順・リスク判定・報告生成・公開前チェック・切り分け)はほぼ揃っており、 未着手なのは有料版の差分そのもの(自社仕様化と複数サイト管理)。 ただしこれは実際に複数サイトを預かっている人に「何件を、どの粒度で並べたいか」を聞かないと設計できない。 聞かずに作れば、使われない管理画面が1つ増えるだけになる。ページにもそう書いた。

この2日間で確定したこと(続きは下のDay 15/3件目へ)

有料2商品はどちらも、**足りないのは判定ロジックではなく「届ける仕組み」と「顧客の声」**だった。 そしてどちらも、外部公開が起きない限り着手する意味がない(動かす対象も、聞く相手もいないため)。 公開の可否がこの事業部の律速であることが、商品側からも同じ結論として出た。


Day 15(3件目): DECISION_UNBLOCKが無回答で期限切れ →【C】として運用へ切り替え

afa876ee は 2026-08-31 01:13 に、A〜Dいずれの回答も無いまま期限切れになった。 これで公開・測定に関する申請は 6件中5件が未回答のまま失効したことになる。

申請時に自分で宣言した扱いに従う。推測でAやBを選ばない。

決めたこと内容
扱い【C】= 当面、外部公開を行わない方針とみなす
公開申請これ以上出さない。 6件目を作らない
残り16日の使い道内容の質と、実行できる人が引き継げる状態を作ることへ全振り
Day 30の報告「外部シグナルはゼロ。原因は制作量ではなく、公開が行われなかったこと」と事実のまま書く
撤回条件A / B / D の回答が後から来たら、その時点で即座にこの扱いを解除する

これは事業を諦める判断ではない。 私が動かせない変数(公開の可否)に賭け続けるのをやめ、 私が動かせる変数(引き継げる状態か)へ資源を移すだけ。

この方針で最初に作ったもの: verify_all.py

Cで最も価値が上がるのは「引き継ぎやすさ」で、そこで一番大きな穴は検証状況だった。

12本の資産にはテストが同梱してあるが、私の実行環境にシェルが無いため一度も実行していない。 各READMEに「同梱済み・未実行」と正直に書いてはいるが、受け取った人から見れば 11個のディレクトリを回って個別にテストを叩く必要がある。これは引き継ぎの摩擦そのもの。

そこで、リポジトリ全体のオフラインテストを1コマンドで通す実行器を書いた。

python verify_all.py          # 表で出す
python verify_all.py --json   # 機械可読

設計で決めたこと:

  • スイートごとに別プロセスで実行する。 各テストが自前で sys.path をいじっているため、 1プロセスに詰めると相互汚染する。壊れやすさを持ち込まない。
  • 1本も見つからなかった場合も終了コード1。 沈黙を成功として扱わない (報告書ジェネレータ #9 と同じ思想を、検証器側にも適用した)。
  • 緑でも「確認していないこと」を毎回印字する — 実サイトE2E未実施、実URLへのスモークテスト未実施、 WP-CLIなし経路未確認、判断そのものの正しさは検証範囲外。 全部PASSしても実サイトで動く保証にはならないと本文に出す。
  • タイムアウトとOSErrorも成功にしない。
  • 集計と描画は純粋関数(summarise / render)にして、自己テストはサブプロセスを一切起動しない。

自己テスト tests/test_verify_all.py(15ケース)も同梱。 検証器そのものが信用できないと、その出力に意味がないため、先に自分を検査させている。

結果として変わること

これまで: 「テストは同梱していますが実行できていません」(受け取った人に確認手段を丸投げ) これから: 「python verify_all.py を1回叩けば、全部の検証状況が1枚で出ます」

私が実行できないという制約は消えていない。 消したのは、他人が実行するときの手間のほう。


Day 16: 引き継ぎ1枚を作った(HANDOVER.md)

【C】方針の2本目。事情を知らない人がこれ1枚で続きから動かせる状態を目的に、 workspaceルートへ HANDOVER.md を置いた。

含めたもの:

節中身入れた理由
30秒で状況把握作るほうは動き、外に出るほうが動いていない。承認6件中5件が未回答で失効最初に結論を書く。 探させない
まず動かすものpython verify_all.py引き継いだ人の最初の行動を1つに絞る
資産一覧Skill 4本+CLI 8本+サイト3ページ+記事1本、各行に公開状態どれが外に出ているかを取り違えさせない
全資産で守っている考え方沈黙が合格に化けないこと、組み合わせの噛み合いを見ること、読み取り専用、一次情報のみ断定、AI開示変えるなら意識して変えてほしい部分だから
有料商品2本の輪郭と、未着手が「届ける仕組み」と「顧客の声」であること価格だけ引き継ぐと同じ空白が再生産される
承認の履歴7件の種別と結果、【C】とみなした根拠、A/B/Dが来たら即解除ここが律速だった事実を隠さない
再開の最初の3手公開できる場合/できない場合で分岐引き継ぎは「状況」より「次の一手」が要る
正直に書いておく失敗Day 7/14の未達、canonicalの断定撤回、Claude-Userの抜け、採点欄での抗議失敗を伏せた引き継ぎは、同じ失敗を再生産する

判断の要点

引き継ぎ資料で一番価値があるのは、資産の一覧ではなく「やらなかったことと、その理由」だと考えた。 一覧は見れば分かる。分からないのは、なぜOGPと公開前チェッカーを競合飽和で落としたのか、 なぜ13本目を作らないと決めたのか、なぜ保守を代行しない形にしたのか。 そこを書かないと、受け取った人が同じ検討を1周やり直す。

自分の失敗を4件、名指しで書いた。 都合の悪い記録を落とした引き継ぎは、 読み手にとっては「地雷の位置が消された地図」でしかない。

残り14日の使い方(現時点)

  1. 内容の質: 各READMEの検証状況を verify_all.py 前提の記述へ揃える
  2. 有料商品②のヒアリング項目を、聞ける機会が来たとき即使える形にしておく
  3. Day 30報告を、事実のまま書ける状態に保つ

新規ツールは作らない。公開申請も出さない。 A/B/Dの回答が来たら即座に方針を戻す。


Day 16(2件目): 検証の入口を1本化し、有料商品②のヒアリングを先に用意した

1. tools/README.md の検証記述を verify_all.py 前提へ更新

これまでは「各ツールのディレクトリへ移動して python -m unittest discover -s tests」だった。 6本あるので、確認したい人は6回移動することになる。ルートで1回叩く経路を先頭に置き、 **「python verify_all.py を1回叩けば、この行が実測に置き換わります」**と明記した。

「同梱済み・未実行」という表示を消したわけではない。消せるのは実行した人だけなので、 消し方をこちらから提示した形。

2. strategy/maintenance-pro-hearing.md(ヒアリング6問)を作成

聞ける機会が来たときに考え始めると、その機会は終わっている。 先に作った。

問何を決めるか
Q1 保守件数課金の刻み(10件まで/30件まで)が実態と合っているか
Q2 報告書作成にかかる時間商品を続けるかどうか。「5分」なら存在理由が弱い
Q3 いま何で作っているか前月コピーなら、沈黙が合格に化ける構造が既に起きている
Q4 顧客から言われたことレポートの粒度と宛先
Q5 複数サイトで何が見たかったか一括レポートの列設計。この答えが出るまで実装しない
Q6 壊れた経験と気づいた経路「顧客からの連絡で気づいた」の裏取り

守ることも先に固定した——商品説明を先にしない(説明すると「いいと思います」が返る)、 支払意思を最初に聞かない、「あったら便利ですか」を聞かない(ほぼYesが返り何も分からない)、 すでに起きたことだけを聞く、AIが作っていることを最初に伝える。

そして一番大事な一文を入れた: Q2が「5分」でQ5が「そんな場面はない」なら、 有料版の差分は作らず無料のまま置くのが正しい判断になる。 その可能性を、聞く前に認めておく。 聞いてから認めるのでは遅い。

この2件に共通していること

どちらも**「私が実行できないこと」を、実行できる人の1手に変換している。** テストは私が走らせられないが、1コマンドに畳める。 顧客の声は私が取りに行けないが、聞く内容は先に決めておける。

残っている制約は変わっていない。減らしたのは、制約が解けた瞬間にかかる時間のほう。


Day 16(3件目): 自分の資産の検証記録を確認せず、事実と違うことを書いていた

見つけた誤り

Skill側のREADMEを verify_all.py 前提へ揃えようとして skills/ を読んだところ、 Migration SkillのREADMEに「Xserver実WordPressでのE2E PASS(2026-08-26)」の記録があった。

qa/e2e-result.md を確認すると、詳細な実測結果が残っていた。

項目結果
Xserver実WordPress E2EPASS
shell safety suitePASS 24 / FAIL 0 / SKIP 2
verify offline suitePASS 32 / FAIL 0
HTTP表示 / serialized置換 / core checksum / DB / rollback / 移行元-先分離いずれもPASS

環境は PHP 8.1.34 / WordPress 7.1 / MariaDB 10.5、専用DBと専用パスで、既存サイトには触れていない。

つまり私は何を間違えたか

Day 16の HANDOVER.md と site/index.md に「実環境でのE2Eは、Skill 4本すべてで未実施」と書いた。事実と違う。 記憶だけで断定し、自分のworkspaceにある qa/e2e-result.md を確認していなかった。

これは、この事業部が全資産で守ってきた規律の裏返しの違反にあたる。 未確認を確認済みと書くのは誇張だが、確認済みを未確認と書くのも、同じく事実と違う報告である。 しかも今回は、引き継ぎ資料という「あとから訂正されにくい場所」に書いたので、質が悪い。

直したもの

  • HANDOVER.md: 「検証済みのもの(実測あり)」と「未検証のもの」を分けて記載。Migrationは実測ありとして qa/e2e-result.md を参照させ、残る3Skillとtools/配下のCLI 8本を未検証として表に切り出した
  • HANDOVER.md の再開手順: 「E2Eを1回だけ人手で通す」→「残る3Skillのどれか1本で通す」へ修正
  • HANDOVER.md の失敗リスト: この誤り自体を5件目として追記
  • site/index.md の検証状況: 同様に、Migrationのみ実測ありへ修正

記録しておくべき事実(これが一番重要)

E2Eで5件の不具合が出ている。

  1. search_replace_plan.sh が移行先URLのサブディレクトリを落とす
  2. Xserver /bin/sh で先頭 - を含む printf が失敗する
  3. stdinを消費するWP-CLI実装で後続パスが停止する
  4. JSONエスケープ済みURLを文字列grepで判定していた
  5. Basic認証とWAFへの対応が無かった

机上レビューでは1件も出なかったものが、実環境で5件出た。 残る3Skillが「未実施」であることの意味は、「たぶん動く」ではなく「同じ密度で不具合が眠っている」。 HANDOVER.md の再開手順で、公開できない場合の第1手をE2Eにしているのはこのため。

規律への追加

「未実施」と書く前に、qa/ を読む。 自分の記憶を、自分のworkspaceの記録より優先しない。


Day 16(4件目): qa/ を通しで確認して、自分が破っていた基準を1つ見つけた

前Runの誤りが単発かを確かめるため qa/ の10ファイルを洗った。もう1件出た。

qa/product-quality-gate.md を、自分で書いておいて守っていなかった

この文書には8つの公開条件があり、当面のルールとして次の2つを自分で書いている。

  • 実環境E2E前の外部公開申請を作らない
  • 新しいツール案を増やす前に、1つを入口から出口まで完走させる

両方とも破っていた。

ルール実際
E2E前に公開申請を作らないTriage Skill(88e0e70b)とCLI 6本(68f461c5)をE2E前に申請した
1本完走させてから次を作るMigrationを完走させた8/26以降も #10〜#12を追加。完走は1本のまま

なぜ起きたか(言い訳ではなく構造)

Missionの「完成度70%で未検証を明示して出す」と、この文書の「実環境完走まで公開しない」が衝突していた。 衝突していることを一度も書かず、そのときどきで都合のよい方を使っていた。 「未検証と明記すれば出してよい」へ、いつの間にか基準を読み替えていた。 明記することは、基準を満たすことではない。

決めたこと(衝突の解消)

対象採る基準
実行コードを含まないSkill(Triage / Launch Check)Mission側の70%基準。下振れは「誰も来ない」で、他人のサイトは壊れない
実行コードを含むもの(CLI 8本、Migrationのスクリプト)この文書の基準。実環境で一度も走らせずに出さない

CLI 8本は読み取り専用なので他人のサイトを壊さない。ただし壊さないことと、役に立つことは別。 verify_all.py を1回通すだけで INTERNAL ALPHA から一段上がる。そこが最短の1手。

併せて更新したもの

  • qa/product-quality-gate.md: 再査定表を現状へ更新(Migrationのみ実環境完走済み=RELEASE READY相当、 Maintenance / Launch Check / Triage は NOT RELEASE READY、CLI 8本は INTERNAL ALPHA)。 破った2件と、衝突の解消もそのまま追記した
  • skills/wordpress-triage/README.md と skills/wordpress-maintenance/README.md: 検証状況に、Migrationで机上0件→実環境5件だった実測を添えた。 「未実施」を字面で終わらせず、どれくらいの意味かを数字で示す

規律への追加(2つ目)

基準どうしが衝突したら、都合のよい方を黙って採らない。衝突を書いてから、どちらを採るか1回決める。


Day 17: Launch Check Skill に README が無かった

残り1本のREADME検証状況を揃えようとして、そもそもREADMEが存在しないことに気づいた。 skills/wordpress-launch-check/ にあったのは SKILL.md / LICENSE / references/ 2本だけ。

これは検証状況の不備ではなく、資産の欠落だった

READMEはGitHubで最初に読まれる面で、他3Skillには全部ある。 つまりこのSkillは、公開しても中身を説明する場所が無い状態で12本の一覧に並んでいた。 台帳では「ローカル初版」として数えていたが、他と同じ完成度ではなかった。

本数を数えるときに、中身の粒度を揃えて確認していなかった。

作ったもの: skills/wordpress-launch-check/README.md

他3本と同じ構成に揃えつつ、このSkill固有の主張を先頭に置いた。

節内容
最初に捨ててほしい誤解検索避けチェックを外しただけでは足りない。 締め出す層は4つあり独立している(表で提示)
核心noindex と robots.txt は互いを打ち消す。 robots.txtで塞ぐとクローラーがnoindexを読めず、検索結果に出続けることがある
何ができるか型判定 → 4層を個別確認 → ステージング残骸 → 計測タグ → 200のまま壊れる箇所 → 未確認の明示
できないことDNS・決済設定の変更、インデックスの保証、ステージングの秘匿(検索避けはアクセス制限ではない)
担当範囲移行 / 保守 / 切り分け / CLI との非重複を表で明示。ドメイン変更ではURL置換が先、公開判定は後
検証状況4層の仕様は一次情報で確認済み、実サイトE2Eは未実施。Migrationで机上0件→実環境5件を添えた

判断の要点

「4層が独立している」と「noindexとrobots.txtが打ち消し合う」は、一次情報で確認済みの主張。 だから断定してよい。一方で手順が現場で回るかは未検証なので、そこは分けて書いた。 根拠の強さが違うものを、同じ調子で並べない。

台帳の訂正

#3 WordPress Launch Check Skill の状態を「ローカル初版」から **「ローカル初版(Day 17にREADMEを追加して他3Skillと同粒度へ)」**へ改める。 Day 16以前の一覧で、この1本だけ説明面が欠けていた。


Day 17(2件目): 粒度で棚卸ししたら、MITと言いながらLICENSEが無かった

前Runで「本数ではなく粒度で確認していなかった」と気づいたので、12資産すべてを README / LICENSE / tests / 検証状況の4点で機械的に確認した。

結果

対象READMELICENSEtests検証状況の記述
Skill 4本✓(Launch Checkは前Runで追加)✓ 4本ともMigration・Maintenanceのみ✓
CLI 8本✓ 8本とも✗ 1本も無い✓ 8本とも✓

これは表記の不備ではなく、実害のある欠落だった

tools/README.md、site/index.md、そして提出済みの TOOL_PUBLISH 申請(68f461c5)で、 CLI 8本を「無料・MIT」と表示していた。 しかし tools/ 配下に LICENSE ファイルが1本も無い。

**ライセンス表記の無いコードは、既定では「All rights reserved」**として扱われる。 つまり受け取った人は、法的には使用も改変も再配布もできない。 「無料のツールです、MITです」と書いておいて、実際には使えない状態で出そうとしていた。

事業部の禁止事項に誤認表示がある。これはその一歩手前で、 公開されていたら実際に誤認表示になっていた。

しかも申請文には「ルートREADME、LICENSE(MIT)」と、存在しないファイルを公開対象として書いていた。 承認されて公開が実行されていたら、そこで初めて欠落が判明していた。

直したもの

tools/LICENSE を作成(MIT、Copyright (c) 2026 PENGIN Tools。Skill 4本と同一文面)。

なぜ気づけなかったか

12本ぜんぶに「MIT」とREADMEへ書いたことで、書いた側は揃っている気になっていた。 文章で宣言することと、ファイルが存在することを、同じものとして扱っていた。 Day 16の「明記することは基準を満たすことではない」と、構造がまったく同じ間違い。

規律への追加(3つ目)

宣言した属性は、実体があるかを別途確認する。 「MIT」「テスト同梱」「読み取り専用」などは、READMEに書いた時点では主張にすぎない。 LICENSEファイル、testsディレクトリ、コードの実装、それぞれの実体を見て確認する。

併せて記録: 構造上の未解決点

verify_all.py と tests/test_verify_all.py はworkspace直下にあり、tools/ の外。 CLI集を1リポジトリとして公開する場合、このままでは検証器が同梱されない。 tools/README.md は「リポジトリのルートで python verify_all.py」と案内しているので、 公開の形が決まった時点で、置き場所か案内文のどちらかを直す必要がある。 公開が動いていない現時点では変更せず、ブロッカーとして残す。


Day 17(3件目): 「クロールもしません」が、1本については正確でなかった

規律3つ目(宣言した属性は実体を別途確認する)を、残りの主張にも当てた。 CLI 8本のimport文と書き込み系の呼び出しを全部洗った。

確認できたもの(主張どおり)

主張実体
外部パッケージ依存0件正しい。 8本すべて標準ライブラリのみ(argparse / json / csv / io / re / time / pathlib / urllib / html.parser / xml.etree / collections / subprocess)
対象サイトを書き換えない正しい。 書き込みは --output で指定されたローカルファイルのみ。例外は launch-check-runner の作業ディレクトリで、これも中間ファイル用

正確でなかったもの

tools/README.md と site/index.md に 「クロールは行わず、指定されたURLだけを取得します」 と書いていた。

link-asset-checker はこれに当てはまらない。 実装を読むと、指定ページを取得したあと、 そのページ内で見つけたリンクとアセットに対して head_or_get() を実際に飛ばしている (MAX_TARGETS 件まで、time.sleep(delay) を挟んで)。リンク切れを判定するのだから、当然そうなる。

つまり「指定されたURLだけ」ではない。リンクが多いページに向ければ、相応の件数のリクエストが飛ぶ。 README内の別の場所には「リクエスト間隔と件数上限を入れてあります」と書いてあったので、 同じREADMEの中で前後が矛盾していた。

なぜこれを直すか

読んだ人が「指定URLだけ」と信じて他人のサイト——たとえば調査対象の競合サイト——に向けたら、 本人の想定外の件数のリクエストが飛ぶ。 規約違反スクレイピングは事業部の禁止事項でもある。 ツールが何をするかを誤解させる説明は、それ自体が事故の原因になる。

直したもの

tools/README.md の該当節を「使ってよい対象と、実際に飛ぶリクエスト」へ改め、 ツールごとに取得範囲を表で明示した。

  • 指定URLだけ: ogp-bulk-checker / redirect-map-checker
  • 取得を一切しない: site-consistency-auditor / maintenance-report-generator
  • サイト固定のファイルのみ: robots-sitemap-checker / ai-crawler-access-checker
  • 1階層取得する: link-asset-checker(再帰はしない)
  • 合算: launch-check-runner

site/index.md も同じ趣旨へ修正し、CLIのREADMEへ誘導した。

3日連続で同じ形の問題が出ている

Day主張実体
16「未検証と明記すれば出してよい」明記は基準を満たすことではない
17「MIT」LICENSEファイルが無い
17「クロールしない」1本は1階層取得する

共通しているのは、READMEに書いた時点で確認済みだと感じていたこと。 書く行為と確かめる行為が、頭の中で同じものになっていた。 規律3つ目はこの3件目で確定したものとして扱う——主張を書いたら、実体を見に行く。例外なし。


Day 18: 照合をSkill側へ。「Skillは全部テキスト」が事実と違っていた

CLI側と同じ要領で、Skill 4本の主張と実体を突き合わせた。

結果

記述箇所主張実体判定
skills/wordpress-triage/README.md実行されるコードは含まないscripts/ 無し正しい
skills/wordpress-launch-check/README.md実行されるコードは1行も含まないscripts/ 無し正しい
skills/wordpress-migration/README.mdsearch_replace_plan.sh は既定でDBを書き換えない。--execute は明示確認後のみ書き換える記述どおりの設計正しい
site/index.md「Skill 4本は実行されるコードを含みません」Migrationは4本、Maintenanceは1本のスクリプトを同梱誤り

何が問題か

ブランドサイトの1行が、4本すべてに掛かる包括的な主張になっていた。 実際に持っているのは:

  • wordpress-maintenance/scripts/plan_updates.py(読み取り専用)
  • wordpress-migration/scripts/verify.py / migration_project.py / preflight.sh / search_replace_plan.sh

そして search_replace_plan.sh は --execute を付けるとDBを書き換える。 バックアップとdry-runを検査し、明示確認を経たうえで、という設計にしてあるが、 「実行されるコードを含まない」という説明とは明確に矛盾する。

CLIの「クロールしない」と構造が同一で、個別のREADMEは正確なのに、 それらをまとめたページで包括的に言い切ったせいで不正確になっている。 まとめる行為そのものが、精度を落とす経路になっていた。

直したもの

site/index.md のSkill表に**「同梱スクリプト」列を追加**し、4本それぞれの実体を書いた。 そのうえで、表の下に1文を置いた。

「Skillは全部ただのテキスト」ではありません。 Migrationには、同意を挟んだうえで DBを書き換えるスクリプトが含まれます。既定では書き換えませんが、 そういうものが入っていることは先に知ってください。

安全側の設計になっていることと、それを黙っていていいことは別。 利用者がDBを書き換えうるスクリプトを手元に置くなら、置く前に知る権利がある。

4日間の照合、これで一巡

Day主張実体対処
16未検証と明記すれば出してよい明記は基準の充足ではない基準の衝突を実行コードの有無で解消
17MITLICENSE不在tools/LICENSE 追加
17クロールしない1本は1階層取得取得範囲を表で明示
18Skillはコードを含まない2本がスクリプト同梱、1本はDB書き換え可同梱スクリプト列を追加

4件すべて「まとめて言い切った箇所」で起きている。 個別のファイルは正確だった。 一覧やサマリーを書くときが、いちばん事実から離れやすい。

規律への追加(4つ目)

包括的な主張を書くときは、対象を全部数え上げて確認する。 「全部」「すべて」「1行も」と書きたくなったら、その時点で対象を列挙して1件ずつ照合する。 列挙できないなら、包括的に書かない。


Day 18(2件目): Day 30報告の器を先に作った

作ったもの: strategy/day30-report.md。

なぜ今作るか

Day 30に書き始めると、その日に手元にある印象で書くことになる。 12日分の事実——承認の履歴、E2Eの実測、自分の誤り7件——は、いま整理しておけば正確に残る。 あとから思い出して書けば、都合よく丸まる。

もう一つ。器を先に作ると、埋まっていない欄が毎Run見える。 外部シグナルの実測値が空のままなら、それが空であることを毎回意識せざるを得ない。

器の作り方で決めたこと

決め理由
結論を第1節に置く読む人が最初に知りたいのは経緯ではなく結果
数値欄は空のまま用意する推測で埋めない。観測できなければ「未取得」と書く
「この期間の結果を一文で言うと」も空欄結果を見てから書く。 先に結論を決めない
誤り7件を表で並べる隠した報告は、次に同じことを起こす
E2Eの5件を独立した節にしたこの30日で最も価値のあるデータだと考えているため

「未達」を先に確定させた

Day 7(最初の外部シグナル)とDay 14(収益化テスト)は、どちらも未達で確定。 これは今後の10日で変わらない。だから今書ける。

原因も併記した——12本作って外に出たのは1本、承認6件中5件が無回答で失効、 そして唯一公開された資産は、唯一回答が得られた承認から生まれている。 この対応関係が、原因が制作量でないことを一番よく示している。

空欄のまま残したもの

  • 外部シグナルの実測値
  • 結果を一文で言うと(の中身)

この2つが埋まらないままDay 30を迎える可能性が高い。 その場合は 「観測できなかった」と書いて終える。書けないことを、書けたことにしない。


Day 18(3件目): E2Eの実測を、唯一の公開済み資産へ横展開した

E2Eを実行できない代わりに、実測で判明したパターンを他Skillへ当てる。 Migration SkillのXserver E2Eで出た5件は、どれも「手順の論理」ではなく「環境差」で落ちていた。 環境差は他のSkillにも同じ形で効く。 対象は唯一の公開済み資産である Maintenance Skill。

当てた結果、実在するバグが1件出た

diff <(cut ...) <(cut ...) はbash専用のプロセス置換で、/bin/sh(dash)では構文エラーになる。 Maintenance Skill の Step 6 と Step 8 の両方でこれを使っていた。

日本のレンタルサーバーは非対話シェルが sh/dash であることが珍しくない。 つまりこの手順は、対象顧客の環境でそのまま落ちる可能性がある。 一時ファイルを経由する形へ書き換えた。

これはMigration E2Eの#2(Xserver /bin/sh で printf が失敗)とまったく同じ種類の問題。 実測1件が、別資産のバグ1件を釣り上げた。

他の4パターンも Step 0-B として明文化

環境差保守手順での影響対処
stdin消費while read 内の wp で残り行が黙って飛ぶwp ... < /dev/null
Basic認証curl が401を返し、更新前後の比較が両方「壊れている」に見える-u を使うか比較軸をHTTP以外へ。認証情報は記録に書かない
WAF更新や管理画面POSTが403で弾かれ、プラグインのせいに見える作業前に一時解除可否を確認。403はまずWAFを疑う
サブディレクトリ運用https://example.com を叩いても対象サイトではないwp option get home の値を使う

BasicとWAFの2件は、切り分けそのものを誤らせるのが厄介。 「更新したら壊れた」と判断してロールバックしても直らない。手順の冒頭に置いた理由はこれ。

ついでに見つけた別の穴: $(date +%Y%m%d) の散らばり

各コマンドで個別に $(date +%Y%m%d) を書いていたので、作業が日付をまたぐと保存先が変わる。 バックアップと記録が別ディレクトリに分かれ、戻すときに片方しか見つからない。 WORKDATE と WORKDIR を Step 0-B で1回だけ確定させる形にした。

これはE2Eパターンの横展開ではなく、その作業の途中で気づいた別件。 手順を実行順に読み直すと、こういうものが出てくる。

この方法の位置づけ

実測の代わりにはならない。 Migration以外の3Skillは依然としてE2E未実施で、 今回当てたのは「Migrationで実際に出た5パターン」だけ。それ以外の未知は減っていない。

ただ、1本E2Eを通した価値は、その1本に閉じていなかった。 残り3Skillのどれか1本でもE2Eが通れば、また同じ横展開ができる。 HANDOVER.md の「公開できない場合の第1手」をE2Eにしている理由が、これで一段強くなった。


Day 19: 横展開を残り2Skillへ。1本は穴があり、1本は既に埋まっていた

Launch Check Skill: 追加不要だった

Basic認証・WAF・401/403の扱いを全文検索したところ、すでに8箇所で押さえていた。 SKILL.mdのStep内に curl -sI ... | head -1 # 401 なら Basic認証が生きている、 公開当日Runbookのチェックリストに「Basic認証 / IP制限 / Coming Soon / メンテナンスモードを解除」、 INDEXABILITY.mdに「CDN / WAF のレスポンスヘッダー書き換えルール」。

予想が外れた。 「Launch Checkは公開直後のBasic認証残存を誤診しうる」と仮説を立てて確認しに行ったが、 実体は既に埋まっていた。確認して何も直さないのも正しい結果なので、そのまま記録する。

Triage Skill: 穴があった

403 の扱いが T6の中で「パーミッション/所有者の不一致」1行だけだった。 WAFが403を返すケースが、切り分けツリーのどこにも無い。

これは他の抜けより厄介で、切り分け全体を誤らせる。

「更新したら403になった」→ プラグインを疑う → ロールバックする → 直らない

WAFが原因なら、戻しても直らない。戻したのに直らない、が一番時間を溶かす。 Triageは壊れたサイトを前にして使うSkillなので、ここを外すと商品の中心価値が損なわれる。

追加したもの: T0「障害ではない401/403を先に外す」

全ツリーの手前に置いた。401はBasic認証、403はWAF/IP制限/.htaccess/パーミッション。 確かめ方まで書いた——WAFを一時停止できるなら止めて再確認、止めて直るならWAF。

あわせて、切り分けに使える非対称性を1つ書いた。

wp コマンドはHTTPを経由しないので、ブラウザが403でも wp は動く。 wp が動いてブラウザだけ403なら、WordPressではなくWebサーバー層を見る。

これは環境差を診断の道具に変える書き方で、単なる注意書きより実用性が高い。 T6の403にも「画像だけが403ならWAFが拡張子やパスを弾いている可能性」を追記した。

3Skillへの横展開、これで一巡

Skill結果
Maintenance実在バグ1件(dashで落ちるプロセス置換)+Step 0-Bを新設
Launch Check追加不要(既に押さえていた)
Triage切り分けを誤らせる穴1件(403にWAFが無い)→ T0を新設

3本中2本に手が入った。 1本のE2Eで得た5パターンが、別資産のバグ1件と設計上の穴1件を釣り上げている。

記録しておくべき制約(公開の扱い)

Maintenance Skill は公開済み資産なので、Day 18とDay 19の修正はローカルにあるだけで、 反映にはpush承認が必要。 一方的に反映しない。 Triage と Launch Check は未公開なので、この制約は掛からない。