Skip to content

orphaned_logic reports 100% dead code for five languages whose invocation model never names the callee — the family #2727 closed without covering #2806

Description

@squid-protocol

orphaned_logic reports every function in the file as unused for five languages whose invocation model does not name the callee. #2727 fixed a different half of this metric and its closing comment says explicitly that it did not reach this one — but the issue is closed COMPLETED and the ledger entry still carries disposition: upstream-bug, still_reproduces: true, pointing at it.

Measured 2026-09-06 on engine main a59ecf6b / corpus main 2038a86. Ledger: orphan-detection-is-name-recurrence.

The measurement

Baseline is 2.50 orphans/file — 13 probe functions across 4 files, 3 of them called from main. Five languages sit at 3.25, which is 13/4: every function orphaned.

raw_state_slop_orphans, per file, corpus median 2.50

3.25  abap, jcl, m4, makefile, objective-c      +30%, amber -- every function reads unused
2.75  cobol                                     +10%, in band (see below)
2.50  median
0.25  scheme                                    -90%

cobol is the near-miss that proves the mechanism rather than an exception to it: 11 of its 13 paragraphs read orphaned, and the two that clear are DISPATCH-PARA and PROBE-BRANCH — the only two paragraph names that appear a second time anywhere in the corpus, in main.cbl's ALTER DISPATCH-PARA TO PROCEED TO PROBE-BRANCH. The nine copybook paragraphs, entered through ENTRY 'PROBE-X' alternate entry points, are named once each and all read dead.

What #2727 fixed, and what it said it did not

#2727 replaced a whole-file token-frequency test with a span-scoped name-reference test. _is_orphan (detector.py ~L7086) now counts occurrences of the name outside [start_idx, end_idx), with a declaration discount when the span contains none, and #2777/#2774 have since corrected the boundary pattern and discounted _visibility_export names. That work is sound and this issue does not touch it.

Its closing comment, verbatim:

The issue's third table row — abap, cobol, jcl, m4, makefile, objective-c, scheme at 3.25 orphans/file, where every function reads orphaned — is unchanged by span-scoping, in the corpus and in the crucible. Those names genuinely do not recur: COBOL PERFORM, m4 macro invocation, Make target prerequisites are invocation forms the token test cannot see at all. That is a separate no-call-graph family, not something this fix shape reaches, and the issue should not be read as closing it.

It was then closed COMPLETED, and no successor was filed. This is that successor.

The point

The post-#2727 test is a correct name-reference test. The defect is one level up: orphaned_logic is consumed as "this function is dead", and for these languages a function can be reached without anything naming it.

  • jcl — a job's steps execute positionally, in the order written. Nothing in JCL references a step by name, ever. All 13 steps run on every submission and all 13 read dead.
  • cobol — paragraphs fall through sequentially; PERFORM is optional, and the corpus's probes are entered through ENTRY alternate entry points, which name the paragraph inside a quoted literal rather than as a call.
  • makefile — a target is invoked as another target's prerequisite, or from the command line.
  • m4 — macro invocation is bare-name expansion.
  • abap, objective-c — per detector: an orphan is a name seen once in the file, so any language that repeats the name in its own syntax reports zero orphans #2727's own table.

So the metric answers "does any other text name this function" and is read as "is this function unused", and for five of 46 languages those are different questions with opposite answers. Downstream it is not cosmetic: orphaned_logic feeds risk_tech_debt (jcl 48.8 against a 38.7 median) and drives the orphan→api conversion that api-contextual-baseline-fix documents.

Candidate shapes

(a) A per-language invocation model. A language declares whether its callable units are reached by name (python, java, go) or positionally / by fall-through (jcl, cobol, makefile, m4). For the second group orphaned_logic is not computed — the question is unanswerable without a real call graph, and a metric that cannot be answered should be absent, not maximal. Mirrors the _dependency_capture pattern: a per-language declaration in the registry, mechanically testable, no heuristics.

(b) Teach the test the invocation forms. COBOL PERFORM <name> and ENTRY '<name>', Make prerequisites, m4 bare expansion. Narrower, and it makes the metric more right where it applies — but the JCL case has no form to teach, because JCL has no invocation-by-name syntax at all. So (b) cannot cover the family and would leave jcl exactly where it is.

(c) Rename and re-scope. orphaned_logic becomes unreferenced_by_name, and the tech-debt formula stops treating it as dead code for languages where the name is not the call. Cheapest, honest, and it fixes the reading rather than the score.

My reading is (a) plus (c): the concept is only meaningful where the language names its callees, and the five languages that do not should read n/a rather than 100%.

Corpus pairing

Under (a) the five languages' raw_state_slop_orphans becomes n/a and their api_orphan_credit re-blesses — note api-declared-orphans-are-not-new-surface and api-contextual-baseline-fix both depend on the current conversion, so this is a coordinated re-bless rather than a one-cell edit. Under (c) nothing in the corpus moves. No plant change under any shape. orphan-detection-is-name-recurrence narrows to whichever shapes remain. rosetta:rebless-owed under (a).

Part of #2669. Successor to #2727 (closed); related #2754, #2768, #2774, #2777, #2731.

Activity

  1. added
    bugUnintended behavior or logic failure in the engine
    metricsHeuristics, risk exposures, and topological math updates
    core-engineModifications to the central physics and parsing engine
    on Sep 6, 2026
  2. squid-protocol commented on Sep 6, 2026

    @squid-protocol
    OwnerAuthor

    Addressed in #2825 (+ corpus keyword-rosetta#82), with one correction to this issue's own premise that a future reader should see before the fix shape:

    Four of the five languages here do name their callees. Measured on both corpora at engine main 87cfc933:

    • abap — PERFORM probe_branch is an invocation by name, and the census sees it: real-world ABAP (language-crucible, abapGit) reads 8 unreferenced subroutines in 124, a 6% rate, entirely through PERFORM.
    • m4 — bare-name macro expansion.
    • makefile — a prerequisite (main.mk's one wired probe, $(call probe_branch), already cleared).
    • objective-c — a selector send.

    What those four had in common was keyword-rosetta, not the engine: every median language's main dispatches its three probes (python's entry() calls probe_branch/probe_io/probe_risk), and in these four main declared a dispatch/entry unit that invoked nothing at all — FORM dispatch wrote a banner, - (int)entry: returned its argument, m4_define(probe_dispatch, [$1]) named no probe. Planting each language's own idiom moves all four from 3.25 to the median with no engine change. The "identical planted intent in every language" premise this issue reasoned from was false for exactly these cells.

    jcl is the real case, and shape (a) is what it got. Nothing in JCL references an EXEC step; the nameable unit is the PROC, which the api rule counts and the slicer does not extract. jcl.py declares invocation_model: "positional" (a top-level language property — not a rule: language_lens.py compiles every string value inside rules into a regex) and the census is not computed there. Worth adding to this issue's evidence: of the 42 crucible steps that did clear the flag, none were invoked — they collided with unrelated text (a step named CREATE beside inline SQL in a SYSIN DD * block, COBOL beside the word inside a DSN, CRTABS repeated seven times in one job). Both the 89% and the 11% were noise.

    Shape (c) landed as a full rename, not just a re-reading: orphaned_logic → unreferenced_by_name, state_slop_orphans → state_unreferenced, raw_state_slop_orphans → raw_state_unreferenced, with an in-place column migration for existing databases. The contract, its six corollaries and the 46-language audit are in docs/unreferenced_by_name_contract.md.

    Shape (b) is still open for two languages, in the opposite direction: #2823 (haskell/scheme read 0.25/file because a module export list and a type signature clear the flag).

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

    bugUnintended behavior or logic failure in the enginecore-engineModifications to the central physics and parsing enginemetricsHeuristics, risk exposures, and topological math updates

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions