Skip to content

fix(diff): node diff keyed by line-bearing id — adding a comment reports functions as added/removed and emits FALSE new_dead #300

Description

@Wolfvin

Mengapa (falsifiable — repro dijalankan 2026-07-17)

Ditemukan saat verifikasi #297 (edge-diff), bukan scope PR #298 — jadi tidak di-fix di sana.

_diff_backend() (scripts/diff_engine.py) mengunci node dengan n["id"]:

old_nodes = {n["id"]: n for n in old.get("nodes", [])}
new_nodes = {n["id"]: n for n in new.get("nodes", [])}

Node id memuat nomor baris (checkout.py:6). Akibatnya menambahkan satu komentar di atas file — tanpa menyentuh satu pun fungsi — membuat SETIAP fungsi di file itu dilaporkan ditambah DAN dihapus, plus new_dead / resolved_dead PALSU.

Repro terisolasi (graph identik, semua baris turun 5 karena komentar ditambahkan):

old = {'nodes':[{'id':'checkout.py:6','fn':'handle_checkout','file':'checkout.py','line':6,'ref_count':0,'status':'dead'},
                {'id':'checkout.py:12','fn':'send_receipt','file':'checkout.py','line':12,'ref_count':1,'status':'active'}],
       'edges':[{'from':'checkout.py:6','to':'checkout.py:12'}]}
new = {'nodes':[{'id':'checkout.py:11','fn':'handle_checkout',...,'line':11,...},
                {'id':'checkout.py:17','fn':'send_receipt',...,'line':17,...}],
       'edges':[{'from':'checkout.py:11','to':'checkout.py:17'}]}
_diff_backend(old, new)

Hasil aktual — perubahan nyata: NOL:

Field Dilaporkan Benar
added_nodes ['handle_checkout', 'send_receipt'] []
removed_nodes ['handle_checkout', 'send_receipt'] []
new_dead ['handle_checkout'] []fungsi ini sudah dead sejak sebelumnya, tidak "baru mati"
resolved_dead ['handle_checkout'] []

Fungsi yang sama muncul di added_nodes dan removed_nodes sekaligus — tanda pasti kunci identitasnya salah.

Terverifikasi juga lewat CLI penuh pada workspace nyata: satu komentar header + satu rewire → impact --check diff melaporkan added: 3, removed: 3, new_dead: 2 padahal yang berubah cuma satu edge.

Konteks

Tujuan

Node diff kebal geser-baris: hanya fungsi yang benar-benar muncul/hilang yang dilaporkan, dan new_dead hanya untuk fungsi yang benar-benar baru mati.

Constraint

  • Pakai ulang _endpoint_key() dari feat(diff): compare call-graph edges in snapshot diff — 25,876 edges saved to disk but never compared #297 — sudah ada di file yang sama, sudah teruji, kuncinya sudah diukur (2.429 node: (file,fn) → 38 tabrakan; (file,impl_for,fn) → 9, dan ke-9 nya sudah ditandai duplicate_define). Jangan bikin skema identitas ketiga.
  • Bentuk output tidak boleh berubahadded_nodes/removed_nodes/changed_nodes tetap {"name","file",...}. commands/diff.py, dashboard, formatter, MCP mengonsumsinya. Yang berubah cuma anggota daftarnya.
  • Fungsi yang benar-benar pindah file harus tetap terhitung removed+added (file bagian dari kunci) — itu benar, bukan bug.
  • Zero dead code. Command count tetap 12.

Definition of Done

  • Geser baris murni (komentar ditambah) → added_nodes, removed_nodes, new_dead, resolved_dead semuanya kosong
  • Fungsi yang benar-benar ditambah/dihapus tetap terdeteksi
  • Fungsi yang benar-benar berubah dead → muncul di new_dead; yang sudah dead sejak awal → TIDAK muncul
  • changed_nodes (ref_count/status) tetap bekerja lintas geser baris — sekarang node yang bergeser tak pernah dibandingkan sama sekali, jadi perubahan rc-nya luput
  • Test regresi: gagal kalau kunci dikembalikan ke n["id"]
  • Bentuk output tidak berubah; _diff_frontend tak regres; suite tak regres

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions