Skip to content

codegraph check --signatures: false positive when a diff shifts line numbers above an untouched function #1737

Description

@carlos-alm

Summary

codegraph check --staged --signatures (predicate signatures, implemented by checkNoSignatureChanges in src/features/check.ts:201) reports a false-positive "signature change" violation on functions whose body is byte-for-byte unchanged, whenever a preceding hunk in the same file shifts line numbers by roughly the same span as the unrelated hunk's own line range.

Root cause

checkNoSignatureChanges(db, oldRanges, noTests) queries the live/current graph DB for symbol definitions (SELECT name, kind, file, line FROM nodes WHERE file = ?) — which, in a repo where the graph is kept up to date after every edit (e.g. via a post-edit hook), reflects post-change (new-file) line numbers. It then compares def.line against oldRanges, which are derived from the diff's -a,b hunk headers, i.e. pre-change (old-file) line numbers (src/features/check.ts:52-56).

Mixing new-file line positions with old-file diff ranges means: whenever a hunk deletes/inserts a different number of lines than another hunk changes, an unrelated, completely untouched function below the diff can have its new line number coincidentally fall inside the old hunk's numeric range, and get flagged as a "signature change" even though nothing about it was touched.

Repro

  1. In src/domain/graph/journal.ts, delete an 11-line block (a function + its leading comment) starting at old line 13, immediately above another untouched function isPidAlive (old line 24).
  2. Add one import line elsewhere in the file (net file shrinks by 10 lines) — isPidAlive is now at line 14 in the new file, body unchanged.
  3. Stage the change and run:
    codegraph check --staged --cycles --blast-radius 30 --boundaries -T --json
    
  4. Output includes:
    {
      "name": "signatures",
      "passed": false,
      "violations": [
        { "name": "isPidAlive", "kind": "function", "file": "src/domain/graph/journal.ts", "line": 14 }
      ]
    }
    git diff --cached --unified=0 for that file shows the old hunk range as @@ -13,11 +13,0 @@ (old range [13,23]). isPidAlive's new line (14) falls inside that old range purely by coincidence — its body is byte-identical before and after.

Fix direction

checkNoSignatureChanges should compare like-for-like coordinate systems. Either:

  • Match changedRanges (new-file ranges) against the current db's def.line (what's already used for cycles/blast-radius via diff.changedRanges), or
  • If the intent is genuinely to diff against the pre-change symbol table, query a pre-change snapshot of the DB (e.g. via git show HEAD:<file> + a throwaway parse) instead of the live DB, and pair that with oldRanges.

Right now the function pairs the live DB with oldRanges, which are never in the same coordinate space once a diff changes line counts.

Context

Found while running the Titan Paradigm forge pipeline (/titan-forge --phase 5) on the worktree-titan-run branch, applying a real bug fix (readFileSafe's Atomics.wait → shared sleepSync) that also removed a small locally-duplicated helper from journal.ts. Confirmed via manual line-by-line diff that isPidAlive was 100% unchanged; not blocking that fix, filed here per project scope-discipline convention.

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