## 摘要 本 PR 含兩個獨立關注點,各一 commit。 ### 1. `diag_refs` — source_ref↔manifest 對帳診斷 (`04f4fd5`) — 回應 #4 lint 的「source_ref 不在 manifest」只報對不上、不辨成因。新增 `tools/diag_refs.py`,把每個對不上的 source_ref 分成五類(hash 不符/路徑字串岔開/檔名 hash 後綴岔開/檔在磁碟未登記/完全無對應),各附修法,讓「補帳本(§14.3)」與「修規則(§12)」的相反處置不再混為一談。純唯讀、結束碼 `0/1`,可當搬機或改 manifest 後的閘門。附 selfcheck 驗五型別分類、結束碼與零寫檔。README §3.3/§6/§7/§8 同步。 ### 2. ingest 進度可見性 + Ctrl-C 安全回復 (`4add7fa`) — Closes #2 `--all-pending` 長時間本地 Ollama 攝入全程靜默,使用者不確定有沒有在跑、又不敢中斷: - **進度可見**:每份來源 `[i/N]`、長文件逐段 `分段 k/n`、每個 item `↳ 新增/更新`、完成 `✓`,全 `flush=True` 即時顯示。 - **Ctrl-C 安全**:獨立攔 `KeyboardInterrupt`(它不是 `Exception` 子類,原本會略過善後)。因 commit 在迴圈之後,**main 必然未受影響**;中斷時印可照做的回復指令並以結束碼 130 收場。 ## 測試 - `python tools/selfcheck_diag_refs.py` — ALL PASS - `python tools/selfcheck_ingest.py` — ALL PASS(含進度標記、中斷後 main 未動、回復指令實測可回乾淨 main) - README markdownlint 0 issues Closes #2 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: LittleYellow <crazytea@gmail.com> Reviewed-on: #5
This commit was merged in pull request #5.
This commit is contained in:
@@ -5,11 +5,13 @@
|
||||
執行:.venv/Scripts/python tools/selfcheck_ingest.py
|
||||
"""
|
||||
import hashlib
|
||||
import io
|
||||
import pathlib
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from contextlib import redirect_stderr, redirect_stdout
|
||||
|
||||
sys.path.insert(0, str(pathlib.Path(__file__).parent))
|
||||
import kb
|
||||
@@ -82,9 +84,14 @@ def main():
|
||||
git(root, "add", "-A")
|
||||
git(root, "commit", "-q", "-m", "init")
|
||||
|
||||
# 1) 攝入:建分支、產頁、commit、回到 main
|
||||
# 1) 攝入:建分支、產頁、commit、回到 main(並驗進度輸出可見,issue #2)
|
||||
kb.llm_json = fake_llm
|
||||
ingest.main(["raw/converted/doc1.md", "--root", str(root)])
|
||||
buf = io.StringIO()
|
||||
with redirect_stdout(buf):
|
||||
ingest.main(["raw/converted/doc1.md", "--root", str(root)])
|
||||
out = buf.getvalue()
|
||||
for marker in ("[1/1] 攝入", "分段 1/", "↳ 新增", "✓ ", "完成"):
|
||||
assert marker in out, f"進度輸出缺少 {marker!r}\n{out}"
|
||||
assert git(root, "branch", "--show-current") == "main"
|
||||
branch = git(root, "branch", "--list", "ingest/*").strip("* ").strip()
|
||||
assert branch, "未建立 ingest 分支"
|
||||
@@ -147,6 +154,45 @@ def main():
|
||||
except RuntimeError as e:
|
||||
assert "fallback" in str(e)
|
||||
print("PASS: 重試 + fallback 切換 + 全失敗報錯")
|
||||
|
||||
# 5) Ctrl-C 安全(issue #2):中斷時 main 不受影響、以 130 收場,
|
||||
# 且文件宣稱的回復指令真的能回到乾淨 main。第 2 份來源開始摘要時中斷,
|
||||
# 此時第 1 份的頁面已寫入(未追蹤、未 commit)——正是使用者最怕的半成品狀態。
|
||||
add_doc(root, "docA.md", "來源A:待攝入。")
|
||||
add_doc(root, "docB.md", "來源B:待攝入。")
|
||||
git(root, "add", "-A")
|
||||
git(root, "commit", "-q", "-m", "docA/docB")
|
||||
head_before = git(root, "rev-parse", "main") # 攝入不得動到的 main 狀態
|
||||
state = {"n": 0}
|
||||
def boom(cfg, task, prompt, validate):
|
||||
state["n"] += 1
|
||||
if state["n"] >= 2: # 第 2 份來源的 summarize 呼叫
|
||||
raise KeyboardInterrupt
|
||||
return ({"title": "來源A", "description": "d", "slug": "fresh-a",
|
||||
"tags": ["x"], "summary_markdown": "- a", "knowledge_items": []},
|
||||
"fake:test")
|
||||
kb.llm_json = boom
|
||||
err = io.StringIO()
|
||||
try:
|
||||
with redirect_stdout(io.StringIO()), redirect_stderr(err):
|
||||
ingest.main(["raw/converted/docA.md", "raw/converted/docB.md",
|
||||
"--root", str(root)])
|
||||
raise AssertionError("KeyboardInterrupt 應轉為 SystemExit(130)")
|
||||
except SystemExit as e:
|
||||
assert e.code == 130, e.code
|
||||
msg = err.getvalue()
|
||||
assert "git checkout main" in msg and "main 未受影響" in msg, msg
|
||||
assert git(root, "rev-parse", "main") == head_before, "main 被動到了!"
|
||||
cur = git(root, "branch", "--show-current")
|
||||
assert cur.startswith("ingest/"), f"中斷後應停在 ingest 分支,實得 {cur!r}"
|
||||
assert git(root, "status", "--porcelain"), "應有未提交的半成品"
|
||||
# 照文件指令回復,斷言真的回到乾淨 main(驗「結果可用」,非「字串存在」,§13.4)
|
||||
git(root, "checkout", "main")
|
||||
git(root, "stash", "-u")
|
||||
git(root, "branch", "-D", cur)
|
||||
assert git(root, "branch", "--show-current") == "main"
|
||||
assert not git(root, "status", "--porcelain"), "回復後工作區仍不乾淨"
|
||||
print("PASS: Ctrl-C → main 未受影響、130 收場、回復指令實測可回乾淨 main")
|
||||
print("ALL PASS")
|
||||
finally:
|
||||
kb.llm_json, kb.ollama_chat = real_llm, real_chat
|
||||
|
||||
Reference in New Issue
Block a user