Skip to content

codegraph check --staged: signature-change detection compares new-file line numbers against old-file diff ranges #1732

Description

@carlos-alm

Summary

While running /titan-forge phase 1 (dead-code removal in src/types.ts, tracked in #1727), codegraph check --staged --cycles --blast-radius 30 --boundaries -T --json reported the signatures predicate as FAILED, flagging ~41 completely untouched interface members (EngineOpts.engine, PipelineContext.rootDir, ComplexityEntry.loc, etc.) as "signature violations" — none of which were touched by the diff.

Root cause

src/features/check.ts:385 calls:

checkNoSignatureChanges(db, diff.oldRanges, noTests)

diff.oldRanges (from parseDiffOutput) holds line ranges in the old-file (pre-change/HEAD) coordinate space — the - side of each diff hunk. But db is the rebuilt graph, which reflects the current working-tree (post-change) file content, so nodes.line values are in the new-file coordinate space. checkNoSignatureChanges then checks def.line >= range.start && def.line <= range.end — comparing two different coordinate spaces directly.

Any diff that changes the total line count before a given point in a file (virtually all deletions/insertions, including near-mechanical dead-code removal) shifts every subsequent symbol's line number. checkMaxBlastRadius, right above it at line 379, correctly uses diff.changedRanges (new-file positions) against the same dbcheckNoSignatureChanges is the outlier.

Confirmed reproduction: deleting a 62-line dead block earlier in src/types.ts shifted EngineOpts.engine from old-line 1179 to new-line 1117. 1117 happens to fall inside the old deleted range [1111, 1172], so it was flagged as "changed" even though it was never touched.

The existing unit tests (tests/integration/check.test.ts, describe('checkNoSignatureChanges', ...)) didn't catch this because their fixture DB's line numbers are constructed to be artificially consistent with the hand-picked oldRanges argument — they validate the overlap-detection logic in isolation, not the real-world coordinate-space mismatch between a rebuilt graph and a git diff's old-side ranges.

Impact

This makes codegraph check --staged's signatures predicate produce false positives on almost any refactor/decomposition/dead-code-removal diff — exactly the kind of change the Titan Paradigm pipeline (/titan-forge) performs continuously. Left unfixed, this would block or pollute the /titan-gate verdict for most forge commits going forward.

Fix applied

Already fixed on branch worktree-titan-run (staged, uncommitted pending an unrelated environment blocker — see companion issue): swapped the call site to pass diff.changedRanges instead of diff.oldRanges, and renamed the function's parameter from oldRanges to changedRanges for clarity. Verified via a standalone mock-DB script (bypassing the broken native module in that worktree) that:

  • A pure-deletion diff (no + lines) now correctly reports zero violations (previously false-positived on shifted downstream symbols).
  • A genuine signature-touching diff still correctly reports a violation.

diff.oldRanges itself is still computed and still exercised by parseDiffOutput's own tests — it's not dead, just no longer misused by the signatures predicate.

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