The Role of the fingerprint.ts Module in Change Detection for Understand-Anything

TLDR: The fingerprint.ts module serves as the core change-detection engine for Understand-Anything, generating deterministic content and structural fingerprints, persisting project state at each Git commit, and classifying file modifications into NONE, COSMETIC, or STRUCTURAL categories to minimize unnecessary knowledge graph rebuilds.

The understand-anything-plugin/packages/core/src/fingerprint.ts file implements the precision change-detection subsystem for the Understand-Anything project. Its primary role is to provide a deterministic, fast, and precision-aware mechanism that distinguishes between trivial formatting updates and meaningful code changes. By comparing fingerprints across Git commits, the module ensures the knowledge graph only rebuilds when structural semantics actually change.

Core Responsibilities of the fingerprint.ts Module

The module fulfills four critical responsibilities that enable efficient incremental updates.

Generating Deterministic Fingerprints

At the heart of the system, the extractFileFingerprint function (implemented at lines 67-115) creates unique identifiers for each source file. It generates two distinct hashes:

  • contentHash: A cryptographic hash of the raw file content for quick comparison.
  • Structural fingerprint: Extracted signatures of functions, classes, imports, and exports via tree-sitter analysis.

This dual-layer approach allows the system to detect both byte-level changes and semantic structural modifications.

Persisting Project State

The buildFingerprintStore function (lines 48-91) captures the entire project state at a specific Git commit. It walks every file in the codebase, obtains either a full structural fingerprint or a fallback hash-only entry, and records the Git commit hash alongside a generation timestamp. The resulting FingerprintStore object serves as the baseline for future comparisons.

Precision Diffing and Change Classification

The compareFingerprints function (lines 124-166) implements the core diffing logic. It first compares contentHash values as a quick "no change" path. When hashes differ, it performs a detailed structural comparison of function signatures, class members, imports, and exports, producing a FileChangeResult with a changeLevel of NONE, COSMETIC, or STRUCTURAL.

Analyzing Sets of Changed Files

The analyzeChanges function (lines 93-155) orchestrates the detection workflow. It accepts a list of files (typically from git diff --name-only), builds fresh fingerprints for each, runs compareFingerprints against the stored baseline, and aggregates results into a ChangeAnalysis object. This categorizes files as new, deleted, unchanged, cosmetically changed, or structurally changed.

Implementation Examples

Building a Fingerprint Store

To capture the current project state:

import { buildFingerprintStore } from "./fingerprint.js";
import { PluginRegistry } from "./plugins/registry.js";

const projectRoot = "/path/to/project";
const allFiles = ["src/index.ts", "src/utils.ts", "src/component.jsx"];
const registry = new PluginRegistry();
const gitHash = "a1b2c3d4";

const store = buildFingerprintStore(projectRoot, allFiles, registry, gitHash);

The resulting FingerprintStore can be serialized to disk and loaded later for diffing operations.

Detecting Changes After a Commit

To analyze which files require knowledge graph updates:

import { analyzeChanges } from "./fingerprint.js";

const changedFiles = ["src/utils.ts", "src/newFeature.ts"];
const previousStore = /* load JSON from previous run */;

const analysis = analyzeChanges(
  "/path/to/project",
  changedFiles,
  previousStore,
  registry
);

console.log(analysis.structurallyChangedFiles); // ["src/utils.ts"]

Only files listed under structurallyChangedFiles trigger expensive rebuilds; cosmetic changes are filtered out.

Direct Fingerprint Comparison

For unit testing or ad-hoc inspection:

import { compareFingerprints, extractFileFingerprint } from "./fingerprint.js";
import { readFileSync } from "fs";

const oldFp = previousStore.files["src/utils.ts"];
const content = readFileSync("/path/to/project/src/utils.ts", "utf-8");
const newFp = extractFileFingerprint(
  "src/utils.ts",
  content,
  registry.analyzeFile("src/utils.ts", content)
);

const result = compareFingerprints(oldFp, newFp);
console.log(result.changeLevel); // "COSMETIC" | "STRUCTURAL" | "NONE"

Integration with the Understand-Anything Ecosystem

The fingerprint.ts module does not operate in isolation. It consumes structural analysis from plugins/registry.ts, which provides the tree-sitter wrapper via analyzeFile. The resulting ChangeAnalysis objects are consumed by staleness.ts to determine if the knowledge graph has become stale. Additionally, change-classifier.ts works alongside fingerprints to apply higher-level semantic significance flags, creating a multi-layered change detection pipeline that optimizes performance while preserving accuracy.

Summary

  • The fingerprint.ts module provides deterministic fingerprinting combining content hashes with tree-sitter structural analysis.
  • buildFingerprintStore persists complete project state at specific Git commits for baseline comparisons.
  • compareFingerprints classifies changes into NONE, COSMETIC, or STRUCTURAL levels to avoid unnecessary rebuilds.
  • analyzeChanges aggregates file-level results into a comprehensive ChangeAnalysis suitable for workflow orchestration.
  • The module integrates with staleness.ts and change-classifier.ts to drive efficient incremental knowledge graph updates.

Frequently Asked Questions

What is the difference between contentHash and structural fingerprint in fingerprint.ts?

The contentHash is a fast cryptographic hash of the raw file bytes, while the structural fingerprint extracts semantic signatures—such as function signatures, class members, and import/export declarations—using tree-sitter analysis. The contentHash enables quick equality checks, whereas the structural fingerprint determines whether changes are merely cosmetic or impact program semantics.

How does fingerprint.ts classify changes as cosmetic or structural?

The compareFingerprints function first compares contentHash values. If they match, the change level is NONE. If they differ, it compares the structured elements (functions, classes, imports). When only whitespace or comments changed, it returns COSMETIC; when function signatures or class structures changed, it returns STRUCTURAL, triggering knowledge graph rebuilds.

What triggers a full knowledge graph rebuild versus a skipped update?

Only files categorized as structurallyChangedFiles in the ChangeAnalysis result trigger full rebuilds. Files with changeLevel: "COSMETIC" or "NONE" are skipped, as they represent formatting changes, comment updates, or identical content that do not affect the underlying code structure or dependencies.

How does fingerprint.ts integrate with Git workflows?

The module accepts a Git commit hash in buildFingerprintStore to tag fingerprint baselines, and typically receives file change lists from git diff --name-only via the analyzeChanges function. This design allows seamless integration into CI/CD pipelines and development workflows where Git serves as the source of truth for what files require analysis.

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 →