セッションラップ(プロジェクト対応クローズアウト) は何をしますか?
2つのパートで構成:§1は汎用、§2はプロジェクトごとにルーティング(状態テーブルはプロジェクト固有 — 他のプロジェクトのテーブルを書き込まない)。§0はセッション中ハートビート(後述)。<MEM> = bash ~/.claude/hooks/session-resume.sh --dir で出力されるメモリディレクトリ。
保存前には必ず編集(ハートビート、ハンドオフメモリ、ルール/nowファイル、DB行、再開プロンプト):APIキー、トークン、パスワード、秘密鍵、アカウント番号と残高、個人データ、稼働中のアカウントを特定する情報を削除する。<REDACTED — see vault> と書き込むか、言い換える(「残高確認済み、ここには記録しない」)。実際の値がどこにあるかを示すのは問題ないが、値自体は記録しない。§2のガードレイル言い換えルールと同じパターン — 両方を維持する。
0. ハートビートチェックポイント — セッション中(ハンドオフメモリのみ)
各サブタスク完了後、および長い/リスクの高いステップ(大規模リファクタリング、デプロイ、DBマイグレーション)の前に、現在のプロジェクトの§1ハンドオフファイルのみを更新する(存在しなければ作成する):## LIVE STATE (heartbeat) ブロック — 完了した作業、進行中の作業、次のステップ。前のブロックを置き換え、追記しない。目標は15行未満。見出しは正確に保つ:フックがこれをマッチする。
MEMORY.mdの再タグ付け、データベースへの書き込み、再開プロンプトの出力は行わない — これらはセッション終了時に残す(§1タグ、§2、§3)。ラップ時に、ブロックを検証済み状態に折り込み、削除する。- この時点でのクラッシュは、最大でも1ステップ分のメモリ損失で済む。
session-resume.shは、ブロックを保持しているすべてのハンドオフを次回起動時にUNWRAPPED HEARTBEATとしてリストするため、タグ付けされていない最初のハートビートファイルでも発見される。
1. ハンドオフメモリ — 常に実行(任意のプロジェクト)
<MEM>/project_<プロジェクト名>_handoff_<YYYYMMDD>.md を書き込む/更新する:検証済み状態(キーSHA、確定した内容、未解決の決定、順序付けられた次の作業、必須事項)。<MEM>/MEMORY.md で、そのインデックス行に ★READ FIRST FOR <プロジェクト名> (current)★ とタグ付けし、以前のものには (superseded) とマークする。古いハンドオフはディスク上に残る:置き換えられたチェーンがプロジェクトの履歴となる。
- SessionStartフック
~/.claude/hooks/session-resume.shはプロジェクト非依存:READ FIRST FOR <任意のプロジェクト>にマッチし、supersededとマークされていないすべての行を注入する → 行をタグ付けすることが、任意のプロジェクトの再開保証となる(プロジェクトごとのフックは不要)。MEMORY.mdが自動ロードされてもフックを維持する:「ハンドオフを読み取ってから作業を開始する」という指示を追加し、どのディレクトリからでも動作し、MEMORY.mdが200行のロード制限を超えても機能する。また、プロジェクトに2つのライブポインターがある場合や、存在しないファイルへのポインターがある場合に警告を出す — ラップ時にそれらを修正する。
ルールは引き継がれる。 新しい所有者ルール、恒久的なガイダンス、作業用コマンド、有料の教訓は、ハンドオフだけでなく、与えられた時点でそのプロジェクトの常時ロードルールファイルに書き込まれる。ルールファイル:<MEM>/<プロジェクト名>_rules.md、<MEM>/session-wrap.conf にリストされたすべてのプロジェクトに対して session-resume.sh によって完全ロードされる。短く保ち、追記ではなく置き換える。ラップ時に確認:このセッションで所有者が繰り返さなければならなかったことは、今はルールファイルに含まれている必要がある。また、<MEM>/<プロジェクト名>_now.md を書き換える(現在の状態と次のステップ、短く保つ:フックコンテキストは約10,000文字でカットされる)。
2. プロジェクト状態ストア同期 — ルーティング
cwd / このセッションの作業からプロジェクトを検出する。その後、そのプロジェクトのルートに従う。所有するプロジェクトごとに1つ ### ルートを追加する。以下の3つはパターン。
<Postgresストアを持つプロジェクト> → そのプロジェクトのデータベース
環境から資格情報を使用して接続する(psql "$PROJECT_DB_URL")、コマンド内にリテラルを使用しない。examples/state-store.sql のテーブル例:
INSERT INTO session_handoff (session_id, handoff_prompt, created_at) VALUES ('<sid>','<resume>',now());
INSERT INTO decisions (title,rationale,applies_to,status,session,"timestamp",decided_at)
VALUES ('<d>','<why>','<project>','implemented','<sid>',now()::text,now()); -- 1行につき1つのINSERT
INSERT INTO code_map (module,path,lines,purpose,bounded_context) VALUES (...); -- 一意制約がない場合は先にDELETE path
注意点(独自のストア用にここに記述する;これらは各セッションを消費する類のもの):キーワード名の列を引用符で囲む(decisions."timestamp";"order" のような予約語には必須、それ以外では無害)、型に注意(これは text + NOT NULL、よって now()::text);CHECK制約のある列はリストされた値のみを受け付ける(pending|implemented|superseded|deferred|rejected);テーブルが存在することを前提にしない — 最初に \d で確認する;複数行のVALUESはアトミックであるため、1行でも不正だとすべてがドロップされる → 1行ずつ -v ON_ERROR_STOP=off で行う。ガードレイルの誤検知:危険なコマンドをブロックするPreToolUseフックはBashコマンド全体をgrepする — SQLテキストにブロックされたパターンのリテラル(reset --hard、git push、git clean)が含まれている場合、コマンド全体がブロックされる。ハンドオフ/決定テキストではそのような文字列を言い換える(例:「破壊的git」、リテラルではない)。
<ライブで動作するプロジェクト> → そのプロジェクトのストア(他のプロジェクトのものではない)
書き込む前にスキーマを確認する;それに関するメモはすぐに古くなる。ライブインフラ — 外部の記録システム(決済プロバイダー、本番DB、実行中のサービス)が真実であり、ハンドオフではない;ラップの一部としてライブフラグや設定を決して変更しない。
<その他すべて> → メモリのみ
DB状態ストアなし — §1のハンドオフメモリが記録となる。これを明記し、他のプロジェクトのテーブルを書き込まない。
3. 再開プロンプトを出力する(最後のアーティファクト — ユーザーが次のClaudeに貼り付けるもの)
保存後、次のセッション用のコピー&ペースト可能なプロンプトをコードフェンスで囲んで出力する。SessionStartフックがハンドオフポインターを既に注入しているため、このプロンプトは以下を追加するのみ:再ロードチェック + ユーザーが決定しなければならない未解決の決定。テンプレート:
Resume <プロジェクト名>. Read memory <ハンドオフファイル> first, then skill /<プロジェクトマップスキル>.
Verified state: <1行: キーSHA + ライブのもの + ゲート/ブロックされているもの>.
First: 1) /context — <再ロードチェック、例:新しくロードされたプラグインのコスト測定>; 2) <ロード確認チェック>.
Then my decision on <未解決の分岐>: [PICK ONE] (a) <推奨> (b) … (c) …
<恒久的なセッションごとの指示、例:完了と主張する前に確認する>
短く保つ — ポインターと決定のみ、状態のダンプではない(状態はハンドオフメモリ + DBに存在する;重複しない → コンテキスト汚染)。また、このプロンプトを ## RESUME PROMPT 見出しの下にハンドオフメモリに貼り付けて、クエリ可能にし、チャット内で失われないようにする。
完了(確認、結果を引用)
- 調整ゲート — 最初に実行、必須。 会話全体(メモリだけでなく)をスキャンし、このセッションからのすべての決定、未解決項目、所有者の修正、必須事項をリストする。それぞれについて、それがどこに落ち着いたかを引用する(ハンドオフメモリ行、ルールファイル行、DB行)か、
DROPPED: <1行の理由>とマークする。「カバーされていると思う」は許可されない — 行を指し示すか、削除済みとマークする。説明のつかないものがあれば → 今すぐ書き込む(未解決の決定が変更された場合は§3を更新)、その後再リストする。 - §1:ハンドオフメモリのMEMORY.md行に
(current)が付いていること;以前のものはsupersededとマークされていること;grep -c 'LIVE STATE (heartbeat)' <ハンドオフ>= 0。 - §2:DBプロジェクトの場合、
SELECT max(created_at) FROM session_handoff;≈ 現在時刻 + 書き込んだ行数。メモリのみの場合は、そのように明記する。 - §3:再開プロンプトが出力され、かつハンドオフメモリに保存されていること。