はじめに
AIコーディングエージェントに指示を出すとき、CLAUDE.md や AGENTS.md に何を書けばいいのか分からないまま、とりあえず思いついたルールを追記し続けていませんか。
この記事では、Learn Harness Engineering というコースを参考に、Hono + TypeScript のタスク管理API向けにマークダウンファイル群(ハーネス)を組んだ過程を紹介します。
最初に断っておくと、この記事の内容はコースの観点をすべて実装できたわけではなく、最善でもありません。 機能リストによる作業制約、E2Eテストによる検証、ランタイムの観測可能性など、手をつけていない領域が多く残っています。
対象読者は「ハーネスエンジニアリングという言葉は知ったが、まず何から手をつければいいか分からない人」です。完璧な設計を目指すより、最初の一歩として何を作ればいいかの具体例として読んでもらえればと思います。
問題
エージェントへの指示ファイルは、放っておくと以下の状態になります。
- 技術スタック、コーディング規約、過去のバグ修正メモ、デプロイ手順が1ファイルに同居する
- ファイルが数百行に膨れ、重要な制約が中盤に埋もれて無視される
- 矛盾したルールが共存し、エージェントの挙動が安定しない
- セッションが切れると、それまでの作業経緯が完全に失われる
特に厄介なのが最後の項目です。エージェントは前回のセッションを覚えていません。「記憶喪失の優秀なエンジニアが毎日出勤してくる」状態で、引き継ぎ資料がなければ毎回ゼロからの調査が発生します。
また、LLM には「lost in the middle」と呼ばれる特性があり、長い文脈の中央にある情報は端よりも活用されにくいことが知られています。1ファイルに全部詰め込むほど、重要なルールが効かなくなります。
解決方法
全体構成
最終的に7ファイルになりました。役割を明確に分けるのがポイントです。
CLAUDE.md # 入口。ルーティング中心。90行
docs/
├── architecture.md # レイヤー構成と依存ルール
├── api-spec.md # エンドポイントとレスポンス形式
├── naming-conventions.md # 命名規則
├── testing.md # テスト方針
├── PROGRESS.md # 現在地(毎回上書き)
├── DECISIONS.md # 設計判断の履歴(追記のみ)
└── notes/
└── session-continuity.md # 未採用の検討メモ
分割の判断軸は「変更頻度 × 寿命」です。
| ファイル | 書くもの | 更新 | 寿命 |
|---|---|---|---|
CLAUDE.md |
常時の制約、ルーティング | 方針変更時のみ | 永続 |
docs/*.md |
主題別の規約 | 規約変更時 | 永続 |
PROGRESS.md |
現在地と次の一手 | 毎セッション上書き | 揮発 |
DECISIONS.md |
決定・理由・却下案 | 決定時に追記 | 永続 |
観点1: 入口ファイルは短く、ルーティングに徹する
CLAUDE.md は90行に抑え、セクションを以下の順に並べました。
プロジェクト概要
Session Routine ← 出勤/退勤ルーティン
Quick Start
技術スタック
Hard Constraints ← 常時適用の制約 11個
Topic Docs ← 詳細ドキュメントへのリンク
Out of Scope
指示の管理
Session Routine を冒頭に置いたのは意図的です。エージェントが最初に実行する手順なので、lost in the middle を避けて端に配置しています。
Topic Docs のセクションは「読むタイミング」を明記し、常時読み込ませないようにしました。
| ドキュメント | 読むタイミング |
|---|---|
| `docs/architecture.md` | ファイル追加時、レイヤー配置に迷ったとき(必読) |
| `docs/api-spec.md` | エンドポイントの追加・変更時(必読) |
| `docs/naming-conventions.md` | クラス・ファイルを新規作成するとき |
| `docs/testing.md` | テストを書くとき |
スーツケースのパッキングキューブと同じ発想です。下着を探すためにバッグ全体を空ける必要はありません。
観点2: 各ドキュメントに「適用条件・由来・失効条件」を持たせる
指示をコードの依存関係と同じように管理するため、トピック文書の冒頭に3点を書きます。
> **適用条件**: エンドポイントの追加・変更・削除時
> **由来**: レスポンス形式とエラーコードを実装ごとにブレさせないため
> **失効条件**: OpenAPI スキーマを導入し生成物が正となった時点で、このファイルは削除する
特に効くのが失効条件です。「いつ消していいか」が書かれていないルールは、誰も消せずに残り続けます。使っていない依存パッケージと同じで、残しておくとシステムを遅くするだけです。
観点3: コードに書けることは、ドキュメントに書かない
当初は CLAUDE.md にドメインモデルの構造(フィールドと型)を書いていましたが、削除しました。エージェントはコードを読むとき自然に型定義を見るため、二重管理になるだけです。
代わりに入口ファイルにはこう書きます。
ドメインモデルの構造は文書化していない。
`src/domain/entities/` と `src/domain/value-objects/` の型定義と JSDoc を直接読む。
同じ理由で、技術スタックのバージョンにも注記を入れました。
バージョンは 2026-08 時点。実際の値は `package.json` が正。
この一文がないと、package.json とドキュメントの二重管理になり、片方が必ず腐ります。
観点4: セッションをまたぐ引き継ぎ資料を用意する
「記憶喪失の職人に日記を持たせる」アプローチです。2ファイルに分けました。
PROGRESS.md(毎回上書き)
現在地だけを書きます。履歴は git log が持っているので、Completed セクションは直近のみ残して古いものは削ります。上限60行。
## Current State
- Latest commit: abc1234
- Test: 42/43 passing
- Lint / Typecheck: passing
## In Progress
- [ ] ページネーション(90% - 境界値テストが失敗)
- 対象ファイル: src/application/usecases/list-tasks.ts
## Known Issues
- 空リストのとき500が返る
## Next Steps
1. ページネーションの境界値バグを修正
2. ...
## Open Questions
- 一覧の絞り込みをリポジトリの引数で受けるか、専用メソッドを分けるか
Open Questions は独自に追加したセクションです。仕様が曖昧な部分をエージェントが勝手に決めてしまうのを防ぎ、次のセッションで人間に確認させる目的があります。
DECISIONS.md(追記のみ)
設計判断とその理由を記録します。詳細な設計書は不要で、「何を決めたか、なぜか、いつか」だけです。
## 2026-08-11: リポジトリの戻り値を Promise にする
- 決定: インメモリ実装でも全メソッドを async にする
- 理由: 将来 DB へ差し替える際に呼び出し元のシグネチャ変更を発生させない
- 却下案: 同期メソッド(差し替え時に全ユースケースの修正が必要)
- 影響: テストでも await が必要
- 関連: docs/architecture.md
運用ルールは3つです。
- 削除しない。覆す場合は新エントリで「〇〇の決定を撤回」と書く
- 恒久ルールに昇格したものはトピック文書へ転記し、転記先だけ残す
- 50件を超えたら年次アーカイブへ移す
却下案を書くのが重要です。これがないと、次のセッションで同じ選択肢を再検討する無駄が発生します。
観点5: 出勤・退勤ルーティンを定義する
CLAUDE.md の Session Routine に手順を書きます。
### 出勤(開始時)
1. `docs/PROGRESS.md` を読み、現在地と Next Steps を把握する
2. `docs/DECISIONS.md` を読み、過去の決定と却下案を確認する
3. `git log --oneline -5` で直近のコミットを確認する
4. `npm run check` を実行し、リポジトリが整合状態か確認する
5. Next Steps の先頭から着手する
### 退勤(終了時)
1. `npm run check` を通す
2. 作業差分をコミットする(コミット前に承認を得る)
3. 設計判断があれば `docs/DECISIONS.md` に追記する
4. `docs/PROGRESS.md` を最新状態に上書きする
5. `chore: update PROGRESS` として PROGRESS のみ単独コミットする
ここには弱点がある
正直に書くと、この退勤ルーティンは仕組みではなく規約です。セッション終了を確実に検知する手段がありません。
Claude Code には SessionEnd フックが存在しますが、ブロック不可で副作用専用、かつ実行予算が短いという制約があります。ターミナルを閉じる、Ctrl+C で強制終了する、SSH が切れる、といったケースでは発火しません。
そのため「フックで機械的情報を自動保存する」「PROGRESS更新をコミット単位に前倒しする」といった代替案を検討メモとして docs/notes/session-continuity.md に残し、採用判断のトリガーだけ決めておきました。
## 採用判断のトリガー
- セッションが不意に切れて PROGRESS.md が古いまま残り、手戻りが発生した
- 退勤ルーティンが実行されないまま終わることが3回以上あった
- 自動 compact 後にエージェントが直前の作業内容を見失った
逆に、上記が起きなければ現行のままでよい。
最初から作り込まず、問題が起きてから移行するという判断を明文化しておく方が、保守対象を無駄に増やさずに済みます。
実際に動かしてみる
ハーネスを組んだ状態で、仕様をほぼ書かずにこう指示しました。
タスク管理 REST APIを実装して
結果、npm run dev で起動するAPIができあがりました。
app.route("/health", createHealthRoutes());
app.route("/api/tasks", createTaskRoutes(deps));
listening on http://localhost:3000
一言の指示にもかかわらず、エンドポイント構成もレイヤー分割も命名規則も、事前に定義したドキュメントに沿ったものが出てきます。仕様をプロンプトに書く代わりに、リポジトリに置いておくという発想の転換が、ハーネスエンジニアリングの核だと感じました。
おわりに
冒頭に書いたとおり、これは完成形ではありません。参考にしたコースの講義一覧を見ると、手つかずの領域がまだあります。
- 機能リストによる作業範囲の制約
- 早すぎる完了宣言の防止(現状は
npm run checkを通すルールのみ) - E2Eテストによる検証
- ランタイムの観測可能性
- 初期化フェーズの自動化(フック / init.sh)
- 手動プロンプトから自律ループへの移行
それでも、入口ファイルを短くする、適用条件と失効条件を書く、PROGRESS と DECISIONS を分ける、この3つだけでもエージェントの挙動は目に見えて安定します。
何から手をつければいいか分からない場合は、まず今ある CLAUDE.md を開いて「これは毎回守るルールか / 今回だけの状況か / 後から理由を聞かれるものか」で仕分けしてみるところからで十分だと思います。全部を一度に整えようとすると、それ自体が別のスーツケースになってしまいます。
参考
- Learn Harness Engineering - 今回の主な参考元。講義・プロジェクト・テンプレートで構成されたコース
- OpenAI: Harness engineering: leveraging Codex in an agent-first world
- Anthropic: Effective harnesses for long-running agents
- Anthropic: Harness design for long-running application development
- Claude Code Hooks リファレンス
JISOUのメンバー募集中!
プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか?
興味のある方は、ぜひホームページをのぞいてみてください!
▼▼▼
https://projisou.jp