[{"data":1,"prerenderedAt":588},["ShallowReactive",2],{"content-/case100-followup-and-casenote-layout":3,"all-pages-for-dir":586,"og-image-/case100-followup-and-casenote-layout":587},{"id":4,"title":5,"body":6,"category":568,"description":569,"extension":570,"meta":571,"navigation":572,"ogImage":573,"path":574,"project_name":575,"published":576,"publishedAt":577,"seo":578,"stem":579,"tags":580,"todo":584,"unpublished":576,"updatedAt":573,"__hash__":585},"pages/2026-05/2026-05-05/case100-followup-and-casenote-layout.md","case100ノート統合の積み残し消化とCaseNoteLayoutリファクタ計画",{"type":7,"value":8,"toc":551},"minimark",[9,13,18,22,35,38,42,49,52,56,63,73,311,315,322,325,329,344,347,351,412,422,424,428,431,435,441,445,460,470,474,480,484,490,510,517,519,522,529,538,547],[10,11,5],"h1",{"id":12},"case100ノート統合の積み残し消化とcasenotelayoutリファクタ計画",[14,15,17],"h2",{"id":16},"_0825-セッション-積み残しを全件クローズ","08:25 セッション — 積み残しを全件クローズ",[19,20,21],"p",{},"前日（2026-05-04）は22件のcase100ノート統合を5並列セッションで走らせて終了した。翌朝、まず全件のHTTP確認から入った。",[23,24,25,29,32],"ul",{},[26,27,28],"li",{},"22件 HTTP 200 確認",[26,30,31],{},"52件 リダイレクト（301）確認",[26,33,34],{},"全件成功",[19,36,37],{},"数字が揃った時点で積み残しの本題に移った。",[39,40,41],"h3",{"id":41},"孤児カテゴリを発見して削除",[19,43,44,48],{},[45,46,47],"code",{},"groupByPrimaryCategory"," のフィルタを眺めていると、「DTAの再評価・追徴」カテゴリが引っかかっていなかった。該当ノートはカテゴリに割り当てられているのに、フィルタの定義リストに存在しない——孤児化していた。",[19,50,51],{},"Codex（GPT-5.5）に状況を投げると「孤児カテゴリは削除を推奨」と返ってきた。迷わず削除した。もし将来ノートが増えてカテゴリが復活するなら、その時に追加すればいい。",[39,53,55],{"id":54},"関連ノートuiサイドバーを追加","関連ノートUIサイドバーを追加",[19,57,58,59,62],{},"ChapterPager に ",[45,60,61],{},"relatedChapters","（関連ノートのサイドバー）を追加実装した。",[19,64,65,68,69,72],{},[45,66,67],{},"index.ts"," に ",[45,70,71],{},"relatedChapstersFor(slug)"," ヘルパーを生やして、incoming リンクも双方向で自動集計する設計にした。既存ノートを一切改修しなくても関連が集計される点が核心で、22件＋110件のノートに手を入れずにサイドバーが成立した。",[74,75,80],"pre",{"className":76,"code":77,"language":78,"meta":79,"style":79},"language-typescript shiki shiki-themes vitesse-light vitesse-light","// index.ts — incoming linkも双方向で集計\nexport const relatedChaptersFor = (slug: string): Chapter[] => {\n  const outgoing = chapters.filter(c => c.relatedSlugs?.includes(slug))\n  const incoming = chapters.filter(c => c.slug !== slug && chapters\n    .find(x => x.slug === slug)?.relatedSlugs?.includes(c.slug))\n  return [...new Set([...outgoing, ...incoming])]\n}\n","typescript","",[45,81,82,91,139,188,227,274,305],{"__ignoreMap":79},[83,84,87],"span",{"class":85,"line":86},"line",1,[83,88,90],{"class":89},"sxvE3","// index.ts — incoming linkも双方向で集計\n",[83,92,94,98,102,106,110,113,117,120,124,127,130,133,136],{"class":85,"line":93},2,[83,95,97],{"class":96},"sHkkW","export",[83,99,101],{"class":100},"stQ0i"," const ",[83,103,105],{"class":104},"senZ8","relatedChaptersFor",[83,107,109],{"class":108},"shFtX"," =",[83,111,112],{"class":108}," (",[83,114,116],{"class":115},"s4oTP","slug",[83,118,119],{"class":108},": ",[83,121,123],{"class":122},"sSkh3","string",[83,125,126],{"class":108},"):",[83,128,129],{"class":122}," Chapter",[83,131,132],{"class":108},"[]",[83,134,135],{"class":108}," =>",[83,137,138],{"class":108}," {\n",[83,140,142,145,148,150,153,156,159,162,165,167,170,172,175,178,181,183,185],{"class":85,"line":141},3,[83,143,144],{"class":100},"  const ",[83,146,147],{"class":115},"outgoing",[83,149,109],{"class":108},[83,151,152],{"class":115}," chapters",[83,154,155],{"class":108},".",[83,157,158],{"class":104},"filter",[83,160,161],{"class":108},"(",[83,163,164],{"class":115},"c",[83,166,135],{"class":108},[83,168,169],{"class":115}," c",[83,171,155],{"class":108},[83,173,174],{"class":115},"relatedSlugs",[83,176,177],{"class":108},"?.",[83,179,180],{"class":104},"includes",[83,182,161],{"class":108},[83,184,116],{"class":115},[83,186,187],{"class":108},"))\n",[83,189,191,193,196,198,200,202,204,206,208,210,212,214,216,219,221,224],{"class":85,"line":190},4,[83,192,144],{"class":100},[83,194,195],{"class":115},"incoming",[83,197,109],{"class":108},[83,199,152],{"class":115},[83,201,155],{"class":108},[83,203,158],{"class":104},[83,205,161],{"class":108},[83,207,164],{"class":115},[83,209,135],{"class":108},[83,211,169],{"class":115},[83,213,155],{"class":108},[83,215,116],{"class":115},[83,217,218],{"class":100}," !== ",[83,220,116],{"class":115},[83,222,223],{"class":100}," && ",[83,225,226],{"class":115},"chapters\n",[83,228,230,233,236,238,241,243,246,248,250,253,255,258,260,262,264,266,268,270,272],{"class":85,"line":229},5,[83,231,232],{"class":108},"    .",[83,234,235],{"class":104},"find",[83,237,161],{"class":108},[83,239,240],{"class":115},"x",[83,242,135],{"class":108},[83,244,245],{"class":115}," x",[83,247,155],{"class":108},[83,249,116],{"class":115},[83,251,252],{"class":100}," === ",[83,254,116],{"class":115},[83,256,257],{"class":108},")?.",[83,259,174],{"class":115},[83,261,177],{"class":108},[83,263,180],{"class":104},[83,265,161],{"class":108},[83,267,164],{"class":115},[83,269,155],{"class":108},[83,271,116],{"class":115},[83,273,187],{"class":108},[83,275,277,280,283,286,289,292,294,297,300,302],{"class":85,"line":276},6,[83,278,279],{"class":96},"  return",[83,281,282],{"class":108}," [...",[83,284,285],{"class":100},"new ",[83,287,288],{"class":104},"Set",[83,290,291],{"class":108},"([...",[83,293,147],{"class":115},[83,295,296],{"class":108},",",[83,298,299],{"class":108}," ...",[83,301,195],{"class":115},[83,303,304],{"class":108},"])]\n",[83,306,308],{"class":85,"line":307},7,[83,309,310],{"class":108},"}\n",[39,312,314],{"id":313},"simplify-レビュー-2件採用","/simplify レビュー → 2件採用",[19,316,317,318,321],{},"実装後に ",[45,319,320],{},"/simplify"," でレビューを回した。2件の指摘を採用して修正した。残りは影響範囲が小さいと判断してスキップした。",[323,324],"hr",{},[14,326,328],{"id":327},"casenotelayout-リファクタ計画書を作成640行","CaseNoteLayout リファクタ計画書を作成（640行）",[19,330,331,332,335,336,339,340,343],{},"次のタスクは、22件＋110件の Vue コンポーネントに散らばった ",[45,333,334],{},"\u003Cstyle scoped>"," の CSS 重複を解消することだった。ファイルごとに同じ ",[45,337,338],{},".callout"," や ",[45,341,342],{},".figure"," を定義しているため、変更のたびに全ファイルを触る必要があった。",[19,345,346],{},"計画書を書いて、Codex に 4 ラウンドのレビューを通した。",[39,348,350],{"id":349},"codex-4-ラウンドの流れ","Codex 4 ラウンドの流れ",[352,353,354,367],"table",{},[355,356,357],"thead",{},[358,359,360,364],"tr",{},[361,362,363],"th",{},"ラウンド",[361,365,366],{},"結果",[368,369,370,385,396,404],"tbody",{},[358,371,372,376],{},[373,374,375],"td",{},"Round 1",[373,377,378,380,381,384],{},[45,379,338],{}," が他系統と衝突するリスクを指摘 → ",[45,382,383],{},".case-note__body"," で隔離する方針に変更",[358,386,387,390],{},[373,388,389],{},"Round 2",[373,391,392,393,395],{},"Phase の粒度が大きすぎると指摘 → Phase 毎に ",[45,394,320],{}," 必須を明記して細分化",[358,397,398,401],{},[373,399,400],{},"Round 3",[373,402,403],{},"「致命的な指摘なし」確定",[358,405,406,409],{},[373,407,408],{},"Round 4",[373,410,411],{},"最終確認（軽微な表記修正のみ）",[19,413,414,415,417,418,421],{},"Round 1 の指摘が一番効いた。",[45,416,338],{}," をそのまま外部 CSS に移すと、他のページのスタイルを壊すリスクがあった。",[45,419,420],{},".case-note__body .callout"," という形で詳細度を上げて隔離する方針に切り替えたことで、計画が安定した。",[323,423],{},[14,425,427],{"id":426},"_1323-セッション-phase-02-を実装","13:23 セッション — Phase 0〜2 を実装",[19,429,430],{},"計画書に従い、午後のセッションで Phase 0〜2 を実装した。",[39,432,434],{"id":433},"phase-0-claudemd-規約変更","Phase 0: CLAUDE.md 規約変更",[19,436,437,440],{},[45,438,439],{},".case-note*"," 名前空間を例外規定として CLAUDE.md に追記した。プロジェクト全体の CSS 命名規約が BEM ベースに統一されているなかで、case-note 系だけ独立した名前空間を持つことを明文化した。",[39,442,444],{"id":443},"phase-1-共通-css-の確定","Phase 1: 共通 CSS の確定",[19,446,447,448,450,451,450,453,456,457,459],{},"対象 52 件のノートを特定して、共通化できるクラスを洗い出した。",[45,449,338],{},"、",[45,452,342],{},[45,454,455],{},".topics-list"," が他系統と衝突するリスクがあったため、すべて ",[45,458,383],{}," の直下子として隔離する方針に固めた。",[19,461,462,465,466,469],{},[45,463,464],{},"app/assets/css/case-note.css"," を新規作成した。既存の ",[45,467,468],{},"steps.css"," と同じディレクトリに置いた。",[39,471,473],{"id":472},"phase-2-case100notelayout-コンポーネント作成","Phase 2: Case100NoteLayout コンポーネント作成",[19,475,476,479],{},[45,477,478],{},"allowance-for-doubtful-accounts-lifecycle.vue"," をパイロット移行先に選んだ。dev server で表示を確認して、スタイルの崩れがないことを目視で検証した。",[39,481,483],{"id":482},"simplify-で-4-件の致命的指摘を修正","/simplify で 4 件の致命的指摘を修正",[19,485,486,487,489],{},"パイロット移行後に ",[45,488,320],{}," を回したところ、致命的な指摘が 4 件出た。",[23,491,492,495,504,507],{},[26,493,494],{},"ChapterPager を NoteLayout の外に置いていた → NoteLayout 内に移動",[26,496,497,499,500,503],{},[45,498,338],{}," の詳細度が不足 → ",[45,501,502],{},".case-note__body > .callout"," に変更",[26,505,506],{},"CLAUDE.md のパス記述が誤っていた → 修正",[26,508,509],{},"もう 1 件の構造的な誤記",[19,511,512,513,516],{},"修正後、",[45,514,515],{},".nuxt"," キャッシュをクリアして dev server を再起動し、再検証した。",[323,518],{},[14,520,521],{"id":521},"振り返り",[19,523,524,528],{},[525,526,527],"strong",{},"孤児カテゴリの発見は、フィルタ定義とデータの乖離が見えた瞬間だった。"," 22件を並列で統合した後処理として、こういった「定義漏れ」が出てくるのは想定内だったが、Codex に聞いて即断できたのは判断の速さという点でよかった。",[19,530,531,534,535,537],{},[525,532,533],{},"Codex 4 ラウンドのレビューは、計画段階の詰めを前倒しにする作業だった。"," 実装に入ってから「",[45,536,338],{}," が衝突する」と気づくより、計画書の段階でつぶしておいた方が手戻りが少ない。Round 3 で致命的指摘がゼロになったことで、実装に踏み切る根拠ができた。",[19,539,540,546],{},[525,541,542,543,545],{},"Phase 2 の ",[45,544,320],{}," で 4 件が出たのは想定外だった。"," パイロット実装のつもりで動かしてみると、計画書では見えていなかった詳細度の問題や構造的な誤配置が浮かぶ。「まず 1 ファイル動かして検証する」という手順が正しかった。",[548,549,550],"style",{},"html pre.shiki code .sxvE3, html code.shiki .sxvE3{--shiki-default:#A0ADA0;--shiki-dark:#A0ADA0}html pre.shiki code .sHkkW, html code.shiki .sHkkW{--shiki-default:#1E754F;--shiki-dark:#1E754F}html pre.shiki code .stQ0i, html code.shiki .stQ0i{--shiki-default:#AB5959;--shiki-dark:#AB5959}html pre.shiki code .senZ8, html code.shiki .senZ8{--shiki-default:#59873A;--shiki-dark:#59873A}html pre.shiki code .shFtX, html code.shiki .shFtX{--shiki-default:#999999;--shiki-dark:#999999}html pre.shiki code .s4oTP, html code.shiki .s4oTP{--shiki-default:#B07D48;--shiki-dark:#B07D48}html pre.shiki code .sSkh3, html code.shiki .sSkh3{--shiki-default:#2E8F82;--shiki-dark:#2E8F82}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);}",{"title":79,"searchDepth":93,"depth":93,"links":552},[553,558,561,567],{"id":16,"depth":93,"text":17,"children":554},[555,556,557],{"id":41,"depth":141,"text":41},{"id":54,"depth":141,"text":55},{"id":313,"depth":141,"text":314},{"id":327,"depth":93,"text":328,"children":559},[560],{"id":349,"depth":141,"text":350},{"id":426,"depth":93,"text":427,"children":562},[563,564,565,566],{"id":433,"depth":141,"text":434},{"id":443,"depth":141,"text":444},{"id":472,"depth":141,"text":473},{"id":482,"depth":141,"text":483},{"id":521,"depth":93,"text":521},"dev","前日の22件並列統合の後処理（孤児カテゴリ削除・関連ノートUI追加）と、CaseNoteLayout.vue リファクタ計画の Codex 4ラウンドレビューを経た Phase 0〜2 実装まで","md",{},true,null,"/case100-followup-and-casenote-layout","eurekapu-nuxt4",false,"2026-05-05T00:00:00.000Z",{"title":5,"description":569},"2026-05/2026-05-05/case100-followup-and-casenote-layout",[581,582,583],"case100","リファクタリング","Vue","memo","gz8_VsxQcO2EbYlvjYbj1bvuInDDdOvA8SoPZ3l4m9o",[],"https://log.eurekapu.com/og/blog/case100-followup-and-casenote-layout.png?v=2026-05-05T00%3A00%3A00.000Z&title=case100%E3%83%8E%E3%83%BC%E3%83%88%E7%B5%B1%E5%90%88%E3%81%AE%E7%A9%8D%E3%81%BF%E6%AE%8B%E3%81%97%E6%B6%88%E5%8C%96%E3%81%A8CaseNoteLayout%E3%83%AA%E3%83%95%E3%82%A1%E3%82%AF%E3%82%BF%E8%A8%88%E7%94%BB&author=Kei%20Komatsu&sig=1923dd145bb4db44",1782528833174]