fix(core-engine): the function population is real functions only (#2691, #2692) - #2693
Merged
Merged
Conversation
…, #2692) Two extraction-layer defects found while working #2669 Batch D, batched because they are the same layer and the same question: what counts as a function, and what counts as one of its arguments. #2691 -- synthetic slices counted as functions The slicer synthesizes a `__global_context__` bucket to hold a file's top-level statements, and it was counted in the function population, so every per-function average was taken over entries that are not functions. On the keyword-rosetta corpus, livecode/lua/matlab/ruby/shell reported 16 functions against 13 planted -- a ~3/16 dilution of every descriptor, and of cog_raw and structural_mass downstream. The verdict is stamped once, in detector.py's orphan/duplicate loop -- the single place every function already passes through the synthetic-name test (#2547's `_is_synthetic_satellite_name`) -- and read by the three consumers that compute population statistics: signal_processor's per-function aggregates, record_keeper's `function_count` (what the corpus reads as `functions_found`), and security_auditor's `avg_func_args`. This beats re-deriving the rule in three files, and `FunctionNode` declares the key rather than growing an undeclared one. The buckets keep existing and keep their signals at file level. Only their membership in "how many functions does this file have, and what is the average one like" was ever wrong -- `file_mass` still sums every slice's impact, and the rows stay in `function_data`. #2692 -- a typed parameter counted as three arguments The comma-free fallback whitespace-split its capture, which is correct for the languages it was written for (Scheme's `(define (f arg1 arg2)`, shell positionals) but is reached by ANY comma-free capture -- and a single typed parameter is exactly that shape. ada's `Env : Integer` scored 3, and `X : Integer; Y : Integer` scored 6, since Ada separates parameters with semicolons. `_count_space_separated_args` applies two ordered discriminators: split on `;` (a parameter separator in the Ada/Pascal family that never appears in a Lisp/shell list, and commas never reach this branch), then treat a colon ADJACENT TO WHITESPACE as a type annotation marking one parameter. The whitespace requirement is what keeps Lisp identifiers with a bare colon (`foo:bar`, a legal Scheme symbol) counting as the separate arguments they are -- that case is tested. Verified end-to-end on the corpus: functions_found 16 -> 13 for all five languages, ada's avg args now identical to python's. All 46 corpus gates still PASS -- the manifests assert signal counts, which neither fix touches. Golden master: 147 zero-dep differences, every one a cognitive-load column (Cognitive Load Exposure, Raw Cognitive Density, and the two aggregates) in exactly the five affected languages. No structural signal count moves at all. #2692 contributes nothing here: ada is not in language-crucible, so its fix is corpus-visible only. Baselines: ruff regenerated, all pure line-shifts from the comment inserts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
…l function (#2691) CI caught this on the first push: tree-sitter accuracy dropped go's found_functions 897 -> 896 and args_exact_match 822 -> 821. The population filter reused `_is_synthetic_satellite_name`, which is correct for the orphan/duplicate checks it was written for (#2547) but also covers "Main" -- so `func main()` stopped being counted as a function. This repo had already reasoned the distinction out once, in tests/tools/tree_sitter_accuracy_audit.py's `_SYNTHETIC_GG_FUNC_NAMES`, whose comment states exactly why "Main" is deliberately NOT excluded (it collides with an extremely common real function name) and why a `_[Truncated]` suffix stays countable (it marks a real block that hit EOF unclosed -- a diagnostic about real code, not a placeholder). `_UNCOUNTABLE_SLICE_NAMES` is that list, in the engine, with the reasoning restated where the filter lives. A test now guards the distinction directly, including an assertion that the broad helper still treats "Main" as synthetic -- so if the two lists ever converge the test says so instead of silently protecting nothing. Golden master re-measured after narrowing: 147 -> 133 zero-dep differences, still every one a cognitive-load column in exactly the five affected languages, with no go movement. tree-sitter 30/30 OK, tri-comparison 3/3 OK, suite 7479 passed, corpus fix intact (functions_found 16 -> 13). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#2692) 133 differences in each fixture, regenerated with `crucible_check.py --update --yes` (user-authorised). Byte-identical to the diff measured and attributed before the bless. Every difference is a cognitive-load column -- `Cognitive Load Exposure` (65), `Raw Cognitive Density` (54), `cognitive_load` (12) and two aggregates -- confined to the five languages whose files carry top-level code: livecode, lua, matlab, ruby, shell. No structural signal count moves, and no per-function descriptor moves outside those five. The direction is the correction: cognitive load DROPS on these files (lua/pandoc 33.4 -> 29.21, ruby/rails 30.85 -> 26.68, matlab/eeglab 65.29 -> 62.99) because it is no longer averaged over the slicer's synthetic `__global_context__` buckets, which were never functions. #2692 contributes nothing to this diff: ada is not in language-crucible, so its fix is corpus-visible only (keyword-rosetta's ada avg_func_args 3.0 -> 1.0, matching the planted single parameter). The earlier over-broad version of this fix moved go as well, by excluding `func main()` from the function population; tree-sitter accuracy caught it (897 -> 896) and `_UNCOUNTABLE_SLICE_NAMES` narrowed it. go is absent from this diff, which is the confirmation that landed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 3, 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.
Closes #2691 and #2692. Both were found while working #2669 Batch D, and both are extraction-layer
defects answering the same question — what counts as a function, and what counts as one of its
arguments — so they batch under the one-layer-per-PR rule.
#2691 — synthetic slices were counted as functions
The slicer synthesizes a
__global_context__bucket to hold a file's top-level statements, and itwas counted in the function population. Every per-function average was therefore taken over entries
that are not functions: on the keyword-rosetta control corpus, livecode/lua/matlab/ruby/shell
reported 16 functions against 13 planted, diluting each descriptor by ~3/16 and carrying into
cog_rawandstructural_massdownstream.The verdict is stamped once, in
detector.py's orphan/duplicate loop — the single place everyfunction already passes through
_is_synthetic_satellite_name(#2547's helper) — and read by thethree consumers that compute population statistics:
signal_processormax_func_comp,avg_func_args,func_count,func_complexity_ginirecord_keeperfunction_count— what the corpus reads asfunctions_foundsecurity_auditoravg_func_argsThat beats re-deriving the rule in three files, and
FunctionNodedeclares the new key ratherthan growing an undeclared one (mypy caught the first attempt; the TypedDict was the right fix, not
a suppression).
The buckets keep existing and keep their signals at file level. Only their membership in "how
many functions does this file have, and what is the average one like" was wrong —
file_massstillsums every slice's impact and the rows stay in
function_data. That distinction is what makes thechange safe, and it has its own test.
#2692 — a typed parameter counted as three arguments
The comma-free fallback whitespace-split its capture. That is correct for the languages it was
written for — Scheme's
(define (f arg1 arg2), shell positionals — but it is reached by anycomma-free capture, and a single typed parameter is exactly that shape. ada's
Env : Integerscored 3;
X : Integer; Y : Integerscored 6, because Ada separates parameters with semicolons._count_space_separated_argsapplies two ordered discriminators:;— a parameter separator in the Ada/Pascal family that never appears in aLisp/shell parameter list (commas never reach this branch; the caller handles them).
The whitespace requirement is the load-bearing part: it keeps Lisp identifiers containing a bare
colon (
foo:bar, a legal Scheme symbol) counting as the separate arguments they are. Testedexplicitly, alongside
x: int,x: numberandconst X: Integer.Verification
functions_found16 → 13 for all five languages, and ada's averageargs now identical to python's.
touches, so there is no corpus rebless in this PR.
audit_checkall clear.origin/main.Golden master — 147 zero-dep differences, needs a bless
Every one is a cognitive-load column —
Cognitive Load Exposure(72),Raw Cognitive Density(58),cognitive_load(15) and two aggregates — in exactly the five affected languages(livecode, lua, matlab, ruby, shell). No structural signal count moves at all, which is the
expected shape: the fix changes a denominator, not what is extracted.
#2692 contributes nothing to this diff: ada is not in language-crucible, so its fix is
corpus-visible only. Worth stating plainly rather than leaving a reader to assume the ada change was
exercised here.
Awaiting a bless go-ahead.
Baselines
ruffregenerated — the tool confirmed all 70 findings were pure line-shifts from the commentinserts.
mypyunchanged. One earlier attempt stripped 17 unrelated trailing-whitespace lines inrecord_keeper.py; reverted, since fixing baselined backlog is a separate, deliberate PR.🤖 Generated with Claude Code