dsh-fs-ctime-guard:緩解 macOS 上 FS_STALE_VERSION 的 ctime 誤報

前言

DeepSeek Harness(DSH)在本地改文件時,會用 fs-local 記錄的版本 token 做樂觀併發校驗。token 由 dev:ino:size:mtimeNs:ctimeNs 拼成;只要寫入後 token 與下一次 edit / write 前讀到的不一致,就會報 FS_STALE_VERSION,並出現類似「file changed since it was read — re-read the file, then retry」的提示。

在 macOS 上,Spotlight(mds)、TCC 等常在不改內容的前提下寫入 com.apple.macl、com.apple.provenance 等擴展屬性,觀測上往往只刷新 ctime,inode、size、mtime 不變。寫入後立刻被 DSH 記下的 token,幾毫秒到兩秒內就可能因 ctime 跳變而對不上,下一次編輯被誤攔。表現上像「每次改完都必須先 read 一遍」,容易誤判成工具用法問題。上游討論見 deepseek-harness #3111。

下面介紹社區插件 dsh-fs-ctime-guard(維護者 wx-yss,目錄分類 admin-security),在 intent 層對「僅 ctime 變化」做有條件放行,減輕上述誤報。

這是什麼

dsh-fs-ctime-guard 是 DSH 的 Cordis 插件包(npm 名 dsh-fs-ctime-guard,當前資料中版本 0.1.2,MIT 許可證)。它不修改 DSH 安裝目錄,通過 bundle patch 掛到 fs/edit-intent 與 fs/write-intent 瀑布最外層(prepend),在 DSH 原有 fs-observation-policy 判定之前介入。

定位一句話:讓文件版本守衛在除 ctime 外其餘字段未變時,不把純元數據噪聲當成內容衝突。

核心功能

插件在每次 edit / write(含 str_replace_editor 等走同一 intent 的路徑)上的流程如下:

  1. 先 await next(),拿到 DSH 原有守衛的判定;
  2. 對當前路徑再 stat 一次;
  3. 若守衛認爲 stale,且當前 stat 與 token 對比後只有 ctime 段不同 → 用當前版本覆蓋守衛版本,放行;
  4. 若 mtime、size、inode、設備變化,文件不存在,版本串不符合 dev:ino:size:mtimeNs:ctimeNs 五段十進制協議,或 stat 失敗、用戶取消等 → 原樣返回原有判定,不額外放寬。

瀑布順序(資料原文示意):

      edit/write 工具
             │
   fs/edit-intent 瀑布
     ├─ fs-ctime-guard(prepend)  ← 本插件
     ├─ fs-observation-policy      ← DSH 原有守衛
     └─ fallback

fs/write-intent 同樣註冊,因此 write 覆蓋寫入也會應用同一套「只忽略 ctime」邏輯。

安裝與啓用

插件隨 DSH 的 profile bundle 安裝,資料中的官方命令爲:

dsh plugin --profile web add dsh-fs-ctime-guard
ds

dsh plugin add 會把包寫入 profile 的 dsh.profile.bundles,重啓 DSH 進程(ds)後生效。

本地開發可用 link 安裝(路徑換成你的 clone 目錄):

dsh plugin --profile web add link:/path/to/dsh-fs-ctime-guard
ds

卸載:

dsh plugin --profile web remove dsh-fs-ctime-guard
ds

安裝前建議自行查看 GitHub 倉庫 源碼與 MIT 許可證;插件以當前 DSH 進程權限運行,與其它 DSH 插件相同。

驗證與測試

維護者在 README 中給出兩類測試命令:

npm test                   # 16 個單元測試
npm run test:integration   # 真實 cordis + dsh-fs-local + fs-observation-policy

集成測試覆蓋要點包括:修復前僅 chmod 改 ctime 時 edit 會報 FS_STALE_VERSION;修復後同場景可通過;真實內容變更仍會報 stale;write 在忽略 ctime 噪聲的同時仍拒絕真實修改。集成測試默認從 ~/.npm/_npx 發現 DSH,也可用環境變量 DSH_NPX_ROOT 指定 node_modules;找不到安裝時會跳過(退出碼 0),不宜當作發佈門禁。

適用場景與注意

適合誰: 主要在 macOS 上用 DSH 做本地文件編輯,且頻繁遇到寫入後立刻 FS_STALE_VERSION、被迫 read 再試的場景(尤其 ~/Documents 等易被 Spotlight 觸達的路徑)。

務必接受的取捨(資料中的「邊界與已知弱化」):

  • 放行依據是「除 ctime 外四字段相同」,不等於內容一定相同。若另一進程同尺寸原地改寫並用 utimes 精確恢復 mtime,token 可能只差 ctime,插件會放行,edit / write 可能基於過期視圖寫入。這與上游 #3111 方案 1 的取捨一致,集成測試中有「已知弱化」用例固定該行爲。
  • chmod、ACL、屬主、部分有語義的 xattr 變化也可能只動 ctime,插件會放行;DSH 原生寫入本就不覆蓋這些語義。
  • DSH 寫入並非跨進程原子 CAS;intent 判定與 provider 之間仍有窗口,ctime 若在兩次 stat 間再變,仍可能偶發一次 stale,重新 read 即可。
  • prepend 註冊順序在 HMR/重載後建議迴歸;依賴當前 fs-local 版本串格式,格式或 provider 變化時插件會保守退回嚴格判定。
  • 上游若在 #3111 落地等價修復,可直接卸載本插件。

當前維護者選擇(README 記載 2026-10-31):默認同時放寬 edit 與 write;若日後只想保留 edit 放寬,可改 bundle 不再註冊 fs/write-intent。

參考鏈接

DSH 生態強調「一切皆插件」;SkillHub 等社區目錄爲獨立站點,與 DeepSeek / 幻方無官方從屬關係。本插件屬於 admin-security 分類,用於在明確接受弱化前提下,減少 macOS ctime 元數據噪聲帶來的誤報。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜