不再把開發用 template 塞進使用者資料夾;上游錯誤不再被退避訊息吞掉
## leo 三條裁決,逐條落實
① 「**別人的錯誤一律要顯示給用戶看,不然就會變成我的錯誤,導致客服**」
→ manifest entry 新增 `LastError`(存**原文**),退避訊息改成
「上次失敗(第 N 次),58m 後重試|**原因:**<上游原文>」。
成功後清空,不留舊錯誤嚇人。
測試 `TestUpstreamErrorSurvivesBackoff` 從上游錯誤字串出發,
驗它活到給使用者看的那句話;反向驗證拿掉即紅。
② 「**拿來開發一般人用不到的根本別安裝**」
→ daemon 不再代裝 system-dev template(`CLAUDE.md`/`scripts/`/`system-dev/`,37 檔)。
兩層傷害:把人家資料夾弄亂 + 那些檔被當知識吃進去
⇒ 知識庫長出 `kb`/`t195-watch` 這種不是使用者內容的庫(leo 實撞)。
`template-install` 子命令保留,開發者情境不受影響。
③ 「**它也不能只看隱藏檔內,因為 template 在我所有的 repo 裡不是隱藏的**」
→ 排除規則用**路徑身分**(`TemplateOwns()`,來源是內嵌 templatefs 的實際路徑
+ `system-dev/`・`scripts/` 目錄前綴),**不是**「有沒有以點開頭」。
測試刻意把 template 檔放成**不隱藏**(就像 leo 的 repo),驗它仍被排除;反向驗證即紅。
這些檔也不計進「有 N 個檔案沒有被整理」——那欄是給使用者看他自己的檔案的。
## 順帶修 leo 截圖上的重複
「有 2 個檔案沒有被整理」底下 `scripts/sdd-active-check.sh` 出現兩次
——多帳號時同一個資料夾被掃多輪、每輪都 append。已去重。
## 殘項(誠實)
App 端失敗卡還沒接上這條線 ⇒ **畫面尚未真的顯示原因**。未打包、未送達。
This commit is contained in:
@@ -648,6 +648,18 @@ func RunDirectOnce(cfg *DirectConfig, dryRun bool) ([]DirectResult, int, *Trigge
|
||||
// 排序=畫面每輪穩定(map 迭代順序隨機,不排的話清單會自己跳動)。
|
||||
st.SkippedOtherCount = skippedOther
|
||||
// 少量時點名(maxOtherNames 個以內)——leo 08-06 封測:只報「1 個」等於沒說。
|
||||
// 🔴 leo 2026-08-06 截圖:「有 2 個檔案沒有被整理」底下同一個檔名出現**兩次**。
|
||||
// 真兇:多帳號時同一個資料夾會被掃很多輪,每輪都把檔名 append 進來。
|
||||
// ⇒ 去重(順帶讓總數與清單一致,不然使用者會覺得我們在亂數)。
|
||||
seen := map[string]bool{}
|
||||
uniq := skippedOtherNames[:0]
|
||||
for _, n := range skippedOtherNames {
|
||||
if !seen[n] {
|
||||
seen[n] = true
|
||||
uniq = append(uniq, n)
|
||||
}
|
||||
}
|
||||
skippedOtherNames = uniq
|
||||
sort.Strings(skippedOtherNames)
|
||||
if len(skippedOtherNames) > maxOtherNames {
|
||||
skippedOtherNames = skippedOtherNames[:maxOtherNames]
|
||||
@@ -744,13 +756,24 @@ func retrySkipReason(m *Manifest, path string, now int64) string {
|
||||
return "暫時跳過"
|
||||
}
|
||||
if e.FailCount >= MaxFailBeforeSkip {
|
||||
return fmt.Sprintf("連續失敗 %d 次,已暫停自動重試(改檔或按「立刻同步」會再試)", e.FailCount)
|
||||
msg := fmt.Sprintf("連續失敗 %d 次,已暫停自動重試(改檔或按「立刻同步」會再試)", e.FailCount)
|
||||
if e.LastError != "" {
|
||||
msg += "|原因:" + e.LastError
|
||||
}
|
||||
return msg
|
||||
}
|
||||
wait := e.NextRetry - now
|
||||
if wait < 0 {
|
||||
wait = 0
|
||||
}
|
||||
return fmt.Sprintf("上次失敗(第 %d 次),%s 後重試", e.FailCount, (time.Duration(wait) * time.Second).String())
|
||||
msg := fmt.Sprintf("上次失敗(第 %d 次),%s 後重試", e.FailCount, (time.Duration(wait) * time.Second).String())
|
||||
// 🔴 leo 2026-08-06:「別人的錯誤一律要顯示給用戶看,不然就會變成我的錯誤,導致客服」。
|
||||
// 只講「幾分鐘後重試」等於把上游的錯(Cloudflare 額度用完、檔案本身沒文字)
|
||||
// 藏起來 ⇒ 使用者只看得到我們在失敗。真因要一路帶到畫面上。
|
||||
if e.LastError != "" {
|
||||
msg += "|原因:" + e.LastError
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
// accountsConnected 回答「這份設定連得上知識庫嗎」——**根層有 或 任一帳號有**。
|
||||
@@ -822,7 +845,7 @@ func runDirectOnceRoot(cfg *DirectConfig, root string, dryRun bool) ([]DirectRes
|
||||
content, rerr := os.ReadFile(full)
|
||||
if rerr != nil {
|
||||
res.Status, res.Error = "failed", "讀檔失敗:"+rerr.Error()
|
||||
m.MarkFailed(ev.Path, now) // t195:讀不到的檔也退避(權限/被鎖/壞掉的外接碟)
|
||||
m.MarkFailed(ev.Path, now, res.Error) // t195:讀不到的檔也退避(權限/被鎖/壞掉的外接碟)
|
||||
results = append(results, res)
|
||||
exit = 1
|
||||
continue
|
||||
@@ -858,7 +881,7 @@ func runDirectOnceRoot(cfg *DirectConfig, root string, dryRun bool) ([]DirectRes
|
||||
res.Status, res.Error = "failed", "本地萃取失敗:"+xerr.Error()
|
||||
// t195:萃取階段失敗同樣要記退避。**這條路徑比上傳更早**,
|
||||
// 漏記的話(連不上知識庫、金鑰壞、模型錯)照樣每輪重撞。
|
||||
m.MarkFailed(ev.Path, now)
|
||||
m.MarkFailed(ev.Path, now, res.Error)
|
||||
results = append(results, res)
|
||||
exit = 1
|
||||
continue
|
||||
@@ -894,7 +917,7 @@ func runDirectOnceRoot(cfg *DirectConfig, root string, dryRun bool) ([]DirectRes
|
||||
} else {
|
||||
// t195:記下失敗並排定退避,否則下輪又把它當新檔重試
|
||||
//(實撞:1387 輪 × 11 小時全在撞同一面 401 的牆,還拖住整個佇列)。
|
||||
m.MarkFailed(ev.Path, now)
|
||||
m.MarkFailed(ev.Path, now, res.Error)
|
||||
exit = 1
|
||||
}
|
||||
results = append(results, res)
|
||||
@@ -1027,20 +1050,19 @@ func runDirect(args []string) int {
|
||||
return exit
|
||||
}
|
||||
|
||||
// 四步定稿第 1 步:daemon 代裝 template——常駐看守前確保每根都鋪好(冪等,不覆寫既有檔)。
|
||||
// dry-run/--once 測試情境不代裝(不留副作用),由 template-install 子命令顯式做。
|
||||
if !*once && !*dryRun {
|
||||
for _, root := range cfg.Folders() {
|
||||
if TemplateInstalled(root) {
|
||||
continue
|
||||
}
|
||||
if res, ierr := InstallTemplate(root); ierr != nil {
|
||||
fmt.Fprintf(os.Stderr, "template 代裝失敗(%s):%v\n", root, ierr)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "template v%s 已鋪進 %s(新 %d 檔)\n", res.Version, root, len(res.Installed))
|
||||
}
|
||||
}
|
||||
}
|
||||
// 🔴 2026-08-06 leo 拍板:**「拿來開發一般人用不到的根本別安裝」**
|
||||
//
|
||||
// 原本 daemon 會把 system-dev template(`CLAUDE.md`/`scripts/`/`system-dev/`
|
||||
// 共 37 檔)代裝進**使用者的文件資料夾**。那是給開發者寫 SDD/wiki 用的東西,
|
||||
// RAG 的一般使用者完全用不到,而且兩層傷害:
|
||||
// ① 把人家的資料夾弄亂(leo:「他原本的資料夾就不會看起來亂掉」)
|
||||
// ② 那些檔案會被當成知識吃進去 ⇒ 知識庫長出 `kb`/`t195-watch` 這種
|
||||
// 不是使用者內容的庫(leo 實撞,見庫目錄管理截圖)
|
||||
// ⇒ **看守資料夾一律不代裝**。要裝的人自己跑 `collector template-install`
|
||||
// (子命令仍在,開發者情境不受影響)。
|
||||
//
|
||||
// 另外還要把「已經被鋪進去的」擋在知識之外——那不是靠隱藏檔判斷,
|
||||
// 因為 leo 自己的 repo 裡 template 本來就不是隱藏的。判準是**路徑身分**,見 scan.go。
|
||||
|
||||
if *once {
|
||||
return runOne()
|
||||
|
||||
Reference in New Issue
Block a user