This release includes breaking changes for platform teams planning a safe upgrade.
✓ No known CVEs patched in this version
Topics
+5 more
Summary
AI summaryUpdates Fix, ampcode.com, and Acknowledgement across a mixed release.
Changes in this release
| Type | Severity | Summary | CVE |
|---|---|---|---|
| Security | Medium |
Nodes with never‑dispatched source_file are dropped from the graph for privacy/security. Nodes with never‑dispatched source_file are dropped from the graph for privacy/security. Source: granite4.1:30b@2026-07-16-audit Confidence: low |
— |
| Performance | Low |
`graphify query` scores the graph once per query instead of T+1 times. `graphify query` scores the graph once per query instead of T+1 times. Source: llm_adapter@2026-07-16 Confidence: high |
— |
| Bugfix | Medium |
Missing manifest.json no longer forces full scan under --code-only. Missing manifest.json no longer forces full scan under --code-only. Source: llm_adapter@2026-07-16 Confidence: high |
— |
| Bugfix | Medium |
Hyperedge‑only documents are now stamped in the manifest. Hyperedge‑only documents are now stamped in the manifest. Source: llm_adapter@2026-07-16 Confidence: high |
— |
| Bugfix | Medium |
PHP extractor disambiguates same‑named classes across namespaces. PHP extractor disambiguates same‑named classes across namespaces. Source: llm_adapter@2026-07-16 Confidence: high |
— |
| Bugfix | Medium |
`detect()` records ignored files/directories in a new `ignored` diagnostic field. `detect()` records ignored files/directories in a new `ignored` diagnostic field. Source: llm_adapter@2026-07-16 Confidence: high |
— |
| Bugfix | Medium |
`_semantic_id_remap` is now idempotent, preventing unbounded ID growth. `_semantic_id_remap` is now idempotent, preventing unbounded ID growth. Source: llm_adapter@2026-07-16 Confidence: high |
— |
| Bugfix | Medium |
`--mode deep` is now effective over a warm cache instead of a silent no‑op. `--mode deep` is now effective over a warm cache instead of a silent no‑op. Source: llm_adapter@2026-07-16 Confidence: high |
— |
| Bugfix | Medium |
Semantic cache no longer persists dangling edges/hyperedges. Semantic cache no longer persists dangling edges/hyperedges. Source: llm_adapter@2026-07-16 Confidence: high |
— |
| Bugfix | Medium |
`graphify update`/watch stops double‑representing documents in the graph. `graphify update`/watch stops double‑representing documents in the graph. Source: llm_adapter@2026-07-16 Confidence: high |
— |
| Bugfix | Medium |
`.cjs` (CommonJS) files are now recognized as code and parsed with the JavaScript grammar. `.cjs` (CommonJS) files are now recognized as code and parsed with the JavaScript grammar. Source: llm_adapter@2026-07-16 Confidence: high |
— |
| Bugfix | Medium |
Excluded files are pruned from both graph and manifest instead of lingering. Excluded files are pruned from both graph and manifest instead of lingering. Source: granite4.1:30b@2026-07-16-audit Confidence: low |
— |
| Bugfix | Low |
PostgreSQL PL/pgSQL functions are no longer silently dropped. PostgreSQL PL/pgSQL functions are no longer silently dropped. Source: granite4.1:30b@2026-07-16-audit Confidence: low |
— |
| Bugfix | Low |
`manifest.json` now records every successfully‑extracted file, not just zero‑node ones. `manifest.json` now records every successfully‑extracted file, not just zero‑node ones. Source: granite4.1:30b@2026-07-16-audit Confidence: low |
— |
| Bugfix | Low |
`graphify hook install` registers the `graph.json` union merge driver as documented. `graphify hook install` registers the `graph.json` union merge driver as documented. Source: granite4.1:30b@2026-07-16-audit Confidence: low |
— |
| Bugfix | Low |
`hook install`/`status` no longer emit spurious "could not read core.hooksPath" warning with duplicate config keys. `hook install`/`status` no longer emit spurious "could not read core.hooksPath" warning with duplicate config keys. Source: granite4.1:30b@2026-07-16-audit Confidence: low |
— |
| Bugfix | Low |
`graphify export obsidian` prunes notes for nodes that left the graph on re‑export. `graphify export obsidian` prunes notes for nodes that left the graph on re‑export. Source: granite4.1:30b@2026-07-16-audit Confidence: low |
— |
| Bugfix | Low |
Non‑English query sentences now filter stopwords correctly, avoiding wrong BFS seeds. Non‑English query sentences now filter stopwords correctly, avoiding wrong BFS seeds. Source: granite4.1:30b@2026-07-16-audit Confidence: low |
— |
| Bugfix | Low |
Python calls to imported modules resolve correctly; `.skill` files are classified as documents instead of being dropped. Python calls to imported modules resolve correctly; `.skill` files are classified as documents instead of being dropped. Source: granite4.1:30b@2026-07-16-audit Confidence: low |
— |
| Bugfix | Low |
The `--postgres` missing‑driver error now points to the correct PyPI package `graphifyy[postgres]`. The `--postgres` missing‑driver error now points to the correct PyPI package `graphifyy[postgres]`. Source: granite4.1:30b@2026-07-16-audit Confidence: low |
— |
Full changelog
-
Acknowledgement: Amp (ampcode.com) platform support, which shipped earlier in v8, was contributed by @zuwasi in #948 (the
skill-amp.mdskill and thegraphify amp installwiring). Belated credit for the work. -
Fix: a missing
manifest.jsonno longer degradesgraphify extract --code-onlyinto a full scan that discards the committed semantic layer (#1925). On a fresh clone (or when the manifest is deliberately untracked because its mtimes churn), the incremental gate required bothmanifest.jsonandgraph.json; with only the graph present it fell to a full scan, and under--code-onlythat dropped every doc/paper/image node — silently replacing a curated graph with an AST-only skeleton. An existinggraph.jsonis now a sufficient incremental baseline:detect_incrementalalready treats an absent manifest as "everything new / nothing deleted", sobuild_merge+_stale_graph_sourcespreserve files that are merely out of this run's scope while still evicting genuinely deleted sources. -
Fix: hyperedge-only documents are now stamped in the manifest instead of being re-extracted on every run (#1920).
_stamped_manifest_files(#1897) decided a semantic doc "produced output" by inspecting onlynodesandedges, neverhyperedges, so a chunk whose only output for a doc was a hyperedge (3+ nodes sharing a concept) left that doc unstamped and perpetually re-queued. Stamping now counts hyperedge output too, mirroring the per-source_filekeying the semantic cache already uses. -
Fix: the PHP extractor now disambiguates same-named classes across namespaces (#1923). A bare class reference (
extends Page) collapsed onto the only internal class namedPage, soApp\Models\Pageand an importedFilament\Pages\Pagefused into one node, manufacturing a falseinherits/importsedge and a bogus cross-community bridge. A new namespace/use-aware resolution pass (mirroring the Java resolver, running before the unique-name rewire) re-points supertype/import references to the real definition, or parks provably-external ones on a fully-qualified stub the bare-name rewire cannot collapse. Plain, non-namespaced PHP is unchanged. -
Fix:
detect()now records files and directories dropped by a.gitignore/.graphifyignorerule in a newignoreddiagnostic field (#1922). The nested-ignore scoping bug itself was fixed in 0.9.16 (#1873); this closes the remaining gap where an ignored path left no trace in any diagnostic, so an over-broad rule looked like a clean scan. Entries are per-directory where a subtree is pruned, keeping the list bounded. -
Fix:
_semantic_id_remapis now idempotent, so incremental rebuilds stop churning (#1917). When a file's canonical stem contained its own legacy stem as a prefix (parent dir name equals the file stem, e.g..claude/CLAUDE.md,docs/docs.md), an already-migrated semantic node id re-matched the legacy branch and gained another stem segment on every build (claude_x->claude_claude_x-> ...). Because_originis persisted, everygraphify updatere-fed nodes through the remap, so the ids grew unboundedly and thesame_topology/same_graph/no_changeshort-circuits never fired — rewritinggraph.jsonand re-running clustering on every zero-delta update. The remap now skips an id that already carries its canonical stem (mirroring thegraph_has_legacy_idscheck), while a genuine one-time legacy migration still applies. (An already-corrupted graph needs onegraphify extract --forceto reset the grown ids.) -
Perf:
graphify querynow scores the graph once per query instead of T+1 times for a T-term query (#1889 / #1918, thanks @Sirhan1). The per-term-guarantee (#1445) previously re-scored the whole graph once per token;_score_querynow computes the combined ranking and each token's singleton winner in a single traversal, feeding_pick_seedsviabest_seed_by_term. Behavior is preserved (verified byte-identical against the old per-term scoring across a differential fuzz); ~1.3-1.4x faster and independent of query length. -
Fix:
--mode deepis now effective over a warm cache instead of a silent no-op (#1894). The semantic cache is namespaced by mode (semanticvssemantic-deep) so a shallow-cached file no longer satisfies a deep run;graphify extractgains--force(and honorsGRAPHIFY_FORCE) to bypass the incremental gate and the cache read; and a deep incremental run widens its dispatch to the full live doc set so the deep namespace actually gets populated. Cache prune/clear now sweep both namespaces so the deep cache can't accumulate orphans. (The skill-side flow that passes the mode through is a follow-up; the CLI is complete and backward compatible — the newmodeargument defaults to the existing behavior.) -
Fix: the semantic cache no longer persists dangling edges/hyperedges (#1916). When a node group was skipped on write (out-of-scope per the batch guard, or a ghost
source_file), edges/hyperedges in the kept groups that referenced those never-written nodes were saved anyway and re-surfaced on every cache replay. Those references are now pruned at write time (gated on the scoping allowlist, so unscoped callers are unchanged), andbuild_from_jsonvalidates hyperedge members against the node set so a dangling hyperedge can't reachgraph.jsoneven from a live extraction. -
Fix:
graphify update/watch no longer produces a bloated graph by double-representing documents (#1915)._rebuild_codeAST-quick-scanned Markdown/doc files and then preserved their existing semantic (LLM) nodes on top, so each doc appeared twice (a real corpus came out ~4x). A doc that already has semantic nodes in the graph is no longer AST-quick-scanned (its semantic nodes are the sole representation), while a doc with no semantic layer still gets the structural quick-scan; incremental rebuilds now preserve a doc's semantic nodes instead of evicting them, and previously-bloated graphs self-heal on the next full rebuild. -
Fix:
.cjs(explicit CommonJS) files are now recognized as code and parsed with the JavaScript grammar (#1912, thanks @Kookwater). The extension was half-registered (present in some internal maps but missing from the classification and dispatch sets), so.cjsfiles were silently dropped. -
Fix: files that become excluded (
.graphifyignore/.gitignore/--exclude) are now pruned from both the graph and the manifest instead of lingering (#1908 / #1909). Two coupled gaps:save_manifestretained any prior row that still existed on disk (disk-existence, not scan-membership), so an excluded-but-present file was reported asdeletedon every run; and the incremental prune set was derived from the manifest alone, so a newly-excluded file's stale nodes carried forward from the existinggraph.jsonwere never removed (the state every 0.9.16 graph is in). Now the incremental prune set also derives from the existing graph's ownsource_files minus the post-exclude corpus (in-root only),save_manifestprunes rows outside the full scan corpus, anddetect_incrementaldistinguishes truly-deleted files from excluded-but-present ones (which are no longer misreported as deleted). -
Fix: PostgreSQL PL/pgSQL functions are no longer silently dropped (#1910).
CREATE FUNCTION ... LANGUAGE plpgsql AS $$...$$withOUTparams, tagged dollar-quotes, or procedural body statements parses as a tree-sitterERRORnode, which the SQL extractor skipped entirely. It now recovers the function/procedure name from an ERROR node via the same regex-fallback pattern the extractor already uses elsewhere, so the function node and itscontainsedge are kept (the unparseable body is left opaque) and surrounding statements are unaffected. -
Security/privacy follow-up: nodes whose
source_filewas never dispatched are now dropped from the graph, not just skipped from the cache (#1895). The #1757 guard stopped a mis-attributed node from clobbering another file's cache entry, but the node itself still flowed intograph.json; it is now filtered out of the merged result (real-file, non-dispatched attributions only), consistent with the cache rejection. -
Fix:
manifest.jsonnow records every successfully-extracted file, not just the zero-node ones (#1897). The #933 stamping filter compared root-relative nodesource_files against absolutedetect()paths, so it dropped every freshly-extracted semantic document from the manifest and broke the incremental-update baseline. Both sides are now resolved before comparison; genuinely omitted/zero-node docs stay unstamped so they retry. -
Fix:
graphify hook installnow registers thegraph.jsonunion merge driver that the README and CHANGELOG have long documented (#1902). It writes themerge.graphifyconfig viagit configand an idempotent, append-onlygraphify-out/graph.json merge=graphifyline in.gitattributes;uninstallremoves them. -
Fix:
hook install/statusno longer print a spurious "could not read core.hooksPath" warning on repos whose.git/configcontains git-legal duplicate keys (VS Code writes these) (#1907). Config is now resolved viagit rev-parse --git-path hooksinstead of a strictconfigparser, which rejected duplicate keys. -
Fix:
graphify export obsidianprunes notes for nodes that left the graph instead of merging old and new on re-export (#1896). Only notes graphify itself wrote (tracked in its ownership manifest) are removed, with a vault-containment guard, so user-authored notes are never touched. -
Fix: non-English query sentences no longer pick wrong BFS seeds because their filler words were unfiltered (#1900). The query stopword set now covers German and the major Romance languages (curated to avoid clobbering English content words), so
Wie funktioniert die Authentifizierung?seeds the keyword, not the stopwords. -
Fix: Python calls to an imported module now resolve (already shipped in 0.9.16);
.skillfiles (Markdown-with-frontmatter agent files) are now classified as documents instead of being silently dropped as an unsupported extension (#1901). -
Fix: the
--postgresmissing-driver error now points at the correct PyPI package,graphifyy[postgres](was the nonexistentgraphify[postgres]) (#1906).
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
About graphify
AI coding assistant skill (Claude Code, Codex, OpenCode, Cursor, Gemini CLI, OpenClaw, Factory Droid, Trae). Turn any folder of code, docs, papers, images, videos, or YouTube links into a queryable knowledge graph
Beta — feedback welcome: [email protected]