為什麼這四個 patch 值得學
v0.9.66~v0.9.69(2026-09-22 至 2026-09-26)四天內連續發布,主題可以分成三條線:
- 廣度:一次補進五個語言 extractor(擷取器),並加深 OCaml、Elixir、Fortran、Julia、Kotlin 的結構擷取。
- 可靠性:
graph.json的逐次一致性(determinism,決定性)、Windows 上的 re-exec 修正,以及增量更新不再默默丟掉跨檔邊。 - 安全:修掉
/graphify add … --watch流程中的 shell injection(命令注入)。
對團隊的實務意義:知識圖譜工具的可信度來自「每次跑出來都一樣、且沒有靜默遺漏」;這批 patch 兩者都在補。
新增五個語言 Extractor(v0.9.66)
| 語言 | 副檔名 | 擷取重點 | 安裝 |
|---|---|---|---|
| COBOL | .cbl / .cob / .cobol / .cpy |
program、paragraph、PERFORM / CALL / COPY;純 regex 實作,不新增相依 |
內建 |
| VB.NET | .vb |
大小寫不敏感的型別 / 方法、Inherits / Implements / Handles |
對應 extra |
| R | .r / .R |
assignment-form 函式定義、library / source、S4 / R6 class |
r extra(無獨立 PyPI wheel) |
| Solidity | .sol |
contract / interface / library、is 繼承、import、modifier |
對應 extra |
| Erlang | .erl / .hrl / .escript |
module、以 arity(參數數量)區分的 function、behaviour、本地與遠端 foo:bar() 呼叫 |
erlang extra(無獨立 PyPI wheel) |
R 與 Erlang 的 grammar 沒有獨立 PyPI wheel,因此走 r / erlang extras;Solidity 與 VB.NET 也有各自的 extra。
語言總數變化:v0.9.66 在既有 36+ 種語言的基礎上新增五個 extractor。release notes 未公布精確總數;上游 README 在 v0.9.65 與 v0.9.69 都維持「約 40 種語言、37 個 tree-sitter grammar」,並未隨這次新增更新數字,因此本頁以新增清單為準,不主張新的總數。
既有語言的結構深度(v0.9.69)
v0.9.69 把五個語言的擷取從「有節點」推進到「有正確的關係」:
- OCaml:class 的方法以
method關係呈現,instance variable(實例變數)一併擷取 - Elixir:
defprotocol/defimpl抽成容器(container)節點,函式掛在容器下,並帶同檔implements邊 - Fortran:derived-type 的
containsblock 把 type-bound procedure 連到型別,並解析=> impl目標 - Julia:macro 定義與
@enum型別(含red = 1的有值形式與Color::UInt8的有型別形式),成員使用case_of關係 - Kotlin:annotation(class / function / property、use-site target)與 primary constructor 的
val/varproperty 開始產生邊
跨檔 Resolution(v0.9.66、v0.9.69)
- Go:interface 的 method requirement(
type Foo interface { Bar() })解析到 interface 本身,且其參數 / 回傳型別發出references邊(#3672、#3737) - Rust:簡單 generic impl(
impl<T> Foo<T>)的self.method()跨檔解析持續(#3653) - TypeScript:solution-style(方案式)
tsconfig.json本身只有references、沒有paths時,改為解析被引用專案設定裡的 path alias;tsc -b的專案佈局中,alias import 不再懸空(#3753、#3745)
決定性與 Windows 修正(v0.9.66 → v0.9.68)
這段是三個版本接力修同一件事,很值得當教材看:
| 版本 | 內容 |
|---|---|
| v0.9.66(#3743) | update / extract / cluster-only / label 以一次性 re-exec pin PYTHONHASHSEED,讓對 hash seed 敏感的 Leiden / Louvain 社群偵測輸出逐次一致,也與 hook 觸發的 rebuild 相符 |
| v0.9.67(#3780) | 改以 python -m graphify 重啟,而不是重播 argv[0];修好 Windows 上透過 console-script .exe launcher 無法重啟的回歸 |
| v0.9.68(#3816、#3799) | os.execvpe 只在 POSIX 是真正的行程替換;Windows 上它 spawn 新行程後父行程繼續前進、未被 pin,導致 update / extract / cluster-only / label 崩潰。Windows 路徑改走 subprocess.run 並以子行程 exit code 結束,POSIX 不變 |
教訓:跨平台的「重新啟動自己」沒有可攜寫法。
exec*的語意差異(行程替換 vs. 生成子行程)是典型的 silent portability trap(可攜性陷阱)。
安全:Shell Injection 修復(v0.9.68,#3742、#3642)
/graphify add … --watch 的參考實作會把 agent 代入的 INPUT_PATH 佔位符未加引號地寫進 shell 指令(… -m graphify.watch INPUT_PATH)。若 scan root 含有 $(…)、backtick 或 ;,就會被 shell 當成指令執行——這是同一個 skill 流程中 Step 1 修復的延伸(follow-on)。
修法:watcher 不再接受可替換的路徑,改讀 Step 1 已解析、受信任的 graphify-out/.graphify_root。
注意:Aider / Devin 的 monolith --watch 片段仍有同一佔位符,上游以另案追蹤。
增量更新與圖譜正確性(v0.9.68)
- 保留跨檔邊(#3812、#3776):增量更新(
update/extract --code-only/watch)保留目標符號位於未變檔案的跨檔imports/calls/uses邊——symbol-resolution facts 的 target index 加入唯讀的 resolution context,重萃取單一檔案不會再默默丟掉它連向圖譜其餘部分的邊;真正被移除的邊仍會 prune - C# 型別解析(#3815、#3795):
inherits/implements/ 參數 / 回傳 / base type 不再解析到同名的非型別節點(同名 enum member、property、field、method 被排除出 type-definition index);沒有contains邊的 partial class 解析也恢復 - Dedup(#3786、#3775):合併重複節點時,保留有真實 source location 的節點,而不是較豐富但無來源的推論 / 外部 stub;loser 的屬性仍會折到 survivor
其他可靠性修正(節錄)
- 建置系統:Maven
pom.xml的依賴可從本地<parent>繼承groupId/version,並展開${property}/${project.*}(離線、一層,v0.9.68/#3823) - Secret redaction:Terraform 的 secret 命名
variabledefault 與outputvalue 也會 redact(v0.9.68/#3817) - COBOL:固定格式(fixed-format)帶序號欄位時正確辨識(v0.9.68/#3813);
PERFORM A THRU/THROUGH Z兩端點都連calls邊(v0.9.68/#3814) - PHP:closure(匿名
function(){…}、arrowfn()=>…、作為引數傳入)成為節點並擷取內部呼叫;route closure 取得VERB /path語意名(v0.9.67/#3461) - Python:絕對 import(
import pkg.sub、from pkg.sub import x)解析到 scan root 內的本地 package / module(v0.9.67/#3729) - Process pool:spawn 無法 re-import
__main__(stdin、python -c、REPL、embedded 呼叫端)時先跳過,改走正確的 sequential run,而不是一堆BrokenProcessPool(v0.9.69/#3754) - Dedup self-loop:保留既有的 self-loop(遞迴呼叫、self-referential FK),只丟棄 merge 新產生的(v0.9.69/#3825)
- 匯出:
export不再每次重寫內容未變的 wiki / Obsidian 頁面,mtime 穩定、git / Obsidian 同步乾淨(v0.9.67/#3760)
升級建議
| 情境 | 建議 |
|---|---|
| Windows 使用者 | 務必升到 v0.9.68 以上;v0.9.66 / v0.9.67 的 hash-seed re-exec 在 Windows 上無效或競速崩潰 |
| 用到本次新增語言(COBOL / VB.NET / R / Solidity / Erlang) | 升級後重新萃取才會出現新節點 |
以 /graphify add … --watch 建圖 |
升級取得注入修復;確認流程不使用未引號的 INPUT_PATH 片段 |
| 長期以 watch / hook 維護 graph | 建議做一次完整 graphify update,讓跨檔邊與 manifest 由新版接手 |
需要逐次可重現的 graph.json |
升到 v0.9.68 以上,Leiden / Louvain 分群輸出才會穩定 |