Leo
|
a6ced32d45
|
collector:積壓分批+新檔優先/額度用完講人話降速/斷點續傳/同內容多格式去重
封測事故(Evan):daemon 逐檔萃取上傳、每個檔在雲端產生一次工作流執行,
690 個檔在自己的免費 CF 帳號上短時間內衝出 1,070 次寫入,撞上免費上限 1,000,
額度爆掉、只有 8 個檔成功。雲端那一半(紀錄改走資料層 API)已修好,
daemon 這一半原本完全沒有節奏——本次補齊四件事:
1. 上傳節奏(direct_pacing.go):單輪最多處理 MaxEventsPerRun 個事件
(預設 25)、每次觸發雲端前節流 700ms;一輪掃到的事件依檔案 mtime
由新到舊排序,今天寫的永遠優先,積壓慢慢消化不擋日常使用。
2. 額度用完講人話(quota.go):偵測到 Workers AI「10,000 neurons」/
「4006」等已知上游訊號後,換成三句話(今天已整理幾份/可換模型或
升級 Cloudflare/不花錢也沒關係、今天或明天早上 8:00 會自動恢復),
不出現裸露的錯誤碼;同帳號同輪與下一輪都不再繼續撞牆
(quotaState 全域冷卻,跨資料夾/跨程序重啟持續,直到台灣時間
早上 8:00 額度重置)。
3. 斷點續傳:每個事件處理完立刻寫回 manifest(不再等整輪跑完才存一次),
process 被殺掉重開只會接著做真正還沒完成的部分;removed 事件另外
用 preScanEntries 快照保護,下架失敗時不會被其他事件的存檔動作
誤標成「已完成」而永遠不再重試。
4. 同內容多格式去重(scan.go):同一批來源轉出的多種格式(如 leo 給的
資料集 27,164 檔=9,045 md+9,044 json+9,043 html,md/json 同檔名
主幹)依檔名主幹分組,只留優先序最高的一份進事件管線,其餘標記在
DuplicateFormats(不吃三倍額度)。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-07 16:58:07 +08:00 |
|
Leo
|
c7675a10a0
|
不再把開發用 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 端失敗卡還沒接上這條線 ⇒ **畫面尚未真的顯示原因**。未打包、未送達。
|
2026-08-06 21:33:37 +08:00 |
|
Leo
|
67effe3b7f
|
首頁「沒被整理的檔案」修兩病:同一件事講兩次/不說是哪一個檔
leo 08-06 封測回報(附截圖+「他放一個 md 檔無法通過」)。
## ① 同一件事講兩次,而且「另外」前面沒有東西
截圖實況:
「看起來不是文件,所以跳過了。」 ← 底下是空的(非文件檔不逐檔點名)
「**另外**有 1 個不是文件的檔案(圖片、影片、壓縮檔之類)也沒有處理。」
真兇:app.go buildSkipped 的 else 分支先寫一句通用說明,
接著無條件再寫 Other 那句——後者的「另外」是為「兩種都有」寫的,
在「只有非文件檔」時就變成前面沒有東西可以「另外」。
解:該分支只講一句、自己帶數量,且不留 Other。「另外」只在兩種都有時出現。
## ② 只報總數=等於沒說(這才是 md 那題卡住的原因)
封測者放 .md 說「無法通過」,但畫面只寫「有 1 個不是文件的檔案」,
**沒說是哪一個** ⇒ 誰也判斷不出發生什麼事。
而 `.md` 明明在 allowedExt 白名單裡(scan.go:26),我在本機實測也一次就過:
"path":"arcrun-md-test.md","status":"ingested","http_status":200
⇒ 那個「1 個」必然不是他以為的那個檔(副檔名被 Windows 藏起來、存成別的格式…),
但沒有檔名就永遠查不出來。
解:scan 收集非文件檔的檔名(上限 5 個),一路帶到 status.json 與首頁。
「只報總數」在幾百張圖時是對的,在 1 個時是失職——少量就點名。
## 驗
· 兩支新測試釘住規則:只有非文件檔時不准出現第二句、不准出現「另外」;
兩種都有時「另外」才成立並帶數量
· 實際文案打出來看過(見下),不是只看測試綠
有 1 個檔案沒有被整理
看起來不是文件(圖片、影片、壓縮檔之類),所以跳過了。這是正常的,你不用做什麼。
· 我的筆記.md.txt
· 312 張圖的情況:列 5 個 +「…還有 307 個」,不洗版
· collector 全測試過、app 測試過、go vet 全綠
|
2026-08-06 16:12:01 +08:00 |
|
Leo
|
4d3a6a09a6
|
v0.18.9:collector 併進同一支執行檔——磁碟上不再攤出第二支 exe
leo 08-06 裁決:「不要兩支,寫成一支檔案」。
## 為什麼
v0.18.7-8 的「單一 exe」其實是**一支包著另一支**:collector.exe 被 go:embed
進 Arcrun.exe,執行時攤到 ~/.arcrun-rag/bin/ 再跑。
那正是防毒軟體眼中的 dropper 特徵 —— 封測者實撞
`Trojan:Win32/Sabsik.FL.A!ml`,檔案當場被隔離、自動刪除。
⚠️ 誠實界定:`!ml` 結尾=**機器學習判定**,Sabsik 是最常見的通用誤判家族,
主因是「未簽章+下載次數少」,**不是**特別指向 dropper 行為。
所以本次改動**不保證**解除誤判——真正的解是上架 MS Store(微軟簽章)。
但「執行時把第二支 PE 寫到磁碟再執行」本來就該拿掉,這是對的方向且順手變小。
## 怎麼做
- `collector/` 39 個檔 `package main` → `package collector`,`main()` → 匯出的 `Run(args) int`
- 新增 `collector/cmd/collector/`(薄殼 CLI,讓單獨跑 collector 這條路仍可用)
- App 直接 import 該套件;`main()` 第一件事就判 `--collector`,是的話走 `collector.Run` 不碰 GUI
- `supervisor` 加 `ArgPrefix`,App 把 `BinPath` 指向 `os.Executable()` 自己
- 刪掉 `bundled_collector_{windows,other}.go`(embed + 攤檔那套)
- 三支打包腳本不再編/複製第二支;版本注入同時打到兩個 package
- build-win.sh 的機械閘改成**直接問它**:`--collector --version` 回得出版本才放行
(舊閘是比大小,只能證明「有 embed」,證明不了「分派是對的」)
## 驗(真機實跑)
· `.app/Contents/MacOS/` 只有 **一個** 執行檔(原本兩個)
· 跑起來兩個行程是**同一個 exe**:
…/MacOS/arcrun-app
…/MacOS/arcrun-app --collector direct --config …
· `~/.arcrun-rag/bin` **不存在**(沒有任何東西被攤出來)
· 端到端:丟檔進看守資料夾 → collector.log `"status":"ingested","http_status":200`
· `lsappinfo` 仍是 `type="UIElement"`、`Version="0.18.9"`
· collector 39 檔測試全過;app 測試過;go vet 全綠;mac + windows 交叉編譯皆過
· 單檔 26MB → 22MB(不再夾帶第二份完整程式)
|
2026-08-06 16:00:47 +08:00 |
|
Leo
|
9cd0adab22
|
G-6.2:讀不了的檔案不再安靜消失——首頁當場說出來
服務 J-1 / S6「我丟進去的檔案,查得到」的考題 G-6.2:
「要嘛查得到,要嘛**當場被告知這種檔案還不支援**,不准安靜地略過。」
本次只做後半句(轉檔本體另有人閘,等 leo 裁「那段 Go 住哪裡」)。
## 真實現況比記載更糟
t16 寫的「PDF 被靜默略過」已不成立(t73 的轉檔層把 pdf/docx/xlsx/csv/pptx
都接上了,實測 2 頁中文 PDF 完整抽出)。**真正還在沉默的是別的東西**:
副檔名不在 allowedExt 的檔案在 scan.go 直接 `return nil`——
不進事件、不進 manifest、不進 status、不進畫面。
使用者丟一份 .doc 進去,從頭到尾一個字都沒有。
實測基線(真檔):丟 .pdf/.md/.doc/.key/.jpg 進資料夾,
`collector scan` 只吐出 pdf 與 md 兩個事件,另外三個檔沒留下任何痕跡。
## 改了什麼
- scan.go:白名單閘不再是死巷。像文件的(.doc/.xls/.ppt/.pages/.key/
.numbers/.odt/.ods/.odp/.rtf/.epub/.wpd/.msg/.eml)逐檔留名;
其餘(圖片/影音/程式碼)只計總數——**避免 Obsidian 附件庫炸出幾百行噪音**。
- 兩個新欄位標 `json:"-"`:collector-trigger schema 是
additionalProperties:false,且 BuildSendablePayload 是淺拷貝
⇒ 有 tag 就會漏到雲端被擋。這是給本機使用者看的,不上 wire。
- direct.go → status.json → App 首頁一張卡:講檔名與格式(「舊版報告.doc
(舊版 Word)」),並告訴他不用重丟、也給替代路(另存成 PDF/.docx)。
- 刻意不進 manifest、不走 CarryForwardActivity:每輪由檔案系統重算,
不製造 t195 那種「跨輪欄位漏 carry 就靜默歸零」的債。
## 順手修掉一個會讓驗收失效的回歸
同綑的 arcrun-collector 一路自稱 `dev`:退役 fyne 版
(arcrun-tray/build-mac.sh:24,t150/t72)本來就有版本注入,
t194 換 Wails 時沒帶過來,Mac 與 Windows 兩邊都掉。
**幹活的是 collector**——它不報版本,就沒人能判斷修復有沒有到使用者手上。
## 實測
- go test ./... 135 過 0 敗(新增 10 條:scan 5+首頁文案 5)
- check-cis / check-render / check-tray 三閘全過;淺色深色都抓過畫面
- 從 **DMG 裡那支** collector 實跑:Info.plist 0.18.6、
collector 回報 v0.18.6 (build 20260806-0101)、
status.json 吐出 skipped_docs=[簡報.key, 舊版報告.doc]、other=1
⚠️ 未出貨:推 bundles repo 在 GitHub,要 leo 開 D20 閘。
DMG 已備妥 dist/Arcrun-v0.18.6.dmg。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-06 01:04:25 +08:00 |
|
Leo
|
47580f4ba5
|
t195:失敗重試加指數退避+上限——一個壞檔不再拖住整個資料夾
leo 2026-08-05:「有封測者在等,一直出錯」「已經等了幾天了」
## 病(leo 實撞,log 實證)
`小果被AFTEE詐貸.pdf` 雲端 401 失敗後,**manifest 完全不記失敗**
⇒ 下輪掃描又當「新檔」⇒ **1387 輪、跨 11 小時**,每輪 3.2~3.8 秒全在撞同一面牆;
且它排在佇列前面 ⇒ **整個資料夾的同步被一個壞檔拖住**。
leo:「原先萃檔案速度也快,現在花了十幾分才萃完」——**萃取沒變慢,慢的是重試**
(log 的 `FOREACH 所有 5 項目均失敗` 證明卡早就萃好了)。
**這是獨立於 401 的架構缺陷**:401 修好了,下次換別的錯照樣卡死。
## 修
· ManifestEntry 加 FailCount / LastFailAt / NextRetry
· MarkFailed():退避階梯 1m→5m→15m→1h→6h(之後維持 6h)
· ShouldRetry():退避窗口內跳過;連續失敗 8 次暫停自動重試
force(使用者按「立刻同步」)忽略退避與上限——**人明確要求不該被機器擋住**
· MarkIngestedBy() 成功時清空失敗狀態(下次再壞從第一階重算)
· direct.go 三個失敗出口都記退避(讀檔失敗/萃取失敗/上傳失敗)
· retrySkipReason():跳過時說人話,不靜默(同 t195 燈號誠實原則)
## 🔴 真兇其實有兩層——第二層才是關鍵
只加退避欄位**沒有用**:`scan.go` 每輪都**重建** ManifestEntry,
原本只 carry IngestedHash/IngestedAt ⇒ 我寫進去的 fail_count 下一輪就被抹掉
⇒ 退避永遠停在「第 1 次失敗」=等同沒有退避。
(順帶發現 ExtractedBy(t73「誰萃的」)原本也一直悄悄丟失。)
⇒ scan.go carry 補齊四個欄位,並留註解:**日後新增跨輪欄位必須加在這裡**。
## 驗(真實跑,非只有單元測試)
單元測試 6 項全過(退避窗口/指數遞增 60/300/900/3600/21600/21600/
上限停止/force 忽略/成功清空/壞檔不連累同輪其他檔)
實跑(cypher_url 指向不存在主機製造必失敗):
第 1 輪 failed
第 2 輪 skipped「上次失敗(第 1 次),1m0s 後重試」
第 3 輪 skipped(同上)
第 4 輪 skipped(同上)
模擬退避到期 → 確實重送、fail_count=2、退避升為 300s ✅
go test ./... 全綠;go vet 通過。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-05 14:46:03 +08:00 |
|
Leo
|
12c8098b5f
|
feat(t73): Excel/CSV 抽取器——企業用很多(leo)
leo 兩句定調:
①「要思考 Excel 和 csv 的問題,因為企業用很多」
②「企業的 excel 通常不會是 1 萬行,人工做不出這麼多,但你可以轉結構資料丟進去」
→ 修正我先前「CSV 會變數字牆、先不加」的判斷——那是拿機器產生的百萬列當前提。
實測 60列x6欄 ≈ 2,200 token,對 LLM 完全不是問題。
統一中間格式=Markdown(leo 提 n8n 對照後定調):
n8n 一切轉 JSON 是對的,因為它下游是程式(.欄位 取值);
我們下游是 LLM ⇒ Markdown 表格更好。實測同一份表格
JSON 6,872 字元 vs Markdown 3,341=JSON 多花 2.1 倍 token(每列重複寫欄位名)。
測試 8/8,用 openpyxl 產的真 Excel 檔(非自捏 XML):
⭐ 測試抓到兩個真 bug(讀原始碼看不出來):
① rels 的 Target 是絕對路徑 /xl/... 我卻又補 xl/ 前綴 → 變 xl/xl/...
② r:id 帶 namespace,寫 xml:"id,attr" 抓不到,要用完整 namespace URI
修好後分頁名從 ## sheet1 變成 ## 維修紀錄(企業分頁名本身就是語意)
CSV:去 BOM(Excel 另存必帶)/引號內逗號不拆欄/| 跳脫/欄數不齊不整份失敗
XLSX:多工作表全讀(只取第一頁會漏)/sharedStrings 索引/壞檔報錯
防呆:超過 500 列截斷並明說截斷了(不靜默丟資料);單格超 500 字截斷。
|
2026-07-27 20:50:45 +08:00 |
|
Leo
|
73a5a67b72
|
feat(daemon-beta t2): template 代裝——embed v1.18.0 快照(36檔)/冪等不覆寫/daemon 啟動自動鋪/template-install 子命令;scan 加 SkipDirNames 防 system-dev 與 CLAUDE.md 被當知識掃
- 測試 3 支新增全綠(首鋪/冪等護用戶檔/掃描跳 template 產物——第三支先紅抓到 CLAUDE.md 洩入再修綠)
- CLI 實跑:新鋪 36 檔→重跑 0 新檔(冪等證據)
|
2026-07-24 12:18:37 +08:00 |
|
Leo
|
bb96bceb04
|
feat(ingest-hash-trigger): 凍結 collector/ingest schema+Go collector 骨架(issue #5, SDD task 1+2)
- schemas/collector-trigger.v1.schema.json:collector→named-webhook 觸發 payload(source_hash/r2_key/renamed/R6 warnings)
- schemas/kbdb-ingest-request.v1.schema.json:source_hash 必填缺=400;儲存映射走 metadata_json.$.source_hash(守 kbdb 表不變鐵律);同 hash 重送=200 already_ingested
- schemas/MIGRATION-webhook-payload.md:舊 Gitea push payload 逐欄對照(給 task 4 改寫 workflow)
- collector/:Go module(純 stdlib)——manifest 讀寫+mtime fast-path 掃描+renamed hash 配對+40% 大量刪除防呆;CLI collector scan
- go test ./... 全綠 7/7(added/modified/removed/renamed/防呆五情境+fast-path+manifest 往返)
- Node 版 collector 標 legacy 並存(SDD task 4 拆除)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
2026-07-19 20:31:11 +08:00 |
|