proj-doc-builder:讓 AI 讀得懂、能校驗的工程文檔索引

前言

接手陌生代碼庫,常見做法是讓模型直接掃源碼:讀得慢,上下文喫緊,每次新會話還要從頭再來。另一種做法是讓模型順手寫一堆 Markdown,問題是「已同步」沒法證僞——沒有清單可覈對,改了什麼全靠寫文檔那一次輸出自己說。

下面介紹的 proj-doc-builder 走第三條路。工具負責結構:目錄大綱、內容哈希基線、發佈事務;正文由人和模型寫;加載時由獨立腳本先校驗 manifest,再交給模型。作者 walvka-code,倉庫在 walvka-code/proj-doc-builder,以 DeepSeek Harness bundle(@walvka-code/proj-doc-builder)和普通 CLI 兩種形態發佈,歸類爲工作流插件。

它解決什麼

README 把問題拆成兩條。

  1. 要快速梳理陌生代碼庫,還要留下「後續改了什麼」的基線。插件爲每個源碼目錄維護一份 Markdown 大綱,內容是文件清單、關鍵符號、依賴關係、該目錄在系統中的角色;同時用 _scan_state.json 記住上次同步時源碼的樣子,下次同步只處理真正變化的目錄。
  2. 要讓新起的 AI 對話直接拿到工程結構。加載器 tools/load_docs.py 先校驗 manifest 與磁盤文檔逐份一致,再輸出概覽(目錄樹、元數據、小節名)或單獨某份文檔。寫側和讀側共用一份契約,不會各寫一套解析而漂移。

它的立場寫在 README 開頭:文檔「已同步」必須由工具寫下的 manifest 背書,而不是由生成它的模型自述。理由也直接——生成者與校驗者必須是兩個執行體,否則那句「已同步」永遠不能被證僞。

核心保證

README 列了七項機制,針對的是批量更新文檔時常見的崩點。

  • 默認零寫入:不帶 --apply 一律只報告,staging 放在系統臨時目錄。
  • 單一提交點:project_docs/_manifest.json 的那一次原子替換是文檔代際的唯一 commit point。
  • 不覆蓋併發寫入:發佈走 no-replace rename,Windows 是 os.rename,Linux 是 renameat2(RENAME_NOREPLACE)。
  • 失敗必須分類:只報 NOT_INSTALLED / INSTALLED / UNKNOWN / PARTIALLY_INSTALLED,不會虛構「已提交」。
  • 不污染工作副本:鎖、事務 journal、CAS 產物都在臨時目錄。
  • 刪除是舉證而非清理:只有源碼目錄確認消失、零入站引用、帶精確 marker 且已註冊時才產生隔離候選,默認只報告。
  • 冪等:零變化運行輸出 planned_writes == [],不刷新 last_synced。

安裝

插件提供三種安裝方式,以下命令照錄 README 原文。

方式一,DSH bundle,推薦路徑。在 Plugins → More → Custom plugin markets → Discover 里加入市場索引地址:

https://walvka-code.github.io/proj-doc-builder/index.json

同一份索引也發佈在 https://raw.githubusercontent.com/walvka-code/proj-doc-builder/main/index.json,但部分網絡無法直連該域名,以 GitHub Pages 地址爲準。不想用市場可以釘住某個 commit,bundle 是倉庫裏的一個包目錄:

git+https://github.com/walvka-code/proj-doc-builder.git#<40 位 commit>

也可以用 plugin_manager 裝本地檢出:

{ "action": "install_bundle", "target": "C:/path/to/proj-doc-builder" }

方式二,當項目技能用。把 skills/proj-doc-builder/ 複製到 <project>/.dsh/skills/proj-doc-builder/,SKILL.md 按宿主報告的技能目錄解析工具路徑,兩種佈局都成立。

方式三,當普通 CLI 用,不依賴 agent 框架:

git clone <this-repo> proj-doc-builder
SKILL="$(pwd)/proj-doc-builder/skills/proj-doc-builder"
python "$SKILL/tools/sync_project_docs.py" /path/to/your/project --dry-run --json

運行條件來自 README:Python 3.12+,運行時只用標準庫;測試需要 pytest,見 requirements-dev.txt。bundle 聲明來自 package.json:node >= 20,dsh >= 0.2.1-rc.5。項目側前置條件是根目錄有 project_docs/ 和 CLAUDE.md 兩個入口。

典型用法

兩個腳本分工明確:sync_project_docs.py 生成和發佈索引,load_docs.py 給新對話提供入口。先 dry-run 看變更、確認後 --apply,是 README 給出的固定姿勢:

# 技能目錄:SKILL.md 和 tools/ 都在這裏
SKILL="$(pwd)/skills/proj-doc-builder"

# 同步文檔索引:先看變更,確認後再發布
python "$SKILL/tools/sync_project_docs.py" . --dry-run --json
python "$SKILL/tools/sync_project_docs.py" . --apply --json

# 給新對話一個入口:先概覽,再按源碼目錄定位
python "$SKILL/tools/load_docs.py" . --json
python "$SKILL/tools/load_docs.py" . --mode target --target src/core --json

加載器的取用粒度更細:

# 全部文檔、只要元數據
python "$SKILL/tools/load_docs.py" . --limit 0 --sections 0 --json

# 定位到一份文檔(源碼目錄、文檔路徑、工程相對路徑都可)
python "$SKILL/tools/load_docs.py" . --mode target --target project_docs/src/core.md --json

# 只取某一份裏的某一節
python "$SKILL/tools/load_docs.py" . --mode target --target src/core --section 依賴關係 --json

加載器的核心是先校驗再加載:manifest 與磁盤文檔不一致,比如被改動過、多出未註冊文檔,就以退出碼 1 報 manifest-stale,不會拿陳舊索引去服務模型。生成側退出碼:0 全過,1 閘門失敗(含存在待恢復事務),2 輸入或運行錯誤,3 發佈失敗。3 不等於未發佈,以 publish.disposition 爲準。

工作流與數據模型

生成過程分六個階段,協調器不生成自然語言,正文由生成方寫,它只做校驗和事務發佈。

  1. 預檢:確認項目根有 project_docs/ 和 CLAUDE.md,無待恢復事務。
  2. --dry-run 得到變化分類和工作清單,鍵是 scan.worklist.scaffold。
  3. 生成方按清單逐份寫正文,各份過 --strict 校驗。
  4. 全局 dry-run 閉環,做斷鏈、阻斷項和 manifest 覆蓋面檢查,要求 validation.blocking_failures == []。
  5. 退役報告:列出源碼已消失且零引用的文檔,默認只報告。
  6. --apply 發佈,產出 manifest、CLAUDE 文檔樹和 scan state,終驗要求 planned_writes == []。

數據落在三個位置。project_docs/_manifest.json 是 schema v2,每份文檔記錄 path、depth、title、status、bytes、sha256、synced_at,另給 generation_id。project_docs/_scan_state.json 是源碼目錄快照,README 直接稱它爲「改動跟蹤基線」。CLAUDE.md 有六組 marker,工具只替換第一組 doc-tree-instruction,其餘只校驗 marker 唯一、成對、不交疊。

平臺與測試

平臺支持按 README 表格:Windows 已驗證,說明提到重解析點與 junction 識別、句柄級刪除、msvcrt 鎖;Linux 已驗證,說明提到 fcntl 鎖、逐級 O_NOFOLLOW、renameat2 和目錄 fsync;macOS 未驗證,有 renamex_np 分支但沒有 CI。兩處細節在原文裏被單獨強調。

Linux 上 renameat2(RENAME_NOREPLACE) 是硬依賴,不可用時拋 NoReplaceRenameUnsupported,明確拒絕回退到會覆蓋的 rename。目錄 fsync 被拒時降級但不中斷,報告暴露 durability-degraded,真實 I/O 錯誤仍上拋。

測試運行方式:

python -m pytest skills/proj-doc-builder/tests -q

README 寫有 360 個用例,Windows 上 354 passed、6 skipped,全部用 tmp_path,不觸碰真實 project_docs/。用例鎖定的不是功能而是崩潰一致性:manifest 之前失敗必須完整回滾,之後失敗只准報 installed / partial / unknown;覆蓋僞造 journal 槽位、ABA 競態、staging 期間文檔被併發修改。

取捨也寫在 README 裏:事務 journal 和 CAS 產物目錄必須與項目根同一文件系統,跨盤會讓 --apply 直接拒絕;路徑只接受相對 POSIX、小寫 .md,拒絕絕對路徑、盤符、反斜槓、..、ADS、保留設備名和大小寫衝突;不跟隨 symlink、junction、reparse point;--full 只是掃描器的 force_full 開關,不等於逐份盤點文件清單。

適用場景與注意

適合這類場景:接手陌生代碼庫,需要快速建立結構認知;希望新起的 AI 會話直接加載工程畫像;希望文檔同步能進 CI,且結論由 manifest 提供,不靠模型自述。

幾條提醒。插件以當前 DSH 進程權限運行,發佈文檔和替換 CLAUDE.md marker 都是寫盤動作,安裝前建議打開源碼和許可證覈對行爲。許可證是 MIT。需要釘版本或離線分發時,用 git+https://... #<commit> 或本地 checkout,不要依賴市場地址的可用性。macOS 上行爲未經驗證。文檔退役是舉證流程而非清理工具,默認只報告。

結尾

proj-doc-builder 解決的不是文檔不夠多,而是文檔無法被證僞。它給出的三樣東西都寫在文件裏:一份工具寫入的 manifest、一份內容哈希基線、一次原子發佈的事務邊界。換來的是每一次「文檔已同步」都能被獨立複覈,而不需要信任寫文檔的那一方。

  • 插件目錄頁:https://www.skillhub.cn/plugins/walvka-code/proj-doc-builder
  • GitHub:https://github.com/walvka-code/proj-doc-builder
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜