Files
arcrun-collector/README.md
T
Leo 1c2fa6b294 ingest-hash-trigger SDD task 4:觸發鏈 Gitea webhook → collector sync 直打 named-webhook
- collector Go:trigger.go+sync 子命令(scan→R2 upload→POST collector-trigger.v1
  →2xx 才 MarkIngested 回寫;失敗不回寫=下輪自然重試;上傳失敗事件不送、同路徑
  renamed 連坐不回寫;防呆警告輪照送 warnings)。go test 19/19 綠(httptest mock)。
- workflow v3:rag-ingest-cards.local.yaml 改吃 collector payload(events[] 取代
  commits[];原稿從 R2 撈 CF REST API objects/{r2_key};新增 renamed 分支=只更新
  路徑映射 R5,跨界搬移轉 added/removed;removed 沿用 G9 雙判準;M3 recompute 尾鏈
  原樣保留,fetch_index 改 R2);rag-ingest.yaml v1 同步等價改。source_uri 遷移
  gitea://→kb://。schema_version!==1 整輪 no-op。
- push-demo-workflow.sh:補 __CF_ACCOUNT_ID__/__R2_BUCKET__/__CF_R2_TOKEN__ 佔位;
  GITEA_* 標 deprecated-for-ingest(rag-extract* 仍用)。
- Gitea 退場(本 repo 範圍):legacy JS collector 六檔刪除(git 記錄可查);
  install.sh 步驟 5 降級 deprecated;README 落「Gitea 退場切換 checklist」
  (推 v3→collector sync 實測→拆 Gitea webhook→觀察期+回滾法)。
- 驗證:yaml parse(raw+sed 模擬)/flow-config 對齊/JS 語法 node --check/
  demo cypher 唯讀 /cypher/search 編譯 missing=[]、出邊序如設計。
  未部署、未實跑 e2e(推 demo=leo 閘)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 21:58:39 +08:00

89 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# collector
> ✂️ **legacy Node 版(Gitea push 鏈)已於 2026-07-19 刪除**SDD `ingest-hash-trigger` task 4
> 「Gitea webhook 接收端程式碼與路由已刪除(git 記錄可查)」)。`index.js`/`transform.js`/
> `git-sync.js`/`config.js` 那套 watch → git commit/push → Gitea webhook 鏈要考古請看 git 歷史
> (本檔同 commit 之前的版本有完整說明)。現役=Go 版 hash 偵測 collector
> **不經 git、不經 Gitea**scan → R2 上傳 → 直打 arcrun named-webhook。
> Markitdown 轉檔(docx/pptx/pdf→md)能力隨 legacy 一併退場,回歸產品化段 task 11 重做進 Go 版。
## Go 版:hash 偵測 collectorSDD ingest-hash-trigger
```
collector scan --root <知識資料夾> --manifest <manifest.json> [--max-removed-ratio 0.4] [--dry-run]
collector upload --root <知識資料夾> --manifest <manifest.json> [--max-removed-ratio 0.4] [--dry-run]
collector sync --root <知識資料夾> --manifest <manifest.json> [--max-removed-ratio 0.4] [--dry-run]
```
一次掃描:走訪資料夾(先只認 .md/.markdown/.txt/.docx/.pptx/.pdf)→ mtime+size fast-path
(沒變→沿用 manifest hash;變了才算 sha256)→ 對照 manifest 產出事件 → 事件 JSON
(符合 `schemas/collector-trigger.v1.schema.json`)輸出 stdout → 更新 manifest`--dry-run` 不寫)。
- **事件分類順序(design §3**:先把本輪 removed×added 以 content_hash 配對成 `renamed`
(只更新路徑映射,不 retire、不重萃);再分 added / modified / removed。
- **大量刪除防呆(R6**removed 數 > manifest 條目 × 40%`--max-removed-ratio` 可調)→
removed 全部不執行、manifest 條目保留、輸出 `mass_delete_guard` 警告。
- **重試語意**`ingested_hash` 只會在整條 ingest 鏈成功後回寫(回寫鉤子=`Manifest.MarkIngested`
`sync` 在觸發回 2xx 後呼叫);掃描與 R2 上傳都不寫它,所以「偵測過但未成功 ingest」的檔
每輪都會重發 added/modified——這是設計(design §2),不是 bugR2 端靠存在檢查 no-op
不會重複上傳。
- daemon 常駐(launchd)=產品化段 task 11。
### `upload`R2 content-addressed 原稿上傳(SDD task 3design §4
`upload``scan`+把本輪 **added/modified** 的原稿上傳 R2renamed/removed 內容未變/已留底,不上傳)。
- **走 Cloudflare REST API**`PUT /accounts/{account_id}/r2/buckets/{bucket}/objects/{key}`
Bearer token)——不用 S3 sigv4,token 模型跟產品其他部分一致(客戶本來就有 CF API token)。
- **key`raw/<sha256hex>`**(不含 `sha256:` 前綴,對齊 `schemas/collector-trigger.v1.schema.json``r2_key`)。
- **冪等**:每 key 先做存在檢查,已存在=`skipped_exists` 不重傳(content-addressed 天然去重)。
⚠️ CF REST API 的 objects 端點**不支援 HEAD**2026-07-19 live 實測回 405),
存在檢查走 `GET``Range: bytes=0-0`(存在=200/206 只讀 1 byte404=不存在)。
- **完整性**:上傳前重算 sha256 核對事件 hash;檔案在掃描後被改動=該筆 `failed` 不上傳
(不能把新內容塞進舊 hash 的 key),下輪重掃自然帶新 hash。
- **失敗語意**:任一筆 `failed` → exit code 1manifest 照存(content_hash 反映現況、
`ingested_hash` 不動)=下輪自動重試。上傳成功也**不**標 ingested——上傳只是鏈的第一環。
- `--dry-run`:不碰網路、不寫 manifest,只列 `planned` 上傳清單。
- 輸出 JSON`{"trigger": <collector-trigger payload>, "uploads": [{path, r2_key, status, error?}]}`
status`uploaded``skipped_exists``failed``planned`
設定(**只走環境變數,絕不落 repo/code**):
| 變數 | 說明 |
|---|---|
| `CF_ACCOUNT_ID` | Cloudflare 帳號 ID |
| `CF_API_TOKEN` | 有該 bucket R2 read+write 權的 API token |
| `R2_BUCKET` | 目的 bucket 名(demo`arcrun-rag-raw-demo` |
| `CF_API_BASE` | 選填,API 基底覆蓋(測試用;預設 `https://api.cloudflare.com/client/v4` |
測試:`go test ./...`——掃描七項(五情境+fast-path+manifest 往返)+上傳七項
(新檔上傳/同 hash 重傳 no-op/上傳失敗不標 ingested+重試/非內容事件不上傳/
hash 不符不上傳/env 缺漏報錯/MarkIngested 鉤子),httptest mock 對齊真 API 行為(HEAD 405)。
live e2e2026-07-19):uncle6 帳號 `arcrun-rag-raw-demo` bucket 真上傳→重傳 no-op→
`wrangler r2 object get --remote` 下載 diff 一致、sha256 與 key 相符,全通。
### `sync`:一條龍(SDD task 4)= scan → upload → 觸發 ingest → 成功回寫
`sync``upload` 再加最後一步:把整輪的 collector-trigger.v1 payload POST 到
`ARCRUN_TRIGGER_URL`arcrun named-webhook 完整 URL,如
`{cypher}/webhooks/named/{ns}/rag_ingest/trigger`——端點是 arcrun 原生觸發機制,
design 鐵律段明言保留;被刪掉的是「Gitea push 事件」這個來源語意)。
- **回寫語意**HTTP 2xx 才對本輪送出的 added/modified/renamed 檔呼叫 `Manifest.MarkIngested`
非 2xx/網路錯不回寫=下輪自然重試(exit 1)。
- **上傳失敗的事件不送**schema 約定 added/modified 的 `r2_key`=原稿已在 R2,上傳失敗還送
=叫消費端去 404 → 該事件本輪擋下(`dispatch.dropped_paths`),下輪重試補送;同路徑的
renamed 也不回寫(防「內容從未上 R2 卻被標 ingested」)。
- **防呆警告輪照送**mass_delete_guard 觸發時 removed 事件已被壓下,但 payload 連同
`warnings[]` 照送——消費端看得到警告、不執行下架;notify 呈現歸 collector 端輸出/daemon。
- **無變更輪不發送**`skipped_no_changes`)。
- 輸出 JSON`{"trigger": <實際送出的 payload>, "uploads": [...], "dispatch":
{status, http_status?, error?, marked_count, dropped_paths?}}`。
設定=upload 的三個環境變數+`ARCRUN_TRIGGER_URL`(皆絕不落 repo/code)。
測試:`go test ./...` 19/19——掃描 7+上傳 7sync/trigger 5(成功回寫+無變更輪不重發/
失敗不回寫+修復後重試成功/防呆警告輪照送 warnings 零事件/上傳失敗事件擋下+renamed
連坐不回寫/URL env 驗證),全部 httptest mock**未實跑雲端 e2e**——等切換日與 leo 一起驗)。