Summary
Graph refresh preserves semantic/non-code nodes even when their source files no longer
exist. Those nodes remain queryable indefinitely, carry no staleness marker, and are
returned in polished, canonical-sounding form.
In our repository this has accumulated 1,444 semantic nodes for files that no longer
exist. Roughly half describe a deliberately retired dispatch_hub architecture. More
seriously, stale nodes also describe deleted security/policy surfaces such as:
- policy_hard_deny.py
- pre_tool_use_policy.py
Our agent instructions make Graphify the first code-navigation path before Grep/Read, so
these stale results can directly influence architectural and security reasoning.
Impact
This is not only graph bloat. The immediate safety problem is false authority:
- deleted architecture is returned without warning;
- retired systems can appear current and canonical;
- stale policy rationale can mislead agents reasoning about safety-critical code;
- refreshes grow the stale set monotonically because preserved non-code nodes have no
source-existence check;
- current behavior provides no way for later refreshes to remove stale semantic nodes.
Requested fix: mark stale now, prune later
Phase 1 — reversible safety fix
On refresh, compare each source-backed semantic node against the current repository tree.
When the source path no longer exists:
- retain the node temporarily;
- mark it explicitly stale, for example:
source_status: missing
stale: true
- include the last-seen source path and refresh timestamp;
- down-rank or exclude stale nodes from default authoritative query results;
- surface a clear warning when stale nodes are returned;
- expose stale counts in the refresh report.
This removes the immediate false-authority risk without destructive cleanup.
Phase 2 — rename-aware pruning
Before deletion, distinguish:
- true deletion;
- file rename/move;
- path normalization changes;
- generated or intentionally external sources.
Use Git rename detection or an equivalent content-identity mechanism to migrate nodes to
renamed paths. Only then prune nodes confirmed deleted under a governed retention policy.
Acceptance criteria
- Deleted-source semantic nodes are no longer returned as current/canonical without warning.
- Refresh reports total, newly stale, restored, renamed, and pruned node counts.
- A renamed file preserves or migrates its semantic content rather than being deleted.
- A genuinely deleted file becomes stale on the next refresh.
- Stale nodes are excluded or clearly down-ranked in default queries.
- Pruning is opt-in or separately gated until rename detection is proven.
- Tests cover deleted, renamed, restored, and security/policy-file cases.
Independent issue boundary
This is independent of drain/refresh latency and graph-size shrink-guard behavior, even if
the fixes touch the same refresh/preserve function.
A green latency result does not prove stale-node correctness, and a stale-marking fix does
not prove drain performance. Track them with separate tests and acceptance results.
Summary
Graph refresh preserves semantic/non-code nodes even when their source files no longer
exist. Those nodes remain queryable indefinitely, carry no staleness marker, and are
returned in polished, canonical-sounding form.
In our repository this has accumulated 1,444 semantic nodes for files that no longer
exist. Roughly half describe a deliberately retired dispatch_hub architecture. More
seriously, stale nodes also describe deleted security/policy surfaces such as:
Our agent instructions make Graphify the first code-navigation path before Grep/Read, so
these stale results can directly influence architectural and security reasoning.
Impact
This is not only graph bloat. The immediate safety problem is false authority:
source-existence check;
Requested fix: mark stale now, prune later
Phase 1 — reversible safety fix
On refresh, compare each source-backed semantic node against the current repository tree.
When the source path no longer exists:
source_status: missingstale: trueThis removes the immediate false-authority risk without destructive cleanup.
Phase 2 — rename-aware pruning
Before deletion, distinguish:
Use Git rename detection or an equivalent content-identity mechanism to migrate nodes to
renamed paths. Only then prune nodes confirmed deleted under a governed retention policy.
Acceptance criteria
Independent issue boundary
This is independent of drain/refresh latency and graph-size shrink-guard behavior, even if
the fixes touch the same refresh/preserve function.
A green latency result does not prove stale-node correctness, and a stale-marking fix does
not prove drain performance. Track them with separate tests and acceptance results.