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 db — checkNoSignatureChanges 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.
Summary
While running
/titan-forgephase 1 (dead-code removal insrc/types.ts, tracked in #1727),codegraph check --staged --cycles --blast-radius 30 --boundaries -T --jsonreported thesignaturespredicate 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:385calls:diff.oldRanges(fromparseDiffOutput) holds line ranges in the old-file (pre-change/HEAD) coordinate space — the-side of each diff hunk. Butdbis the rebuilt graph, which reflects the current working-tree (post-change) file content, sonodes.linevalues are in the new-file coordinate space.checkNoSignatureChangesthen checksdef.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 usesdiff.changedRanges(new-file positions) against the samedb—checkNoSignatureChangesis the outlier.Confirmed reproduction: deleting a 62-line dead block earlier in
src/types.tsshiftedEngineOpts.enginefrom old-line 1179 to new-line 1117.1117happens 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-pickedoldRangesargument — they validate the overlap-detection logic in isolation, not the real-world coordinate-space mismatch between a rebuilt graph and agit diff's old-side ranges.Impact
This makes
codegraph check --staged'ssignaturespredicate 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-gateverdict 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 passdiff.changedRangesinstead ofdiff.oldRanges, and renamed the function's parameter fromoldRangestochangedRangesfor clarity. Verified via a standalone mock-DB script (bypassing the broken native module in that worktree) that:+lines) now correctly reports zero violations (previously false-positived on shifted downstream symbols).diff.oldRangesitself is still computed and still exercised byparseDiffOutput's own tests — it's not dead, just no longer misused by the signatures predicate.