# graphify changelog > graphify release notes. - Vendor: graphify - Category: AI - Official site: https://www.graphify.com/ - Tracked by: What's New (https://whatsnew.fyi/product/graphify) - Harvested from: GitHub (Graphify-Labs/graphify) - Entries below: 10 (newest first) What's New is an index, not a publisher: every entry below links to the vendor's own release notes, which are the authoritative source. Entries are labelled where they are hand-curated sample data, pre-releases, or drawn from a secondary source such as a developer blog. ## Releases ### v0.9.29 - Date: 2026-07-28 - Version: v0.9.29 - Original notes: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.29 - Permalink: https://whatsnew.fyi/product/graphify/releases/v0.9.29 - **fixed** — Absolute-path and machine-slug node ids are now canonicalized to root-relative node ids to ensure graph.json link endpoints are portable across machines and clones - **fixed** — The post-commit hook no longer overwrites an existing graph.json when it fails to read it; instead it refuses to write if the graph is over the size cap or unparseable - **fixed** — The post-commit hook launcher no longer displays a focus-stealing console window on Windows - **fixed** — False indirect_call edges from JavaScript and TypeScript closure arguments are removed by shadowing closure parameters with outer names - **added** — Scala self-type annotations now emit requires edges to the required traits - **fixed** — Rationale node labels are whitespace-normalized before the 80-character truncation to ensure clean labels and well-formed filenames - **changed** — Committed .env.example, .env.sample, and .env.template templates are now indexed instead of dropped by the sensitive-file filter - **changed** — Obsidian export no longer hides notes whose label starts with a dot, converting dot-prefixed labels appropriately - **changed** — The post-commit hook write is now atomic with a protected-graph backup - **fixed** — Real .env files and templates under a secrets directory remain excluded from indexing Portability, hook-safety, and resolution-accuracy fixes. **Node-id portability** - Absolute-path / machine-slug node ids no longer leak into edge endpoints (#2231, #2243). Module-top-level `indirect_call` sources, bash `source`/script-invocation targets, and other producers that minted an id from an absolute path are now canonicalized to the root-relative node id by a general backstop, so `graph.json` link endpoints are portable across machines and clones. **Hook safety** - The post-commit hook no longer overwrites an existing `graph.json` it merely failed to read (#2251). If the existing graph is over the size cap or unparseable, the rebuild refuses to write (matching the CLI) instead of replacing it with a code-only extraction; the `--no-cluster` write is now atomic with a protected-graph backup. - The post-commit hook launcher no longer pops a focus-stealing console window on Windows (#2253, thanks @hopstreax). **Resolution / extraction** - False `indirect_call` edges from JS/TS closure arguments are gone (#2241, thanks @Yyunozor): a closure parameter now shadows outer names, so `rows.map(r => ...)` no longer binds `r` to a corpus-wide callable of the same name. - Scala `self`-type annotations (`self: A with B =>`) emit `requires` edges to the required traits (#2052, thanks @Yyunozor). - Rationale node labels are whitespace-normalized before the 80-character truncation, so labels are clean and filenames aren't malformed (#2206, thanks @Yyunozor). **Filtering / export** - Committed `.env.example` / `.env.sample` / `.env.template` templates are indexed instead of dropped by the sensitive-file filter, while real `.env` files (and templates under a secrets directory) stay excluded (#2184, thanks @SyedFahad7). - Obsidian export no longer hides notes whose label starts with a dot (`.env` -> `dot-env`; an all-dot label falls back to `unnamed`) (#2205, thanks @SyedFahad7). ### v0.9.28 - Date: 2026-07-27 - Version: v0.9.28 - Original notes: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.28 - Permalink: https://whatsnew.fyi/product/graphify/releases/v0.9.28 - **fixed** — Incremental runs no longer drop cross-file edges whose target file wasn't in the batch by canonicalizing Python relative imports and markdown reference links to root-relative node IDs - **fixed** — Incremental extraction no longer prunes alive files as deleted by comparing manifest paths with NFC normalization and performing liveness checks - **fixed** — --update on macOS no longer re-extracts everything when the corpus path or filename contains non-ASCII characters by normalizing manifest keys to NFC - **fixed** — Incremental rebuilds no longer reuse stale community labels and graphs that outgrow the visualization cap now keep an aggregated view instead of deleting graph.html - **fixed** — graphify benchmark, the graph merge-driver, and the call-flow HTML export no longer crash or fail on a --no-cluster graph.json that stores edges under edges rather than links - **fixed** — claude/gemini/codebuddy uninstall no longer delete the user-global skill when called with a project_dir, and graphify uninstall --project no longer deletes the global codebuddy skill - **fixed** — Swift computed and observed properties including var body, get/set, and willSet/didSet now emit graph nodes so SwiftUI views are no longer erased Fixes for incremental extraction correctness, graph loading, uninstall scoping, macOS paths, and Swift extraction. **Incremental extraction** - Incremental runs no longer drop cross-file edges whose target file wasn't in the batch (#2211, #2213). Python relative imports and markdown reference links emitted absolute-path target ids without the stamp the incremental canonicalization needs, so a re-extracted file's imports/references dangled or vanished. Both now canonicalize to the root-relative node. - Incremental extraction no longer prunes alive files as "deleted" (#2210). The stale-source check compared paths with a raw string test (no Unicode NFC) and pruned non-matches without a liveness check, so macOS NFD paths and legacy basename spellings lost their nodes. It now compares NFC on both sides and is fail-closed. - `--update` on macOS no longer re-extracts everything when the corpus path or a filename contains non-ASCII characters (#2221, thanks @SyedFahad7). Manifest keys are NFC-normalized. - Incremental rebuilds no longer reuse stale community labels, and a graph that outgrows the visualization cap now keeps an aggregated view instead of deleting `graph.html` (#2218, thanks @bobspryn). **Other fixes** - `graphify benchmark`, the graph merge-driver, and the call-flow HTML export no longer crash or silently fail on a `--no-cluster` `graph.json` (#2212), which stores edges under `edges` rather than `links`. - `claude`/`gemini`/`codebuddy` uninstall no longer delete the user-global skill when called with a `project_dir` (#2215); this also fixes `graphify uninstall --project` deleting the global codebuddy skill. - Swift computed and observed properties (`var body: some View { ... }`, `get`/`set`, `willSet`/`didSet`) now emit graph nodes, so SwiftUI views are no longer erased (#2181, thanks @ozdemirsarman). ### v0.9.27 - Date: 2026-07-26 - Version: v0.9.27 - Original notes: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.27 - Permalink: https://whatsnew.fyi/product/graphify/releases/v0.9.27 - **fixed** — claude/gemini/codex/codebuddy install no longer overwrite settings/hooks files they cannot parse, now reading utf-8-sig, refusing to modify non-JSON-object files, and backing up to .graphify-bak before any write - **fixed** — Incremental extract --no-cluster no longer overwrites the full graph with just changed files, instead merging the existing graph forward with replace/prune semantics and canonicalizing cross-file edge targets - **fixed** — Running the test suite no longer touches the developer's real ~/.claude/~/.gemini/~/.codebuddy/~/.copilot directories - **changed** — stat-index.json is now stored with root-relative keys re-anchored on load and pruned of deleted-file entries so a moved or cloned corpus keeps cache hits - **fixed** — JavaScript/TypeScript regex-rescued imports in Svelte/Astro/Vue no longer create ghost target nodes with absolute-path ids - **fixed** — Cross-file concept nodes with identical normalized labels now merge - **fixed** — Absolute source_file paths no longer break node identity and build_from_json folds legacy field aliases so alias-carrying nodes stop entering the graph invisible and unmergeable - **changed** — C# member calls on a typed receiver now resolve namespace-aware with base./this.field receivers and inherited-member lookup through the inherits chain - **added** — Python decorators now create graph edges so affected finds what a decorator touches, with builtin/stdlib decorators excluded - **changed** — JavaScript/TypeScript non-relative imports resolve through jsconfig.json/tsconfig.json baseUrl and paths - **changed** — Swift/Foundation/SwiftUI builtins are filtered from call resolution and god-node ranking - **fixed** — graphify query/explain no longer fabricate indirect_call edges to class definitions - **changed** — Bash calls into a source'd file resolve for extensionless scripts and bare source lib.sh - **changed** — Bash source "${VAR}/lib.sh" resolves against the variable's real directory - **fixed** — SQL routines with PL/pgSQL-only bodies that tree-sitter cannot parse are recovered by a raw-text scan gated on a failed parse - **fixed** — The git-hook interpreter allowlist accepts Windows backslash paths and interpreter paths containing a space - **fixed** — The rebuild timeout is armed on Windows via a threading fallback - **fixed** — The extraction process pool is skipped when only one worker is available - **changed** — The Codex PreToolUse hook is documented as an intentional no-op A large maintenance release: install-safety fixes, node-identity/canonicalization fixes, cross-file resolution improvements, and a batch of community contributions. **Install and data safety** - `claude`/`gemini`/`codex`/`codebuddy install` no longer overwrite a settings/hooks file they cannot parse (#2167). On any JSON parse error they used to fall back to an empty config and rewrite the whole file, destroying the user's settings (most often triggered by a UTF-8 BOM). They now read `utf-8-sig`, refuse to modify a non-JSON-object file, and back up to `.graphify-bak` before any write. - Incremental `extract --no-cluster` no longer overwrites the full graph with just the changed files (#2169). It now merges the existing graph forward with the same replace/prune semantics as the clustered path and canonicalizes cross-file edge targets. - Running the test suite no longer touches the developer's real `~/.claude`/`~/.gemini`/`~/.codebuddy`/`~/.copilot` (#2168). **Node identity and caching** - `stat-index.json` is stored with root-relative keys (re-anchored on load, mirroring `manifest.json`) and pruned of deleted-file entries, so a moved or cloned corpus keeps its cache hits instead of re-extracting everything (#2199). - JavaScript/TypeScript regex-rescued imports (Svelte/Astro/Vue) no longer create ghost target nodes with absolute-path ids (#2195). - Cross-file `concept` nodes with identical normalized labels now merge, matching the behavior already applied to near-identical labels (#2182). - Absolute `source_file` paths (for example from a Windows scan) no longer break node identity (#2197), and `build_from_json` folds legacy field aliases (`name`/`path`/`type`/`confidence_score`) so alias-carrying nodes stop entering the graph invisible and unmergeable (#2194). **Resolution** - C# member calls on a typed receiver now resolve namespace-aware, with `base.`/`this.field` receivers, inherited-member lookup through the `inherits` chain, and shadow-poisoning so a local shadowing a field of a different type no longer produces a wrong edge (#1609, adapted from #1620 by @TheFedaikin). - Python decorators now create graph edges so `affected ` finds what a decorator touches, with builtin/stdlib decorators excluded (#2154, thanks @Rishet11). - JavaScript/TypeScript non-relative imports resolve through `jsconfig.json`/`tsconfig.json` `baseUrl` and `paths` (#2153, thanks @Rishet11). - Swift/Foundation/SwiftUI builtins are filtered from call resolution and god-node ranking (#2147, thanks @MasterFede5). - `graphify query`/`explain` no longer fabricate `indirect_call` edges to class definitions (#2137, thanks @Rishet11). - Bash calls into a `source`d file resolve for extensionless scripts and bare `source lib.sh` (#2171), and `source "${VAR}/lib.sh"` resolves against the variable's real directory (#2172). Both thanks @Souptik96. - SQL routines with PL/pgSQL-only bodies that tree-sitter cannot parse are recovered by a raw-text scan, gated on a failed parse (#2180, thanks @Souptik96). **Windows and hooks** - The git-hook interpreter allowlist accepts Windows backslash paths (#2126, thanks @Rishet11) and interpreter paths containing a space (#2166, thanks @Souptik96); the rebuild timeout is armed on Windows via a threading fallback (#2148, thanks @Rishet11); the extraction process pool is skipped when only one worker is available (#2173, thanks @Souptik96). - The Codex `PreToolUse` hook is documented as an intentional no-op (#2165, thanks @Souptik96). ### v0.9.26 - Date: 2026-07-24 - Version: v0.9.26 - Original notes: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.26 - Permalink: https://whatsnew.fyi/product/graphify/releases/v0.9.26 - **fixed** — graphify query/explain no longer fabricate indirect_call edges to class definitions; classes are now excluded from indirect_call in both the intra-file and cross-file paths while direct instantiation still emits its calls edge - **fixed** — The post-commit hook's interpreter allowlist now accepts Windows backslash paths by using a verified character class that admits backslashes while still rejecting shell metacharacters - **fixed** — The hook rebuild timeout is now armed on Windows using a threading.Timer fallback to terminate runaway rebuilds where SIGALRM is unavailable - **fixed** — Bash calls into functions defined in a sourced file now get calls edges; both source file and . file are handled - **fixed** — Bash source edges built from a variable path now resolve by stripping the leading expansion and resolving the literal suffix against the script's directory - **fixed** — Ignore files saved with a UTF-8 BOM are now honored by reading them as utf-8-sig instead of utf-8 Maintenance release. Correctness fixes across Python call-graph inference, the git hook (Windows), and bash source resolution. **Fixes** - `graphify query`/`explain` no longer fabricate `indirect_call` edges to class definitions (#2137, thanks @Rishet11). Passing a class as a value (`select(Model)`, `db.get(Model, id)`, `except (ErrorA, ErrorB)`, `getattr(obj, "Name", 0)`) produced a false inferred call edge; classes are now excluded from `indirect_call` in both the intra-file and cross-file paths, while direct instantiation still emits its `calls` edge. - The post-commit hook's interpreter allowlist now accepts Windows backslash paths (#2126, thanks @Rishet11). The shell `case` glob silently emptied any interpreter path containing a backslash, so the hook failed on Windows uv/venv installs. Both allowlist sites use a verified character class that admits backslashes while still rejecting shell metacharacters. - The hook rebuild timeout is now armed on Windows (#2148, thanks @Rishet11). It relied on `signal.SIGALRM`, which does not exist on Windows, so `GRAPHIFY_REBUILD_TIMEOUT` was a silent no-op and a hung rebuild ran unbounded. A `threading.Timer` fallback now terminates a runaway rebuild where SIGALRM is unavailable; the Unix path is unchanged. - Bash calls into functions defined in a `source`d file now get `calls` edges (#2141, thanks @HerenderKumar). Resolution was gated on same-file definitions, so a call to a sourced-library function looked like an external command and produced no edge. Both `source file` and `. file` are handled; resolution is in-corpus and single-match only, so a genuine external command still fabricates nothing. - Bash `source` edges built from a variable path now resolve (#2079, thanks @HerenderKumar). `source "${BENCH_DIR}/lib/x.sh"` baked the unexpanded `${VAR}` into a dead node id; the leading expansion is stripped and the literal suffix resolved against the script's directory, emitted as INFERRED only when it resolves to a real file. Calls into a `${VAR}`-sourced library resolve too. - Ignore files saved with a UTF-8 BOM are now honored (#2163). `.gitignore`/`.graphifyignore`/`info/exclude` were read as `utf-8`, so a leading BOM stayed on the first line and silently dropped the first pattern. The ignore read sites now use `utf-8-sig`, matching git. ### v0.9.25 - Date: 2026-07-22 - Version: v0.9.25 - Original notes: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.25 - Permalink: https://whatsnew.fyi/product/graphify/releases/v0.9.25 - **changed** — graphify is now licensed under the Apache License, Version 2.0 instead of MIT - **removed** — Remove .graphifyinclude handling as it had been non-functional since dot directories became indexed by default Maintenance release: a license change to Apache 2.0 and a dead-code removal. **License** - graphify is now licensed under the Apache License, Version 2.0 (previously MIT). Apache 2.0 adds an explicit patent grant, a patent-retaliation clause, and explicit inbound-contribution terms. Contributions made before the relicensing were submitted under MIT and remain available under those terms; the original MIT text is retained in `LICENSE-MIT` and referenced from `NOTICE`. **Removed** - `.graphifyinclude` handling is gone (#2112). The file had been non-functional since dot directories became indexed by default (#873): its loader and matchers had no consumers, so `detect` parsed the file on every run and then discarded the result, making a `.graphifyinclude` a silent no-op. The dead loader and matchers are deleted, a leftover `.graphifyinclude` no longer appears in the `unclassified` list, and `detect` prints a one-time note when one is present at the scan root. To re-include ignored paths, use `!` negation patterns in `.graphifyignore`. ### v0.9.24 - Date: 2026-07-22 - Version: v0.9.24 - Original notes: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.24 - Permalink: https://whatsnew.fyi/product/graphify/releases/v0.9.24 - **fixed** — The XAML code-behind .cs scan is now bounded and prunes noise directories, so it can no longer hang on large or shared parent directories - **fixed** — The sensitive-file filter no longer silently drops legitimate topic docs and source files, and now correctly identifies genuine secrets while preserving prose files whose slug merely ends in a keyword - **fixed** — Both graphify extract and the skill flow now name the skipped files instead of only printing a count - **fixed** — calls edges now resolve through aliased Python imports so downstream alias.func() calls are properly recorded - **fixed** — dedup preserves a node's attributes when two exact-ID records from the same source file collapse, retaining non-conflicting attributes deterministically - **fixed** — The claude-cli backend now reads the CLI's structured-output channel instead of free-form prose - **fixed** — graphify explain on a high-degree node groups the cut connections by file instead of showing a bare and N more message - **fixed** — graphify query and MCP query_graph no longer print calls edges backwards - **added** — get_neighbors and get_community (MCP) now honor a token_budget parameter (default 2000) to prevent flooding the client's context on god nodes or large communities - **added** — Truncation of results is announced at the top of the output when token_budget is exceeded - **changed** — --code-only is now surfaced in the extract usage text and README Maintenance release. Correctness fixes across extraction, dedup, query rendering, and the sensitive-file filter, plus a hang fix in the .NET/XAML path. **Fixes** - The XAML code-behind `.cs` scan is now bounded and prunes noise directories, so it can no longer hang. A standalone extraction on a `.xaml` under a large or shared parent (a temp dir, a big monorepo) could resolve the project root to a broad ancestor and recursively scan the whole tree. It now walks with `node_modules`/`.venv`/`.git`/dot-dir pruning and a directory cap: a real project scans fully, a runaway root degrades to a fast partial scan. - The sensitive-file filter no longer silently drops topic docs and real source (#2106). Prose files whose slug merely ends in a keyword (`privacy-tokens.md`) and real source like `service_account.py` were dropped with no trace, while some genuine secrets (`.npmrc`, `.pypirc`, `.git-credentials`, case variants) were missed. The filter is now stricter on real secrets and no longer loses legitimate files, and both `graphify extract` and the skill flow now name the skipped files instead of only a count. - `calls` edges now resolve through an aliased Python import (#2082, thanks @Yyunozor). `from pkg import mod as alias` recorded the import but dropped every downstream `alias.func()` call, so the callee looked like dead code. - `dedup` preserves a node's attributes when two exact-ID records from the same source file collapse (#2091, thanks @Synvoya). Non-conflicting attributes are retained deterministically, records from different files stay isolated, and a dropped record can never stamp a false origin onto the survivor. - The `claude-cli` backend now reads the CLI's structured-output channel instead of free-form prose (#2076, thanks @Yyunozor), which had parsed to zero nodes and bisected forever on newer Claude Code. - `graphify explain` on a high-degree node groups the cut connections by file instead of a bare `... and N more` (#2009, thanks @Yyunozor). - `graphify query` and MCP `query_graph` no longer print `calls` edges backwards (#2080, thanks @Yyunozor); the renderer recovers the stored direction from the edge. **Features** - `get_neighbors` and `get_community` (MCP) now honor a `token_budget` (default 2000) so one call on a god node or large community can't flood the client's context (#2069, thanks @ojmucianski). Truncation is announced at the top of the output. **Docs** - `--code-only` is surfaced in the `extract` usage text and README (#2071, thanks @HerenderKumar). - README troubleshooting note for an older `graphifyy` in system site-packages shadowing `uv run --with graphifyy` (#1540, thanks @HerenderKumar). ### v0.9.23 — graphify 0.9.23 - Date: 2026-07-21 - Version: v0.9.23 - Original notes: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.23 - Permalink: https://whatsnew.fyi/product/graphify/releases/v0.9.23 - **fixed** — caller and call sites listings now report the actual call-site line, not the caller function's definition line - **fixed** — query no longer silently drops the answer past its output budget and now ranks nodes by hop distance from the query seeds with the seed always rendered first - **fixed** — query output now displays a prominent notice at the top stating how many of how many nodes were shown and how to widen the budget - **fixed** — graphify uninstall no longer deletes a user-authored ### graphify section by matching the heading only when a line is exactly the marker - **fixed** — graphify path and the MCP shortest_path tool now return a deterministic route by traversing over a sorted graph instead of a hash-seeded undirected view - **fixed** — graphify path and the MCP shortest_path tool now label each hop with the edge's actual stored relation instead of an arbitrarily-collapsed parallel edge - **fixed** — cluster-only --no-label no longer permanently suppresses real community labels by not persisting placeholder-only runs and treating stored placeholders as absent - **fixed** — build_from_json ghost-duplicate merge now keys on the full source path instead of the bare basename to prevent unrelated nodes from different files from being silently merged - **fixed** — Python import resolution no longer depends on the scan root by resolving absolute imports against nested package roots detected via the __init__.py chain - Fix: caller / "call sites" listings now report the actual call-site line, not the caller function's definition line. `explain`, `affected`, and the MCP `get_neighbors`/`query` tools printed the caller node's `source_location` (its `def` line) for an incoming call, so a precise-looking citation sent users to the wrong line. The `calls` edge already carries the true call-site line; every caller/relation listing now reads the traversed edge's `source_file`:`source_location`, falling back to the node's own line only when the edge has none. - Fix: `query` no longer silently drops the answer past its output budget. Rendered nodes were ordered by degree (so a low-degree definition node ranked last and was cut first), the queried symbol was not guaranteed to appear, and the truncation marker sat only at the end so silence read as absence. Nodes are now ranked by hop distance from the query seeds (deterministically), the seed the question named is always rendered first and never truncated, and a prominent notice at the TOP states how many of how many nodes were shown and how to widen the budget. (A branch merge had also silently dropped the seed-first ordering the renderer already supported; it is rewired.) - Fix: `graphify uninstall` no longer deletes a user-authored `### graphify` section (#2062). The uninstall strip used an unanchored `## graphify` pattern that matched inside a user's H3 heading (and the "already installed" guard was a substring test), so hand-written content was destroyed. The heading is now matched only when a line is exactly the marker (mirroring the install-side #1688 hardening), across all six strip sites (CLAUDE.md, AGENTS.md, GEMINI.md, copilot-instructions.md, CODEBUDDY.md, and the H1 skill registration). - Fix: `graphify path` (and the MCP `shortest_path` tool) now return a deterministic route and label each hop with the edge's actual stored relation (#2074). The route was computed over a hash-seeded undirected view, so it varied run-to-run among equal-length paths; and the printed relation was read from an arbitrarily-collapsed parallel edge, so it could show `calls` on a pair that only carries `references`. The traversal is now over a sorted graph, and each hop shows the real relation(s), falling back to an honest `related` when none is stored. - Fix: `cluster-only --no-label` no longer permanently suppresses real community labels (#2073). It wrote `Community N` placeholders (plus a matching signature) into `.graphify_labels.json`, which the reuse path then treated as fresh forever. Placeholder-only runs no longer persist the sidecar, a stored placeholder is treated as absent so already-polluted graphs self-heal, and the watch/update rebuild got the same treatment. - Fix: `build_from_json`'s ghost-duplicate merge now keys on the full source path, not the bare basename (#2068). Unrelated nodes from different files sharing a common basename (`index.md`, `README.md`) and a generic label were silently merged onto one survivor with their edges rewired, corrupting multi-corpus doc graphs. The legitimate AST/LLM same-file merge is preserved; cross-directory false merges are eliminated. - Fix: Python import resolution no longer depends on the scan root (#2072). A src-layout project (code under `src/`) lost most of its `imports`/`imports_from` edges when scanned from the repo root, because absolute imports resolved only against the scan root while file-node ids are scan-root-relative, so the dangling edges were silently dropped. Absolute imports now resolve against nested package roots (detected via the `__init__.py` chain), and import edges are repointed to the real file nodes, so the graph is identical whether scanned from the repo root or from `src/`. ### v0.9.22 — graphify 0.9.22 - Date: 2026-07-20 - Version: v0.9.22 - Original notes: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.22 - Permalink: https://whatsnew.fyi/product/graphify/releases/v0.9.22 - **fixed** — A node whose source_file is a URL/virtual scheme (gdoc://, s3://, http://, etc.) is no longer evicted on the second graphify update by tolerantly matching the scheme instead of checking for a literal "://" - **fixed** — A real source directory named env/.env/*_env is no longer silently pruned as a false-positive Python virtualenv; the venv heuristic for those names is now gated on an actual marker (pyvenv.cfg, an activate script, lib/python*, or conda-meta/), and every pruned-as-noise directory is recorded in a pruned_noise_dirs bucket for traceability - **fixed** — Office (.docx/.xlsx) and Google-Workspace sidecars are now named from the scan-root-relative path instead of the absolute path, making the sidecar name stable across checkouts - **fixed** — serve.py's "graph.json is corrupted" recovery message is now reachable by ordering the JSONDecodeError exception clause before the broader ValueError clause - **fixed** — graphify god-nodes/god_nodes is now a real CLI subcommand - **fixed** — graphify extract --output DIR is now honored as an alias of --out - **fixed** — A nested class/object/trait now gets its contains edge from the enclosing type instead of the file node, creating a proper nested containment tree - **fixed** — File nodes that share a basename now get a directory-qualified label so explain/discovery can tell them apart; colliding file nodes are relabelled to the shortest unique path suffix while unique basenames stay bare - Fix: a node whose `source_file` is a URL/virtual scheme (`gdoc://`, `s3://`, `http://`, ...) is no longer evicted on the second `graphify update` (follow-up to #2051). The #2051 disk-absence sweep guarded such sources with a literal `"://"` check, but write-side path normalization collapses the double slash (`gdoc://x` becomes `gdoc:/x`), so the guard missed the node on the next run and dropped it into the disk-absence eviction branch. The scheme is now matched tolerantly (and a Windows drive letter like `C:/` is not misread as remote). - Fix: a real source directory named `env`/`.env`/`*_env` is no longer silently pruned as a false-positive Python virtualenv (#2058). `detect`'s directory-noise heuristic matched those names before `.graphifyignore` negation and with no trace in any output bucket, so codebases using them as source dirs (common in UVM/ASIC verification) lost large subtrees undetectably. The venv heuristic for those names is now gated on an actual marker (`pyvenv.cfg`, an `activate` script, `lib/python*`, or `conda-meta/`); `venv`/`.venv`/`*_venv` stay name-only, and every pruned-as-noise directory is now recorded in a `pruned_noise_dirs` bucket for traceability. - Fix: Office (`.docx`/`.xlsx`) and Google-Workspace sidecars are now named from the scan-root-relative path, not the absolute path (#2059). The absolute-path hash salted the sidecar name with the checkout location, so committing `graphify-out/` (a supported workflow) produced a new duplicate `.md` per clone/worktree, each ingested as a distinct source document. The relative hash is stable across checkouts while still disambiguating same-stem files; the Google-Workspace sidecar path additionally gains the NFC normalization it was missing. - Fix: `serve.py`'s "graph.json is corrupted" recovery message is now reachable (#2005, thanks @kimdzhekhon). `json.JSONDecodeError` subclasses `ValueError`, and the broad `except (ValueError, FileNotFoundError)` clause was ordered first, so a truncated graph printed the bare `Expecting value...` instead of the documented rebuild hint. The `JSONDecodeError` clause now comes first. - Fix: `graphify god-nodes`/`god_nodes` is now a real CLI subcommand, and `graphify extract --output DIR` is honored as an alias of `--out` (#2004). `god_nodes` was an analyzer, an MCP tool, and a documented capability but had no CLI command; `--output` was silently dropped on `extract` even though `graphify tree` documents it. (The `affected`/reverse-dep import-id mismatch from the same report is tracked separately.) - Fix: a nested class/object/trait now gets its `contains` edge from the enclosing type instead of the file node (#2040). Across ~19 languages the edge was hard-coded to source from the file, so the containment tree was flat (`file -> Inner`) rather than nested (`file -> Outer -> Inner`); it now sources from the enclosing type when present, with top-level types still contained by the file. - Fix: file nodes that share a basename now get a directory-qualified label so `explain`/discovery can tell them apart (#2032). In directory-per-entrypoint repos (Supabase Edge Functions, Next.js `page.tsx`, Rust `mod.rs`, Python `__init__.py`) dozens of files named e.g. `index.ts` collided under one label, breaking free-text discovery for exactly those files. Colliding file nodes are relabelled to the shortest unique path suffix (`process-order/index.ts`); unique basenames stay bare, and node ids/edges are unchanged. ### v0.9.21 — graphify 0.9.21 - Date: 2026-07-20 - Version: v0.9.21 - Original notes: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.21 - Permalink: https://whatsnew.fyi/product/graphify/releases/v0.9.21 - **fixed** — graphify extract (headless, no --backend) now auto-detects Ollama from the standard OLLAMA_HOST env var, normalizing it the way the Ollama client does, while explicit OLLAMA_BASE_URL still takes precedence - **fixed** — Flag-less graphify extract now honors persisted --exclude patterns from .graphify_build.json instead of silently re-including them - **fixed** — Pathless --postgres extract for live database introspection no longer crashes before introspection by properly initializing the detection variable and guarding semantic-cache operations - **fixed** — Bare import aliases no longer collapse into file-level self-loops by dropping any imports/imports_from/re_exports edge whose endpoints are identical - **fixed** — Alias re-exports and imports through a barrel now resolve to the defining symbol by walking the barrel chain in a cycle-safe manner - **fixed** — Full graphify update now evicts semantic nodes whose non-code source file (doc, image, etc.) was deleted from disk - **fixed** — Incremental rebuild no longer treats a file that exists but has no AST extractor as a deletion, preserving such files while still evicting genuine on-disk deletions - **fixed** — Code-typed nodes that the semantic pass surfaces from within a document now count as that document's semantic layer, preventing re-scanning and dropping of those nodes - **fixed** — The --update runbook no longer marks a semantic file as done when its extraction produced no output, ensuring empty files are retried on the next update - **fixed** — build_merge now prunes a deleted file's nodes, edges, and hyperedges regardless of whether their stored source_file is absolute or relative - Fix: `graphify extract` (headless, no `--backend`) now auto-detects Ollama from the standard `OLLAMA_HOST` env var, not only graphify's `OLLAMA_BASE_URL` (#1940, thanks @kimdzhekhon). An explicit `OLLAMA_BASE_URL` still wins; `OLLAMA_HOST` is normalized the way the Ollama client does (adds `http://`, defaults the port to 11434 when omitted, appends the `/v1` OpenAI-compat suffix). Wired through both the client base URL and backend auto-detection, so ollama stays opt-in and never shadows a configured paid key. (Supersedes the vendored-bulk #1966.) - Fix: a flag-less `graphify extract` now honors the persisted `--exclude` patterns instead of silently re-including them (#2027, thanks @oleksii-tumanov). Mirrors the #1971 gitignore-persistence fix: the exclude set is read from `.graphify_build.json` when `--exclude` is absent and applied to the scan, and a flag-less run no longer clobbers it; an explicit `--exclude` still replaces the persisted list. - Fix: a pathless `--postgres` extract (introspect a live DB with no filesystem corpus) no longer crashes before introspection (#2030, thanks @oleksii-tumanov). The no-path branch left `detection` unbound; it's now initialized, the semantic-cache prune and manifest writes are guarded so a DB-only run can't wipe the file cache or leave a poisoning manifest, and a stale manifest from a prior filesystem run is invalidated. - Fix: bare import aliases no longer collapse into file-level self-loops (#2037, thanks @Endogen). A single-file import whose bare stem matched the file's own legacy id was remapped onto the importing file, producing a `source == target` self-loop reported as a 1-file import cycle. `build_from_json` now drops any `imports`/`imports_from`/`re_exports` edge whose endpoints are identical; pre-existing self-loops self-heal on the next rebuild. - Fix: alias re-exports and imports through a barrel resolve to the defining symbol (#1983, thanks @HerenderKumar). `import { X } from './barrel'` where the barrel re-exports `X` from another module now points at `X`'s real defining node, walking the barrel chain (bounded, cycle-safe). When a barrel re-exports the same local name from two different modules the name is ambiguous and left unresolved rather than guessed, so no wrong edge is fabricated. Builds on #1984. - Fix: a full `graphify update` now evicts semantic nodes whose non-code source file (a `.txt`/`.pdf`/`.png` with no AST extractor) was deleted from disk (#2051). The corpus sweep only checked files it could re-extract, so a deleted doc's or image's LLM-derived nodes survived indefinitely and were served as authoritative. Disk absence is now used as the deletion signal for such sources; remote and virtual sources (anything with a `://` scheme) are left untouched. - Fix: an incremental rebuild whose change set names a file that exists but has no AST extractor (a doc, paper, image, or an excluded path) no longer treats it as a deletion (#2056). The change-set loop routed any present-but-untracked file to the deletion path, which both evicted its semantic nodes and disabled the shrink guard that would otherwise have caught the loss. Such files are now preserved; a genuine on-disk deletion is still evicted, and the shrink guard now falls through to its per-source accounting instead of being waved off wholesale by the mere presence of a deletion in the change set. - Fix: code-typed nodes that the semantic pass surfaces from within a document now count as that document's semantic layer (#2014). A doc represented only by code-typed nodes was not recognized as semantically backed, so a rebuild re-scanned it for headings and dropped those nodes. The doc is now correctly treated as semantic-backed and left alone. - Fix: the `--update` runbook no longer marks a semantic file as done when its extraction produced no output (#2015). Step 9 stamped the entire detected corpus into the manifest, so a doc, paper, or image whose chunk failed or was omitted was recorded as complete and neve _[Truncated at 4000 characters — full notes: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.21]_ ### v0.9.20 — graphify 0.9.20 - Date: 2026-07-18 - Version: v0.9.20 - Original notes: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.20 - Permalink: https://whatsnew.fyi/product/graphify/releases/v0.9.20 - **fixed** — The graphify-first search nudge now fires on Claude Code's Grep tool in addition to Bash by matching both tool types and recognizing the Grep tool's input shape - **fixed** — Hook commands now use forward slashes in the graphify executable path on Windows so Git Bash does not treat backslashes as escape characters and strip them - **fixed** — With --out, semantic-cache writes now anchor against the scan root and cache directory sits at the output root so check, save, checkpoint, and prune operations agree on cache location - **fixed** — Alias-based named re-exports no longer emit dangling absolute-path symbol targets by rewriting aliased re-export targets to canonical symbol nodes when unambiguous - Fix: the graphify-first search nudge now fires on Claude Code's dedicated **Grep tool**, not just Bash (#1986, thanks @mdshzb04). The installed PreToolUse hook only matched `Bash`, so a `Grep` tool call (whose `tool_input` is `{pattern, path, glob, ...}`, not `{command}`) slipped through and never got nudged toward `graphify query`. The matcher is now `Bash|Grep` and the search guard recognizes the Grep shape; it stays nudge-only (never the strict deny), and the uninstall filters + #1840 gating are unchanged. - Fix: installed hook commands now use forward slashes in the graphify exe path so Git Bash doesn't strip them (#1987, thanks @varuntej07). On Windows the resolved exe path had backslashes, which Git Bash (how Claude Code shells hooks) treats as escapes and drops, breaking the hook with "command not found". `_resolve_graphify_exe` now normalizes `\` to `/` at the single choke point, covering every emitter (Claude/CodeBuddy PreToolUse, Gemini BeforeTool, Codex); quoting and the `--strict` suffix are preserved and POSIX is unaffected. - Fix: with `--out`, semantic-cache writes now anchor correctly so the cache round-trips (#1990, #1991, thanks @mdshzb04). The final semantic-cache save resolved a relative `source_file` against the output dir and wrote 0 entries, and per-chunk recovery checkpoints landed in the wrong directory (under the corpus instead of `--out`). Cache entries now key on the scan root (portable, matching #1989) while the cache directory sits at the output root, so `check`/`save`/checkpoint/prune all agree; composes with the #1989 salt-keying and #1939 prompt-fingerprint namespacing. - Fix: alias-based named re-exports no longer emit dangling absolute-path symbol targets (#1983, thanks @oleksii-tumanov). `export { X as Y } from './mod'` produced a `re_exports` edge whose symbol target was an absolute-path-prefixed id with no matching node — the symbol-level residual left by #1967 (imports-only) and #1976 (file-level). The aliased re-export target is now rewritten to the canonical symbol node when unambiguous; external re-exports and owned ids are left untouched, so no real edge is dropped.