fix(collector): daemon 不再改用戶版控中的檔案,接 repo 只讀整理好的 wiki(#105/#104)
## #105 daemon 會改用戶的檔案(不可逆,先修) 2026-08-14 21:45 實撞:InkStoneCo 在看守清單裡,daemon 回報「已把 1 張舊卡片歸位」, 實際把 system-dev/wiki/cards/autonomy/ 整個子目錄壓平改名,16 個版控中的檔案變成刪除。 真兇是 MigrateCardNames 的一句假設:「那兩個目錄從頭到尾只有 daemon 會寫」。 那句話在 vault 上成立,在 repo 上不成立——system-dev/wiki/cards/ 是 template 的規約 路徑,而 template 就是要裝進開發者自己的 repo,那裡本來就有人家自己的檔案。 - 新增 repoguard.go:`.git` 判準(含 linked worktree),與 #104 共用同一個判準源 - 版控中的資料夾一個檔都不自動動;改成記帳+講給使用者聽,出口是 collector tidy --apply - Blocked 只算「確定是我們寫的」(帶 arcrun- 標記)——報使用者自己的卡等於發假訊息 - 落卡與工作區改走 .arcrun-rag/(與 vault 同待遇),自帶 .gitignore(*) 讓它對 git 隱形 - 非版控資料夾(含 vault)行為與 #60 第三輪完全一致,不推翻前兩輪的成果 ## #104 接上開發 repo 會把上萬個原始檔排進佇列 leo 的規格:「它要辨識這個庫已經有 wiki,那就直接 ingest 了」「只有文件要讀,程式碼不用讀」。 - 新增 ingestplan.go:掃描前先問「這個資料夾是什麼」——all/curated-wiki/docs-only - 排除靠路徑身分不靠副檔名:依賴、建置產物、templatefs 範本、linked worktree、巢狀子 repo - 策略與擋掉的數字經 TriggerPayload.Plan 走進 status.json,CLI 走 stderr(排除規則要看得見) - 子專案自己的 wiki 刻意不收,但一定列出來讓使用者知道去哪裡找 - 與 TemplateOwns(把 system-dev/ 整棵當開發用的)的對撞用身分化解,不拿掉任一條: 我們代裝的資料夾沒有 .git(舊規則照舊),他自己的 repo 有(收那份 wiki) ## 實測 #105:舊版 daemon 對真 repo 跑一輪 → git status 32 行(16 個 D + 16 個 ??,與 21:45 撞到的一字不差);新版跑同一輪 → 0 行。另有真 git init 的端到端測試。 #104:造出 leo 那棵樹的形狀(InkStoneCo + products/arcrun-rag + 3 份出貨 worktree, 967 個文件檔)→ 舊版送 91 個檔(多收的全是 -wt*ship/ 裡的 templatefs 範本、 benchmark 結果、docs-site 產物),新版送 32 個,全部落在 system-dev/wiki/ 底下。 MachineMark 規約新增唯一例外 IsMachineOwnedRel:.arcrun-rag/ 底下的檔以目錄名為標記 (.gitignore 的檔名是 git 定的,改不得)。迴歸網判準同步換過去。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UUwsLkFEGN8496bZTqhjFq
This commit is contained in:
@@ -949,13 +949,30 @@ func runDirectOnceRoot(cfg *DirectConfig, root string, dryRun bool, qs *quotaSta
|
||||
// arcrun-rag#60:把上一版 daemon 落下的舊卡歸位——沒帶 arcrun- 標記的改名,
|
||||
// 位置不對的(第三輪:監看根在筆記庫裡,卡卻落在看得見的 system-dev/wiki/cards/)搬進隱藏目錄。
|
||||
// 自動跑而不是叫人下指令——leo 的紅線:「他不該為了保護自己的筆記去學新選項」。
|
||||
// 只碰卡片產物區(那整個目錄只有我們會寫),目標已存在就跳過、永不刪檔,見 tidy.go。
|
||||
// 目標已存在就跳過、永不刪檔,見 tidy.go。
|
||||
// template 殘留不在這裡處理:那要人確認「這是不是你自己的 repo」,走 `collector tidy`。
|
||||
//
|
||||
// 🔴 arcrun-rag#105:監看根在版控裡時**一個檔都不動**(MigrateCardNames 自己擋),
|
||||
// 這裡負責把「沒動、以及為什麼沒動」講出來——靜默跳過等於讓使用者猜。
|
||||
if !dryRun {
|
||||
if n := MigrateCardNames(absRoot); n > 0 {
|
||||
// daemon 自己的工作區先自我忽略,之後落卡/收容/身分標記檔才不會弄髒使用者的
|
||||
// git status(#105 驗收條件就是「跑一輪 git status 必須乾淨」)。
|
||||
EnsureWorkspaceIgnored(absRoot)
|
||||
mig := MigrateCardNames(absRoot)
|
||||
if mig.Moved > 0 {
|
||||
results = append(results, DirectResult{
|
||||
Type: "warning", Path: absRoot, Status: "skipped",
|
||||
Error: fmt.Sprintf("已把 %d 張舊卡片歸位(加上 arcrun- 前綴/搬離筆記軟體看得到的位置)", n),
|
||||
Error: fmt.Sprintf("已把 %d 張舊卡片歸位(加上 arcrun- 前綴/搬離筆記軟體看得到的位置)", mig.Moved),
|
||||
})
|
||||
}
|
||||
if mig.Blocked > 0 {
|
||||
results = append(results, DirectResult{
|
||||
Type: "warning", Path: absRoot, Status: "skipped",
|
||||
Error: fmt.Sprintf(
|
||||
"這個資料夾在版本控制裡(%s),所以有 %d 個舊卡片我沒有自動整理"+
|
||||
"——改名搬移會變成你 git status 上的刪除。要整理請自己跑:"+
|
||||
"collector tidy --folder %s(先看清單,確認後再加 --apply)",
|
||||
mig.RepoRoot, mig.Blocked, absRoot),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -973,6 +990,23 @@ func runDirectOnceRoot(cfg *DirectConfig, root string, dryRun bool, qs *quotaSta
|
||||
for k, v := range m.Entries {
|
||||
preScanEntries[k] = v
|
||||
}
|
||||
// arcrun-rag#104:走訪之前先問「這個資料夾是什麼」——是開發專案就只讀它整理好的
|
||||
// wiki(沒有 wiki 才退到文件區),是一般資料夾/筆記庫才全收。見 ingestplan.go。
|
||||
plan := PlanIngest(absRoot)
|
||||
|
||||
// 🔴 #104 的一個必然後果:curated-wiki 模式要收的正是 `system-dev/wiki/`,
|
||||
// 而 daemon-beta task 2 為了「template 代裝的產物區不要被當原稿」把整個
|
||||
// `system-dev` 列進 SkipDirNames——兩者直接對撞,不處理的話這個模式會一個檔都收不到。
|
||||
//
|
||||
// 解法不是拿掉那條保護,是**看它保護的是誰**:task 2 擋的是「**我們自己**代裝進
|
||||
// 使用者資料夾的 template 產物」;curated-wiki 模式的前提則是「**使用者自己**
|
||||
// 在他的 repo 裡整理好的知識庫」——同一個路徑,兩種身分,由 PlanIngest 分辨
|
||||
// (他的 repo 有 `.git`,我們代裝的資料夾沒有)。所以只在 curated-wiki 模式解除。
|
||||
skipDirNames := map[string]bool{"system-dev": true}
|
||||
if plan.Mode == IngestCuratedWiki && strings.HasPrefix(plan.WikiRelDir, "system-dev/") {
|
||||
skipDirNames = map[string]bool{}
|
||||
}
|
||||
|
||||
payload, err := Scan(absRoot, m, ScanOptions{
|
||||
MaxRemovedRatio: cfg.MaxRemoved,
|
||||
SkipPaths: map[string]bool{
|
||||
@@ -981,7 +1015,8 @@ func runDirectOnceRoot(cfg *DirectConfig, root string, dryRun bool, qs *quotaSta
|
||||
filepath.Join(absRoot, "CLAUDE.md"): true,
|
||||
},
|
||||
// template 代裝後 system-dev/(wiki 產物區)不得被當原稿掃進 ingest(task 2)
|
||||
SkipDirNames: map[string]bool{"system-dev": true},
|
||||
SkipDirNames: skipDirNames,
|
||||
Plan: plan,
|
||||
})
|
||||
if err != nil {
|
||||
return append(results, DirectResult{Status: "failed", Error: err.Error()}), 1, nil, rootProgress{}
|
||||
|
||||
Reference in New Issue
Block a user