Skip to content

fix(callgraph): reference_count severely undercounts cross-file calls #210

Description

@Wolfvin

Context

Ditemukan saat audit CodeLens terhadap codebase nyata (Coretax-Auto-Downloader, vps-deploy/api/kds backend). reference_count (field rc di output search) yang menjadi basis banyak command (dead-code, impact, trace ref_count) ternyata SALAH — undercounting parah, bukan cuma di kasus edge (0 vs beberapa).

Mengapa

Dua bukti konkret dari real codebase:

  1. requirePermission (middleware factory, src/middleware/permission-gate.ts:56) — rc: 0 di output search --mode symbol. Ground truth: dipanggil di 13+ file sebagai requirePermission('admin') yang dipassing sebagai argument ke route registration (router.post(path, requirePermission('admin'), handler)). Verified via SQLite langsung: SELECT * FROM graph_edges WHERE target_id LIKE '%requirePermission%' → 0 rows. Tidak ada satupun CALLS edge terbentuk.

  2. hasPermission (src/lib/permissions.ts:75) — rc: 1. Ground truth via grep (exclude comment lines): 7 real call sites di assignments.ts (3x) dan task-templates.ts (3x) plus 1x di permission-gate.ts. rc seharusnya 7, bukan 1.

Kedua kasus menunjukkan call-graph builder (scripts/callgraph_engine.py, Phase 3 _resolve_call() dan resolusi edge di sekitar baris 590-610) tidak reliable untuk cross-file call counting — entah under-resolve import cross-file, entah dedup logic yang salah collapse banyak call site jadi satu edge, atau miss calls yang dipassing sebagai argument ke call lain (middleware factory pattern).

Konteks

Field ref_count/rc dipakai di banyak tempat: search result ranking, dead-code (ref_count == 0 sebagai basis "possibly dead"), impact (jumlah dependent). Kalau undercounting ini luas, confidence semua command tersebut ikut tidak reliable — worker perlu investigasi apakah ini kasus terisolasi (khusus middleware-factory-argument pattern) atau sistemik (semua cross-file call undercounting).

Tujuan

  1. Investigasi root cause: kenapa CALLS edge tidak terbentuk untuk call yang dipassing sebagai argument (requirePermission('admin') di dalam router.post(...)), dan kenapa hasPermission yang dipanggil 7x hanya tercatat 1x
  2. Fix root cause di callgraph_engine.py — kemungkinan di traversal (_walk recursion ke argument dari call_expression lain), resolusi (_resolve_call), atau edge dedup logic
  3. Pastikan fix tidak regress command lain yang depend pada graph (impact, trace, dead-code sudah baru saja di-fix minggu ini — jangan buat regresi baru di sana)

Constraint

  • Baca dulu _parse_calls_treesitter (baris ~1589) dan _resolve_call (baris ~2055) sebagai starting point — TAPI investigasi mandiri, jangan asumsikan lokasi bug ada di situ tanpa verifikasi
  • Test terhadap 2 kasus di atas sebagai regression test minimal
  • Jangan ubah command files (commands/*.py) kecuali memang diperlukan — fix seharusnya cukup di callgraph_engine.py atau file engine terkait
  • Setelah fix, re-run dead-code, impact, trace di codebase yang sama untuk pastikan tidak ada regresi (angka-angka boleh berubah karena lebih akurat, tapi commandnya harus tetap jalan tanpa crash)

Definition of Done

  1. search "requirePermission" <kds-path> --mode symbol menunjukkan rc >= 13 (atau jumlah real call site yang benar)
  2. search "hasPermission" <kds-path> --mode symbol menunjukkan rc == 7 (atau jumlah real call site yang benar, exclude comments)
  3. Root cause didokumentasikan di PR description — bukan cuma "sudah fixed" tanpa penjelasan kenapa
  4. python -m pytest tests/ -k "callgraph or ref_count" — 0 regresi baru
  5. Minimal 1 unit test baru yang reproduce kedua kasus di atas (middleware-factory-argument pattern + multi-site same-file calls)

Checklist Laporan Worker

  • Root cause detail: bagian mana di callgraph_engine.py yang salah dan kenapa
  • rc value untuk requirePermission dan hasPermission sebelum dan sesudah fix
  • Hasil test suite lengkap (bukan cuma yang terkait callgraph)
  • Re-test dead-code/impact/trace di KDS backend — screenshot/output before-after untuk pastikan tidak regresi

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions