fix(detector): exclude synthetic slicer bucket names from orphan/duplicate census (#2547) - #2618
Merged
Conversation
…icate census (#2547) Languages sliced by Mode D (_slice_by_keywords: shell/ruby/lua/elixir/livecode/ matlab) or Mode E (_slice_by_terminator: sql/sqlite/plpgsql/...) have no real same-file call graph. Mode D bundles any top-level loose code before the first real scope into a synthetic "__global_context__" satellite; Mode E names every top-level statement generically from its keyword ("SELECT_Statement", "CREATE_Statement", ...) rather than a real captured identifier. Neither is a genuine callable function, so the intra-file orphan census's "does this name appear anywhere else in the file" check was structurally guaranteed to flag them -- confirmed against the #1096 control corpus: shell a.sh/b.sh reported 4 orphans for 3 real functions (the extra was __global_context__), and sqlite main.sql reported orphaned_logic=7 for a file with zero real callable names at all (every satellite was a synthetic Mode-E bucket). The existing exclusion set (Unknown_Sat/Anonymous_Block/Main/Declarative_Block) already recognized this class of problem but was incomplete: it missed __global_context__ entirely, didn't strip the _[Truncated]/_[Unterminated] suffixes the slicer appends to those same names, and never covered Mode E's <KEYWORD>_Statement shape. It also only guarded the orphan branch, not the duplicate-detection branch reachable by the same synthetic names. Replace it with _is_synthetic_satellite_name(), which recognizes the full family (suffix-stripped exact names + the Statement-name pattern) and gates both branches. This directly fixes the orphan->api conversion (Contextual Baseline Fix, galaxyscope.py ~2145) inflating a popular file's api count with non-function shapes. Re-verified against the real corpus post-fix: a.sh/b.sh api 4->3, main.sh orphans 2->1, main.sql orphans 7->0, a/b/c.sql's bogus api 2/4/3->0/0/0 (SQL statements were never real API surface -- Mode E never captures a real identifier for any SQL statement type, so the correct count is zero, not one per CREATE INDEX as initially assumed). Golden master fixtures regenerated (tests/tools/update_golden_master.py, both full-precision and zero-dependency modes) -- the ~2200 diffs are the expected, intentional consequence of this fix across every shell/sql/lua/livecode/ruby file in the language-crucible corpus with either shape. crucible_check.py and the full test suite (7168 tests) pass clean after regeneration. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Contributor
This was referenced Aug 31, 2026
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.
Summary
Closes #2547. Languages sliced by Mode D (
_slice_by_keywords: shell/ruby/lua/elixir/livecode/matlab) or Mode E (_slice_by_terminator: sql/sqlite/plpgsql/t-sql/mysql/psql) have no real same-file call graph:"__global_context__"(galaxyscope.py's Contextual Baseline Fix downstream,detector.py~5063)."SELECT_Statement","CREATE_Statement", ...) rather than any real captured identifier (detector.py~5235) —sql'sfunc_start/class_startregexes exist for signal-counting purposes only; Mode E's own satellite naming never uses their captured groups.Neither is a genuine callable function, so the intra-file orphan census's "does this name appear anywhere else in the file" check (
detector.py'stoken_counts[func_name] <= 1) is structurally guaranteed to flag them every time — not a signal about real dead code, just an artifact of how the slicer buckets non-function shapes.Verified directly against the
#1096control corpus (keyword-rosetta'sdata/{shell,sqlite}/) viagalaxyscope --debug+ the recorder DB:a.sh/b.sh: 4 "orphans" reported for 3 real functions (the extra was__global_context__).main.sql:orphaned_logic = 7for a file where every satellite is a synthetic Mode-E bucket (zero real callable names at all).Root cause
The orphan/duplicate loop already tries to exclude known-synthetic bucket names (
Unknown_Sat/Anonymous_Block/Main/Declarative_Block), but the set was incomplete:__global_context__entirely._[Truncated]/_[Unterminated]suffixes the slicer appends to these same names elsewhere (detector.py~5039, ~5043, ~5277).<KEYWORD>_Statementshape at all.Fix
Replace the hardcoded exact-match set with
_is_synthetic_satellite_name()(detector.py, module-level, right beforeStructuralExtractor): strips the known suffixes, checks the full synthetic-name family, and additionally recognizes Mode E's<KEYWORD>_Statementpattern (always synthesized, never a real identifier — see the"sql" in lang_keybranch atdetector.py~5235). Gates both the orphan and duplicate branches of the classification loop.Verification
test_detector_orphan_census_excludes_synthetic_slicer_names(tests/core_engine/test_detector.py), covering both Mode D (__global_context__) and Mode E (<KEYWORD>_Statement).tests/tools/audit_check.py: clean, no new ruff/mypy/dead-key/ast-accuracy findings.a.sh/b.shapi4→3,main.shorphans 2→1,main.sqlorphans 7→0,a/b/c.sql's bogusapi2/4/3→0/0/0 (SQL statements were never real API surface — Mode E never captures a real identifier for any SQL statement type, so the correct count is zero, not one perCREATE INDEXas the issue's own reporter — me — initially assumed before this investigation).tests/tools/crucible_check.py: bothfull_precisionandzero_dependencypass clean after fixture regen.tests/tools/update_golden_master.py, both modes) — ~2200 diffs, all intech_debt/api_exposure/documentationexposure ratios for shell/sql/lua/livecode/ruby/matlab corpus repos, exactly the intentional effect of no longer inflating those files'orphaned_logic/apiwith non-function shapes.Notes
#2536(raw pre-adjustment hit_vector recording) is a separate, broader auditability enhancement — not superseded by this fix, left open.RUN/CMD/HEALTHCHECKsatellite-name reuse is a related-but-distinct mechanism (real captured keywords, not synthetic labels); the live corpus shows zero actualorphaned_logicinflation for the dockerfile control files today, so it's out of scope here (no observed symptom).keyword-rosetta'sdeviation_ledger.jsonentry (orphan-conversion-opaque-counts) will need its own re-validation sweep in that repo after this merges — not part of this PR.🤖 Generated with Claude Code