feat(daemon-beta B2): 萃取品質 lint(H1–H6)重新接到現行 direct.go

Gitea #24:分支 work/b2-quality-lint-0726(2f3e9ec)躺在 main 外兩週,
今天重新整合進現行 direct.go(原分支落後 main 130+ 筆,direct.go 本身
差 667 行,需要真正的整合工作而非硬併,見 wiki status.md 與頂層
daemon-beta/tasks.md 已記載的殘項)。

- collector/lint.go/lint_test.go:package main → package collector
  (main.go 在 4a26856/v0.18.9 已從兩支執行檔併成一支,整個 collector/
  目錄已改名 package collector;分支停在改名之前)。
- collector/main.go:func main() → func Run(args []string) int
  (同一次重構),新增 case "lint" 呼叫 runLint。
- collector/direct.go:DirectConfig.LintStrict 欄位+extractor 萃完、
  POST rag_ingest_card 前掛 LintCard 閘(硬缺→不送標 rejected;
  軟項→照送帶 quality:low+quality_warnings);CLI --strict 旗標。
- 既有測試共用的 cardFixture(direct_extract_test.go)/direct_pacing_test.go
  三處 inline 卡片,從「兩段最簡卡」補成 B2 合格四段卡——不是新增例外,
  是 lint 正確地在做它的事,舊卡片本來就不合格。

驗證:go build/vet/test ./... 全綠;另以真實 PDF(docs/onboarding/封測說明.pdf,
2MB 二進位)跑過完整 direct.go 同步鏈,證實送進 Gemini 的 prompt 是
5,071 字乾淨中文(0 個控制字元/NUL),不是原始位元組——ConvertToText
(t73)已完整解決 900b895 想止血的問題,故該分支的 looksLikeText
二進位止血、Windows 交叉編譯腳本判定作廢,不併。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-09 14:26:21 +08:00
parent ef8126201c
commit 63bea13f21
6 changed files with 802 additions and 13 deletions
+28 -2
View File
@@ -92,6 +92,13 @@ type DirectConfig struct {
// 為真時忽略失敗退避與次數上限,一律重送——**人明確要求時不該被機器的退避擋住**。
// 不寫進 config.json`json:"-"`):它是單次執行旗標,不是使用者設定。
ForceSync bool `json:"-"`
// B2 萃取品質檢查(daemon 端 lint,草案 §3 第一層;分支 work/b2-quality-lint-0726 移入,
// 2026-08-09 重新接到現行 direct.go——原分支落後 main 130 筆,不能硬併,見
// wiki status.md:1570-1573):extractor 模式萃完、POST 前跑。
// LintStrict=軟項也擋(測試/CI);預設 false=硬缺拒收、軟項照送標 quality:low。
// CLI `--strict` 會覆蓋成 true。
LintStrict bool `json:"lint_strict,omitempty"`
}
// librarySlug 把資料夾名轉成合法庫名(A-Za-z0-9_-;中文等非 ASCII 轉為底線分段)。
@@ -1086,15 +1093,30 @@ func runDirectOnceRoot(cfg *DirectConfig, root string, dryRun bool, qs *quotaSta
ok = false
break
}
// B2 品質檢查(草案 §3 第一層):萃完、POST 前跑。原稿在手=H6 可做。
// 硬缺(H1/H2/H5)=拒收、不 POST、標 failed(manifest 不回寫=下輪重萃自癒);
// 軟項(H3/H4/H6)=照送但帶 quality:lowquality_warnings(裁決 T4-C)。
lr := LintCard(string(cardData), LintOptions{Source: string(content)})
if lr.Blocks(cfg.LintStrict) {
res.Status = "rejected"
res.Error = "品質未過(不送):" + strings.Join(append(lr.HardMessages(), lr.SoftMessages()...), "")
ok = false
break
}
// path 帶「原檔路徑」不是卡片路徑(07-24 真機第五枚坑):
// source_uri=kb://<path> 是 takedown 的比對鍵,也是 B4 溯源該指的原文——
// 帶卡片路徑會讓「刪原檔→下架」永遠 0 命中。
status, _, perr := cfg.postJSON(cfg.triggerURL(cfg.CardIngestWF), map[string]any{
cardBody := map[string]any{
"page_name": pageNameOf(cardRel),
"path": ev.Path,
"card_content": string(cardData),
"library": cfg.libraryFor(absRoot),
})
}
if warns := lr.SoftMessages(); len(warns) > 0 {
cardBody["quality"] = "low"
cardBody["quality_warnings"] = warns
}
status, _, perr := cfg.postJSON(cfg.triggerURL(cfg.CardIngestWF), cardBody)
res.HTTPStatus = status
if perr != nil {
res.Status, res.Error = "failed", perr.Error()
@@ -1230,6 +1252,7 @@ func runDirect(args []string) int {
configPath := fs.String("config", "", "direct 模式設定檔(JSON)路徑(必填)")
once := fs.Bool("once", false, "掃一輪即退出(測試/cron;預設常駐輪詢)")
dryRun := fs.Bool("dry-run", false, "只列出會送出的動作,不 POST、不寫 manifest")
strict := fs.Bool("strict", false, "B2 品質檢查嚴格模式:軟項(H3/H4/H6)也擋,不只硬缺")
if err := fs.Parse(args); err != nil {
return 2
}
@@ -1242,6 +1265,9 @@ func runDirect(args []string) int {
fmt.Fprintln(os.Stderr, "collector direct:", err)
return 2
}
if *strict {
cfg.LintStrict = true // CLI 旗標覆蓋 config
}
// t176t92 的 claude_bin 回寫已隨 claude 萃取路一併退役——RunDirectOnce 不再解析
// claude 執行檔,cfg.ClaudeBin 不會被改寫,故沒有東西需要回寫。