工作階段包裝(專案感知結束)
分兩部分:§1 是通用的;§2 按專案路由(狀態表是每個專案各自的 — 絕不為其他專案寫入其表格)。§0 = 會話中心跳(見下文)。<MEM> 是 bash ~/.claude/hooks/session-resume.sh --dir 列印的記憶體目錄。
在每次儲存前進行編輯(心跳、交接記憶、rules/now 檔案、資料庫行、恢復提示):移除 API 金鑰、令牌、密碼、私鑰、帳號和餘額、個人資料、任何可識別真實帳戶的資訊。寫入 <REDACTED — see vault> 或改寫(例如「已驗證即時餘額,未記錄於此」)。指出真實值存在於何處是允許的;但值本身不可以。與 §2 中的護欄改寫規則模式相同 — 兩者都要遵守。
0. 心跳檢查點 — 會話中(僅交接記憶)
在每個完成的子任務之後,以及在任何長時間/高風險步驟(大型重構、部署、資料庫遷移)之前,僅重新整理專案當前的 §1 交接檔案(如果沒有則建立它):一個 ## LIVE STATE (heartbeat) 區塊 — 剛剛完成了什麼、正在進行中的是什麼、下一步要做什麼。取代前一個區塊,絕不要附加;目標少於 15 行。保持標題精確:鉤子會比對它。
- 不要重新標記
MEMORY.md、觸碰任何資料庫或發出恢復提示 — 這些保留到會話結束(§1 標記、§2、§3)。在包裝時,將該區塊摺疊為已驗證狀態並刪除它。 - 此時發生的崩潰最多只會損失一個步驟的記憶。
session-resume.sh會在下次啟動時,將每個仍持有該區塊的未標記交接檔案列為UNWRAPPED HEARTBEAT,因此未標記的第一個心跳檔案仍會被找到。
1. 交接記憶 — 總是執行(任何專案)
寫入/更新 <MEM>/project_<proj>_handoff_<YYYYMMDD>.md:已驗證的狀態(關鍵 SHA、已完成的項目、未解決的決定、有序的下一步工作、任務要求)。在 <MEM>/MEMORY.md 中,將其索引行標記為 ★READ FIRST FOR <PROJECT> (current)★;將前一個標記為「(superseded)」。舊的交接檔案保留在磁碟上:被取代的鏈結就是專案的歷史。
- SessionStart 鉤子
~/.claude/hooks/session-resume.sh是專案無關的:它會注入每一行符合READ FIRST FOR <ANY-PROJECT>且未被標記為已取代的內容 → 標記該行就是任何專案的恢復保證(無需每個專案的鉤子)。即使 MEMORY.md 是自動載入的,也要保留此鉤子:它會添加「在開始工作前先讀取交接」的指令,可從任何目錄運作,並能在 MEMORY.md 成長超過 200 行載入限制時存活。它還會在專案有兩個活動指標或指標指向遺失的檔案時發出警告 — 在包裝時修復這些問題。
規則會延續。 任何新的所有者規則、長期指導方針、有效命令或學到的教訓,都應在給出的那一刻立即放入該專案始終載入的 rules 檔案中,而不僅僅是放入交接檔案中。Rules 檔案:<MEM>/<proj>_rules.md,由 session-resume.sh 為 <MEM>/session-wrap.conf 中列出的每個專案強制完整載入。保持簡短;取代而非附加。在包裝時檢查:所有者在本會話中需要重複的任何內容現在都必須在該檔案中。同時改寫 <MEM>/<proj>_now.md(當前狀態和下一步,保持簡短:鉤子上下文大約被截斷在 10,000 個字元)。
2. 專案狀態儲存同步 — 先路由
從 cwd /本會話的工作中偵測專案。然後遵循該專案的路由。為你擁有的每個專案添加一個 ### 路由;以下是三種模式。
<使用 PostgreSQL 儲存的專案> → 其自己的資料庫
使用環境中的憑證連線(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()); -- 每行一個 INSERT
INSERT INTO code_map (module,path,lines,purpose,bounded_context) VALUES (...); -- 如果沒有唯一約束,則先 DELETE 該路徑
陷阱(在這裡寫下你自己儲存的陷阱;這些是那種每次會話都會造成損失的類型):對關鍵字命名的欄位使用引號(decisions."timestamp";對於保留字如 "order" 是必需的,否則則無害),並注意其型別(這是 text + NOT NULL,因此使用 now()::text);具有 CHECK 約束的欄位只接受列舉的值(pending|implemented|superseded|deferred|rejected);不要假定表格存在 — 先使用 \d 檢查它;多行 VALUES 是原子性的,因此一個錯誤行會導致全部失敗 → 使用 -v ON_ERROR_STOP=off 逐行執行。護欄誤報:PreToolUse 鉤子會阻止危險命令,它會 grep 整個 Bash 命令 — 如果你的 SQL 文字包含字面上的被阻止模式(reset --hard、git push、git clean),則整個命令會被阻止。在交接/決定文字中改寫此類字串(例如使用「destructive git」而非字面值)。
<執行中的專案> → 其自己的儲存(不是其他專案的)
在寫入前驗證結構;關於它的筆記會過時。即時基礎設施 — 外部記錄系統(支付提供商、生產資料庫、正在運行的服務)是真相,而非交接檔案;永遠不要因為包裝而翻轉即時標誌或設定。
<其他所有情況> → 僅記憶體
沒有資料庫狀態儲存 — §1 的交接記憶就是記錄。請注意這一點;不要寫入任何其他專案的表格。
3. 發出恢復提示(最後的成品 — 使用者貼入下一個 Claude 的內容)
在儲存之後,輸出一個圍欄格式的複製貼上提示,供下次會話使用。SessionStart 鉤子已經注入了交接指標,因此此提示僅添加:重新載入檢查 + 使用者必須做出的未解決決定。範本:
Resume <project>. Read memory <handoff-file> first, then skill /<project-map-skill>.
Verified state: <1-line: key SHA + what's live + the gated/blocked thing>.
First: 1) /context — <any reload-check, e.g. measure newly-loaded plugin cost>; 2) <confirm-loaded check>.
Then my decision on <OPEN FORK>: [PICK ONE] (a) <rec> (b) … (c) …
<your standing per-session instructions, e.g. verify before claiming done>
保持簡短 — 指標 + 決定,而非狀態傾印(狀態存在於交接記憶 + 資料庫中;不要重複 → 導致情境污染)。同時將此提示貼入手冊記憶中的 ## RESUME PROMPT 標題下,使其可查詢,而不會在聊天中丟失。
完成(驗證,引用結果)
- 協調閘門 — 首先執行,強制性。 掃描整個對話(不僅是記憶),列出本會話中的每個決定、待辦事項、所有者修正和任務要求。對於每個項目,引用它 + 其落腳處(交記憶行、rules 檔案行或資料庫行),或標記為
DROPPED: <一行原因>。不允許「我認為已經涵蓋」 — 指向具體行或標記為已丟棄。任何未說明的項目 → 立即寫入(如果未解決的決定已更改則重新整理 §3),然後重新列出。 - §1:交接記憶的 MEMORY.md 行帶有
(current);先前的標記為已取代;grep -c 'LIVE STATE (heartbeat)' <handoff>= 0。 - §2:對於資料庫專案,
SELECT max(created_at) FROM session_handoff;≈ 現在的時間 + 你寫入的行數。對於僅記憶體專案,請說明。 - §3:恢復提示已被發出並儲存到交接記憶中。