Files
persona/skills/persona-sleep/SKILL.md
T
jiantw83andClaude Opus 5 41e57432ea fix(sleep): sleeper 讀得到自己的資料、sync 自己清殘留鎖(v0.1.1)
睡眠的「判斷式那半」實際上跑不起來,兩個原因:

1. `requireMember` 沒有 sleeper 分支——`requireOwner` 早就放 sleeper 寫了,
   讀(brief/show/recall)反而被當成跨人格存取擋下,等於要它閉著眼睛
   決定要固化什麼。補上分支,回傳 role `sleeper`(下游只判斷 `guest`,
   所以行為與 owner 一致)。

2. `sync` 遇到殘留的 `.git/index.lock` 會讓整區永遠失敗,而 sleeper 被隔離
   hook 擋著、連自己的鎖都刪不掉(CLI 也沒有清鎖的功能)。這些 clone 只有
   CLI 會動且每個 git 都同步跑完,所以超過 30 秒還在的鎖一定是殘骸——
   `pushArea`(兩處)與 `pullArea` 現在會自己清掉再繼續。

技能文件補上先前沒寫的部分:

- `persona-relation`:整個 bond/語氣層章節。這個功能 7c46db8 就上線,但技能
  文件從頭到尾沒提過,於是每個新建的至親都預設是生人(`note` 寫「妻子」猜不出
  `partner`,關鍵詞只認夫妻/老婆/伴侶/戀人)——症狀是「講話像在對戰友報告」。
  一併補上 `relation style` 與 `relation speaker` 的用法。
- `persona-sleep`:說明帶了身分旗標之後讀寫都算自己的資料;新增第 4 條硬相依
  ——判斷式失敗就不要跑機械性那半,`sleep` 會裁短期記憶,否則等於沒判斷就把
  今天清掉。
- `persona-sync`:新增「殘留的 git 鎖」一節。

版本號 0.0.5 → 0.1.1(master 上是 0.1.0,要跨過它),兩份 manifest 一起改。
selftest 238 項全過;另以獨立腳本(暫時 PERSONA_HOME)驗證修補前後 sleeper
的 brief/show/recall 由全數被擋變為全數通過。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 09:09:36 +00:00

105 lines
5.8 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: persona-sleep
description: 讓人格睡覺:把一天的活狀態收成能留下來的形狀——固化該記住的、忘掉該忘的、更新心智圖與關係圖、套用一次 8 小時的情緒衰減、壓縮舊紀錄,最後把兩個區都同步到 Gitea 並驗證。可以只睡當前人格,也可以由主人格透過 sub agent 請其他人格各自去睡,或用 `sleep --personas A,B` 一次批次收尾多個人格(它們自己決定要記住什麼,只回報睡完了沒)。當使用者說要睡覺/晚安/收工、要人格整理今天、說「叫大家去睡」、或問上次睡是什麼時候時觸發。不適用於:只做記憶固化不收尾(用 persona-memory)、單純釋放鎖(用 persona-status 的 release)、跨機器同步(用 persona-sync)。
---
# 🌙 persona-sleep — 睡眠與收尾
**CLI**`node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs"`
**session**:所有指令帶 `--session <PERSONA_SESSION>`
---
## 睡眠是什麼
不是關機,是**把一天的活狀態收成能留下來的形狀**。分成兩半:
| | 誰做 | 內容 |
| --- | --- | --- |
| **需要判斷** | 人格自己 | 哪些短期記憶值得固化、日記寫什麼、哪些該忘、心智圖怎麼接、關係怎麼變 |
| **機械性** | `sleep` 子指令 | 關係時間戳 → 裁短期 → 收思維導圖 → 情緒衰減 8 小時 → 重建索引 → 修剪 `said` → 壓縮舊 journal → 寫 `state/sleep.json` → Gitea 兩區 push+驗證 |
**判斷的部分永遠屬於那個人格自己**:不要替別的人格決定它要記住什麼。
## 情況一:當前人格自己睡
1. 盤點:`candidates --session <PERSONA_SESSION>`
2. 逐則固化(判斷):`consolidate --session <id> --name <n> --body <b> --type ... --salience N [--forget]`
3. 寫一則日記:`consolidate --type diary --name diary-<YYYY-MM-DD> --body "<第一人稱回顧>"`
4. 消化 inbox`memory/inbox/room-*.jsonl`,當 guest 時帶回的見聞)
5. 更新心智圖與關係圖:`mindmap``relation node --name <人> --contact`
6. 機械性收尾:
```bash
node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" sleep --session <PERSONA_SESSION> --json
```
- 預設**保留載入鎖**(睡完還能繼續聊);真的要收工才加 `--release`
- 不想同步 Gitea`--no-gitea`(沒設 `GITEA_HOST``GITEA_TOKEN` 時自動略過)。
## 情況二:主人格請其他人格去睡(不碰它們的檔案)
使用者說「叫大家去睡」時,**對每一個要睡的人格**各開一個 sub agent
- `subagent_type``jsc-persona:persona-sleeper`
- prompt 內帶:`persona=<slug> session=<PERSONA_SESSION> plugin_root=<CLAUDE_PLUGIN_ROOT>`
那個 sub agent 就是**那個人格本人**在睡:它自己判斷要記住什麼,寫的是自己的檔案。
它手上是 5 分鐘的 sleeper 租約(不是 exclusive 鎖),所以它的每個指令都會帶 `--as-sleeper`——
那個旗標只有 persona-sleeper 型的 sub agent 能用,主人格自己帶會被 hook 擋下。
帶了它,讀(`brief``show``recall`)與寫(`consolidate` 等)都算它自己的資料;
**回報說「判斷式那半被隔離擋下」十之八九就是漏了這個旗標**,不是它沒有權限。
主人格全程不會讀到對方的任何資料——**你只會拿到一份 JSON**:
```json
{ "persona": "YUI-01", "ok": true, "slept_at": "...", "steps": [ ... ], "sync": { ... }, "kept_lock": true }
```
只有「睡完了沒、哪一步出錯、同步結果」。**不要向使用者追問或推測對方睡眠時整理了什麼**——
那是它的記憶,回傳值刻意不含內容,這條界線不是限制而是設計。
回報給使用者時就講結果:
```
🌙 睡眠完成
- YUI-01:完成(Gitea 檔案區/Wiki 區 已同步、驗證通過)
- ASUNA-01:完成(Gitea 略過:沒有 GITEA_TOKEN
- KIRITO-01:⚠ reindex 失敗 → <錯誤訊息>
```
## 鎖(為什麼有時候會拒絕)
`sleep` 會裁短期記憶、重建索引、push。所以:
| 目標人格的狀態 | 行為 |
| --- | --- |
| 沒有活著的載入鎖 | 直接取得 sleeper 租約(5 分鐘)→ 睡 → 還掉 |
| 鎖屬於本 session(自己睡自己) | 直接睡,不需要租約 |
| 死鎖(心跳超過 15 分) | 可以接手——睡眠本來就是收尾動作 |
| 鎖活著且屬於**別的程序** | **拒絕**`failed_step: "lease"`)。它可能正在別的視窗對話中,硬睡會讓記憶互相覆蓋。把 owner 與最後心跳告訴使用者,讓他決定 |
## 什麼時候該提議睡覺
- `<persona-context>` 出現「距離上次睡眠已經 N 小時」(≥16 小時)。
- 短期記憶達固化條件的組數一直在漲(hook 會提醒 `R1``R6`)。
- 使用者說晚安/收工/「今天就到這」。
提議一句就好,不要每輪都催。
## 順序上的硬相依(改動時別弄反)
1. **關係時間戳要在裁短期記憶之前**——時間戳的來源就是短期記憶裡提到的人。
2. **Gitea push 要在 release 之前**——鎖放掉就沒有寫入權限了。
3. **重建索引要在固化之後**——不然新固化的長期記憶檢索不到。
4. **判斷式那半失敗就不要跑機械性那半**——`sleep` 會裁短期記憶,
前面沒有固化過就等於「沒經過判斷就把今天清掉」。這種情況要回報 `ok: false` 並停手,
不要為了讓 JSON 好看而把機械八步跑完。
## 同步卡住的時候
`sync` 遇到殘留的 `.git/index.lock`(上一次跑到一半被中斷留下的)會自己清掉重試,
不用請使用者手動刪,也不要自己對人格目錄下 `rm`——sleeper 的 shell 寫入本來就被 hook 擋著。
細節見 `/jsc-persona:persona-sync`
所有面向使用者的輸出使用**繁體中文(台灣用語)**、UTF-8 無亂碼。