Skip to content

rtk diff produces cascading false positives on insertions/deletions #1869

Open

Description

@beyond-infra

Description

rtk diff file1 file2 produces cascading false positives when lines are inserted or deleted. It uses a naive index-based line comparison that shifts all subsequent line indices, causing unchanged lines to be reported as removed and added.

This is distinct from #781 (similarity threshold causing "Files are identical"), which PRs #792 and #1144 address.

Version

rtk 0.39.0

Minimal reproduction

# Create a 20-line file
seq 1 20 > a.txt

# Insert one line in the middle
cp a.txt b.txt
sed -i '5i\INSERTED' b.txt

# Compare
rtk diff a.txt b.txt

Actual output:

a.txt → b.txt
   +17 added, -16 removed, ~0 modified

-   5 5
+   5 INSERTED
-   6 6
+   6 5
-   7 7
+   7 6
... (continues through line 20)

Expected output: Only the inserted line should be reported, consistent with /usr/bin/diff:

4a5
> INSERTED

Additional reproduction

# 3-line file, delete first line
printf "a\nb\nc\n" > a.txt
printf "b\nc\n" > b.txt
rtk diff a.txt b.txt

Actual: reports line 2 and 3 as changed.
Expected: only line 1 as removed.

Environment

  • rtk 0.39.0
  • macOS / Linux

Activity

  1. pszymkowiak commented on May 27, 2026

    @pszymkowiak
    Collaborator

    wshm · Automated triage by AI

    🔍 Automated Triage

    🐛 Category bug
    🟠 Priority high
    🎯 Confidence 97%

    Summary

    rtk diff uses naive index-based line comparison causing cascading false positives on insertions/deletions; PR #1981 is in progress.

    📁 Relevant files
    • cmds/git/diff

    Triaged automatically by wshm · This is an automated analysis, not a human review.

  2. EvolveAegis commented on Aug 1, 2026

    @EvolveAegis

    I ran into this with a larger file and measured the token impact, in case it helps with prioritization.

    With a 1,000-line file and one line inserted at the top:

    rtk diff before.txt after.txt  →  31,811 bytes / 2,003 lines / 10,008 tokens (cl100k_base)
    diff before.txt after.txt      →  26 bytes / 2 lines / 9 tokens
    

    So roughly 1,112× more tokens than diff output for the same change. When the rtk diff result gets fed back into the model context (e.g. in an agent loop), it's essentially sending both files in full rather than a diff.

    The repro is just:

    seq 1 1000 | awk '{print "stable-line-" $1}' > before.txt
    { printf '%s\n' 'inserted-first-line'; cat before.txt; } > after.txt
    rtk diff before.txt after.txt | wc -c   # 31811
    diff before.txt after.txt | wc -c        # 26

    Tested with rtk 0.43.0. Looks like #2668 should address this — happy to run the same test against the PR branch if that would help.

  3. added a commit that references this issue on Aug 14, 2026
    b75875a
  4. KuSh commented on Aug 31, 2026

    @KuSh
    Collaborator

    Confirming this is still live, and explaining why it currently looks unreproducible — the needs-reproduction label is misleading rather than wrong.

    Why the repro in the description no longer shows the cascade

    On develop today, the repro prints neither the cascade nor a sane diff:

    $ seq 1 20 > a.txt && cp a.txt b.txt && sed -i '5i\INSERTED' b.txt
    $ rtk diff a.txt b.txt
    1
    2
    3
    ...
    ---
    1
    2
    3
    4
    INSERTED
    ...

    The cascade is still generated — it is then thrown away by the never-worse guard, which
    notices the cascading render is bigger than the raw input and substitutes a dump of both
    files. So the symptom mutated from "wrong diff" into "no diff at all", which is what
    @EvolveAegis measured above as 1,112× the tokens of diff. Same root cause, and their
    numbers stand.

    The defect is in compute_diff, not in any renderer

    compute_diff walks both files by index and compares line i against line i. One
    inserted line shifts every subsequent index, so every later line is reported as changed.
    Nothing downstream can recover the alignment, so this survives any change to how the
    result is printed. Reproduced against the raw comparison, the 3-line case from the
    description gives:

    rtk:  1,2c1,2 / < a / < b / --- / > b / > c   then   3d2 / < c
    GNU:  1d0 / < a
    

    and the 20-line case emits 36 lines where diff emits 2.

    How to test it

    The trap is that a wrong-but-well-formed diff still applies. I generated 900 random file
    pairs (0–12 lines each side, half derived from the first by 1–3 edits, some with an empty
    side) and checked the emitted patches: 885 non-empty patches, all 885 apply with patch
    and reproduce the second file byte-for-byte.
    Validity is not the problem. Only 289 of
    885
    match GNU's edit script; the rest are correct but not minimal, averaging ~40% more
    bytes across the corpus.

    So the acceptance criterion has to be minimality, not applicability:

    for a random pair (a, b):
        assert rtk_edit_script(a, b) == `diff a b`
    

    A property test over generated pairs is the cheap version — the generator matters more than
    the count, since it needs to produce insertions and deletions in the middle of a file,
    not just at the ends. Two regression cases worth pinning directly, both from this issue:

    • seq 1 20 vs the same with one line inserted at position 5 → must be exactly 4a5 / > INSERTED
    • a\nb\nc vs b\nc → must be exactly 1d0 / < a

    Neither passes today.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions