[{"data":1,"prerenderedAt":202},["ShallowReactive",2],{"vymFhUBKL2":3,"rrI6o1i1Hk":28,"8rNOjjNXvN":175},{"contents":4,"totalCount":25,"offset":26,"limit":27},[5,10,15,20],{"id":6,"createdAt":7,"updatedAt":8,"publishedAt":7,"revisedAt":7,"name":9},"organization","2022-12-21T03:40:34.782Z","2022-12-21T09:20:51.495Z","組織",{"id":11,"createdAt":12,"updatedAt":13,"publishedAt":12,"revisedAt":12,"name":14},"technology","2022-08-31T02:41:54.498Z","2022-08-31T02:46:16.330Z","技術記事",{"id":16,"createdAt":17,"updatedAt":18,"publishedAt":18,"revisedAt":18,"name":19},"training","2022-08-31T02:45:35.765Z","2024-09-30T10:22:17.808Z","新卒研修",{"id":21,"createdAt":22,"updatedAt":23,"publishedAt":22,"revisedAt":22,"name":24},"intern","2022-08-31T02:42:45.699Z","2022-08-31T02:46:25.436Z","インターン",4,0,10,{"contents":29,"totalCount":173,"offset":26,"limit":174},[30,34,38,42,46,50,55,59,65,70,75,80,84,90,95,100,105,110,115,120,124,130,135,140,145,149,153,158,163,168],{"id":31,"createdAt":32,"updatedAt":32,"publishedAt":32,"revisedAt":32,"name":33},"z6p844x67ko","2025-11-22T02:57:11.491Z","Java",{"id":35,"createdAt":36,"updatedAt":36,"publishedAt":36,"revisedAt":36,"name":37},"ckp1og2uagsw","2025-10-08T08:13:39.810Z","Nuxt4",{"id":39,"createdAt":40,"updatedAt":40,"publishedAt":40,"revisedAt":40,"name":41},"ycs3reu0e3","2025-03-28T02:57:04.686Z","WEB開発",{"id":43,"createdAt":44,"updatedAt":44,"publishedAt":44,"revisedAt":44,"name":45},"nzjz_bbyz6y","2025-03-28T02:47:11.136Z","サイバー攻撃",{"id":47,"createdAt":48,"updatedAt":48,"publishedAt":48,"revisedAt":48,"name":49},"tzeoepl83t7","2024-10-31T02:59:31.290Z","Oracle Database",{"id":51,"createdAt":52,"updatedAt":53,"publishedAt":52,"revisedAt":53,"name":54},"nlkwa5glx","2024-10-31T02:58:41.489Z","2024-10-31T02:58:54.976Z","SQL",{"id":56,"createdAt":57,"updatedAt":57,"publishedAt":57,"revisedAt":57,"name":58},"s32c5tf_uq","2024-10-31T02:58:05.734Z","DB",{"id":60,"createdAt":61,"updatedAt":62,"publishedAt":63,"revisedAt":63,"name":64},"vba","2024-09-20T09:06:06.996Z","2024-09-20T09:06:48.977Z","2024-09-20T09:06:12.844Z","VBA",{"id":66,"createdAt":67,"updatedAt":68,"publishedAt":68,"revisedAt":68,"name":69},"cpp","2024-09-09T02:35:37.527Z","2024-10-03T08:57:26.900Z","C++",{"id":71,"createdAt":72,"updatedAt":73,"publishedAt":73,"revisedAt":73,"name":74},"nextjs","2024-08-16T03:14:46.465Z","2024-08-16T03:20:35.864Z","Next.js",{"id":76,"createdAt":77,"updatedAt":78,"publishedAt":77,"revisedAt":77,"name":79},"playwright","2024-07-17T05:25:02.312Z","2024-08-16T03:15:18.921Z","Playwright",{"id":81,"createdAt":82,"updatedAt":82,"publishedAt":82,"revisedAt":82,"name":83},"tool","2024-03-25T09:34:14.864Z","ツール",{"id":85,"createdAt":86,"updatedAt":87,"publishedAt":88,"revisedAt":88,"name":89},"other","2024-02-27T01:59:22.834Z","2024-10-03T08:57:49.645Z","2024-09-30T10:22:30.758Z","その他",{"id":91,"createdAt":92,"updatedAt":93,"publishedAt":93,"revisedAt":93,"name":94},"cms","2024-02-20T07:09:50.439Z","2024-03-25T09:42:44.026Z","CMS",{"id":96,"createdAt":97,"updatedAt":98,"publishedAt":98,"revisedAt":98,"name":99},"adobe","2024-02-20T07:08:53.662Z","2025-11-26T07:45:15.789Z","Adobe",{"id":101,"createdAt":102,"updatedAt":103,"publishedAt":103,"revisedAt":103,"name":104},"figma","2024-02-20T07:08:36.238Z","2025-11-26T07:43:06.002Z","Figma",{"id":106,"createdAt":107,"updatedAt":108,"publishedAt":108,"revisedAt":108,"name":109},"ui-ux","2024-02-20T07:05:12.468Z","2025-11-26T07:42:55.613Z","UI/UX",{"id":111,"createdAt":112,"updatedAt":113,"publishedAt":113,"revisedAt":113,"name":114},"design","2024-02-20T07:04:54.082Z","2025-11-26T07:43:15.904Z","デザイン",{"id":116,"createdAt":117,"updatedAt":118,"publishedAt":118,"revisedAt":118,"name":119},"direction","2024-02-20T07:04:06.909Z","2024-09-30T10:22:42.799Z","ディレクション",{"id":121,"createdAt":122,"updatedAt":122,"publishedAt":122,"revisedAt":122,"name":123},"generative-ai","2024-02-05T00:55:41.413Z","生成AI",{"id":125,"createdAt":126,"updatedAt":127,"publishedAt":126,"revisedAt":128,"name":129},"ci-cd","2023-08-09T10:03:22.203Z","2024-02-20T07:02:28.063Z","2024-02-20T07:02:11.829Z","CI/CD",{"id":131,"createdAt":132,"updatedAt":133,"publishedAt":133,"revisedAt":133,"name":134},"dot-net","2023-06-21T20:46:39.622Z","2023-06-23T11:48:15.578Z",".NET",{"id":136,"createdAt":137,"updatedAt":138,"publishedAt":138,"revisedAt":138,"name":139},"azure","2023-06-21T20:44:35.779Z","2023-06-23T11:47:33.374Z","Azure",{"id":141,"createdAt":142,"updatedAt":143,"publishedAt":143,"revisedAt":143,"name":144},"edr","2023-06-09T06:32:49.938Z","2023-06-09T11:26:32.649Z","EDR",{"id":146,"createdAt":147,"updatedAt":147,"publishedAt":147,"revisedAt":147,"name":148},"nuxt3rc","2023-03-10T04:46:51.238Z","Nuxt3rc",{"id":150,"createdAt":151,"updatedAt":151,"publishedAt":151,"revisedAt":151,"name":152},"nuxt3","2023-03-10T04:45:29.480Z","Nuxt3",{"id":154,"createdAt":155,"updatedAt":156,"publishedAt":155,"revisedAt":155,"name":157},"self-introduction","2022-08-29T04:46:26.716Z","2022-09-30T09:13:14.924Z","自己紹介",{"id":159,"createdAt":160,"updatedAt":161,"publishedAt":160,"revisedAt":160,"name":162},"engineer","2022-08-29T04:45:53.624Z","2022-09-30T09:13:19.012Z","エンジニア",{"id":164,"createdAt":165,"updatedAt":166,"publishedAt":165,"revisedAt":165,"name":167},"license","2022-08-29T04:46:41.319Z","2022-08-30T02:43:14.592Z","資格",{"id":169,"createdAt":170,"updatedAt":171,"publishedAt":170,"revisedAt":170,"name":172},"book-review","2022-08-29T04:46:08.443Z","2022-08-30T02:44:44.851Z","書評",38,30,{"id":176,"createdAt":177,"updatedAt":178,"publishedAt":179,"revisedAt":178,"title":180,"authorName":181,"content_2":183,"category":184,"tag":185,"related":187,"meta_description":201,"is_migrate":200},"jdqkqgjavou","2026-03-27T02:07:41.004Z","2026-03-30T11:38:56.745Z","2026-03-30T11:30:20.784Z","Claude Codeで試すHarness Engineeringの実験的検証",[182],"Akiyoshi","\u003Ch2 id=\"h8d027c8ed3\">はじめに\u003C/h2>\u003Cp>ハーネスエンジニアリングの話題は増えましたが、実務で知りたいのは「何を足すと、どこに効くのか」です。\u003C/p>\u003Cp>\u003Cbr>今回の検証は、簡易的なアプリを実際にAIエージェントに作ってもらって、事前に与えるコンテキストを変えたときの差分を評価しました。\u003Cbr>\u003Cbr>人間がやることは「〇〇をXXという条件で作って」というもので無味な作業です。\u003Cbr>ただ実行しても面白くないので、私自身はコマンド実行係となり、実行から結果の文章まとめまでALL AIエージェントでやってみました。\u003Cbr>\u003Cbr>今回はClaude Codeを使って、技術スタックを事前に渡した上で実行します。\u003Cbr>やってみたかったターミナルの分割して、4並列でCCを動かすというもの達成できたので満足です。\u003Cbr>\u003Cbr>今回はCLAUDE.md＋Hooksによるオーケストレーションです。\u003Cbr>SkillsやSubagentsは使用しておりません。\u003Cbr>\u003Cbr>何かの参考になれば幸いです。\u003Cbr>\u003Cbr>※以下はAIによる実験概要のまとめから結果の出力をまとめたものです。※\u003C/p>\u003Ch2 id=\"h9707d3a59a\">概要\u003C/h2>\u003Cp>prompt only から \u003Ccode>CLAUDE.md\u003C/code>、PostToolUse hook、UI checklist、Stop completion gate、Gate TDD までを段階的に足し、途中経過と完了判定がどう変わるかを見ています。実測は A-E の 5 条件で行いました。\u003C/p>\u003Cp style=\"text-align: start\">結論だけ先に書くと、この規模の題材なら prompt only でも最終到達点には届きます。初回の run-01 の完成画面だけを見ると、何も足していない条件 A が最も見栄えよく見えます。\u003C/p>\u003Cp style=\"text-align: start\">ここだけ見ると、Harness 不要と思えるほどです。\u003C/p>\u003Cp style=\"text-align: start\">ただし、2026年3月24日 23:34 JST に同じ条件 A を run-02 として再実行すると、UI は英語寄りに振れ、seed データと実装の置き場も変わりました。Harness の差は最終結果の最良値ではなく、途中経過と完了判定の固定に出ます。\u003C/p>\u003Cp style=\"text-align: start\">hook を入れた条件では高頻度で block が返り、agent は編集のたびに失敗を突き返されていました。さらに Stop gate を足した条件では、agent が「終わった」と判断したあとも completion が拒否されています。D で拾えなかった日本語統一、seed の自然さ、批判的 UI review、内部構造 review まで completion 条件に含めたのが条件 E です。この記事では、A-E の hook 履歴と Stop gate のログに加えて、条件 A の run-02 も使います。\u003C/p>\u003Cp style=\"text-align: start\">どの要素が何に効き、何には効かなかったのかを整理します。\u003C/p>\u003Ch2 style=\"text-align: start\" id=\"h0ac9503d1d\">何を確かめたいのか\u003C/h2>\u003Cp style=\"text-align: start\">題材に選んだのは、小さな顧客管理画面です。一覧表示、検索、絞り込み、作成、編集、削除、バリデーション、永続化をまとめて見られるので、UI だけでも API だけでもない失敗を観察しやすいからです。フォームの挙動、一覧の更新、テストの網羅、完了宣言の妥当性まで、Harness の差が出やすい題材だと考えました。\u003C/p>\u003Cp style=\"text-align: start\">今回確かめたかったのは、アプリを作れるかどうかではありません。\u003Ccode>CLAUDE.md\u003C/code>、PostToolUse hook、UI checklist、Stop gate を足したときに、途中で起きる破綻の扱いと、終わったと見なす条件がどう変わるかです。つまり、生成結果そのものよりも、作業中の制御と完了判定に何が効くのかを見ています。加えて、D が最低ラインしか保証しないなら、Red / Green / Refactor の gate を足すことでブラッシュアップまで完了条件に持ち込めるかも、条件 E として実装して確かめます。\u003C/p>\u003Ch2 style=\"text-align: start\" id=\"h8bd3d816b6\">実験条件\u003C/h2>\u003Cp style=\"text-align: start\">今回の実測は A-E の 5 条件です。E では、D が保証できなかった UI の良さや日本語対応まで完了条件に含めました。差は、増やした Harness 要素だけに絞っています。\u003C/p>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>条件\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>追加した Harness\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>扱い\u003C/strong>\u003C/p>\u003C/th>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>A\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>prompt only\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>実測\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>B\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>CLAUDE.md\u003C/code>\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>実測\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>C\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>B + PostToolUse hook\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>実測\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>D\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>C + UI checklist + Stop completion gate\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>実測\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>E\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>D + Gate TDD（Red = 自動検証、Green = 批判的 UI review、Refactor = 内部構造 review）\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>実測\u003C/p>\u003C/td>\u003C/tr>\u003C/tbody>\u003C/table>\u003Cp style=\"text-align: start\">A-E の実測条件を比較しやすくするため、次の前提は固定しました。\u003C/p>\u003Cul>\u003Cli>fixed prompt から追加ファイルの読込指示を外す\u003C/li>\u003Cli>C-E の PostToolUse hook は \u003Ccode>Edit\u003C/code> / \u003Ccode>MultiEdit\u003C/code> / \u003Ccode>Write\u003C/code> を対象にし、失敗時は \u003Ccode>exit 2\u003C/code> と \u003Ccode>stderr\u003C/code> を返す\u003C/li>\u003Cli>D と E は \u003Ccode>docs/ui-checklist.md\u003C/code> を task list にし、checklist 未達または \u003Ccode>pnpm e2e\u003C/code> 未通過では Stop できないようにする\u003C/li>\u003Cli>E は D に加えて \u003Ccode>docs/critical-ui-review.md\u003C/code> と \u003Ccode>docs/refactor-checklist.md\u003C/code> を持ち、Stop gate が Red / Green / Refactor の 3 段を順に判定する\u003C/li>\u003Cli>実行元の \u003Ccode>projects/\u003C/code> は immutable template とし、毎回 \u003Ccode>runs/.../workspace\u003C/code> のコピーで回す\u003C/li>\u003Cli>run ごとに \u003Ccode>workspace-start/\u003C/code>、hook history、最終 checklist、diff を残す\u003C/li>\u003C/ul>\u003Ch2 style=\"text-align: start\" id=\"hf484dd3cac\">何を渡したか\u003C/h2>\u003Cp style=\"text-align: start\">実験の追体験ができるよう、agent に渡したものをそのまま載せます。\u003C/p>\u003Ch3 style=\"text-align: start\" id=\"hd1264cd494\">固定プロンプト\u003C/h3>\u003Cp style=\"text-align: start\">A-E の 5 条件すべてに共通の prompt です。Claude Code の custom command として渡しています。\u003C/p>\u003Cpre>\u003Ccode>このディレクトリに、小さな顧客管理画面を実装してください。\n\n追加の仕様確認はしないでください。合理的な仮定で進めてください。\n\n## 必須要件\n\n- 顧客一覧テーブルが表示される\n- `name` / `email` / `company` / `status` / `updatedAt` を表示する\n- `name` または `email` のキーワード検索ができる\n- `status` の絞り込みができる\n- 新規作成と編集ができる\n- 削除ができる\n- 入力バリデーションがある\n- 初期データを seed できる\n- 件数サマリーを表示する\n- 成功 / 失敗通知を出す\n- 空状態 UI を用意する\n- 並び順の切り替えを入れる\n\n## 技術スタック\n\n- Nuxt 4 を SPA モードで使う\n- TypeScript を全面採用する\n- API は `server/` 配下で受ける\n- h3 のリクエストを Web 標準 `Request` に変換してから Hono に渡す\n- Hono 側で API ルーティングを組む\n- 永続化は Drizzle ORM + SQLite で行う\n- `pnpm lint` / `pnpm typecheck` / `pnpm test` / `pnpm e2e` を揃える\n\n## 実装ルール\n\n- 顧客 CRUD の API はすべて Hono を通す\n- Nuxt の `server/api` へ CRUD ロジックを直書きしない\n- Drizzle の schema / migration / seed を用意する\n- データの seed を実行する command を用意する\n- UI 側は管理画面として desktop と mobile の両方で破綻しないようにする\n- 実装後に `lint` / `typecheck` / `test` / `e2e` を自分で確認する\n\n## 完了条件\n\n- 必須要件を満たしている\n- `pnpm lint` / `pnpm typecheck` / `pnpm test` / `pnpm e2e` の結果を確認して報告する\n\u003C/code>\u003C/pre>\u003Cp style=\"text-align: start\">「追加の仕様確認はしないでください」で agent に対話を挟ませず、一発で走りきらせています。技術スタックで h3 → Hono ブリッジを指定しているのは、構成に自由度を残しつつ、API 層の設計判断を prompt 側で固定するためです。\u003C/p>\u003Ch3 style=\"text-align: start\" id=\"h7b36b14c0a\">条件 B：\u003Ccode>CLAUDE.md\u003C/code>\u003C/h3>\u003Cp style=\"text-align: start\">B では、workspace の root に \u003Ccode>CLAUDE.md\u003C/code> を置いています。Claude Code は \u003Ccode>CLAUDE.md\u003C/code> を project memory として自動で読み込むため、prompt に「このファイルを読め」と書く必要がありません。\u003C/p>\u003Cpre>\u003Ccode># Small Management App Experiment\n\nこの project は条件Bです。固定 prompt に加えて、次の制約を守ってください。\n\n## 目的\n\n- 小さな顧客管理画面をこの project root に実装する\n- 守るべき差分は `CLAUDE.md` のみで、余計な native harness は追加しない\n\n## 推奨構成\n\n- `app.vue`, `pages/`, `components/`, `composables/`\n- `server/api/` に h3 入口を置く\n- `server/hono/` に Hono app と route を置く\n- `server/db/` に Drizzle client, schema, seed を置く\n- `test/` と `e2e/` を用意する\n\n## 守ること\n\n- 顧客 CRUD のロジックを Nuxt の `server/api` に直書きしない\n- `server/` で h3 request を Web 標準 `Request` に変換して Hono に渡す\n- 永続化は Drizzle ORM + SQLite を使う\n- `pnpm lint` / `pnpm typecheck` / `pnpm test` / `pnpm e2e` を揃える\n- seed 用の script を用意する\n\n## 完了条件\n\n- 必須要件をすべて満たす\n- `pnpm lint` / `pnpm typecheck` / `pnpm test` / `pnpm e2e` を自分で確認する\n- 既知の未実装や妥協があれば最終報告に明記する\n\u003C/code>\u003C/pre>\u003Cp style=\"text-align: start\">prompt とほぼ同じ内容ですが、ディレクトリ構成を「推奨構成」として提示している点が異なります。prompt にはどこに何を置けとは書いていないので、構成の判断を agent に委ねるか \u003Ccode>CLAUDE.md\u003C/code> に置くかが B の差分です。\u003C/p>\u003Ch3 style=\"text-align: start\" id=\"ha98825ed7f\">条件 C：PostToolUse hook を足す\u003C/h3>\u003Cp style=\"text-align: start\">C の \u003Ccode>CLAUDE.md\u003C/code> は B をベースに、hook の存在と使い方を明示しています。B からの差分だけ抜粋します。\u003C/p>\u003Cpre>\u003Ccode>## 守ること（追加分）\n\n- `.claude/settings.json` や `.claude/hooks/` を無効化しない\n\n## hook の使い方\n\n- file edit 後に `lint` / `typecheck` / `test` の結果が返る\n- 失敗を受け取ったら原因を直して再確認する\n- `package.json` 未作成の間だけ hook は skip される\n\n## 完了条件（変更分）\n\n- hook failure が解消している\n\u003C/code>\u003C/pre>\u003Cp style=\"text-align: start\">hook は \u003Ccode>.claude/settings.json\u003C/code> で登録します。\u003Ccode>Edit\u003C/code>、\u003Ccode>MultiEdit\u003C/code>、\u003Ccode>Write\u003C/code> のいずれかが使われるたびに、\u003Ccode>post-edit-check.sh\u003C/code> が走ります。\u003C/p>\u003Cpre>\u003Ccode>{\n  &quot;hooks&quot;: {\n    &quot;PostToolUse&quot;: [\n      {\n        &quot;matcher&quot;: &quot;Edit|MultiEdit|Write&quot;,\n        &quot;hooks&quot;: [\n          {\n            &quot;type&quot;: &quot;command&quot;,\n            &quot;command&quot;: &quot;\\&quot;$CLAUDE_PROJECT_DIR\\&quot;/.claude/hooks/post-edit-check.sh&quot;\n          }\n        ]\n      }\n    ]\n  }\n}\n\u003C/code>\u003C/pre>\u003Cp style=\"text-align: start\">\u003Ccode>post-edit-check.sh\u003C/code> の判定ロジックの骨格は次のとおりです。\u003C/p>\u003Cpre>\u003Ccode># package.json がなければ skip（プロジェクト初期化前）\nif [ ! -f &quot;$PROJECT_ROOT/package.json&quot; ]; then\n  exit 0\nfi\n\n# lint / typecheck / test を順に実行\nfor CHECK_NAME in lint typecheck test; do\n  if (cd &quot;$PROJECT_ROOT&quot; &amp;&amp; pnpm run &quot;$CHECK_NAME&quot;) &gt;&quot;$LOG_FILE&quot; 2&gt;&amp;1; then\n    # PASS → 記録して次へ\n  else\n    HAS_FAILURE=1\n  fi\ndone\n\n# ひとつでも FAIL があれば exit 2 + stderr で agent に返す\nif [ &quot;$HAS_FAILURE&quot; -eq 1 ]; then\n  printf &quot;%s\\n&quot; &quot;$message&quot; &gt;&amp;2\n  exit 2\nfi\n\u003C/code>\u003C/pre>\u003Cp style=\"text-align: start\">\u003Ccode>exit 2\u003C/code> は Claude Code に「この hook は失敗」と伝える規約です。agent は stderr のメッセージを受け取って修正に入ります。hook が通過した場合は \u003Ccode>exit 0\u003C/code> で、agent にはなにも伝わりません。すべての実行結果は \u003Ccode>.experiment/hook-history/\u003C/code> にタイムスタンプ付きで保存されるため、あとから時系列を追えます。\u003C/p>\u003Ch3 style=\"text-align: start\" id=\"h7e320df974\">条件 D：UI checklist + Stop gate を足す\u003C/h3>\u003Cp style=\"text-align: start\">D の \u003Ccode>CLAUDE.md\u003C/code> は C をベースに、checklist と Stop gate の使い方を追加しています。\u003C/p>\u003Cpre>\u003Ccode>## 守ること（追加分）\n\n- `docs/ui-checklist.md` を source of truth として更新する\n\n## hook の使い方（変更分）\n\n- stop 前には checklist 未達または `pnpm e2e` fail があると block される\n\n## UI 完了判定\n\n- `docs/ui-checklist.md` を task list として使い、確認できた項目だけ `- [x]` に変える\n- Playwright または同等のブラウザ確認を優先する\n- checklist 未達や `pnpm e2e` fail のまま完了宣言しない\n\n## 完了条件（変更分）\n\n- `docs/ui-checklist.md` がすべて `- [x]` になっている\n\u003C/code>\u003C/pre>\u003Cp style=\"text-align: start\">\u003Ccode>.claude/settings.json\u003C/code> には、PostToolUse に加えて \u003Ccode>Stop\u003C/code> hook を登録しています。\u003C/p>\u003Cpre>\u003Ccode>{\n  &quot;hooks&quot;: {\n    &quot;PostToolUse&quot;: [\n      {\n        &quot;matcher&quot;: &quot;Edit|MultiEdit|Write&quot;,\n        &quot;hooks&quot;: [{ &quot;type&quot;: &quot;command&quot;, &quot;command&quot;: &quot;...post-edit-check.sh&quot; }]\n      }\n    ],\n    &quot;Stop&quot;: [\n      {\n        &quot;hooks&quot;: [{ &quot;type&quot;: &quot;command&quot;, &quot;command&quot;: &quot;...stop-completion-gate.sh&quot; }]\n      }\n    ]\n  }\n}\n\u003C/code>\u003C/pre>\u003Cp style=\"text-align: start\">\u003Ccode>Stop\u003C/code> は Claude Code が作業完了と判断したタイミングで発火します。\u003Ccode>stop-completion-gate.sh\u003C/code> の判定ロジックの骨格は次のとおりです。\u003C/p>\u003Cpre>\u003Ccode># checklist に未消化の項目があれば block\nINCOMPLETE_ITEMS=$(grep -n &apos;^- \\[ \\]&apos; &quot;$CHECKLIST_FILE&quot; || true)\nif [ -n &quot;$INCOMPLETE_ITEMS&quot; ]; then\n  printf &quot;[hook] stop blocked.\\n- ui checklist is incomplete.\\n%s&quot; &quot;$HUMAN_ITEMS&quot; &gt;&amp;2\n  exit 2\nfi\n\n# pnpm e2e を実行し、fail なら block\nif ! (cd &quot;$PROJECT_ROOT&quot; &amp;&amp; pnpm run e2e) &gt;&quot;$E2E_LOG_FILE&quot; 2&gt;&amp;1; then\n  TAIL_OUTPUT=$(tail -n 15 &quot;$E2E_LOG_FILE&quot;)\n  printf &quot;[hook] stop blocked.\\n- pnpm e2e: FAIL\\n%s&quot; &quot;$TAIL_OUTPUT&quot; &gt;&amp;2\n  exit 2\nfi\n\u003C/code>\u003C/pre>\u003Cp style=\"text-align: start\">agent が \u003Ccode>- [ ]\u003C/code> のままの項目を見落としていれば block し、checklist を全部埋めても e2e が落ちれば block します。実際の実行では、この gate が 4 回 block を返しています。詳細は結果セクションで示します。\u003C/p>\u003Cp style=\"text-align: start\">最後に、D に渡した \u003Ccode>docs/ui-checklist.md\u003C/code> の初期状態です。\u003C/p>\u003Cpre>\u003Ccode># UI Checklist\n\n完了宣言の前に、次の項目を順に確認してください。\n確認できた項目だけ `- [x]` に変えてください。\n\n- [ ] 一覧画面が表示され、`name` / `email` / `company` / `status` / `updatedAt` が見える\n- [ ] seed 済みデータで初回表示が成立している\n- [ ] `name` 検索が動く\n- [ ] `email` 検索が動く\n- [ ] `status` filter が動く\n- [ ] 新規作成が動く\n- [ ] 編集が動く\n- [ ] 削除が動く\n- [ ] バリデーションエラーが UI 上で見える\n- [ ] 成功通知と失敗通知が見える\n- [ ] summary 表示が更新される\n- [ ] 空状態 UI が壊れていない\n- [ ] 並び順切り替えが動く\n- [ ] `pnpm e2e` が通る\n\u003C/code>\u003C/pre>\u003Ch3 style=\"text-align: start\" id=\"hc0bac7c7c8\">条件 E：Gate に TDD を取り入れる\u003C/h3>\u003Cp style=\"text-align: start\">D は checklist と \u003Ccode>pnpm e2e\u003C/code> で最低ラインを上げましたが、UI の言語選択、seed データの現実感、見た目の密度までは測っていませんでした。そこで E では Stop gate 自体を Red / Green / Refactor の 3 段に分けました。狙いは「最低限動く」ではなく、「批判的な見直しと構造整理まで終えたら止める」に変えることです。\u003C/p>\u003Cp style=\"text-align: start\">E の \u003Ccode>CLAUDE.md\u003C/code> では D に対して次を追加します。\u003C/p>\u003Cpre>\u003Ccode>## 守ること（追加分）\n\n- `docs/ui-checklist.md` を Red の source of truth として更新する\n- `docs/critical-ui-review.md` と `docs/refactor-checklist.md` を source of truth として更新する\n- 主要ラベル、通知、空状態は自然な日本語でそろえる\n- seed データに `test` / `dummy` / `sample` のような仮置き文言を残さない\n\n## Gate TDD の進め方\n\n1. Red: `docs/ui-checklist.md` を埋めながら `pnpm lint` / `pnpm typecheck` / `pnpm test` / `pnpm e2e` を通す\n2. Green: `docs/critical-ui-review.md` で UI を批判的に見直し、未解消論点が 0 になるまで直す\n3. Refactor: `docs/refactor-checklist.md` に沿って内部構造を整え、最後に Red の 4 コマンドを再確認する\n\u003C/code>\u003C/pre>\u003Cp style=\"text-align: start\">workflow と DoD は次のとおりです。\u003C/p>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>フロー\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>役割\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>DoD\u003C/strong>\u003C/p>\u003C/th>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>Red\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>機能要件と自動検証で最低ラインを固定する\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>docs/ui-checklist.md\u003C/code> 完了 + \u003Ccode>pnpm lint\u003C/code> / \u003Ccode>pnpm typecheck\u003C/code> / \u003Ccode>pnpm test\u003C/code> / \u003Ccode>pnpm e2e\u003C/code> がすべて pass\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>Green\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>UI の良さ、日本語対応、seed の自然さを批判的に見直す\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>docs/critical-ui-review.md\u003C/code> が \u003Ccode>status: PASS\u003C/code>、\u003Ccode>remaining_issues: 0\u003C/code>、未消化 checkbox なし\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>Refactor\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>Green で直した実装を整理し、保守性を確保する\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>docs/refactor-checklist.md\u003C/code> 完了 + Refactor 後も Red の 4 コマンドが再度 pass\u003C/p>\u003C/td>\u003C/tr>\u003C/tbody>\u003C/table>\u003Cp style=\"text-align: start\">Stop gate の骨格も \u003Ccode>checklist + e2e\u003C/code> から 3 段判定に変わります。\u003C/p>\u003Cpre>\u003Ccode># Red: lint / typecheck / test / e2e が fail なら block\nfor CHECK_NAME in lint typecheck test e2e; do\n  ...\ndone\n\n# Green: critical UI review が PASS でない、remaining_issues が 0 でない、\n# 未消化 checkbox が残るなら block\n\n# Refactor: refactor checklist に未消化項目があれば block\n\u003C/code>\u003C/pre>\u003Cp style=\"text-align: start\">この条件では、Red の自動検証に加えて、Green の review 文書と Refactor の checklist がどちらも DoD を満たすまで completion 条件に含まれます。詳細は結果セクションで見ます。\u003C/p>\u003Cp style=\"text-align: start\">以上が A-E の 5 条件に渡した全量です。ここからは、この差が実行中にどう出たかを見ていきます。\u003C/p>\u003Ch2 style=\"text-align: start\" id=\"hfc59d72230\">どう評価するか\u003C/h2>\u003Cp style=\"text-align: start\">固定 prompt で求めた必須要件は、次の 12 項目です。\u003C/p>\u003Cul>\u003Cli>顧客一覧テーブル\u003C/li>\u003Cli>\u003Ccode>name\u003C/code> / \u003Ccode>email\u003C/code> / \u003Ccode>company\u003C/code> / \u003Ccode>status\u003C/code> / \u003Ccode>updatedAt\u003C/code> 表示\u003C/li>\u003Cli>\u003Ccode>name\u003C/code> または \u003Ccode>email\u003C/code> のキーワード検索\u003C/li>\u003Cli>\u003Ccode>status\u003C/code> 絞り込み\u003C/li>\u003Cli>新規作成と編集\u003C/li>\u003Cli>削除\u003C/li>\u003Cli>入力バリデーション\u003C/li>\u003Cli>seed\u003C/li>\u003Cli>件数サマリー\u003C/li>\u003Cli>成功 / 失敗通知\u003C/li>\u003Cli>空状態 UI\u003C/li>\u003Cli>並び順切り替え\u003C/li>\u003C/ul>\u003Cp style=\"text-align: start\">条件 D では、この 12 項目を検証しやすい粒度に組み替えた 14 項目の checklist（\u003Ccode>docs/ui-checklist.md\u003C/code>）を使っています。「\u003Ccode>name\u003C/code> または \u003Ccode>email\u003C/code> のキーワード検索」は \u003Ccode>name\u003C/code> 検索と \u003Ccode>email\u003C/code> 検索に分割し、「新規作成と編集」もそれぞれ独立項目にしました。一方で「顧客一覧テーブル」と「各カラム表示」は 1 項目に統合し、\u003Ccode>pnpm e2e\u003C/code> 通過を追加して合計 14 項目です。条件 E では、ここに「主要ラベル、通知、空状態が自然な日本語でそろっている」「seed データに \u003Ccode>test\u003C/code> / \u003Ccode>dummy\u003C/code> / \u003Ccode>sample\u003C/code> が残っていない」を足しました。さらに Green 用の \u003Ccode>docs/critical-ui-review.md\u003C/code> と Refactor 用の \u003Ccode>docs/refactor-checklist.md\u003C/code> を使います。\u003C/p>\u003Cp style=\"text-align: start\">評価軸は 2 つです。ひとつは、最終成果物が \u003Ccode>pnpm lint\u003C/code>、\u003Ccode>pnpm typecheck\u003C/code>、\u003Ccode>pnpm test\u003C/code>、\u003Ccode>pnpm e2e\u003C/code> を通るかどうかです。もうひとつは、実行中に Harness が何回 failure や block を返したか、そして完了宣言がどこまで外側の条件に結び付いていたかです。\u003C/p>\u003Cp style=\"text-align: start\">この 2 つを分けて見る理由は、最終的に動くものができても、途中の破綻を agent 任せで見逃している可能性があるからです。個人開発で Harness を入れる価値は、最後の一発合格よりも、崩れた時点で差し戻せることと、終わり方を固定できることにあります。\u003C/p>\u003Ch2 style=\"text-align: start\" id=\"hb6509f48bf\">結果\u003C/h2>\u003Ch3 style=\"text-align: start\" id=\"h35fbba4cb4\">最終チェック\u003C/h3>\u003Cp style=\"text-align: start\">まず、記事で採用した A-E 各条件の実測結果を比べると、最終成果物はすべて \u003Ccode>pnpm lint\u003C/code>、\u003Ccode>pnpm typecheck\u003C/code>、\u003Ccode>pnpm test\u003C/code>、\u003Ccode>pnpm e2e\u003C/code> を通過しました。この点だけを見ると 5 条件に差はありません。\u003C/p>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>条件\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>追加した Harness\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>lint\u003C/code>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>typecheck\u003C/code>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>test\u003C/code>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>e2e\u003C/code>\u003C/p>\u003C/th>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>A\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>prompt only\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>B\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>CLAUDE.md\u003C/code>\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>C\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>B + PostToolUse hook\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>D\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>C + UI checklist + Stop gate\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>E\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>D + Gate TDD\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass\u003C/p>\u003C/td>\u003C/tr>\u003C/tbody>\u003C/table>\u003Ch3 style=\"text-align: start\" id=\"h92e5329165\">完成画面\u003C/h3>\u003Cp style=\"text-align: start\">A-E の 5 条件が最終的に出力した画面です。同じ prompt から出発していますが、見た目はそれぞれ異なります。\u003C/p>\u003Cp style=\"text-align: start\">\u003Cstrong>条件 A（prompt only）\u003C/strong>\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/730c64335a1744ccbff060ce425d00ac/5eb38bb5b061434e995c4b06019013ee/condition-a.png\" alt=\"\" width=\"1280\" height=\"800\">\u003C/figure>\u003Cp style=\"text-align: start\">\u003Cstrong>条件 B（\u003C/strong>\u003Ccode>CLAUDE.md\u003C/code>）\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/730c64335a1744ccbff060ce425d00ac/faaf1b5c93654c07b847b6571eb890f4/condition-b.png\" alt=\"\" width=\"1280\" height=\"800\">\u003C/figure>\u003Cp style=\"text-align: start\">\u003Cstrong>条件 C（B + PostToolUse hook）\u003C/strong>\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/730c64335a1744ccbff060ce425d00ac/1de5bd2218bd461cae1651098bb0c4fe/condition-c.png\" alt=\"\" width=\"1280\" height=\"800\">\u003C/figure>\u003Cp style=\"text-align: start\">\u003Cstrong>条件 D（C + UI checklist + Stop gate）\u003C/strong>\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/730c64335a1744ccbff060ce425d00ac/0a8911b956ea490298919872763b1b6c/condition-d.png\" alt=\"\" width=\"1280\" height=\"800\">\u003C/figure>\u003Cp style=\"text-align: start\">\u003Cstrong>条件 E（D + Gate TDD）\u003C/strong>\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/730c64335a1744ccbff060ce425d00ac/8ad3bf39b183412f98f558b52768e10b/condition-e.png\" alt=\"\" width=\"1280\" height=\"800\">\u003C/figure>\u003Cp style=\"text-align: start\">条件 A だけは、2026年3月24日 23:34 JST に同じ prompt で run-02 を追加実行しました。次の画面が実測の run-02 です。\u003C/p>\u003Cp style=\"text-align: start\">\u003Cstrong>条件 A の再実行（run-02）\u003C/strong>\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/730c64335a1744ccbff060ce425d00ac/72a5f43512a54af1b940b5d01a700378/condition-a-run-02.png\" alt=\"\" width=\"1280\" height=\"800\">\u003C/figure>\u003Cp style=\"text-align: start\">スクリーンショットと artifact を合わせて見ると、初回の見た目だけなら何も足していない A が強く見えます。最終チェックはすべて all pass なので、数字だけ見れば「A で十分」に見えます。ただし E まで進めると、D が取りこぼしていた日本語統一、自然な seed、UI の批判的見直し、内部構造整理まで completion 条件に含まれます。以下で、各条件が到達した点と到達しなかった点を整理します。\u003C/p>\u003Cp style=\"text-align: start\">\u003Cstrong>条件 A\u003C/strong> の run-01 は日本語 UI（「顧客管理」）で、現実的な日本語の seed データ（田中太郎、佐藤花子など 10 件）を持ち、ステータスバッジの配色やレイアウトもこなれていました。5 条件の中でも完成度が高く見えたのは事実です。ただし、同じ条件を 2026年3月24日 23:34 JST に run-02 として再実行すると、見出しと操作ラベルは英語 UI（\u003Ccode>Customer Management\u003C/code>、\u003Ccode>New Customer\u003C/code>、\u003Ccode>Delete\u003C/code>）に変わりました。seed も「日本語 10 件」ではなく、日本語 6 件と英語 4 件の混在に変わっています。実装の置き場も run-01 の \u003Ccode>db/schema.ts\u003C/code> と \u003Ccode>scripts/seed.ts\u003C/code> から、run-02 では \u003Ccode>server/db/schema.ts\u003C/code> と \u003Ccode>server/db/seed.ts\u003C/code> に移りました。\u003Ccode>package.json\u003C/code> の \u003Ccode>nuxt\u003C/code> 依存も \u003Ccode>^3.16.1\u003C/code> から \u003Ccode>^4.4.2\u003C/code> に変わっています。さらに e2e 観点は run-01 が 9 件、run-02 が 10 件でした。検索確認に使う値も \u003Ccode>田中\u003C/code> から \u003Ccode>tanaka\u003C/code> に変わっています。どちらも最終的には \u003Ccode>lint\u003C/code> / \u003Ccode>typecheck\u003C/code> / \u003Ccode>test\u003C/code> / \u003Ccode>e2e\u003C/code> を通しています。それでも、同じ prompt only で UI 言語、seed の質感、ディレクトリ構成、検証観点は揃っていません。A の弱点は、通ること自体より、返す解の内容が安定しないことです。\u003C/p>\u003Cp style=\"text-align: start\">\u003Cstrong>条件 B\u003C/strong> は同じく日本語 UI ですが、seed データの中身が壊れています。\u003Ccode>test-create-1736...\u003C/code>、\u003Ccode>Updated Name\u003C/code>、\u003Ccode>dup-user-1736...\u003C/code> のような自動で生成した文字列が 25 件並び、管理画面のデモとしては成立していません。\u003Ccode>CLAUDE.md\u003C/code> がディレクトリ構成を安定させた一方、データ品質までは制御できなかった例です。A と同じく途中の記録がないため、seed がこの品質に落ちた経緯も追えません。\u003C/p>\u003Cp style=\"text-align: start\">\u003Cstrong>条件 C\u003C/strong> は英語 UI（Customer Management）に変わり、seed データは \u003Ccode>Seed User\u003C/code> の 1 件だけです。コンポーネント分割は 5 条件中でも細かい 7 ファイルですが、最終成果物に lint 警告が 60 件残り、画面はほぼ空の状態です。hook が途中の build 品質を押さえた反面、agent が「これで十分」と判断した時点で止まっています。hook は「壊れたら差し戻す」仕組みであって、「もっと良くしろ」とは言わないためです。\u003C/p>\u003Cp style=\"text-align: start\">\u003Cstrong>条件 D\u003C/strong> も英語 UI（Customer Management）で、seed データは 18 件あるものの Notification Test、Summary Test のようなテスト用途の名前が目立ちます。lint 警告は 0 件、checklist 14 項目はすべて消化済みです。gate が品質の下限を引き上げた一方、UI の言語選択や seed データの現実感といった gate が測れない観点は制御できていません。\u003C/p>\u003Cp style=\"text-align: start\">\u003Cstrong>条件 E\u003C/strong> は日本語 UI（「顧客管理」）です。seed データは田中 太郎、佐藤 花子、鈴木 一郎など 10 件で、\u003Ccode>test\u003C/code> / \u003Ccode>dummy\u003C/code> / \u003Ccode>sample\u003C/code> のような仮置き語は含まれていません。Red の \u003Ccode>docs/ui-checklist.md\u003C/code> は 17 項目が全消化です。Green の \u003Ccode>docs/critical-ui-review.md\u003C/code> は \u003Ccode>status: PASS\u003C/code> / \u003Ccode>remaining_issues: 0\u003C/code> で、Refactor の checklist 6 項目も全消化です。実行中の介入も記録されており、PostToolUse は 19 回発火して 10 回 block、Stop gate は 9 回発火して 8 回 block でした。\u003C/p>\u003Cp style=\"text-align: start\">Green の review 文書では、Playwright スクリーンショットで全画面確認済みとされ、\u003Ccode>Invalid Date\u003C/code> は出ていません。日付はすべて \u003Ccode>YYYY/MM/DD\u003C/code> 形式で、フォームとモーダルの visual language もそろっています。並び替えはテーブルヘッダー操作と ▲/▼ 表示で確認されており、\u003Ccode>Fixes Applied\u003C/code> は「初回実装時に全項目を満たす設計としたため、追加修正なし。」でした。\u003C/p>\u003Cp style=\"text-align: start\">最終的には API / validation / persistence の責務分離、components / composables / server の境界確認、Red の 4 コマンド再実行まで終えて pass しました。\u003Ccode>pnpm test\u003C/code> は 7 件、\u003Ccode>pnpm e2e\u003C/code> は 10 件が通過しています。E は D が拾えなかった「日本語対応と seed の自然さ」「批判的な見直し」「構造整理」を completion 条件に含めた条件です。\u003C/p>\u003Cp style=\"text-align: start\">この結果から見えるのは、Harness を足すほど出力の最良値が上がるのではなく、completion 条件が外側に広がり、最低ラインが上がるということです。A が最もそれらしく見えたのは初回 run-01 では事実ですが、2026年3月24日の run-02 では UI が英語寄りになり、seed とテスト観点も別物になりました。prompt only は「通る解」には再到達しても、「同じ解」には再到達しません。D の lint 0 件、checklist 全消化、e2e 全通過は gate が通すまで止まらなかった結果であり、E ではそこに Green review と Refactor まで加わりました。ただし、レイアウトの密度や見栄えそのものは依然として review 文書の設計と agent の批判性に依存します。Harness は万能ではなく、「何を終わりに含めるか」を増やす仕組みです。\u003C/p>\u003Cp style=\"text-align: start\">Harness は「たまたまうまくいく」を「確実に最低ラインを守る」に置き換える仕組みであり、最良の出力を保証するものではありません。\u003C/p>\u003Cp style=\"text-align: start\">差が出たのは見た目ではなく、実行中に Harness が返した failure と block の回数、そしてその中身です。\u003C/p>\u003Ch3 style=\"text-align: start\" id=\"hf7955de46a\">実行中の介入\u003C/h3>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>条件\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>PostToolUse 発火\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>PostToolUse block\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>block 率\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>Stop 発火\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>Stop block\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>最終 lint 警告数\u003C/strong>\u003C/p>\u003C/th>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>A\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>—\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>—\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>—\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>—\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>—\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>未計測\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>B\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>—\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>—\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>—\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>—\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>—\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>未計測\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>C\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>37\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>31\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>84 %\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>—\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>—\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>60\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>D\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>27\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>13\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>48 %\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>5\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>4\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>0\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>E\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>19\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>10\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>53 %\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>9\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>8\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>0\u003C/p>\u003C/td>\u003C/tr>\u003C/tbody>\u003C/table>\u003Cp style=\"text-align: start\">A と B は hook を持たないため、実行中に外側から agent を差し戻す仕組みがありません。agent が自分の判断で先に進み、最後に verify を通すだけです。\u003C/p>\u003Cp style=\"text-align: start\">E も hook 履歴が残っており、PostToolUse は 19 回中 10 回が block、Stop gate は 9 回中 8 回が block でした。以下では、Red / Green / Refactor の順に何が completion 条件として効いたかを見ます。\u003C/p>\u003Ch3 style=\"text-align: start\" id=\"h290dfad1e1\">条件 C：PostToolUse hook の効き方\u003C/h3>\u003Cp style=\"text-align: start\">C では、ファイル編集のたびに \u003Ccode>post-edit-check.sh\u003C/code> が lint / typecheck / test を走らせ、失敗があれば \u003Ccode>exit 2\u003C/code> と \u003Ccode>stderr\u003C/code> を返します。37 回の発火のうち 31 回が block でした。\u003C/p>\u003Cp style=\"text-align: start\">hook 履歴を時系列で見ると、大きな流れとしては lint → typecheck → test の順に通過が広がっていきます。ただし単調に改善するわけではなく、途中で regression が入ります。\u003C/p>\u003Col>\u003Cli>初期（〜034144Z）：lint / typecheck / test の 3 項目すべて FAIL\u003C/li>\u003Cli>中盤（034209Z〜）：lint が先に PASS し、034238Z で typecheck も PASS。しかし 034412Z で typecheck が再び FAIL に戻り、034429Z では 3 項目すべてが FAIL に逆戻りした\u003C/li>\u003Cli>復帰（034445Z〜）：lint と typecheck は再び PASS するが、034528Z と 035521Z でも typecheck の regression が起きている\u003C/li>\u003Cli>収束（035708Z〜）：3 項目すべて PASS が安定\u003C/li>\u003C/ol>\u003Cp style=\"text-align: start\">この regression パターンが重要です。agent がある箇所を直すと別の箇所が壊れる、という連鎖が hook を通じて可視化されています。hook がなければ、これらの failure は agent 内部の判断に委ねられ、修正タイミングと経緯が artifact として残りません。C の hook は「途中の破綻を見逃さない」だけでなく、「どこで何が壊れたかを記録する」という点で機能しています。\u003C/p>\u003Cp style=\"text-align: start\">一方で、C の最終成果物には lint 警告が 60 件残っていました（error 0、warning 60）。hook は edit ごとに block を返しますが、「全体として完了か」の判定は agent に委ねられているため、agent が「warning は許容範囲」と判断すれば、そのまま終了します。\u003C/p>\u003Ch3 style=\"text-align: start\" id=\"h7179f3d471\">条件 D：Stop gate が加わるとどう変わるか\u003C/h3>\u003Cp style=\"text-align: start\">D では PostToolUse hook に加えて、\u003Ccode>stop-completion-gate.sh\u003C/code> が Stop event を監視しています。agent が「完了した」と判断しても、次の 2 条件を満たさなければ completion が拒否されます。\u003C/p>\u003Cul>\u003Cli>\u003Ccode>docs/ui-checklist.md\u003C/code> の全項目が \u003Ccode>- [x]\u003C/code> であること\u003C/li>\u003Cli>\u003Ccode>pnpm e2e\u003C/code> が通ること\u003C/li>\u003C/ul>\u003Cp style=\"text-align: start\">Stop gate は 5 回発火し、4 回を block しました。内訳は次のとおりです。\u003C/p>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>回\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>block 理由\u003C/strong>\u003C/p>\u003C/th>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>1\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>checklist 未消化\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>2\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>checklist 未消化\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>3\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>checklist 未消化\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>4\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>pnpm e2e\u003C/code> FAIL —「成功通知と失敗通知が見える」「summary 表示が更新される」の 2 テストが失敗\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>5\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass — checklist 全消化 + e2e 全通過\u003C/p>\u003C/td>\u003C/tr>\u003C/tbody>\u003C/table>\u003Cp style=\"text-align: start\">4 回目が重要です。この時点で checklist は全消化済みでしたが、e2e が 2 件落ちていました。gate がなければ、checklist を埋め終えた時点で作業終了になっていた可能性があります。gate が拒否したことで、agent は通知表示と件数サマリーの更新ロジックを修正し、5 回目で通過しました。\u003C/p>\u003Cp style=\"text-align: start\">D の最終成果物は lint 警告 0 件です。C が 60 件残していたのと比べると、completion gate が「まだ終わっていない」と返し続けたことで、agent が品質の底上げまで到達したと読めます。\u003C/p>\u003Ch3 style=\"text-align: start\" id=\"ha72395c39b\">条件 E：Green と Refactor を完了条件に含める\u003C/h3>\u003Cp style=\"text-align: start\">E では PostToolUse hook と Stop gate の両方が履歴に残っています。PostToolUse は 19 回発火し、10 回が block でした。序盤は lint / typecheck / test の 3 項目すべてが fail で、003409Z で初めて 3 項目すべて pass します。ただしその後も 004222Z で lint が fail に戻り、004558Z では typecheck と test が再び fail になりました。E でも regression は消えておらず、hook がそれを都度差し戻しています。\u003C/p>\u003Cp style=\"text-align: start\">Stop gate は 9 回発火し、8 回を block しました。内訳は次のとおりです。\u003C/p>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>回\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>block 理由\u003C/strong>\u003C/p>\u003C/th>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>1\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>Red: \u003Ccode>docs/ui-checklist.md\u003C/code> が未完了\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>2\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>Green: \u003Ccode>docs/critical-ui-review.md\u003C/code> が \u003Ccode>PASS\u003C/code> ではない\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>3\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>Green: critical UI review の checklist が未完了\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>4-8\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>Refactor: \u003Ccode>docs/refactor-checklist.md\u003C/code> が未完了\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>9\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>pass — Red / Green / Refactor がすべて \u003Ccode>PASS\u003C/code>\u003C/p>\u003C/td>\u003C/tr>\u003C/tbody>\u003C/table>\u003Cp style=\"text-align: start\">Red の \u003Ccode>docs/ui-checklist.md\u003C/code> は 17 項目がすべて \u003Ccode>- [x]\u003C/code> で、\u003Ccode>summary\u003C/code>、空状態、日本語統一、\u003Ccode>Invalid Date\u003C/code> 回避、ヘッダーでの並び替えまで確認対象に入っています。\u003C/p>\u003Cp style=\"text-align: start\">Green の \u003Ccode>docs/critical-ui-review.md\u003C/code> は \u003Ccode>status: PASS\u003C/code> / \u003Ccode>remaining_issues: 0\u003C/code> です。Findings には「Playwright スクリーンショットで全画面確認済み」「\u003Ccode>Invalid Date\u003C/code> の表示なし」「全日付が \u003Ccode>YYYY/MM/DD\u003C/code> 形式」が並んでいます。加えて、フォームとモーダルの統一、テーブルヘッダー click と ▲/▼ による並び替えも確認されています。\u003Ccode>Fixes Applied\u003C/code> は「初回実装時に全項目を満たす設計としたため、追加修正なし。」でした。\u003C/p>\u003Cp style=\"text-align: start\">Refactor の \u003Ccode>docs/refactor-checklist.md\u003C/code> は 6 項目がすべて \u003Ccode>- [x]\u003C/code> です。API route / validation / persistence の責務分離と、components / composables / server の境界整理まで含まれています。seed / test / production code の分離と、Red の 4 コマンド再確認も確認できます。最終チェックでも \u003Ccode>lint\u003C/code> / \u003Ccode>typecheck\u003C/code> / \u003Ccode>test\u003C/code> / \u003Ccode>e2e\u003C/code> はすべて pass しました。unit test 7 件、e2e 10 件も通っています。\u003C/p>\u003Cp style=\"text-align: start\">E が示しているのは、「動く」「テストが通る」だけでなく、「批判的に見直した」「構造整理した」までを completion 条件に持ち込めることです。Gate TDD は stop 判定を外側へ広げ、UI の言語選択、seed の自然さ、見直し済みかどうかまで終わりに含められます。\u003C/p>\u003Ch2 style=\"text-align: start\" id=\"h4156b92006\">何がどのように効いたか\u003C/h2>\u003Cp style=\"text-align: start\">\u003Ccode>CLAUDE.md\u003C/code> は、この規模では完走可否の決定打ではありませんでした。A と B は最後まで到達しています。ただし、project memory として構成と前提を prompt の外に逃がせる点は機能しています。hook や gate の前提を書けるため、C 以降の条件すべての土台になっていました。\u003C/p>\u003Cp style=\"text-align: start\">PostToolUse hook は、途中の品質を外側から押さえる仕組みとして効きました。C では 37 回中 31 回 block が返り、agent は編集のたびに lint / typecheck / test の結果を突き返されています。hook 履歴の時系列を見ると、lint → typecheck → test の順に通過が広がりつつも途中で regression が入る過程まで追えます。hook がなければ、これらの failure は agent 内部の判断に委ねられ、修正タイミングと経緯が artifact として残りません。\u003C/p>\u003Cp style=\"text-align: start\">UI checklist と Stop gate は、完了判定そのものを agent の外に出しました。D では agent が 5 回「終わった」と判断していますが、そのうち 4 回は gate に止められています。3 回は checklist の消化不足、1 回は e2e の failure です。4 回目の block では「通知表示」と「件数サマリー更新」のテストが落ちており、gate がなければ regression を残したまま終了していた可能性があります。5 回目で通過したときには、checklist 14 項目すべてが消化済みかつ \u003Ccode>pnpm e2e\u003C/code> が全通過という客観条件を満たしていました。\u003C/p>\u003Cp style=\"text-align: start\">E の Gate TDD は、この stop 判定をさらに一段外へ広げました。E では PostToolUse が 19 回中 10 回 block、Stop gate が 9 回中 8 回 block しています。1 回目の stop block は Red の checklist 未達、2-3 回目は Green review 未達、4-8 回目は Refactor checklist 未達です。9 回目で初めて Red / Green / Refactor がそろって通過しました。最終成果物には、日本語統一、自然な seed、\u003Ccode>YYYY/MM/DD\u003C/code> の日付表示、テーブルヘッダーでの並び替え、責務分離と境界整理が artifact として残っています。これにより「動く」「テストが通る」に加えて、「批判的に見直した」「構造整理した」までを completion 条件にできます。\u003C/p>\u003Cp style=\"text-align: start\">4 つの要素をまとめると、効く対象が異なります。\u003C/p>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>要素\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>効く対象\u003C/strong>\u003C/p>\u003C/th>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>CLAUDE.md\u003C/code>\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>前提の固定（何を作り、何を守るか）\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>PostToolUse hook\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>途中の品質（壊れたまま先に進まない）\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>UI checklist + Stop gate（Red）\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>機能要件と自動検証の完了判定\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>Critical UI review + Refactor checklist（Green / Refactor）\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>批判的見直しと構造整理の完了判定\u003C/p>\u003C/td>\u003C/tr>\u003C/tbody>\u003C/table>\u003Cp style=\"text-align: start\">この 4 つは独立しています。hook だけ入れても完了判定は agent 任せのままだし、Red gate だけでは「最低限動く」で止まります。C と D の差が示しているのは、途中の介入と完了の固定は別レイヤーだということです。さらに D と E の差は、completion 条件を tests/checklist から review/refactor へ拡張できることを示しています。\u003C/p>\u003Cp style=\"text-align: start\">同時に、D までの Harness が効かない領域もはっきりしました。UI の言語選択、seed データの現実感、レイアウトの密度は、D までの要素では捕捉していません。A が日本語 UI に現実的な日本語データを載せたのは、agent がたまたまそう判断しただけです。D が英語 UI にテスト用途のデータを載せたのも同様です。\u003C/p>\u003Cp style=\"text-align: start\">E では、この一部を gate の射程に入れられました。日本語統一と seed の自然さは ui-checklist と critical review に入り、最終成果物にも日本語 UI と自然な seed データが残っています。\u003Ccode>updatedAt\u003C/code> の表示、ヘッダーでの並び替え、フォームとモーダルの visual language は review 文書で確認され、日付形式も \u003Ccode>YYYY/MM/DD\u003C/code> にそろっていました。ただし、レイアウトの密度や「見栄えの良さ」そのものは今も review 文書の設計と agent の批判性に依存します。すべてを自動測定できるわけではありません。\u003C/p>\u003Cp style=\"text-align: start\">Harness の設計では、何を gate に入れるかだけでなく、何が gate の射程外に残るかまで意識すべきです。\u003C/p>\u003Ch2 style=\"text-align: start\" id=\"h0f677df05b\">個人開発でどう使うか\u003C/h2>\u003Cp style=\"text-align: start\">この 1 run から見える傾向として、個人開発での導入順は次の形が自然です。\u003C/p>\u003Col>\u003Cli>\u003Ccode>CLAUDE.md\u003C/code> で構成と完了条件の最低ラインを書く。prompt の中に前提を全部詰めると、条件が増えたときに管理できない。\u003C/li>\u003Cli>PostToolUse hook で static check を返す。lint / typecheck / test を edit ごとに走らせるだけで、途中の破綻を agent へ返せるようになる。C の 84 % block 率が示すとおり、hook は想像以上に頻繁に発火する。\u003C/li>\u003Cli>UI 比重が高い題材なら、checklist + Stop gate まで足す。D の Stop gate は 4 回 completion を拒否しており、そのうち 1 回は checklist 全消化後の e2e failure を理由にしている。gate がなければ見逃されていた regression である。\u003C/li>\u003Cli>D でも UI の良さや日本語対応が取りこぼれるなら、E のように Gate TDD へ進める。E では Stop gate が 9 回中 8 回 completion を拒否し、内訳は Red 1 回、Green 2 回、Refactor 5 回であった。自動検証だけでは拾えない見直しと整理を終わりに含められる。\u003C/li>\u003C/ol>\u003Cp style=\"text-align: start\">今回の横並び比較は B/C/D/E が各 1 本で、A だけ 2026年3月24日に run-02 を 1 本追加しています。それでも run 数は少ないため、具体的な数値（block 率や発火回数）の分散を語るには足りません。ただし、A では run-01 と run-02 のあいだで UI 言語、seed、構成、検証観点が実際にずれました。一方で、hook と gate を入れた条件だけが、失敗の回数・内訳・block 理由を artifact として残せていたという構造的な差は変わりません。次にやるなら、E を含めて同じ条件の run 数を増やし、所要時間と手戻り回数まで含めて比較します。hook の追加による token 消費と所要時間の増加も、今回は未計測です。次の課題として残ります。\u003C/p>",{"id":11,"createdAt":12,"updatedAt":13,"publishedAt":12,"revisedAt":12,"name":14},[186],{"id":35,"createdAt":36,"updatedAt":36,"publishedAt":36,"revisedAt":36,"name":37},[188],{"id":189,"createdAt":190,"updatedAt":191,"publishedAt":191,"revisedAt":191,"title":192,"authorName":193,"content_2":194,"category":195,"tag":196,"related":198,"meta_description":199,"is_migrate":200},"kh_s9ohsgv","2025-11-24T14:09:41.583Z","2025-12-09T00:07:53.513Z","Nuxt4でスキーマ駆動開発を実践するフロントエンド実装Tips",[182],"\u003Ch2 id=\"h8d027c8ed3\">はじめに\u003C/h2>\u003Cp style=\"text-align: start\">こんにちは、Akiyoshiです。\u003Cbr>\u003Cbr>最近、AIで実装を進めるにも、チームで実装を進めるにも、いかに認知負荷を下げて実装に集中するかということを考えています。\u003Cbr>フロントエンドにおいては、重複するような型定義を避けることで負荷を軽減することが可能です。\u003Cbr>\u003Cbr>本記事では、Nuxt4プロジェクトでスキーマ駆動開発を実装する際のフロントエンド側のTipsをまとめます。 \u003Cbr>ある程度の規模のプロジェクトを想定し、ドメイン分割アーキテクチャと型定義の最適化を重視した実装パターンを紹介します。\u003C/p>\u003Ch2 id=\"hff042186b4\">この記事で扱うこと\u003C/h2>\u003Cul>\u003Cli>OpenAPI仕様からTypeScript型定義を自動生成する方法\u003C/li>\u003Cli>ドメイン別型定義による軽量・高速な開発環境の構築\u003C/li>\u003Cli>型安全なAPIクライアントの実装パターン\u003C/li>\u003Cli>Prismによるモックサーバー構築とフロントエンド並行開発\u003C/li>\u003Cli>Nuxt4のcomposablesと型定義の連携\u003C/li>\u003C/ul>\u003Ch2 id=\"h03726fb0df\">技術スタック\u003C/h2>\u003Cul>\u003Cli>\u003Cstrong>Nuxt4\u003C/strong> (v4.2.1) - フルスタックフレームワーク\u003C/li>\u003Cli>\u003Cstrong>openapi-typescript\u003C/strong> - OpenAPI仕様からTypeScript型を自動生成\u003C/li>\u003Cli>\u003Cstrong>openapi-fetch\u003C/strong> - 型安全なAPIクライアント\u003C/li>\u003Cli>\u003Cstrong>Prism\u003C/strong> - OpenAPI仕様ベースのモックサーバー（オプション）\u003C/li>\u003C/ul>\u003Ch2 id=\"h262fcef4ea\">プロジェクト構成\u003C/h2>\u003Cpre>\u003Ccode>app/\n  ├── composables/\n  │   ├── auth/                  # 認証ドメイン\n  │   │   └── useAuth.ts        → domains/auth.d.ts\n  │   ├── todos/                 # Todoドメイン\n  │   │   └── useTodos.ts       → domains/todos.d.ts\n  │   └── useApiClient.ts        # 共通APIクライアント\n  ├── types/generated/\n  │   └── domains/               # ドメイン別型定義（軽量）\n  │       ├── auth.d.ts         # 認証ドメイン型（約100行）\n  │       ├── todos.d.ts        # Todoドメイン型（約100行）\n  │       └── index.ts          # バレルエクスポート\n\nopenapi/\n  ├── openapi.yaml               # メインAPI仕様（統合版）\n  ├── domains/                   # ドメイン別API仕様\n  │   ├── auth.yaml             # 認証ドメイン\n  │   └── todos.yaml            # Todoドメイン\n  ├── paths/                    # エンドポイント定義\n  └── shared/components/        # 共通スキーマ\n\nscripts/\n  └── generate-barrel-exports.js  # バレルエクスポート自動生成\u003C/code>\u003C/pre>\u003Ch2 id=\"ha988167b89\">1. スキーマ駆動開発のワークフロー\u003C/h2>\u003Ch3 id=\"hf904175dde\">基本的な開発フロー\u003C/h3>\u003Cp style=\"text-align: start\">スキーマ駆動開発では、API仕様を唯一の信頼できる情報源（Single Source of Truth）として扱います。バックエンドの実装を待たずに開発を進められるのが大きな利点です。\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/730c64335a1744ccbff060ce425d00ac/3fb03353ccbd41b7860417fc90b398cb/Untitled%20diagram-2025-11-24-143103.png\" alt=\"\" width=\"1460\" height=\"502\">\u003C/figure>\u003Ch4 id=\"hef33dd03eb\">Step 1: API仕様を定義\u003C/h4>\u003Cpre>\u003Ccode class=\"language-bash\"># ドメイン別にAPI仕様を作成・更新\nvim openapi/domains/auth.yaml\nvim openapi/domains/todos.yaml\u003C/code>\u003C/pre>\u003Ch4 id=\"hac887d6f70\">Step 2: TypeScript型定義を生成\u003C/h4>\u003Cpre>\u003Ccode class=\"language-bash\">pnpm run generate:api\u003C/code>\u003C/pre>\u003Ch4 id=\"h07490d12fb\"> Step 3: 生成された型を使ってコードを実装\u003C/h4>\u003Cpre>\u003Ccode class=\"language-typescript\">// composables/auth/useAuth.ts\nimport type { operations, components } from &apos;~/types/generated/domains/auth&apos;\n// 型安全に実装\u003C/code>\u003C/pre>\u003Ch3 id=\"h098d7a2b8e\">型定義生成コマンド\u003C/h3>\u003Cpre>\u003Ccode class=\"language-bash\"># すべてのドメイン別型定義を生成\npnpm run generate:api\n\n# 個別生成\npnpm run generate:types:auth    # 認証ドメインのみ\npnpm run generate:types:todos   # Todoドメインのみ\u003C/code>\u003C/pre>\u003Cp style=\"text-align: start\">\u003Cbr>\u003Cstrong>package.jsonの設定例:\u003C/strong>\u003C/p>\u003Cpre>\u003Ccode class=\"language-json\">{\n  &quot;scripts&quot;: {\n    &quot;generate:api&quot;: &quot;run-p generate:types:* &amp;&amp; node scripts/generate-barrel-exports.js&quot;,\n    &quot;generate:types:auth&quot;: &quot;openapi-typescript ./openapi/domains/auth.yaml -o ./app/types/generated/domains/auth.d.ts&quot;,\n    &quot;generate:types:todos&quot;: &quot;openapi-typescript ./openapi/domains/todos.yaml -o ./app/types/generated/domains/todos.d.ts&quot;\n  }\n}\u003C/code>\u003C/pre>\u003Ch2 id=\"hd25057ec49\">2. 型定義の使い方\u003C/h2>\u003Cp style=\"text-align: start\">プロジェクトの規模が大きくなるにつれて、型定義の扱い方が開発体験（DX）に大きな影響を与えます。 プロジェクトが拡大し、APIのドメインが10、20と増えていくと、全ての型定義を1つのファイルにまとめる方式では、ファイルが数万行に達することがあります。 このような巨大な型定義ファイルは、IDE（特にVSCode）のTypeScriptサーバーに大きな負荷をかけ、以下のような問題を引き起こします。\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>入力補完が遅れる\u003C/strong>: \u003Ccode>client.POST\u003C/code> の後に補完候補が表示されるまで数秒待たされる。\u003C/li>\u003Cli>\u003Cstrong>エラー表示が遅延する\u003C/strong>: コードを修正しても、型エラーが消えるまでに時間がかかる。\u003C/li>\u003Cli>\u003Cstrong>定義ジャンプが重い\u003C/strong>: 型の定義元にジャンプしようとすると、巨大なファイルが開き、目的の定義を探すのが困難になる。\u003C/li>\u003C/ul>\u003Ch3 id=\"hc1e2128ef3\">解決策：ドメイン別の型定義を直接利用する\u003C/h3>\u003Cp style=\"text-align: start\">この課題を解決するために、\u003Cstrong>ドメインごとに分割された軽量な型定義ファイルを直接インポートする\u003C/strong>アプローチを取ります。\u003C/p>\u003Cpre>\u003Ccode class=\"language-typescript\">// composables/auth/useAuth.ts\n// &apos;auth&apos;ドメインに関連する型定義のみを直接インポートする\nimport type { operations, components } from &apos;~/types/generated/domains/auth&apos;\n\ntype LoginRequest = operations[&apos;login&apos;][&apos;requestBody&apos;][&apos;content&apos;][&apos;application/json&apos;]\ntype User = components[&apos;schemas&apos;][&apos;User&apos;]\n\nexport const useAuth = () =&gt; {\n  // ...\n}\n\u003C/code>\u003C/pre>\u003Cp style=\"text-align: start\">\u003Cbr>この方法では、authドメインのcomposablesは、auth.d.tsという100行程度の軽量な型定義ファイルのみを参照します。 \u003C/p>\u003Cp style=\"text-align: start\">IDEは最小限の範囲だけを解析すればよいため、入力補完やエラーチェックが瞬時に行われ、快適な開発リズムが保たれます。\u003C/p>\u003Ch2 id=\"h37d8c95316\">3. 型安全なAPIクライアントの実装\u003C/h2>\u003Ch3 id=\"h1e4854458a\">useApiClient composable\u003C/h3>\u003Cp style=\"text-align: start\">Nuxt4のcomposablesを活用して、型安全なAPIクライアントを実装します。\u003C/p>\u003Cpre>\u003Ccode class=\"language-typescript\">// composables/useApiClient.ts\nimport createClient from &apos;openapi-fetch&apos;\nimport type { paths } from &apos;~/types/generated/api&apos; // 全体のパス定義のみ利用\n\nexport const useApiClient = () =&gt; {\n  const config = useRuntimeConfig()\n  const baseUrl = (config.public.apiBaseUrl || &apos;/api&apos;) as string\n\n  // openapi-fetchクライアントの作成\n  const client = createClient&lt;paths&gt;({ baseUrl })\n\n  // リクエストインターセプター（認証トークン付与）\n  client.use({\n    async onRequest({ request }) {\n      const token = useCookie(&apos;accessToken&apos;)\n      if (token.value) {\n        request.headers.set(&apos;Authorization&apos;, `Bearer ${token.value}`)\n      }\n      return request\n    },\n  })\n\n  return client\n}\n\u003C/code>\u003C/pre>\u003Cp style=\"text-align: start\">\u003Cbr>\u003Cstrong>ポイント:\u003C/strong>\u003C/p>\u003Cul>\u003Cli>\u003Ccode>createClient&lt;paths&gt;\u003C/code>で全エンドポイントの型安全性を確保します。\u003C/li>\u003Cli>インターセプターで認証トークンを自動付与します。\u003C/li>\u003C/ul>\u003Ch2 id=\"h37dafe535d\">4. ドメイン別composablesの実装\u003C/h2>\u003Cp style=\"text-align: start\">composablesをドメインごとにディレクトリ分割し、対応する型定義を使用します。\u003C/p>\u003Cp style=\"text-align: start\">　\u003C/p>\u003Cp>\u003Cstrong>認証ドメイン\u003C/strong>\u003C/p>\u003Cpre>\u003Ccode class=\"language-typescript\">// composables/auth/useAuth.ts\nimport type { operations, components } from &apos;~/types/generated/domains/auth&apos;\n\ntype LoginRequest = operations[&apos;login&apos;][&apos;requestBody&apos;][&apos;content&apos;][&apos;application/json&apos;]\ntype UserResponse = components[&apos;schemas&apos;][&apos;User&apos;]\n\nexport const useAuth = () =&gt; {\n  const client = useApiClient()\n  const user = useState&lt;UserResponse | null&gt;(&apos;auth:user&apos;, () =&gt; null)\n  const accessToken = useCookie(&apos;accessToken&apos;, { /* ... */ })\n\n  const login = async (credentials: LoginRequest) =&gt; {\n    const { data, error } = await client.POST(&apos;/auth/login&apos;, {\n      body: credentials,\n    })\n\n    if (error) {\n      throw createError({\n        statusCode: error.code === &apos;UNAUTHORIZED&apos; ? 401 : 400,\n        message: error.error || &apos;ログインに失敗しました&apos;,\n      })\n    }\n\n    if (data) {\n      accessToken.value = data.accessToken\n      user.value = data.user ?? null\n    }\n    return data\n  }\n  // ... logout, isAuthenticatedなど\n}\n\u003C/code>\u003C/pre>\u003Ch2 id=\"h13df998301\">5. 推奨されるディレクトリ構成\u003C/h2>\u003Cp style=\"text-align: start\">ドメインごとにディレクトリを分け、関連するcomposablesをグループ化することで、プロジェクトの見通しが良くなります。\u003C/p>\u003Cpre>\u003Ccode>composables/\n  ├── auth/\n  │   ├── useAuth.ts          # 認証処理\n  │   └── useSession.ts       # セッション管理\n  ├── todos/\n  │   ├── useTodos.ts         # Todo CRUD\n  │   └── useTodoFilters.ts   # フィルタリング\n  └── useApiClient.ts         # 共通APIクライアント\n\u003C/code>\u003C/pre>\u003Ch2 id=\"h8956605884\">6. Prismによるモックサーバー構築\u003C/h2>\u003Cp style=\"text-align: start\">Prismを使用すると、OpenAPI仕様からモックサーバーを自動生成でき、バックエンドの実装を待たずにフロントエンド開発を進められます。\u003C/p>\u003Ch3 id=\"h6440c6cc28\">Prismのセットアップ\u003C/h3>\u003Cpre>\u003Ccode class=\"language-bash\"># Prismのインストール\npnpm add -D @stoplight/prism-cli\n\u003C/code>\u003C/pre>\u003Cp style=\"text-align: start\">\u003Cbr>\u003Ccode>package.json\u003C/code>にスクリプトを追加します。\u003C/p>\u003Cpre>\u003Ccode class=\"language-json\">{\n  &quot;scripts&quot;: {\n    &quot;mock&quot;: &quot;prism mock openapi/openapi.yaml -p 4010&quot;\n  }\n}\n\u003C/code>\u003C/pre>\u003Ch3 id=\"h4053f259a0\">モックサーバーの起動と活用\u003C/h3>\u003Cpre>\u003Ccode class=\"language-bash\"># モックサーバーを起動\npnpm run mock\n\u003C/code>\u003C/pre>\u003Cp style=\"text-align: start\">\u003Cbr>\u003Ccode>nuxt.config.ts\u003C/code>で開発時にモックサーバーを参照するように設定します。\u003C/p>\u003Cpre>\u003Ccode class=\"language-typescript\">// nuxt.config.ts\nexport default defineNuxtConfig({\n  runtimeConfig: {\n    public: {\n      apiBaseUrl: process.env.NUXT_PUBLIC_API_BASE_URL || \n                  (process.env.NODE_ENV === &apos;development&apos; \n                    ? &apos;http://localhost:4010&apos; // 開発時はモックサーバー\n                    : &apos;/api&apos;)\n    }\n  }\n})\n\u003C/code>\u003C/pre>\u003Ch3 id=\"haa7137125e\">フロントエンド実装の並行開発\u003C/h3>\u003Cp style=\"text-align: start\">Prismを使うことで、バックエンドの完成を待たずにフロントエンド開発を開始できます。\u003C/p>\u003Ch2 id=\"h0c5e85b8e7\">7. 新しいドメインの追加手順\u003C/h2>\u003Cp style=\"text-align: start\">プロジェクトに新しい機能ドメイン（例: プロフィール管理）を追加する手順です。\u003C/p>\u003Ch3 id=\"h00a2a0c2b1\">Step 1: OpenAPI仕様を作成\u003C/h3>\u003Cp style=\"text-align: start\">\u003Ccode>openapi/domains/profile.yaml\u003C/code> を作成します。\u003C/p>\u003Ch3 id=\"hab1803b457\">Step 2: package.jsonにスクリプト追加\u003C/h3>\u003Cpre>\u003Ccode class=\"language-json\">{\n  &quot;scripts&quot;: {\n    &quot;generate:types:profile&quot;: &quot;openapi-typescript ./openapi/domains/profile.yaml -o ./app/types/generated/domains/profile.d.ts&quot;\n  }\n}\n\u003C/code>\u003C/pre>\u003Ch3 id=\"h04b7280496\">Step 3: バレルエクスポート設定を更新\u003C/h3>\u003Cp style=\"text-align: start\">\u003Ccode>scripts/generate-barrel-exports.js\u003C/code> の \u003Ccode>domains\u003C/code> 配列に \u003Ccode>profile\u003C/code> を追加します。\u003C/p>\u003Ch3 id=\"h22a4668319\">Step 4: 型定義を生成\u003C/h3>\u003Cpre>\u003Ccode class=\"language-bash\">pnpm run generate:api\n\u003C/code>\u003C/pre>\u003Ch3 id=\"h1a0919355a\">Step 5: composableを実装\u003C/h3>\u003Cp style=\"text-align: start\">\u003Ccode>composables/profile/useProfile.ts\u003C/code> を作成し、生成された型 \u003Ccode>~/types/generated/domains/profile\u003C/code> を使って実装します。\u003C/p>\u003Ch2 id=\"ha214098e44\">まとめ\u003C/h2>\u003Cp style=\"text-align: start\">\u003Cstrong>推奨事項\u003C/strong>\u003C/p>\u003Col>\u003Cli>\u003Cstrong>API変更は必ずOpenAPI仕様から開始する\u003C/strong>\u003Cul>\u003Cli>仕様を更新 → 型を生成 → 実装の順序を守る\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>ドメイン別型定義の直接インポートを活用する\u003C/strong>\u003Cul>\u003Cli>\u003Ccode>import type { ... } from &apos;~/types/generated/domains/auth&apos;\u003C/code>\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>composablesでドメインロジックをカプセル化\u003C/strong>\u003Cul>\u003Cli>状態管理とビジネスロジックを1箇所に集約\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Ccode>readonly()\u003C/code>で状態を保護\u003Cul>\u003Cli>\u003Ccode>return { user: readonly(user) }\u003C/code>\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>Prismでフロントエンド開発を加速\u003C/strong>\u003Cul>\u003Cli>バックエンドの進捗に依存せず開発を開始\u003C/li>\u003C/ul>\u003C/li>\u003C/ol>\u003Cp style=\"text-align: start\">\u003Cstrong>非推奨事項\u003C/strong>\u003C/p>\u003Col>\u003Cli>\u003Cstrong>生成された型定義ファイルを直接編集しない\u003C/strong>\u003Cul>\u003Cli>\u003Ccode>app/types/generated/\u003C/code> 配下は自動生成されるため\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Ccode>any\u003C/code>型を使わない\u003Cul>\u003Cli>型安全性が失われるため、必ず生成された型を利用\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>APIクライアントをページコンポーネントで直接使わない\u003C/strong>\u003Cul>\u003Cli>必ず\u003Ccode>composables\u003C/code>にロジックを集約\u003C/li>\u003C/ul>\u003C/li>\u003C/ol>\u003Ch4 id=\"h63e0635981\">スキーマ駆動開発の効果\u003C/h4>\u003Cp style=\"text-align: start\">本記事で紹介したアプローチにより、以下の効果が得られます:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>開発効率の向上\u003C/strong>: 型定義の自動生成、並行開発によるリードタイム短縮\u003C/li>\u003Cli>\u003Cstrong>品質の向上\u003C/strong>: コンパイル時の型エラー検出、仕様と実装の乖離防止\u003C/li>\u003Cli>\u003Cstrong>保守性の向上\u003C/strong>: ドメイン分割による影響範囲の明確化、安全なリファクタリング\u003C/li>\u003Cli>\u003Cstrong>スケーラビリティ\u003C/strong>: ドメインが増えてもIDEパフォーマンスが劣化しない\u003C/li>\u003C/ul>\u003Cp style=\"text-align: start\">この手法は、特にチーム開発や長期運用が想定されるプロジェクトで威力を発揮します。\u003C/p>\u003Ch2 id=\"h03e7af0d39\">参考リンク\u003C/h2>\u003Cul>\u003Cli>\u003Ca href=\"https://swagger.io/specification/\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">OpenAPI Specification\u003C/a>\u003C/li>\u003Cli>\u003Ca href=\"https://github.com/drwpow/openapi-typescript\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">openapi-typescript\u003C/a>\u003C/li>\u003Cli>\u003Ca href=\"https://openapi-ts.dev/openapi-fetch/\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">openapi-fetch\u003C/a>\u003C/li>\u003Cli>\u003Ca href=\"https://nuxt.com/docs\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">Nuxt4 Documentation\u003C/a>\u003C/li>\u003Cli>\u003Ca href=\"https://stoplight.io/open-source/prism\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">Prism - OpenAPI Mock Server\u003C/a>\u003Cp>　\u003C/p>\u003Cp>\u003C/p>\u003C/li>\u003C/ul>\u003Cp>ーーーーーーーーーーーーーーーーーーーーーーー\u003C/p>\u003Cp>株式会社SAKURUGは「寄付月間2025」に参画しています。\u003C/p>\u003Cp>12月中のテックブログのpv数に応じて、アフリカの支援団体に寄付をおこないます。\u003C/p>\u003Cp>\u003Ca href=\"https://giving12.jp/\">https://giving12.jp/\u003C/a>\u003C/p>\u003Cp>ーーーーーーーーーーーーーーーーーーーーーーー\u003C/p>",{"id":11,"createdAt":12,"updatedAt":13,"publishedAt":12,"revisedAt":12,"name":14},[197],{"id":35,"createdAt":36,"updatedAt":36,"publishedAt":36,"revisedAt":36,"name":37},[],"OpenAPI仕様を中心としたスキーマ駆動開発は、フロントエンドとバックエンドの型安全性を保ちながら効率的に開発を進める手法です。本記事では、Nuxt4プロジェクトでスキーマ駆動開発を実装する際のフロントエンド側のTipsをまとめます。",false,"prompt only から CLAUDE.md、PostToolUse hook、UI checklist、Stop completion gate、Gate TDD までを段階的に足し、 5 条件で比較を行いました。",1774870784083]