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>
This commit is contained in:
@@ -258,6 +258,29 @@ function gitOrThrow(args, cwd, what) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// 這些 clone 只有本 CLI 會動,而且每個 git 都是同步跑完才回來,所以一個超過
|
||||
// STALE_LOCK_SECONDS 還在的 index.lock 一定是上一次跑到一半被砍掉留下的殘骸。
|
||||
// 不自己清的話整區會一直失敗,而 sleeper 被隔離 hook 擋著、連自己的鎖都刪不掉。
|
||||
const STALE_LOCK_SECONDS = 30;
|
||||
|
||||
export function clearStaleIndexLock(dir) {
|
||||
const lock = path.join(dir, ".git", "index.lock");
|
||||
let stat;
|
||||
try {
|
||||
stat = fs.statSync(lock);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
const ageSeconds = (Date.now() - stat.mtimeMs) / 1000;
|
||||
if (ageSeconds < STALE_LOCK_SECONDS) return null;
|
||||
try {
|
||||
fs.rmSync(lock);
|
||||
} catch (err) {
|
||||
return { cleared: false, ageSeconds, reason: String(err.message || err) };
|
||||
}
|
||||
return { cleared: true, ageSeconds };
|
||||
}
|
||||
|
||||
/** 確保 `<persona>/.sync/<area>/` 是該區的 clone;空存取庫也能處理。 */
|
||||
export function ensureClone(slug, area, url) {
|
||||
const dir = syncDir(slug, area);
|
||||
@@ -546,6 +569,7 @@ export async function pushArea(slug, area, { message = "", code = null, owner =
|
||||
pl.writeText(path.join(dir, "Icon.md"), wikiIconPage(slug, theCode));
|
||||
}
|
||||
const staged = stageArea(slug, area, dir);
|
||||
clearStaleIndexLock(dir);
|
||||
gitOrThrow(["add", "-A"], dir, "git add");
|
||||
const dirty = git(["diff", "--cached", "--quiet"], dir);
|
||||
if (dirty.ok) {
|
||||
@@ -567,6 +591,7 @@ export async function pushArea(slug, area, { message = "", code = null, owner =
|
||||
pl.writeText(path.join(dir, "Home.md"), wikiHome(slug, theCode));
|
||||
pl.writeText(path.join(dir, "Icon.md"), wikiIconPage(slug, theCode));
|
||||
}
|
||||
clearStaleIndexLock(dir);
|
||||
git(["add", "-A"], dir);
|
||||
if (!git(["diff", "--cached", "--quiet"], dir).ok) {
|
||||
git(["commit", "-q", "-m", `${message || "sync"}(與遠端合併後重推)`], dir);
|
||||
@@ -596,6 +621,7 @@ export async function pullArea(slug, area, { code = null, owner = null, force =
|
||||
const { host } = giteaEnv();
|
||||
const dir = ensureClone(slug, area, repoUrl(host, theOwner, theCode, area));
|
||||
stageArea(slug, area, dir); // 先把本機現況放進 clone,才看得出本機動過什麼
|
||||
clearStaleIndexLock(dir);
|
||||
const localChanged = new Set(
|
||||
git(["status", "--porcelain"], dir)
|
||||
.stdout.split("\n")
|
||||
|
||||
@@ -159,6 +159,13 @@ function requireMember(slug, sessionId, asGuest = false) {
|
||||
if (!slug) die("未指定人格,且本 session 沒有載入人格。");
|
||||
if (!pl.personaExists(slug)) die(`人格 \`${slug}\` 不存在。`);
|
||||
const data = pl.loadSession(sessionId);
|
||||
// sleeper 讀自己的資料:`requireOwner` 早就放它寫了,讀反而被擋在外面——
|
||||
// 少了這個分支,它就得在看不到 brief/recall 的情況下決定要固化什麼。
|
||||
if (CURRENT_FLAGS["as-sleeper"]) {
|
||||
if (asGuest) die("`--as-sleeper` 與 `--as-guest` 不能一起用(那是兩種不同的身分)。");
|
||||
sleeperAccess(slug, sessionId, str(CURRENT_FLAGS["agent-id"]));
|
||||
return [data, "sleeper"];
|
||||
}
|
||||
if (data.host === slug) {
|
||||
if (asGuest) die(`\`${slug}\` 是本 session 的 host 人格,不需要也不得使用 \`--as-guest\`。`);
|
||||
pl.heartbeatLock(slug, sessionId);
|
||||
|
||||
Reference in New Issue
Block a user