AST cache pins node IDs to the original extraction root; WordPress block.json never indexed
Two separate problems found while auditing a graph that had drifted. Both are in
0.9.16. The second is the more serious one — it makes the graph quietly wrong in a
way update cannot repair.
1. WordPress block.json is classified as data JSON and skipped
_is_config_json() in graphify/extractors/json_config.py gates JSON extraction on a
filename allowlist (_CONFIG_JSON_NAMES) plus a top-level key probe (_CONFIG_JSON_KEYS).
block.json is in neither, so every WordPress block manifest is treated as data JSON and
skipped by the structural pass (the #1224 behavior).
On a WordPress monorepo with 73 blocks, 72 produced zero nodes. The single exception
declared a top-level "$schema" key, which happens to trip the fallback probe.
block.json is the WordPress block manifest — it is config, not data, and it carries
genuinely graph-worthy relationships: name, parent, ancestor, allowedBlocks,
providesContext / usesContext, and the editorScript / style handles. It is as much
a manifest as package.json, and it is the file that describes which blocks may nest
inside which.
Suggested fix: add "block.json" to _CONFIG_JSON_NAMES.
Workaround today: add "$schema": "https://schemas.wp.org/trunk/block.json" to each
block.json. That is valid WordPress practice and gives editor autocomplete, but it means
touching every block in the repo.
2. The AST cache stores node IDs, so they stay pinned to the original extraction root
Node IDs are derived from the extraction root path. The per-file AST cache stores the fully-formed nodes, IDs included. So when the same repo is later extracted from a different root, every cache hit replays the IDs built under the original root.
Seen concretely on a repo first indexed inside a container at /workspace, then updated
from the macOS host checkout:
- 1,022 of 1,423 cache entries held
workspace_-prefixed IDs - 5,370 of 17,363 nodes in the graph carried them (31%)
- 508 files existed in the graph only as stale container-era nodes
- 51 more carried both a stale and a fresh copy of themselves
Because the old and new IDs never collide, they are not detected as duplicates — they look
like unrelated nodes. Node counts go up on every run, nothing warns, and explain returns
plausible results. They are just partly results from a tree that no longer exists.
The distribution is the part that makes this hard to notice: contamination tracks edit frequency inversely. Our actively-edited frontend was 0.4% stale, while the least-touched backend plugin was 84% stale — so the code you are most likely to need the graph for is the code most likely to be described wrongly.
graphify update --force does not help, since --force only permits a rebuild with fewer
nodes and does not invalidate a cache entry. The only repair we found was moving
cache/ast/ aside and rebuilding, which produced a correct 18,386 nodes with zero stale
IDs, and widened the extraction from 2,177 files to the full 2,901.
Suggested fixes (either would do):
- Store root-relative IDs in the cache and apply the root prefix at graph-assembly time, so a cache entry is portable across roots (the file-hash keys already are).
- Or record the extraction root in the cache namespace alongside the existing package-version namespace, so a root change invalidates the cache the way a version change does.
A cheap mitigation in the meantime: warn when the graph contains node IDs carrying more than one root prefix.
3. Minor: the zero-node warning truncates with no way to recover the full list
The warning names 5 files then (+68 more), with no flag or log file carrying the rest.
Recovering the full list meant diffing the graph against a filesystem walk. Writing it to
graphify-out/ would make this self-service.
Source: Graphify-Labs/graphify