fix(collector): 排除判準不看版控、不誤殺筆記庫、剪掉什麼講得出來(arcrun-rag#104)
票上寫的真兇是錯的。`direct.go` 那一行 `skipDirNames{"system-dev"}` 不是唯一的
排除清單——同一個 Scan 呼叫下面幾行就是 `Plan: plan`,#104 的清單一直都接著。
拿 leo 真實的 `pms` 唯讀跑一輪現行 main:策略 docs-only、送 9 個檔、node_modules 零個。
他 08-16 看到 undici 文件,是因為手上的 daemon 是 v0.18.27,而修法 cc6e500 要到
v0.18.28(08-16 16:13,7f379d0)才被戳版號——那支 commit 自己就寫著
「changelog 停在 v0.18.27,而 collector/ 早已往前走 30 個檔(…cc6e500…)」。
但那個誤判之所以會發生,是因為底下有四個真的缺陷,這一版把它們一起修掉:
① 兩張表分居兩處 ⇒ 讀源碼的人只看得到一張。
`system-dev` 的保護搬進 IngestPlan(templateOwnedDirNames),
direct.go 不再手捏第二張清單。判準只剩一個地方。
② 排除規則生不生效,取決於呼叫端記不記得傳 Plan。
改成 Scan 自己算(Mode == "" ⇒ PlanIngest)。「忘了接」這個失敗模式不存在了。
③ 一張大表把「沒有人會這樣命名」與「這是普通英文字」混在一起,於是**誤殺**。
實測:一般筆記庫 8 份筆記只送出 1 份(build/樂高作品集、out/外出旅遊、
vendor/廠商聯絡簿…全被當成建置產物),而且回報「擋掉 0 個」。
拆成三種理由,強度不同、要求的佐證也不同:
① 使用者的 .gitignore 說的(新增 ignorerules.go,git 語法的安全子集)
② 名字本身就不是人話(node_modules、__pycache__…)——無條件
③ 泛用名(build/dist/out/vendor…)——**旁邊真的擺著專案檔才算**
🔴 判準一律不看 `.git`(leo 2026-08-16:「你不需要判斷有沒有 git,
我的 KB 筆記庫也有 git,是否用 github/gitea 追蹤完全沒意義」)。
`.gitignore` 只讀內容當線索,不拿存在當門檻。
順帶:鎖定檔(pnpm-lock.yaml…)不是知識——`.yaml` 進白名單後它變成了「知識」。
④ 「排除規則要看得見」只做了一半:整棵剪掉的子樹一個都沒數(pms 實測回報 0),
而且 Plan/ExcludedByPlan 只有 CLI 讀,daemon(使用者真正走的那條路)拿到就丟。
新增 ExcludedDirs(路徑+人話理由)+ SyncStatus.FolderPlans 寫進 status.json。
實測(唯讀跑 leo 的 `/Users/youlinhsieh/Documents/tech_projects/pms`):
139 個文件檔 → 送出 7 個,全是他自己的 README/docs;
6 個資料夾整棵跳過,每個都講得出理由;node_modules 與授權條款 0 個。
測試:collector 全綠(新增 12 案,含「裸呼叫 Scan 也必須排除別人的套件」、
「不准再有第二張排除清單」的源碼層守門、筆記庫不誤殺、同名看旁邊擺什麼決定);
arcrun-app 全綠。未出貨、未推 main。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -632,6 +632,10 @@ func RunDirectOnce(cfg *DirectConfig, dryRun bool) ([]DirectResult, int, *Trigge
|
||||
skippedOther := 0
|
||||
var skippedOtherNames []string
|
||||
|
||||
// arcrun-rag#104:每個看守資料夾這一輪的收檔策略與「少收了什麼」。
|
||||
// key=資料夾路徑(同一個根被多帳號看守時後寫覆蓋——策略只看資料夾,與帳號無關)。
|
||||
folderPlans := map[string]FolderPlanStatus{}
|
||||
|
||||
// t210:跨帳號、跨資料夾累加的總量進度(見 rootProgress 註解)。
|
||||
var totalProgress SyncProgress
|
||||
var stuckReasons []string
|
||||
@@ -732,6 +736,16 @@ func RunDirectOnce(cfg *DirectConfig, dryRun bool) ([]DirectResult, int, *Trigge
|
||||
skippedOther += p.SkippedOther
|
||||
// 同理,每一根的檔名都要收(上限在寫進 status 時才裁)。
|
||||
skippedOtherNames = append(skippedOtherNames, p.SkippedOtherNames...)
|
||||
// #104:這一根用了什麼策略、少收了什麼 —— 以前到這裡就被丟掉了
|
||||
// (只有 CLI 的 stderr 講得出來,App 走的這條路一個字都不說)。
|
||||
folderPlans[root] = FolderPlanStatus{
|
||||
Mode: string(p.Plan.Mode),
|
||||
Reason: p.Plan.Reason,
|
||||
ExcludedFiles: p.ExcludedByPlan,
|
||||
ExcludedDirs: p.ExcludedDirs,
|
||||
ExcludedDirCount: p.ExcludedDirCount,
|
||||
OtherWikiDirs: p.Plan.OtherWikiDirs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -808,6 +822,10 @@ func RunDirectOnce(cfg *DirectConfig, dryRun bool) ([]DirectResult, int, *Trigge
|
||||
skippedOtherNames = skippedOtherNames[:maxOtherNames]
|
||||
}
|
||||
st.SkippedOtherNames = skippedOtherNames
|
||||
// #104:收檔策略與被排除的東西 —— 使用者要知道「有幾千個檔沒被收、為什麼」。
|
||||
if len(folderPlans) > 0 {
|
||||
st.FolderPlans = folderPlans
|
||||
}
|
||||
st.SkippedDocCount = len(skippedSeen)
|
||||
for _, sf := range skippedSeen {
|
||||
st.SkippedDocs = append(st.SkippedDocs, sf)
|
||||
@@ -1220,19 +1238,13 @@ func runDirectOnceRoot(cfg *DirectConfig, root string, dryRun bool, qs *quotaSta
|
||||
// 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{}
|
||||
}
|
||||
|
||||
// 🔴 2026-08-16:這裡以前還手捏了**第二張**排除表
|
||||
//(`skipDirNames := {"system-dev": true}`,daemon-beta task 2 的 template 產物區保護),
|
||||
// 而 #104 的排除清單住在 ingestplan.go。**兩張表分居兩處** ⇒ 2026-08-16 讀源碼的人
|
||||
// 只看到這一張,就把「排除清單根本沒接上」寫成了真兇——實際上兩張都接上了
|
||||
//(下面那行 `Plan: plan` 就是)。誤判本身正是「同一件事有兩個地方管」的代價。
|
||||
// ⇒ 那條保護已搬進 IngestPlan(templateOwnedDirNames),連同「curated-wiki 模式
|
||||
// 要收的正是 system-dev/wiki」這個例外一起 ⇒ **判準只剩一個地方,沒有第二張表可漏看。**
|
||||
payload, err := Scan(absRoot, m, ScanOptions{
|
||||
MaxRemovedRatio: cfg.MaxRemoved,
|
||||
SkipPaths: map[string]bool{
|
||||
@@ -1240,9 +1252,7 @@ func runDirectOnceRoot(cfg *DirectConfig, root string, dryRun bool, qs *quotaSta
|
||||
// template 代裝的根層 CLAUDE.md 是 CC 設定檔,永遠不是用戶知識(task 2)
|
||||
filepath.Join(absRoot, "CLAUDE.md"): true,
|
||||
},
|
||||
// template 代裝後 system-dev/(wiki 產物區)不得被當原稿掃進 ingest(task 2)
|
||||
SkipDirNames: skipDirNames,
|
||||
Plan: plan,
|
||||
Plan: plan,
|
||||
})
|
||||
if err != nil {
|
||||
return append(results, DirectResult{Status: "failed", Error: err.Error()}), 1, nil, rootProgress{}
|
||||
|
||||
Reference in New Issue
Block a user