概述
兩個 patch 版本在同一天(2026-10-12)發布,且都以社群 pull request 為主:
| 版本 | 官方標題的一句話 | 規模 |
|---|---|---|
| v0.9.87 | Eighteen community pull requests:語言抽取修正(PHP、C#、Python、TypeScript、Julia)、retrieval 與 watch 硬化、skill/benchmark 正確性 | 18 PR |
| v0.9.88 | Twenty community pull requests:語言抽取與呼叫解析修正(Java、C/C++、Python、Swift、Go、PowerShell、C#、JS/EJS)、dedup/shrink guard 硬化、查詢與 graph.html 人體工學 | 20 PR |
主軸:
- 呼叫解析更接近各語言真正的語意——鏈式呼叫、跨檔繼承、跨目錄 include、套件層級的 import 錨定。
- 不再鑄造假的 stub 節點(Python 的
@property家族與Self、Swift 的Self、Go 的泛型型別參數、前方引用)。 - 新抽取器與可宣告的副檔名語意:EJS 抽取器、
.graphifyrc的language.<ext>。 - 寫入保護:dedup 之後仍不會誤刪語意節點,
update/export的 node id 語意更嚴謹。
呼叫解析:同名不再誤綁
- PHP(v0.9.87):
Class::method()的靜態呼叫改解析到被呼叫的方法,不再綁到 class 節點;同 namespace、不需useimport 的呼叫也不再停在 INFERRED 信心。self/static/parent與小寫開頭的 class receiver 都走 PHP resolver,任何歧義或語料庫外的 receiver 一律 fail-closed(#3872、#3874)。 - C#(v0.9.87):巢狀與泛型
partial型別改用完整型別路徑合併(C# 泛型 arity 標記如Outer`1.Inner),因此跨兩個檔案的巢狀 partial 會被合成一個,而Foo與Foo<T>保持不同;跨檔的非限定呼叫在「自己是唯一候選」時綁到呼叫者合併後的型別;有真實同名基底時,partial 上無來源的 placeholder 基底會被丟棄(#4363、#4364)。另外var改依被呼叫方法的回傳型別定型,讓後續的成員呼叫能解析(#4266、#4268)。 - Java(v0.9.88):鏈式、
new與 cast receiver 的呼叫(factory.builder().size(3).build()、new Builder().size(1)、((Builder) o).build())改沿方法鏈上宣告的回傳型別解析,且只在單一明確目標時才綁定(#4306、#4307);從跨檔父型別繼承而來的裸呼叫與欄位 receiver 解析到最近的宣告父型別(class 勝過 interface default,平手則不綁)(#4304、#4305)。 - TypeScript(v0.9.87):公開呼叫
c.name()只在 class 真的宣告了公開name時才連到#name節點,且foo與#foo共用一個節點時公開呼叫仍保留自己的邊(#4298)。 - JS/TS 匯入錨定(v0.9.88):裸或 scoped 的外部 import 改錨在套件名而非路徑末端——
@scope-a/utils與@scope-b/utils不再混同,lodash/fp/map收斂到單一lodash節點(#3084、#3092)。 - C/C++ include(v0.9.88):引號形式的跨目錄 include(
#include "../lib/foo.h"、STM32 的Core/Src到Core/Inc)改以唯一的最長共同目錄前綴匹配解析,同分 fail-closed,且對./、../、絕對路徑與角括號 include 永不猜測(#4354、#4392)。 - PowerShell(v0.9.88):帶引號或
$PSScriptRoot路徑的 dot-source include 開始產生 import 邊,function 之外的 script-scope 指令也被收為呼叫(#4356、#4369)。 - Julia(v0.9.87):parametric 與 module-qualified 的 functor(可呼叫 struct)型別以其型別命名,不再退回變數綁定(#4351)。
隨堂測驗
Java 的 `factory.builder().size(3).build()` 這種鏈式呼叫,v0.9.88 之後是如何解析的?
💡 想先看個提示?
鏈式呼叫的型別資訊藏在「每個方法回傳什麼」裡。
v0.9.88 原文:「a call on a chained, `new` or cast receiver now resolves through the declared return types on the method chain, binding only on a single unambiguous target」。鏈式呼叫的關鍵不是接收者是誰,而是每一步回傳什麼型別。
不再鑄造假的 stub 節點
一批修正的共同句型是「不再 mint 一個無來源(sourceless)stub」——這些節點過去會污染圖譜、甚至吞掉真正的節點:
- Python(v0.9.88):
@name.setter/.getter/.deleter裝飾器不再鑄造無來源 stub(#4380、#4385);Self(PEP 673)型別註解(-> Self、: Self、typing.Self)不再鑄造無來源的selfstub(#4381、#4386)。 - Swift(v0.9.88):
Self型別註解不再鑄造無來源selfstub(#4382、#4387)。 - Go(v0.9.88):簽名、struct 欄位與 interface 方法中的泛型型別參數(
[T any])不再鑄造無來源的t/rstub(#4383、#4388)。 - Python 前方引用(v0.9.88):在定義之前就被型別註記的 class(forward reference)不再鑄造與檔案節點碰撞、並把檔案節點吞掉的裸 stub(#4334、#4343)。
- C# verbatim 字串(v0.9.88):抽取器現在能從「以轉義引號結尾的 verbatim interpolated string」之後恢復成員宣告,不再靜默丟掉檔案其餘部分;修正只遮罩出問題的位元組,且若重寫後無法乾淨重解析就整批丟棄(#3376、#3612)。
- Python
@property讀取(v0.9.87,新功能):讀取@property(obj.prop,含x.prop += v)改為連結到 property 的 getter,不再被當成裸屬性存取而丟棄(#4337)。
新抽取器與可宣告的副檔名語意
- EJS 抽取器(v0.9.88):
.ejs樣板現在有專屬抽取器——scriptlet 函式/類別、require/import 邊、dynamic import,以及<%- include('partial') %>的組合邊;此前整個檔案只產生一個 top-level 解析錯誤(#3811、#4338)。 .graphifyrc的語言宣告(v0.9.87):專案可用language.<ext>=<language|.ext>這類行宣告模糊副檔名(.inc、.h、TS 家族)代表什麼語言。宣告會貫穿分類、抽取器選擇、imports、watch 與 AST cache;未宣告時偵測行為逐位元不變(#2961、#3789)。
隨堂測驗
專案裡有一個 `.inc` 檔案,Graphify 偶爾猜錯語言。v0.9.87 之後的正規解法是什麼?
💡 想先看個提示?
設定檔在 repo 根目錄,檔名以點開頭。
v0.9.87 原文:「a project can declare what an ambiguous extension (.inc, .h, TS-family) means via language-dot-ext lines in .graphifyrc」,而且「default detection is byte-for-byte unchanged when absent」——宣告是純加法。
查詢與圖形介面
graphify query --seed(v0.9.88):可指定遍歷的起始節點(ids、labels、callable 名稱或路徑),讓外部 ranker 自己決定起始集合;無法解析的 seed 會被回報並跳過,整組都無法解析時 fail-closed,不會退回關鍵字 seed(#4293、#4289)。graphify serve的點號查詢(v0.9.88):pkg.mod.func、pkg.mod:func這類 Python 查詢改以 module 路徑解析到符號,同檔歧義時 fail-closed,裸名查詢不受影響(#4366、#4367)。- graph.html:新增明/暗/跟隨系統主題切換,且暗色預設的前後輸出逐位元相同(#3894、#4300);側邊欄新增 hover tooltip,顯示被截斷的 legend/search/neighbor 全名與 legend 節點數,使用者文字以
textContent寫入以保留內嵌 JSON 的轉義(#4286、#4291)。 - MCP
query_graph的 seed 順序(v0.9.87):改以匹配強度排序,字母序較前的詞的弱匹配不再領先特定詞的精確匹配;每詞保證的 seed 仍在(#4357、#4358)。
寫入保護:dedup、shrink guard 與 node id
graphify affected <PR>(v0.9.87):影響範圍改以指定 PR 界定,不再用 repository 的預設分支;PR 預設分支的偵測獨立受測試(#3829)。update與export的 node id(v0.9.87):update在節點總數成長時拒絕丟掉已儲存的語意節點 id(視為不完整的重新抽取),但仍允許移除已消失的 import stub;export保留仍在圖中的整數節點 id(#4115)。_origin標記(v0.9.87):update skill 在語意層蓋上_origin,讓只跑 AST 的update不能刪除它沒有重新抽取的語意節點(#2843、#3110)。- dedup 的路徑限定(v0.9.88):語意 id 若丟掉了目錄,dedup 現在會做路徑限定——
a/x.md與b/x.md不再塌成一個而遺失其中一個節點;第三方檔案對裸 id 的引用則留為未解析並附警告,而不是猜測(#4281、#4362)。 - shrink guard 的豁免(v0.9.88):被 dedup 合法併入 AST 雙胞胎的語意節點不再被 shrink guard 誤擋;豁免範圍嚴格限定在「同路徑、同標籤、
_origin=="ast"的雙胞胎」,真正的部分建置遺失仍會被拒。LLM 節點/邊的欄位別名(file/filepath、relationship)會被折疊,且以標準欄位為準(#4389、#4391)。
watch、hook 與 install
- watch 的整批重建(v0.9.87):rebuild lock 忙碌時不再靜默丟棄整批重建,而是排入佇列(取代已排入的 incremental 路徑)並在進行中的 rebuild 之後清空;且只在真的排入時才印出 changes queued(#4274、#4276)。
- hook 的軟性提示(v0.9.87):read/search 的軟性提示改為每 session 有界,不再對每個符合的檔案都觸發(#3435、#3443)。
- update skill 的輸出(v0.9.87):只印出增量步驟的短摘要,完整結果留在
.graphify_incremental.json,不再把每個檔案路徑倒進 agent context(#3952)。 - install/uninstall 的邊界(v0.9.88):只移除自己的 hook 條目——以
hook-guard/hook-check指令鍵判斷,而不是寬鬆的 graphify 子字串,因此同群組的其他使用者 hook 與指令會被保留,且 uninstall 走備份(#4359、#4373);CLAUDE.md、AGENTS.md 與同類檔案在 install/uninstall 前後保留原本的行尾(CRLF 或 LF),不再被正規化為平台預設(#4360、#4372)。 - wiki 索引(v0.9.88):兩個 community 或 god-node 同名時,索引列改為各自連到自己的文章,而不是都指向第一個註冊的 slug 而讓另一篇孤立(#3032、#3033、#3095)。
- benchmark 與 labeling:benchmark 改為計算
query_graph實際回傳答案的 token(呼叫真正有預算的查詢路徑),而不是另一份無預算的重新實作(#4347、#4348);labeling 遵守GRAPHIFY_CLAUDE_CLI_MODEL環境變數,不再靜默退回 session 的預設模型(#3846、#3876)。
動手升級檢查清單
升級 v0.9.88:動手檢查清單
逐項勾選,確認你已完成升級必要的步驟(進度會保存在此瀏覽器)
全部完成!你已就緒 v0.9.88 的所有新功能。可清除勾選重新練習。
延伸閱讀
- 官方 v0.9.88 release notes
- 官方 v0.9.87 release notes
- 上一課:
v0980-to-v0986-resolution-and-platforms——解析正確性攻堅、OpenCode 2 與輸出安全 - 版本紀錄:本主題的 Changelog 頁有完整的 v0.9.87/v0.9.88 條目