fix(trace,impact): render module-level callers instead of silently dropping (closes #223)#248
Open
Wolfvin wants to merge 1 commit into
Open
fix(trace,impact): render module-level callers instead of silently dropping (closes #223)#248Wolfvin wants to merge 1 commit into
Wolfvin wants to merge 1 commit into
Conversation
…opping (closes #223) PR #219 (issue #210) fixed ref_count undercounting by adding a synthetic source_id = '<file>:0:<module>' for module-top-level calls. The synthetic id has NO matching entry in graph_nodes (intentional — keeps list/search output free of fake <module> function entries). Pre-#223 this caused trace --direction up and impact to silently drop ALL module-level callers: the BFS JOIN graph_nodes.node_id = ? returned no row, and the caller was skipped. ref_count (computed from target side) was correct, but trace/impact (computed from source side) were wrong — inconsistent and dangerous for anyone using trace to decide 'safe to delete?'. Concrete repro from issue body: search requirePermission shows rc:49, but trace --direction up showed only 1 entry (itself). Fix: - graph_model.py: add _MODULE_LEVEL_SENTINEL constant + helper is_module_level_source_id() + _module_level_caller_entry() builder. _bfs now emits a synthesized entry for synthetic <file>:0:<module> source_ids instead of dropping them, in BOTH the normal path and the cyclic-visited path. Module-level entries are terminal (no further BFS) since module scope is the top of a file's call hierarchy. - trace_engine.py: _bfs_trace_graph preserves module_level marker on chain entries (both normal + cyclic) so formatters can render 'module-level caller in <file>' distinctly. fn is set to '<module>' for clarity. - impact_engine.py: analyze_impact emits a 'module-level caller' entry in affected['direct'] AND affected['indirect'] when from_id is a synthetic module-level id. Helper _file_from_module_level_id() extracts the file path for the human-readable relation string. Constraint from issue #223: do NOT create a fake <module> node in graph_nodes. The synthetic source_id stays edge-only — list/search output remains free of <module> entries. Verified by tests. Tests: tests/test_issue223_module_level_callers.py — 16 tests covering helper detection, query_callers, trace_via_graph, analyze_impact, and the no-pollution constraint.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
3 tasks
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Closes #223
Patch Peta (delta struktural)
Files:
M scripts/graph_model.py — add
_MODULE_LEVEL_SENTINELconstant,is_module_level_source_id()helper, and_module_level_caller_entry()builder._bfsnow emits a synthesized entry for synthetic<file>:0:<module>source_ids instead of silently dropping them, in BOTH the normal-resolution path AND the cyclic-visited path. Module-level entries are terminal (no further BFS enqueue) since module scope is the top of a file's call hierarchy.M scripts/trace_engine.py —
_bfs_trace_graphpreservesmodule_levelmarker on chain entries (normal + cyclic) and setsfn="<module>"for clarity. Module-level entries are NOT enqueued for further BFS.M scripts/impact_engine.py —
analyze_impactemits a "module-level caller — calls X" entry inaffected["direct"]AND a "N hops from X (module-level caller)" entry inaffected["indirect"]whenfrom_idis a synthetic module-level id. New helper_file_from_module_level_id()extracts the file path for the human-readable relation string.Endpoints: none
Schema: no SQL schema change. New field
module_level: true(boolean) appears on result dicts fromgraph_model.query_callers/query_calleesand on chain entries fromtrace_engine.trace_via_graph— consumers can opt-in to render it distinctly. No existing field is removed or renamed.Konvensi: synthetic module-level source_ids follow the format
<file>:0:<module>(emitted byjs_backend_parser.py:248,ts_backend_parser.py:125,fallback_js_backend.py:196). Future parsers that emit module-top-level calls MUST use the same format sois_module_level_source_id()recognizes them. Do NOT create a matching entry ingraph_nodes— the synthetic id stays edge-only (constraint from issue fix(trace,impact): module-level callers invisible due to synthetic source_id, inconsistent with rc #223, keepslist/searchoutput free of fake<module>function entries).Klaim + Bukti
import:
python3 -c "import sys; sys.path.insert(0,'scripts'); import graph_model, trace_engine, impact_engine"→ exit 0 (no errors)unit tests:
python3 -m pytest tests/test_graph_model.py tests/test_issue223_module_level_callers.py tests/test_edge_resolver.py tests/test_search_engine.py tests/test_command_count.py tests/test_command_registry.py tests/test_doctor.py tests/test_orient.py→ 128 passed in 6.62sparser:
python3 -m pytest tests/test_ts_backend_parser.py tests/test_js_backend_parser.py→ 18 passed, 29 skipped (skips = tree-sitter grammars not installed in this env, pre-existing)dod #1 (trace up shows module-level callers): end-to-end smoke test on synthetic fixture (1 module-level caller + 1 regular caller) →
trace_via_graph requirePermission --direction up up_chain (3 entries): depth=0 fn='requirePermission' file='auth.ts' node_id='auth.ts:10:requirePermission' depth=1 fn='<module>' file='auth.ts' node_id='auth.ts:0:<module>' [module-level] depth=1 fn='handler' file='auth.ts' node_id='auth.ts:5:handler'Pre-fix: only depth=0 + depth=1 handler entries (module-level silently dropped).
dod #2 (impact dependent count consistent with rc): same fixture →
analyze_impact requirePermissionreturns 2 entries inaffected["direct"](1 module-level + 1 regular handler). Pre-fix: only 1 entry (handler).direct dependents (2): name='<module>' file='auth.ts' relation='module-level caller — calls requirePermission' [module-level] name='handler' file='auth.ts' relation='calls requirePermission'dod #3 (list/search NOT polluted):
find_nodes_by_name("<module>", db_path)→[](no fake node).SELECT * FROM graph_nodes WHERE node_id LIKE '%:0:<module>' OR name = '<module>'→ 0 rows. Synthetic source_id stays edge-only as required by the issue constraint.dod #4 (0 new test regressions): 3 failures in
tests/test_compact_format.py(TestSearchPagination ×2, TestGraphSchemaCommand::test_returns_correct_counts) — verified pre-existing on clean main (root cause:tree-sitter-typescriptgrammar not installed in worker env → TSBackendParser falls back → CALLS edge count differs). NOT caused by this PR.Klaim yang TIDAK bisa dibuktikan di sini:
requirePermissiondi KDS backend real (rc:49 case from issue body): tidak dijalankan karena KDS workspace tidak tersedia di sesi worker ini. Reproduksi di fixture sintetik (2 callers: 1 module-level + 1 regular) sudah memvalidasi structural fix. Verifikasi pada codebase nyata KDS diserahkan ke BOS saat review.Catatan Pendekatan
none — implementasi mengikuti instruksi issue (cari cara lain tanpa buat fake node entry; fallback query yang follow dangling source_id dan render sebagai "module-level caller").
Breaking / Found-not-fixed
_bfsnow emits a cyclic entry for module-level source_ids that are revisited. In practice this branch is hard to trigger (module-level callers are terminal — they're added tovisitedon first encounter and never enqueued for further BFS, so they can't naturally reappear as a neighbor). The code path exists for defensive correctness; if a future change makes module-level callers non-terminal, the cyclic path becomes relevant.tests/test_compact_format.py::TestSearchPagination(2) and::TestGraphSchemaCommand::test_returns_correct_counts(1) — verified pre-existing on clean main, root cause is missing tree-sitter grammars in worker env, NOT this PR.test_large_file_parsing.pysegfault (Known Gap [ARCH] Replace flat registry with true graph data model (nodes + edges) #7 in CONTEXT.md): not run — would abort the test session. Pre-existing.