Skip to content

fix(detector): exclude synthetic slicer bucket names from orphan/duplicate census (#2547) - #2618

Merged
squid-protocol merged 1 commit into
mainfrom
fix/2547-orphan-census-non-function-shapes
Aug 31, 2026
Merged

fix(detector): exclude synthetic slicer bucket names from orphan/duplicate census (#2547)#2618
squid-protocol merged 1 commit into
mainfrom
fix/2547-orphan-census-non-function-shapes

Conversation

@squid-protocol

Copy link
Copy Markdown
Owner

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:

  • Mode D bundles any top-level, non-comment code that sits before the file's first real scope into a synthetic satellite literally named "__global_context__" (galaxyscope.py's Contextual Baseline Fix downstream, detector.py ~5063).
  • Mode E names every top-level statement generically from its leading keyword ("SELECT_Statement", "CREATE_Statement", ...) rather than any real captured identifier (detector.py ~5235) — sql's func_start/class_start regexes 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's token_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 #1096 control corpus (keyword-rosetta's data/{shell,sqlite}/) via galaxyscope --debug + the recorder DB:

  • a.sh/b.sh: 4 "orphans" reported for 3 real functions (the extra was __global_context__).
  • main.sql: orphaned_logic = 7 for 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:

  1. Missing __global_context__ entirely.
  2. Exact-match only — never stripped the _[Truncated]/_[Unterminated] suffixes the slicer appends to these same names elsewhere (detector.py ~5039, ~5043, ~5277).
  3. Never covered Mode E's <KEYWORD>_Statement shape at all.
  4. Only guarded the orphan branch — the duplicate-detection branch had no exclusion, so the same synthetic names could also be mis-flagged as "duplicate logic".

Fix

Replace the hardcoded exact-match set with _is_synthetic_satellite_name() (detector.py, module-level, right before StructuralExtractor): strips the known suffixes, checks the full synthetic-name family, and additionally recognizes Mode E's <KEYWORD>_Statement pattern (always synthesized, never a real identifier — see the "sql" in lang_key branch at detector.py ~5235). Gates both the orphan and duplicate branches of the classification loop.

Verification

  • New regression test 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).
  • Full suite: 7168 passed, 2 skipped, 9 xfailed, 3 xpassed (all pre-existing/unrelated).
  • tests/tools/audit_check.py: clean, no new ruff/mypy/dead-key/ast-accuracy findings.
  • Re-ran 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 the issue's own reporter — me — initially assumed before this investigation).
  • tests/tools/crucible_check.py: both full_precision and zero_dependency pass clean after fixture regen.
  • Golden master regenerated (tests/tools/update_golden_master.py, both modes) — ~2200 diffs, all in tech_debt/api_exposure/documentation exposure ratios for shell/sql/lua/livecode/ruby/matlab corpus repos, exactly the intentional effect of no longer inflating those files' orphaned_logic/api with non-function shapes.

Notes

  • #2536 (raw pre-adjustment hit_vector recording) is a separate, broader auditability enhancement — not superseded by this fix, left open.
  • Dockerfile's Mode A RUN/CMD/HEALTHCHECK satellite-name reuse is a related-but-distinct mechanism (real captured keywords, not synthetic labels); the live corpus shows zero actual orphaned_logic inflation for the dockerfile control files today, so it's out of scope here (no observed symptom).
  • keyword-rosetta's deviation_ledger.json entry (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

…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>
@github-actions

Copy link
Copy Markdown
Contributor

🐦‍⬛ Muninn Security Scan

✅ No security issues found.

🐦‍⬛ Powered by Muninn · Skald Lab

@squid-protocol
squid-protocol merged commit 1aa4181 into main Aug 31, 2026
30 checks passed
@squid-protocol
squid-protocol deleted the fix/2547-orphan-census-non-function-shapes branch August 31, 2026 21:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

orphan census counts non-function shapes on non-call-graph languages, making orphan→api conversion unauditable

1 participant