本卡屬 FR-111(母卡見建卡後補號),第 2 棒:批次分類主幹:上傳/封口/分類/審核/歸檔/清理(jedi-evidence-classification,2 檔 1,900 行)。只掃不修。
證據批次的完整生命週期:建批次→上傳檔→封口→按「分類」起背景執行緒跑容器→人工審核結果→歸檔進任務證據→清理剩餘檔/刪整批。十個端點的服務本體與路由。
這是 FR-107 新做的主線(舊 Drive 線已標 legacy),也是使用者實際在用的路徑。守門形狀是「批次→專案→你是不是 manager/participant」,分類跑在背景執行緒(身分要自己帶過去)、審核結果由前端整包 PUT 回來、歸檔採信那份結果——三個地方各是一種「採信誰」的問題。
evidence_batch_route.py:49-236):列出每支 method 呼叫的 service 方法,對照 evidence_batch_service.py 裡 _require_manager(:1472)/_require_participant(:1478)/_require_run(:922)各掛在哪。首腦已核 classify(:863)掛 manager、get_run_file_preview(:861)掛 participant 且比對檔案屬於該 run 的批次、put_run_state(:815)掛 manager。要逐支對剩下的:create_batch/add_file/remove_file/complete_upload/list_batches/get_batch_detail/upload_files/delete_file/seal/archive/purge/delete_batch——特別是 list_batches(:413)的查詢條件有沒有專案維度必填(空條件回整租戶=總表第 70 項同型)。put_run_state :815-858)與歸檔採信(archive :546-643):前端 PUT 整份 state 進來,歸檔時依 state 的 placements 把檔案掛進任務證據(ITaskEvidenceSink.attach)。state 驗到什麼程度:能不能塞別批次的 file_uid、別專案的 part_id、ao_id;_placements_by_file_uid(:758)有沒有比對 file_uid 屬於本批次。這決定「歸檔進任務的證據是誰說了算」。:1095-1160 _run_classify_job、_Heartbeat):分類跑在執行緒,set_user_context 重設身分、session_scope() 才有 RLS 脈絡。驗:user_ctx 從哪來、執行緒內每個 session_scope() 前有沒有都帶到(CM-1912 剛在主專案 Drive 那邊踩過「子執行緒不繼承 ContextVar、RLS 擋成 0 列還標成功」);心跳執行緒(_Heartbeat)自己再開一層。:1145-1150 呼叫 _fail_batch :1214):f"{type(exc).__name__}: {exc}" 存進批次、前端可讀。拉檔(stage_to_dir)或儲存後端的例外可能含主機路徑、儲存後端連線資訊、Python 物件 repr。:1409-1415 _read_container_log、get_run_validation_report :893):E1 ② 那份 _container-log.txt 被存進 run 的 container_log 欄位、build_validation_report 帶出去給 participant 讀。participant(不是 manager)能不能讀到容器 stderr 原文——若能,E1 ② 的問題影響面就是「同專案所有成員」。purge :645、delete_batch :703、_delete_stored_files :683、_cleanup_staged_files :1417):刪批次時儲存後端的檔真的刪了嗎、工作目錄(~/.cm-jobs/<job>/)的 files//_state.json/_container-log.txt 清不清(不清=證據副本永久留在 BE 主機);狀態機(resolve_transition :209)擋不擋「分類中」刪批次。upload_files :494、add_file :306、_guess_mime :202):走 IEvidenceStorage.save(主專案 upload provider),套件側有沒有驗副檔名/大小/數量;_same_content_hints(:465)用 content_hash 跨批次找重複——會不會把別專案的同內容檔名回給你(資訊洩漏)。本套件從未掃過。 E1 是本棒的下游(容器),E5 是本棒的資料層(query entity/repo/RLS)——研究員追去讀 domain/service/*(239 行)與 run_state_keys.py(82 行)算預期內,那些檔在 E5 也會掃、重複報由首腦挑掉。🔴 本棒 1,900 行最逼近上限:研究員若被停滯偵測砍(/workflows 看到 retrying),停下回報,預備切法是主幹服務檔上下半各跑一次(同 jedi-detection D3-4a/4b),不重試同一設定。守門 adapter 在主專案 core/plugins/evidence_classification.py:63-121(is_project_manager 查 project_participants.role == 'manager')不在本棒,只看套件有沒有呼叫。
第一步:驗 scope 檔數與行數,對上 2 檔/約 1,900 行才啟動(scope 以套件 HEAD 955e409 為準;git log -1 --format=%h 不是它就先回報,不自行改 scope):
cd /Users/chouraymond/Projects/Jedicogy/module/jedi-python-package/jedi-evidence-classification && git ls-files -- jedi_evidence_classification/app/service/evidence_batch_service.py jedi_evidence_classification/api/routes/evidence_batch_route.py | wc -l # 要 = 2
cat jedi_evidence_classification/app/service/evidence_batch_service.py jedi_evidence_classification/api/routes/evidence_batch_route.py | wc -l # 要 ≈ 1900
第二步:把啟動指令交給決策者(你不能自己啟動)。/claude-security 這個 skill 帶 disable-model-invocation: true,模型用 Skill tool 叫會被擋;也不可以自己叫 Workflow 或自己派研究員/verifier 代替它——面板票數是工具算的、報告的驗證章蓋在那個數字上。這是刻意設計不是故障,撞到不要 debug、不要找繞路。
檔數對上後停下回報:說「檔數 2 已對上,可啟動」,並把下面兩行原樣附在回報裡交還給決策者,由決策者在本 session 親手打(單次送出、第二行不能省、每個新 session 都要重打):