Skip to content

Stale semantic nodes for deleted files are preserved indefinitely and returned as authoritative #2051

Description

@YmGavin

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions