v0.9.96

Graphify-Labs/graphifyv0.9.96Oct 26, 2026by safishamsi

Upgrading?

Release notes summary

Graphify 0.9.96 is a maintenance and security release that raises dependency floors for Pillow and pypdf and refreshes transitive dependencies to address Dependabot advisories. It also delivers a broad set of correctness fixes across LLM handling, watch mode, PR CI status, exports, deduplication, and multiple language extractors, plus performance improvements for update and watch workflows.

Key Highlights

  • Security dependency updates: Pillow >=13.0.0 on Python 3.11+ (>=12.3.0 on 3.10), pypdf >=6.19.0, and refreshed transitive dependencies.
  • LLM and watch fixes improve failed-chunk reporting, DeepSeek token caps, Ollama context sizing, and paper detection during watch quick scans.
  • Language extractor fixes for Python, TypeScript, JavaScript, Rust, Zig, Dart, VB.NET, XAML, and robot tests improve binding and node extraction correctness.
  • Performance: graphify update skips the pipeline when no relevant inputs changed, and watch rebuilds are bounded by GRAPHIFY_REBUILD_TIMEOUT.
  • CLI/export fixes include cost ledger recording for graphify extract, complete Obsidian parallel relations, and clearer PreToolUse hook reminders.

Breaking Changes

  • Minimum dependency versions changed: Pillow is now >=13.0.0 on Python 3.11+ (still >=12.3.0 on Python 3.10) and pypdf is now >=6.19.0, which may require dependency updates.

New Features

  • Configurable watch rebuild timeout via GRAPHIFY_REBUILD_TIMEOUT (default 600s; 0 disables).
  • No-op graphify update runs skip detect/extract/build when scan inputs, config, .graphifyrc, extractor epoch, and output completeness are unchanged.
  • graphify extract records per-run token usage to graphify-out/cost.json.
  • Improved extractor support for unnamed Dart extensions, chained TypeScript decorators, external JavaScript require bindings, scoped Rust use declarations, and Python member-call resolution.
  • Obsidian export now lists every parallel relation between two nodes instead of only the first edge.

Full Release Notes

## 0.9.96 (2026-10-25)

- Security: **deps** the Pillow floor is raised to `>=13.0.0` on Python 3.11+ (staying at `>=12.3.0` on 3.10, which Pillow 13 no longer supports) and pypdf to `>=6.19.0`, and the lockfile is refreshed to patched versions of transitive dependencies (pyjwt, cryptography, certifi, urllib3, anyio, fsspec, soupsieve, virtualenv). This clears the bulk of the open Dependabot advisories; the Pillow image-parsing CVEs are only patched upstream for Python 3.11+, so a 3.10 install keeps the newest 3.10-compatible Pillow.
- Fix: **llm** a chunk that failed before returning a response no longer has its files reported as model omissions; the warning distinguishes a failed chunk from a genuine omission, and both are still re-dispatched (#3616, #4752, thanks @jaimemartinez; reported by @GREYGROUPJP).
- Fix: **watch** a Markdown file that `detect()` classifies as a paper (arXiv markers, an Abstract) is included in the watch quick-scan rebuild instead of contributing zero nodes; a paper that already has a semantic layer is still excluded from the AST quick-scan (#4750, #4751, thanks @jaimemartinez; reported by @yeochul-jeon).
- Fix: **prs** every unfinished CheckRun status (`WAITING`, `REQUESTED`, `PENDING`, ...) counts as pending CI, so a PR gated on a deployment-protection job is no longer read as ready (#4730, thanks @rudycelekli).
- Fix: **export** an Obsidian note lists every parallel relation between two nodes (for example both `imports` and `calls`) instead of only the first edge (#4731, thanks @rudycelekli).
- Fix: **build** a hyperedge whose members are given under the `members`/`node_ids` aliases has its numeric member ids coerced to strings like the canonical `nodes` list, so the hyperedge is no longer dropped (#4732, thanks @rudycelekli).
- Fix: **llm** a code node whose confidence is written in a different case (`extracted`/`Extracted`) is still evidence-checked before it folds to the `EXTRACTED` enum, so an un-verified node is not presented as solid (#4733, thanks @rudycelekli).
- Fix: **python** a member call binds to the method it actually spells rather than a case/underscore-folded twin (`run()` no longer resolves to `_run()`); a lone folded method still resolves, and a genuine collision fails closed (#4734, thanks @rudycelekli).
- Fix: **zig** a call no longer crosses between a method and a same-named free function; a bare call binds the free function and a receiver call binds the method (#4755, thanks @ayushcodes10).
- Fix: **dart** an unnamed `extension on T { }` declaration is detected and extracted (the name is optional), so its members attach to the extended type (#4741, thanks @ayushcodes10).
- Fix: **llm** the DeepSeek backend sends its output cap as `max_tokens` (which DeepSeek honours) rather than `max_completion_tokens` (which it ignores), so responses are no longer silently uncapped (#4739, #4753, thanks @Ethereal49; reported by @snapp-kenya).
- Fix: **extract** XAML codebehind (`*.xaml` to `*.xaml.cs`) resolves deterministically regardless of directory iteration order, so a graph built on a case-sensitive filesystem is stable (#4747, thanks @anishraja04).
- Fix: **extract** the AST cache hit rate is reported over the file total, not the uncached set, so a warm run no longer prints a misleading "100% uncached" line (#4740, #4746, thanks @Deepjyoti-Sarmah; reported by @lavaxun).
- Fix: **dedup** a node id legitimately shared by two files as a `module`/`namespace` no longer prints a spurious "minted by two different files" warning (#4756, thanks @idachev).
- Fix: **python** the member-call resolver binds only Python callers to Python targets, so a same-named class in another language is no longer mis-bound as a call (#4757, thanks @idachev).
- Fix: **cli** the PreToolUse hook reminders are worded as facts rather than imperatives, so the agent's tool-result stream no longer reads them as prompt injection and ignores them (#2202, #4764, thanks @smngvlkz).
- Fix: **llm** the Ollama context window is sized from an estimate that counts non-Latin characters as whole tokens and from the real system-prompt length, so a non-English or long prompt is no longer silently truncated (#4414, #4761, thanks @smngvlkz).
- Fix: **cli** `graphify extract` records each run's token usage to `graphify-out/cost.json` (the same ledger the skill writes), so a standalone extract refresh updates the cost history (#1769, #4765, thanks @smngvlkz).
- Fix: **ts** chained TypeScript decorators (`@(Link().toMany())`) are recovered as a decorator reference, and a lossy-parse warning names the real error region (#4711, #4715, thanks @Deepjyoti-Sarmah).
- Fix: **js** a `const X = require('pkg')` binding for an external package keeps its node (with the package as source) so `new X()` still resolves, while a require of a corpus-local file still folds to the required module (#4688, #4702, thanks @ayushcodes10).
- Fix: **detect** generated MSBuild `obj/` and `bin/` output is pruned only when build evidence is present (restore/runtime markers nearby), so a C# project scanned after a build no longer ingests generated sources while a real `src/bin` stays (#4742, thanks @Great-Peace).
- Fix: **watch** each watch rebuild is bounded by `GRAPHIFY_REBUILD_TIMEOUT` (default 600s, `0` disables), with a thread-Timer fallback where `SIGALRM` is unavailable or `watch()` runs off the main thread (a `SIGALRM` arm there raises `ValueError` and would tear the watcher down before the rebuild ran), so a hung rebuild no longer stalls the watcher (#2173, #4724, thanks @danieldutu).
- Fix: **robot** each duplicate test-case name gets its own node (salted by line), so two tests with the same name are kept distinct (#4697, thanks @ayushcodes10).
- Fix: **vbnet** external namespaces and types emit a sourceless stub carrying an empty `source_file` sentinel like other languages, instead of a node that fails validation (#4725, #4728, thanks @madanlalit; reported by @alopezradicle).
- Fix: **rust** a `use` is scoped to the crate it names, so `use serde_json::Value` no longer resolves to a same-named type in another crate; a named `pub use` re-export is followed and a manifest-less binding stays `INFERRED` (#4699, #4712, thanks @Deepjyoti-Sarmah).
- Fix: **python** a call through a field injected from a constructor parameter, or through a local typed by a method's return annotation, resolves to the target method (`INFERRED`); it requires a single unambiguous producer and class (#4726, thanks @keny11300).
- Fix: **dedup** an LLM-marked cross-document reference stub that collides with its real definition is namespaced instead of silently dropped, so neither node is lost (#4738, #4743, thanks @SinghAman21; reported by @snapp-kenya).
- Perf: **update** a `graphify update` with no relevant input change skips the detect/extract/build pipeline and leaves the outputs untouched, keyed on the scan file-set, config, `.graphifyrc`, extractor epoch, and an output-completeness stamp; a scanned file with no manifest row (including one whose extraction previously failed) still forces a full rebuild so a later fix is never skipped (#4740, #4748, thanks @Deepjyoti-Sarmah; reported by @lavaxun).

Related Repositories