Troubleshooting Understand-Anything Incremental Updates: Why Files Aren't Detected

When Understand-Anything skips files during an incremental update, the root cause is almost always stale Git state, an incorrect project directory, or untracked changes that git diff cannot see.

Understand-Anything maintains a knowledge-graph that reflects the current state of a project. When you run an incremental update (/understand --incremental), the system relies on Git to detect modifications since the last analysis. If the tool fails to detect changed files, the knowledge graph will not update, leaving you with stale code intelligence. According to the Lum1104/Understand-Anything source code, the detection logic resides in packages/core/src/staleness.ts and depends on a strict contract between the file system, Git history, and the stored project metadata.

How Incremental Change Detection Works

The incremental pipeline relies on three core functions exported from packages/core/src/staleness.ts to determine what has changed and how to merge new data.

File Change Detection with getChangedFiles

At lines 13–26 of staleness.ts, the getChangedFiles function executes a shell command to identify modifications:

// Conceptual implementation from staleness.ts#L13-L26
const result = execFileSync('git', [
  'diff', 
  `${lastCommitHash}..HEAD`, 
  '--name-only'
], { cwd: projectDir });

This function returns an array of file paths that have changed between the stored lastCommitHash and the current HEAD. If this list is empty, the incremental update will process nothing, even if files appear modified in your editor.

Staleness Validation via isStale

The isStale function (lines 31–42) wraps getChangedFiles and returns a structured result:

interface StaleCheckResult {
  stale: boolean;
  changedFiles: string[];
}

If changedFiles contains any entries, stale is set to true, triggering the incremental rebuild. A false result short-circuits the pipeline, assuming the existing graph in .understand-anything/knowledge-graph.json is current.

Graph Merging with mergeGraphUpdate

When changes are detected, the system parses only the modified files and calls mergeGraphUpdate (lines 45–90). This function performs a surgical update:

  1. Removes nodes and edges belonging to the changed file paths from the existing graph.
  2. Appends newNodes and newEdges generated by the analysis modules (graph-builder.ts, layer-detector.ts).
  3. Updates the project metadata (gitCommitHash and analyzedAt) to prevent redundant future processing.

Why Files Go Undetected: Four Root Causes

If isStale returns an empty changedFiles array incorrectly, one of the following conditions is occurring according to the source code and test suite in staleness.test.ts.

Incorrect projectDir Path

The getChangedFiles function uses execFileSync with the cwd option set to projectDir. If this path does not point to the root of the Git repository, git diff executes in the wrong context and returns an empty list. This commonly happens when the tool is invoked from a subdirectory or when a symbolic link obscures the repository root.

Untracked or Ignored Files

git diff --name-only only reports changes to tracked files. New files that have not been added to the index (git add) or files excluded by .gitignore will never appear in the diff output. Consequently, mergeGraphUpdate receives no instructions to refresh the graph for these paths, leaving them invisible to the knowledge graph until they are tracked and committed.

Invalid or Orphaned Commit Hash

Understand-Anything stores the last analyzed commit hash in graph.project.gitCommitHash. If this hash becomes invalid—such as after a force-push or branch deletion—git diff throws an error. The current implementation catches this error silently and falls back to an empty array, causing isStale to report false even though the graph is materially outdated.

Shell Execution Failures

Any failure during execFileSync—including a missing git binary, permission denials, or environment path issues—triggers the error handler in getChangedFiles. Rather than crashing the process, the function returns an empty list, masking the underlying system error and appearing as "no files detected."

Practical Diagnostics and Fixes

Use these TypeScript snippets to manually verify the incremental update mechanism and resolve detection issues.

Verify Staleness Manually

If the CLI reports no changes, run this diagnostic to see exactly what Git sees:

import { isStale } from '@understand-anything/core';
import { join } from 'path';

const projectRoot = '/absolute/path/to/repo-root'; // Must be the git root
const lastHash = 'abc123def456'; // From .understand-anything/knowledge-graph.json

const { stale, changedFiles } = isStale(projectRoot, lastHash);

console.log('Stale:', stale);
console.log('Changed files:', changedFiles);

// Manual verification
if (!stale && changedFiles.length === 0) {
  console.warn('Git reports no diff. Check if files are staged/tracked.');
}

Source: isStale implementation – see lines 34–42 in [staleness.ts](https://github.com/Lum1104/Understand-Anything/blob/main/understand-anything-plugin/packages/core/src/staleness.ts#L34-L42).

Force a Graph Merge After Detection

Once you have the correct changedFiles list, you can simulate the merge step to verify graph integrity:

import { mergeGraphUpdate } from '@understand-anything/core';
import type { KnowledgeGraph, GraphNode, GraphEdge } from '@understand-anything/core';
import { readFile, writeFile } from 'fs/promises';

// Load existing graph
const graphPath = './.understand-anything/knowledge-graph.json';
const existingGraph: KnowledgeGraph = JSON.parse(await readFile(graphPath, 'utf-8'));

// These would come from your parser/analysis phase
const changedPaths = ['src/utils.ts', 'src/helpers.ts'];
const newNodes: GraphNode[] = []; // Populate with parsed AST nodes
const newEdges: GraphEdge[] = []; // Populate with dependency edges
const newCommit = process.env.GIT_COMMIT || 'HEAD';

const updated = mergeGraphUpdate(
  existingGraph,
  changedPaths,
  newNodes,
  newEdges,
  newCommit
);

await writeFile(graphPath, JSON.stringify(updated, null, 2));

Source: mergeGraphUpdate logic – see lines 54–90 in [staleness.ts](https://github.com/Lum1104/Understand-Anything/blob/main/understand-anything-plugin/packages/core/src/staleness.ts#L54-L90).

Quick Reference: Symptoms and Solutions

Symptom Root Cause Resolution
changedFiles is always empty projectDir points to a sub-folder Pass the absolute repository root, not a sub-package or symlink.
New files never appear Files are untracked or in .gitignore Run git add <file> to index changes before incremental analysis.
isStale returns false after rebase/force-push Stored gitCommitHash no longer exists Delete .understand-anything/knowledge-graph.json and run a full analysis (--full) to reset the baseline hash.
Silent failures with no error log Git binary missing or permission denied Verify git is in the system PATH and the Node.js process has execute permissions.

Summary

  • Detection relies on Git: The getChangedFiles function in packages/core/src/staleness.ts uses git diff <hash>..HEAD --name-only, meaning untracked files and invalid commit hashes will always yield empty results.
  • Path sensitivity: The projectDir parameter must be the repository root; otherwise, Git executes in the wrong context and reports no changes.
  • Silent failures: All shell errors in the detection phase are caught and converted to empty arrays, so verify your Git state manually if files are missing.
  • Recovery: When the stored commit hash becomes invalid (e.g., after force-push), delete the knowledge graph and run a full analysis to re-establish the baseline.

Frequently Asked Questions

Why are my new TypeScript files ignored during incremental updates?

Understand-Anything only analyzes tracked Git changes. If you create a new file but do not run git add, git diff --name-only will not report it, and the incremental pipeline will skip it. Stage the file or run a full analysis (--full) to include untracked files.

How do I fix incremental updates after a force-push?

After a force-push, the commit hash stored in knowledge-graph.json may no longer exist in your history, causing git diff to fail silently. Delete the .understand-anything/knowledge-graph.json file and run /understand --full to generate a fresh graph with the current HEAD hash.

Where does Understand-Anything store the last analyzed commit hash?

The hash is stored inside the knowledge-graph.json file in the .understand-anything directory, specifically at the path project.gitCommitHash. The isStale function reads this value to determine the baseline for git diff comparisons.

What happens if the projectDir is incorrect?

If projectDir points to a subdirectory or a path outside the Git repository, getChangedFiles executes git diff in the wrong working directory. Because Git cannot find the repository metadata, it returns an empty file list, and isStale incorrectly reports that no files have changed. Always provide the absolute path to the repository root.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →