[{"data":1,"prerenderedAt":809},["ShallowReactive",2],{"content-/test-recovery-plan-codex-rounds":3,"all-pages-for-dir":807,"og-image-/test-recovery-plan-codex-rounds":808},{"id":4,"title":5,"body":6,"category":789,"description":790,"extension":791,"meta":792,"navigation":793,"ogImage":794,"path":795,"project_name":796,"published":797,"publishedAt":798,"seo":799,"stem":800,"tags":801,"todo":794,"unpublished":797,"updatedAt":798,"__hash__":806},"pages/2026-05/2026-05-22/test-recovery-plan-codex-rounds.md","ビルドが postgenerate で落ち、テスト失敗復旧計画を Codex に4ラウンドレビューさせた話",{"type":7,"value":8,"toc":770},"minimark",[9,14,26,41,51,54,58,77,90,93,97,103,106,111,114,118,137,140,185,188,195,198,212,218,222,243,249,257,264,274,385,396,400,407,444,449,452,524,528,531,566,577,581,584,597,603,678,682,704,711,714,718,744,747,752,755,766],[10,11,13],"h2",{"id":12},"きっかけ-ビルド時間を測っていたら-postgenerate-が-exit-1-で止まった","きっかけ — ビルド時間を測っていたら postgenerate が exit 1 で止まった",[15,16,17,21,22,25],"p",{},[18,19,20],"code",{},"measure-deploy.ps1"," で Nuxt のビルド & デプロイのフェーズ別タイマーを回していた。",[18,23,24],{},"pnpm generate"," がどこに時間を食っているのか、SSG 本体・サイトマップ生成・redirect 生成・dist 検証のどれが伸びているのかを切り分けたかった。",[15,27,28,29,31,32,36,37,40],{},"10 分待った末、ターミナルが赤くなった。",[18,30,24],{}," 自体は通過したが、直後の ",[33,34,35],"strong",{},"postgenerate"," で ",[18,38,39],{},"scripts/verify-unpublished-excluded.mjs"," が exit 1。標準出力に「Leaked links: 9」と並び、ビルド全体を止めた。",[42,43,44],"blockquote",{},[15,45,46,47],{},"Leaked unpublished links found in published HTML: 9\nsee dist/",[48,49,50],"path",{},"/index.html",[15,52,53],{},"時間計測のはずがリグレッション検知になった。",[10,55,57],{"id":56},"原因-フラグを立てた当日内部リンクが本文に残っていた","原因 — フラグを立てた当日、内部リンクが本文に残っていた",[15,59,60,61,64,65,68,69,72,73,76],{},"朝に Phase 1 で ",[18,62,63],{},"unpublished: true"," を 19 件に拡大した。その中の ",[18,66,67],{},"nvda-bofa-pt-raise-may2026.md"," は ",[18,70,71],{},"publishedAt: \"2026-05-21\""," で、同じ日の ",[18,74,75],{},"diary-2026-05-21.md"," から「BofA が NVIDIA の PT を引き上げ」への Markdown リンクで参照されている。",[15,78,79,81,82,85,86,89],{},[18,80,63],{}," は「この記事の HTML を生成しない」を意味する。だが日記側の本文は普通の Markdown で、レンダラはリンクを ",[18,83,84],{},"\u003Ca href=\"(非公開記事のパス)\">"," に変換する。dist の HTML を grep すると、9 本の日記から 12 本の ",[18,87,88],{},"href"," が、もう生成されていない URL を指していた。",[15,91,92],{},"スクリプトはバグっていない。むしろ正しく漏えいを検出してビルドを止めてくれた。壊れていたのは運用側だった。",[10,94,96],{"id":95},"最初にunpublished-を-9-件外せば楽と言ってユーザーに止められた","最初に「unpublished を 9 件外せば楽」と言って、ユーザーに止められた",[15,98,99,100,102],{},"検出された 9 件のリンク先を見て、最初に思いついた逃げ道は「リンク先 9 件の ",[18,101,63],{}," を外す」だった。フラグを外した瞬間、検証スクリプトは「unpublished 記事ではない URL への href なので関係ない」と判定する。1 行コメントアウトを 9 ファイル分流せば終わる。",[15,104,105],{},"ユーザーに提案した。返事は数秒で来た。",[42,107,108],{},[15,109,110],{},"待って、unpublished を外したら公開記事になっちゃう。それは意図と違う。",[15,112,113],{},"朝に Tier 1〜2 を 19 件に絞り込んだ判断と矛盾する、と気づいた。フラグはそのまま、日記側の Markdown リンクをプレーンテキスト化する方針に戻した。",[10,115,117],{"id":116},"_12-箇所の一括-edit","12 箇所の一括 Edit",[15,119,120,121,124,125,128,129,132,133,136],{},"Grep で ",[18,122,123],{},"\\]\\(/(nvda-bofa-pt-raise|amd-openai-...|memory-investment|...)\\)"," を打って、9 本の日記から 12 箇所を全件洗い出した。Edit ツールで ",[18,126,127],{},"[テキスト](/path)"," → ",[18,130,131],{},"テキスト（未公開）"," の形に書き換えた。同一ファイル内で完全一致するパターンは ",[18,134,135],{},"replace_all: true"," で一括処理した。",[15,138,139],{},"書き換え後にもう 4 件残ったが、これは unpublished 記事同士の相互リンクで、リンク元の HTML 自体が生成されない。検証スクリプトはこの 4 件を無視する。",[141,142,147],"pre",{"className":143,"code":144,"language":145,"meta":146,"style":146},"language-bash shiki shiki-themes vitesse-light vitesse-light","node scripts/verify-unpublished-excluded.mjs --sources-only\n# Unpublished: 25\n# Published: 1050\n# Leaked links: 0\n","bash","",[18,148,149,166,173,179],{"__ignoreMap":146},[150,151,154,158,162],"span",{"class":152,"line":153},"line",1,[150,155,157],{"class":156},"senZ8","node",[150,159,161],{"class":160},"sdGka"," scripts/verify-unpublished-excluded.mjs",[150,163,165],{"class":164},"snbK4"," --sources-only\n",[150,167,169],{"class":152,"line":168},2,[150,170,172],{"class":171},"sxvE3","# Unpublished: 25\n",[150,174,176],{"class":152,"line":175},3,[150,177,178],{"class":171},"# Published: 1050\n",[150,180,182],{"class":152,"line":181},4,[150,183,184],{"class":171},"# Leaked links: 0\n",[15,186,187],{},"dist を作らず Markdown ソースだけで判定するオプションを足し、Vitest からも回せる形にした。",[10,189,191,192,194],{"id":190},"vitest-化-pnpm-generate-の-10-分を待たずに拾う","Vitest 化 — ",[18,193,24],{}," の 10 分を待たずに拾う",[15,196,197],{},"postgenerate でしか落ちないバグ、という構造が気持ち悪い。フラグを立てた瞬間 / 日記を書いた瞬間に拾える経路を作りたい。",[15,199,200,203,204,207,208,211],{},[18,201,202],{},"app/utils/find-leaked-unpublished-links.ts"," にロジックを切り出した。",[18,205,206],{},"ArticleSource[]"," を受け取り ",[18,209,210],{},"LeakedLink[]"," を返す純粋関数。frontmatter 抽出、unpublished 判定、リンク抽出を別関数に分け、入出力だけで完結させた。",[15,213,214,217],{},[18,215,216],{},"tests/unpublished-link-leakage.test.ts"," で実 content/ を流し込むテストを書いた。1075 ファイルを scan して 1 秒で終わる。これで「フラグを立てた次の Vitest 実行で落ちる」流れに変わった。",[10,219,221],{"id":220},"build-に-testrun-を前置きする案-revert","build に test:run を前置きする案 → revert",[15,223,224,225,227,228,231,232,235,236,239,240,242],{},"ここまで来て「",[18,226,20],{}," と ",[18,229,230],{},"package.json"," の ",[18,233,234],{},"predeploy:cloudflare"," チェーンに ",[18,237,238],{},"pnpm test:run"," を前置きすれば、漏えいリンクを残したまま ",[18,241,24],{}," が走ることは無くなる」と思いついた。10 分後に postgenerate で落ちるのは最悪のフィードバックループだ。前置きすれば 1 秒で落ちる。",[15,244,245,246,248],{},"組み込んでみた。",[18,247,238],{}," が常に赤い。漏えいリンク検出は通る。だが別件で 22/15688 件のテストが落ちていた。連結精算表エンジンが 16 件、OG メタタグが 4 件、URL 移行整合性が 2 件。build まで届かない。",[15,250,251,252,227,254,256],{},"前置きを足しても build が永遠に通らない。",[18,253,20],{},[18,255,230],{}," を revert した。「組み込みは諦めて、まず 22 件側を計画書化しよう」と方針を切り替えた。",[10,258,260,261],{"id":259},"計画書-test-failures-recovery-planmd","計画書 — ",[18,262,263],{},"test-failures-recovery-plan.md",[15,265,266,269,270,273],{},[18,267,268],{},"memo/2026-05-22/test-failures-recovery-plan.md"," に失敗 23 件（うち 1 件はあとから判明したタイムアウト含む）の内訳を整理した。あとで ",[18,271,272],{},"memo/2026-05-23/"," に移動した。",[275,276,277,297],"table",{},[278,279,280],"thead",{},[281,282,283,287,290,294],"tr",{},[284,285,286],"th",{},"グループ",[284,288,289],{},"テストファイル",[284,291,293],{"align":292},"right","失敗数",[284,295,296],{},"推定原因",[298,299,300,317,333,348,366],"tbody",{},[281,301,302,306,311,314],{},[303,304,305],"td",{},"A",[303,307,308],{},[18,309,310],{},"tests/check-sequential-ids.test.ts",[303,312,313],{"align":292},"1",[303,315,316],{},"5 秒タイムアウト超過",[281,318,319,322,327,330],{},[303,320,321],{},"B",[303,323,324],{},[18,325,326],{},"tests/consolidation-engine-consistency.test.ts",[303,328,329],{"align":292},"12",[303,331,332],{},"連結精算表の最終残高が期待値と乖離",[281,334,335,337,342,345],{},[303,336,321],{},[303,338,339],{},[18,340,341],{},"tests/consolidation-engine.test.ts",[303,343,344],{"align":292},"4",[303,346,347],{},"連結精算表の貸借一致が崩れている",[281,349,350,353,358,360],{},[303,351,352],{},"C",[303,354,355],{},[18,356,357],{},"tests/og-meta-tags.test.ts",[303,359,344],{"align":292},[303,361,362,365],{},[18,363,364],{},"useSeoMeta"," が無い / 不足",[281,367,368,371,376,379],{},[303,369,370],{},"D",[303,372,373],{},[18,374,375],{},"tests/url-migration.test.ts",[303,377,378],{"align":292},"2",[303,380,381,384],{},[18,382,383],{},"title"," 無しが 1 件混入",[15,386,387,388,391,392,395],{},"A/C/D は 30 分以内で消化できそうだったが、Group B が一番重い。連結精算表の最終残高が、追加取得 70→80% パターンで ",[18,389,390],{},"expected 524600"," に対して ",[18,393,394],{},"received 1199600","、約 2.3 倍ズレている。仕様変更でロジックが動いたのか、fixture/期待値が古いのか、切り分けが必要だった。",[10,397,399],{"id":398},"codex-4-ラウンドレビュー","Codex 4 ラウンドレビュー",[15,401,402,403,406],{},"計画書が書けたところで、毎回やっているレビューフロー（",[18,404,405],{},"~/.claude/rules/plan-codex-review.md","）に従って Codex に投げた。",[141,408,410],{"className":143,"code":409,"language":145,"meta":146,"style":146},"codex exec -m gpt-5.5 \"このプランをレビューして。瑣末な点へのクソリプはしないで。致命的な点だけ指摘して: \\\n  memo/2026-05-22/test-failures-recovery-plan.md (ref: CLAUDE.md)\"\n",[18,411,412,436],{"__ignoreMap":146},[150,413,414,417,420,423,426,430,433],{"class":152,"line":153},[150,415,416],{"class":156},"codex",[150,418,419],{"class":160}," exec",[150,421,422],{"class":164}," -m",[150,424,425],{"class":160}," gpt-5.5",[150,427,429],{"class":428},"sMJiu"," \"",[150,431,432],{"class":160},"このプランをレビューして。瑣末な点へのクソリプはしないで。致命的な点だけ指摘して: ",[150,434,435],{"class":164},"\\\n",[150,437,438,441],{"class":152,"line":168},[150,439,440],{"class":160},"  memo/2026-05-22/test-failures-recovery-plan.md (ref: CLAUDE.md)",[150,442,443],{"class":428},"\"\n",[445,446,448],"h3",{"id":447},"ラウンド-1-致命的指摘-3-件","ラウンド 1 — 致命的指摘 3 件",[15,450,451],{},"Codex の最初のレビューが返ってきた。3 件の致命傷が並んでいた。",[453,454,455,466,491],"ol",{},[456,457,458,461,462,465],"li",{},[33,459,460],{},"Group B-2 の「ブラウザで目視一致を確認するだけでは不十分」","。当初の計画書には「dev でブラウザを開き、画面の数字が期待値と一致するか確認する」と書いていた。Codex は「画面も同じエンジンで描画しているので、壊れた実装同士で整合して見える危険がある」と指摘してきた。確かにブラウザの数字が合っても、エンジン側のバグがテスト fixture と同じ向きにズレているだけ、という事故が起きうる。",[33,463,464],{},"独立検算ベース（手計算 / スプレッドシート）で「真の期待値」を出す","手順に書き直した。",[456,467,468,482,483,486,487,490],{},[33,469,470,473,474,477,478,481],{},[18,471,472],{},"consolidation-engine.ts"," のパスが ",[18,475,476],{},"composables/"," ではなく ",[18,479,480],{},"app/utils/"," 配下","。計画書が誤ったパスを書いていた。Grep で ",[18,484,485],{},"app/composables/consolidation-engine.ts"," を探しても見つからず、",[18,488,489],{},"app/utils/consolidation-engine.ts"," だけが存在する。",[456,492,493,498,499,501,502,504,505,508,509,512,513,231,516,519,520,523],{},[33,494,495,497],{},[18,496,238],{}," を入れる位置が間違っている","。",[18,500,20],{}," の repo root のままで叩くと ",[18,503,230],{}," に ",[18,506,507],{},"test:run"," スクリプトが無く ",[18,510,511],{},"Missing script"," で失敗する。",[18,514,515],{},"Push-Location -LiteralPath $webRoot",[33,517,518],{},"直後"," に置くか、",[18,521,522],{},"pnpm --dir $webRoot test:run"," で明示する。",[445,525,527],{"id":526},"ラウンド-2-パス修正と過去-commit-特定方法の補強","ラウンド 2 — パス修正と過去 commit 特定方法の補強",[15,529,530],{},"ラウンド 1 を反映したあと、resume で 2 回目を回した。",[141,532,534],{"className":143,"code":533,"language":145,"meta":146,"style":146},"codex exec resume --last -m gpt-5.5 \"プランを更新したからレビューして。瑣末な点へのクソリプはしないで。致命的な点だけ指摘して: \\\n  memo/2026-05-22/test-failures-recovery-plan.md\"\n",[18,535,536,559],{"__ignoreMap":146},[150,537,538,540,542,545,548,550,552,554,557],{"class":152,"line":153},[150,539,416],{"class":156},[150,541,419],{"class":160},[150,543,544],{"class":160}," resume",[150,546,547],{"class":164}," --last",[150,549,422],{"class":164},[150,551,425],{"class":160},[150,553,429],{"class":428},[150,555,556],{"class":160},"プランを更新したからレビューして。瑣末な点へのクソリプはしないで。致命的な点だけ指摘して: ",[150,558,435],{"class":164},[150,560,561,564],{"class":152,"line":168},[150,562,563],{"class":160},"  memo/2026-05-22/test-failures-recovery-plan.md",[150,565,443],{"class":428},[15,567,568,569,572,573,576],{},"Codex は「過去に pass していた commit を特定する方法が ",[18,570,571],{},"git log -- tests/..."," だけでは不十分」と指摘してきた。テストファイルが変わっていないリグレッションは log では拾えない。エンジン本体の変更履歴も追う必要がある、と。",[18,574,575],{},"git log -p --since=\"2026-04-01\" -- apps/web/app/utils/consolidation-engine.ts apps/web/tests/consolidation-engine*"," の形に直した。",[445,578,580],{"id":579},"ラウンド-3-git-checkout-を-git-worktree-に直す","ラウンド 3 — git checkout を git worktree に直す",[15,582,583],{},"ラウンド 3 で Codex が一番大きい指摘を投げてきた。",[42,585,586],{},[15,587,588,589,592,593,596],{},"過去 commit に戻して二分探索するなら、現在の作業ツリーで ",[18,590,591],{},"git checkout \u003Csha>"," するのは危険。未コミット変更や進行中の修正が混線する。",[18,594,595],{},"git worktree add ../mdx-playground-bisect \u003Csha>"," で別ディレクトリに展開して、そっちで pnpm install / test:run を回せ。",[15,598,599,600,602],{},"完全に正しい。当初の計画書には「",[18,601,591],{}," で過去に戻して」と書いていたが、いま走らせている修正と二分探索が同じ作業ツリーを取り合う。worktree なら本線をいじらず、別ディレクトリで完全に独立した state を持てる。書き換えた。",[141,604,606],{"className":143,"code":605,"language":145,"meta":146,"style":146},"# 【cwd: repo root (mdx-playground/)】から実行\ngit worktree add ../mdx-playground-bisect \u003Ccommit-sha>\npnpm --dir ../mdx-playground-bisect/apps/web install\npnpm --dir ../mdx-playground-bisect/apps/web test:run tests/consolidation-engine*\n# 終わったら git worktree remove ../mdx-playground-bisect\n",[18,607,608,613,641,655,672],{"__ignoreMap":146},[150,609,610],{"class":152,"line":153},[150,611,612],{"class":171},"# 【cwd: repo root (mdx-playground/)】から実行\n",[150,614,615,618,621,624,627,631,634,638],{"class":152,"line":168},[150,616,617],{"class":156},"git",[150,619,620],{"class":160}," worktree",[150,622,623],{"class":160}," add",[150,625,626],{"class":160}," ../mdx-playground-bisect",[150,628,630],{"class":629},"stQ0i"," \u003C",[150,632,633],{"class":160},"commit-sh",[150,635,637],{"class":636},"sG7-3","a",[150,639,640],{"class":629},">\n",[150,642,643,646,649,652],{"class":152,"line":175},[150,644,645],{"class":156},"pnpm",[150,647,648],{"class":164}," --dir",[150,650,651],{"class":160}," ../mdx-playground-bisect/apps/web",[150,653,654],{"class":160}," install\n",[150,656,657,659,661,663,666,669],{"class":152,"line":181},[150,658,645],{"class":156},[150,660,648],{"class":164},[150,662,651],{"class":160},[150,664,665],{"class":160}," test:run",[150,667,668],{"class":160}," tests/consolidation-engine",[150,670,671],{"class":164},"*\n",[150,673,675],{"class":152,"line":674},5,[150,676,677],{"class":171},"# 終わったら git worktree remove ../mdx-playground-bisect\n",[445,679,681],{"id":680},"ラウンド-4-コマンドの実行ディレクトリを明記","ラウンド 4 — コマンドの実行ディレクトリを明記",[15,683,684,685,688,689,692,693,696,697,700,701,703],{},"ラウンド 4。Codex は「コマンドの cwd を明記してほしい」と返してきた。「",[18,686,687],{},"apps/web/"," から打つと ",[18,690,691],{},"../mdx-playground-bisect"," が repo 外ではなく ",[18,694,695],{},"apps/"," 配下に作られてしまう。",[18,698,699],{},"git log"," のパスも ",[18,702,687],{}," から打つと pathspec がズレる」。",[15,705,706,707,710],{},"計画書の Bash ブロックすべてに ",[18,708,709],{},"# 【cwd: repo root (mdx-playground/)】から実行する"," のコメントを足した。再度 resume で投げると、Codex から「致命的な指摘なし」が返ってきた。",[15,712,713],{},"4 ラウンドで終わった。最初のラウンドで「ブラウザ目視で確認」と書いていた箇所は、Codex の独立検算ベースの指摘で完全に潰れた。同じエンジンで描いた画面を信用して fixture を作り直す事故を未然に防げた、と思う。",[10,715,717],{"id":716},"google-タスクに積み残し登録","Google タスクに積み残し登録",[15,719,720,723,724,727,728,731,732,735,736,739,740,743],{},[18,721,722],{},"add-task"," スキルで「明日 23 件のテスト失敗を消化する」を Google タスクに登録した。スキル実行中に小さい不具合を踏んだ。スキル本体が ",[18,725,726],{},"/tmp/\u003Ctemp_file>"," を作って Python (Windows) に渡そうとして、Git Bash 側の ",[18,729,730],{},"/tmp"," と Python 側の ",[18,733,734],{},"tempfile.gettempdir()"," の解決パスがズレていた。",[18,737,738],{},"mktemp"," ベースに直し、Python 側でも ",[18,741,742],{},"Path()"," で受けるようにしてもらった。",[10,745,746],{"id":746},"今日の学び",[15,748,749,751],{},[18,750,24],{}," の最後に postgenerate で漏えい検出が走るのは正しい設計だが、10 分待ってから落ちるのはフィードバックが遅すぎる。検証スクリプトを Vitest 化して 1 秒で落ちる経路を増やしたのは正解だった。",[15,753,754],{},"「unpublished を外せば楽」と提案してユーザーに止められた瞬間、フラグの意味そのものを忘れかけていた。AI に書かせた一括 Edit でも、判断（フラグを外すか / リンク側を直すか）は人間が握る。",[15,756,757,758,761,762,765],{},"Codex のレビューが 4 ラウンドかかったが、特にラウンド 3 の ",[18,759,760],{},"git worktree"," 指摘は自分だけでは絶対に出てこなかった。二分探索を ",[18,763,764],{},"git checkout"," で本線ツリー上でやっていたら、進行中の Phase 1 修正と混ざって週末に取り返しがつかない事故を起こしていた可能性がある。",[767,768,769],"style",{},"html pre.shiki code .senZ8, html code.shiki .senZ8{--shiki-default:#59873A;--shiki-dark:#59873A}html pre.shiki code .sdGka, html code.shiki .sdGka{--shiki-default:#B56959;--shiki-dark:#B56959}html pre.shiki code .snbK4, html code.shiki .snbK4{--shiki-default:#A65E2B;--shiki-dark:#A65E2B}html pre.shiki code .sxvE3, html code.shiki .sxvE3{--shiki-default:#A0ADA0;--shiki-dark:#A0ADA0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMJiu, html code.shiki .sMJiu{--shiki-default:#B5695977;--shiki-dark:#B5695977}html pre.shiki code .stQ0i, html code.shiki .stQ0i{--shiki-default:#AB5959;--shiki-dark:#AB5959}html pre.shiki code .sG7-3, html code.shiki .sG7-3{--shiki-default:#393A34;--shiki-dark:#393A34}",{"title":146,"searchDepth":168,"depth":168,"links":771},[772,773,774,775,776,778,779,781,787,788],{"id":12,"depth":168,"text":13},{"id":56,"depth":168,"text":57},{"id":95,"depth":168,"text":96},{"id":116,"depth":168,"text":117},{"id":190,"depth":168,"text":777},"Vitest 化 — pnpm generate の 10 分を待たずに拾う",{"id":220,"depth":168,"text":221},{"id":259,"depth":168,"text":780},"計画書 — test-failures-recovery-plan.md",{"id":398,"depth":168,"text":399,"children":782},[783,784,785,786],{"id":447,"depth":175,"text":448},{"id":526,"depth":175,"text":527},{"id":579,"depth":175,"text":580},{"id":680,"depth":175,"text":681},{"id":716,"depth":168,"text":717},{"id":746,"depth":168,"text":746},"dev","measure-deploy.ps1 でビルド時間を測っている最中に verify-unpublished-excluded.mjs が exit 1 を出し、公開済み日記から非公開記事への内部リンクが9件残っていたことが判明。フラグを外すか/リンクをプレーンテキスト化するかの判断、内部リンク12箇所の一括 Edit、Vitest化、build 前置きの試行と revert、test-failures-recovery-plan.md を Codex に4ラウンドレビューさせて致命的指摘を全て潰すまでの作業ログ。","md",{},true,null,"/test-recovery-plan-codex-rounds","mdx-playground",false,"2026-05-22T00:00:00.000Z",{"title":5,"description":790},"2026-05/2026-05-22/test-recovery-plan-codex-rounds",[802,803,804,24,805,760],"Codex","テスト復旧","Vitest","計画書レビュー","CdTA9hI2C06Q9C9uVwn_N3-iAqexItEk6coRr8M4Uko",[],"https://log.eurekapu.com/og/blog/test-recovery-plan-codex-rounds.png?v=2026-05-22T00%3A00%3A00.000Z&title=%E3%83%93%E3%83%AB%E3%83%89%E3%81%8C%20postgenerate%20%E3%81%A7%E8%90%BD%E3%81%A1%E3%80%81%E3%83%86%E3%82%B9%E3%83%88%E5%A4%B1%E6%95%97%E5%BE%A9%E6%97%A7%E8%A8%88%E7%94%BB%E3%82%92%20Codex%20%E3%81%AB4%E3%83%A9%E3%82%A6%E3%83%B3%E3%83%89%E3%83%AC%E3%83%93%E3%83%A5%E3%83%BC%E3%81%95%E3%81%9B%E3%81%9F%E8%A9%B1&author=Kei%20Komatsu&sig=cbab9ad0b7b3d261",1782528841525]