f10d10747c
事故:daemon 完全沒有 vault 辨識,把萃取卡片寫進使用者的 Logseq vault (system-dev/wiki/cards/ 對一般資料夾沒事,對 vault=憑空多出頁面, 2026-08-10 leo 實撞:25 個簡體字卡片污染 ~/Documents/KB)。 修法(照票上指示搬現成的,不重新設計): - collector/vault.go:DetectVaultType/IsVault,判準逐條抄自 system-dev-template/scripts/install.sh:209-221(先查 logseq/、再查 .obsidian/)。 已用同一批 fixture 資料夾跑過 install.sh 與這支 Go 版,五種情境(logseq/ obsidian/一般資料夾/兩者皆有/空資料夾)IS_VAULT 判斷逐一比對一致。 - extract.go:cardsRelDirFor() 依 IsVault 決定卡片相對路徑——非 vault 不變 (system-dev/wiki/cards/),vault 改落 .arcrun-rag/wiki/cards/(點開頭隱藏 目錄,Logseq/Obsidian 預設不掃描,跟 daemon 自己 scan.go 的隱藏目錄跳過規則 一致)。extract_workersai.go/extract_gemma.go/direct.go 的落卡與下架清除 都改用這個函式,三處對同一個 absRoot 保證同一個答案。 - safewrite.go:safeWriteCard() 取代兩處無條件 os.WriteFile——目標已存在且 內容不同就先備份成 <dest>.bak-<unixnano> 才覆寫;內容相同則不動(不產生 垃圾備份);備份失敗就整個中止,不無聲蓋掉使用者機器上已有的東西。 驗證(見 PR/commit 說明附的實測輸出): - 用 fake Logseq vault fixture 重現舊行為(卡片確實落在 system-dev/wiki/cards/), 再用同一份 fixture 驗新行為(卡片改落 .arcrun-rag/wiki/cards/,vault 根目錄 非隱藏 .md 數量不變、journals/ 原稿位元不動)。 - 故意放同名既有卡片,跑完既有內容被備份、新內容確實寫入,未無聲遺失。 - 非 vault 既有測試(extract_gemma_test.go 原有三支)全數不動照過,確認 一般資料夾行為零改變。 範圍外(留給下一輪):ExtractWithClaude(extract.go 的 claude 路)與其 templatefs/.claude/commands/rag-extract-file.md 技能檔仍硬寫 system-dev/wiki/cards/, 但這條路目前在 direct.go 的 RunDirectOnce 是不支援狀態(cfg.Extractor 只認 workers-ai/gemma),非本次事故的作用路徑,故未動。 不影響:leo 機器上的 daemon(未重啟、未重新打包);此修復要生效還要 ①總管審過併 main ②重新打包桌面版裝上他機器 ③總管用新二進位實測, 三件缺一不可(見 issue #60 leo 的重啟條件)。
55 lines
2.2 KiB
Go
55 lines
2.2 KiB
Go
// vault.go — 偵測監看根是不是筆記軟體的 vault(Logseq/Obsidian)。
|
||
//
|
||
// 🔴 為什麼有這支檔(arcrun-rag#60,2026-08-10 leo 實撞):
|
||
//
|
||
// daemon 把萃取產物(知識卡)寫進了使用者的 Logseq vault,讓 vault 平白多出一堆
|
||
// 機器寫的頁面——Logseq/Obsidian 這種筆記軟體,vault 底下每個 .md 都會變成一頁,
|
||
// 一般資料夾則不會。daemon 原本沒有這層辨識(改寫成 Go 版時沒把舊邏輯帶過來)。
|
||
//
|
||
// 判準**逐條抄自** system-dev-template/scripts/install.sh:209-221(「偵測 vault 類型」段):
|
||
//
|
||
// if [ -d "logseq" ]; then VAULT_TYPE="logseq"; IS_VAULT="yes"
|
||
// elif [ -d ".obsidian" ]; then VAULT_TYPE="obsidian"; IS_VAULT="yes"
|
||
// else VAULT_TYPE="docs" (不是 vault)
|
||
//
|
||
// 🔴 同一個資料夾,安裝器(bash)與這支 daemon(Go)必須得出同一個答案——兩邊分歧本身
|
||
// 就是 bug。之後任一邊改判準,要同步改另一邊(或把判準抽成單一真相源腳本)。
|
||
package collector
|
||
|
||
import (
|
||
"os"
|
||
"path/filepath"
|
||
)
|
||
|
||
// VaultType 是偵測結果的字串常數,值刻意跟 install.sh 的 VAULT_TYPE 用同一組字。
|
||
type VaultType string
|
||
|
||
const (
|
||
VaultNone VaultType = "" // 一般資料夾(install.sh 裡叫 "docs")
|
||
VaultLogseq VaultType = "logseq"
|
||
VaultObsidian VaultType = "obsidian"
|
||
)
|
||
|
||
// DetectVaultType 判斷 absRoot 是不是筆記軟體的 vault,回傳偵測到的類型。
|
||
// 只看 absRoot 這一層(不遞迴往上找),跟 install.sh 在目標資料夾根目錄跑 `[ -d "logseq" ]`
|
||
// 語意一致。順序也刻意跟 install.sh 一致:先查 logseq/,再查 .obsidian/。
|
||
func DetectVaultType(absRoot string) VaultType {
|
||
if isDir(filepath.Join(absRoot, "logseq")) {
|
||
return VaultLogseq
|
||
}
|
||
if isDir(filepath.Join(absRoot, ".obsidian")) {
|
||
return VaultObsidian
|
||
}
|
||
return VaultNone
|
||
}
|
||
|
||
// IsVault 是 DetectVaultType 的布林簡寫,供只關心「是不是 vault」的呼叫端用。
|
||
func IsVault(absRoot string) bool {
|
||
return DetectVaultType(absRoot) != VaultNone
|
||
}
|
||
|
||
func isDir(p string) bool {
|
||
info, err := os.Stat(p)
|
||
return err == nil && info.IsDir()
|
||
}
|