Skip to content

length leaks: func_internal_density, avg_func_loc and control_flow_ratio read file length with content and tier held equal (rosetta F.1 design pass) #2705

Description

@squid-protocol

Found by the length-leak check added to keyword-rosetta's bias_report.py in gitgalaxy#2669 F.1. Design pass requested (T1), in the #2655 shape: no engine edit until the shape is approved.

What the check does

The rosetta corpus is one 12-probe program written in 46 languages, at 46 lengths (coding_loc from 4 to 19 lines per file; every shell is under the 50-line evidence-mass floor). For each derived metric, take the languages whose measured inputs for that metric are all inside the green band (content held equal) and that share the engine's scoring tier (_get_tier; the tier-3 languages are largely the short shells, so without this a tier effect reads as a length effect), and rank-correlate the metric against coding_loc. With content and tier held, length is the only thing left to vary, so a strong correlation means the formula is reading length where it should be reading content.

Threshold: |Spearman rho| ≥ 0.6 over ≥ 8 languages. Engine a334839, corpus main, 2026-09-04.

What it found

metric languages (tier3) rho inputs held in band where length enters
func_internal_density 11 −0.96 branch, args, func_start avg_func_complexity / avg_func_loc (record_keeper.py ~L487) — the denominator is length by construction
avg_func_loc 29 +0.92 func_start loc / functions_found — length by definition
control_flow_ratio 21 −0.67 branch branch / (branch + structural_boundaries) (detector.py ~L1288); structural_boundaries is a per-language token tally that grows with the file

Weak (0.4 ≤ |rho| < 0.6), reported but not asserted: risk_cognitive_load −0.58 (n=8), risk_tech_debt −0.58 (n=16), cog_raw −0.55 (n=9), risk_verification +0.48 (n=25, no LOC term located), classes_found −0.41 (n=31, nothing held). Note that cog_raw read −0.63 before tier was held equal and dropped to weak after: most of what looked like a length leak there was #2653's irc / mass_loc term. Everything under _mass_loc (cog load, safety, state flux, tech debt) is floored at 50 by #2655, so no rosetta shell should carry a length term through those densities at all — the residual weak correlations are either an unheld input (concurrency, reflection_metaprogramming are not cached) or the per-file mean over four files, and are worth a look but are not the finding.

Why it matters

These three are the per-function morphology descriptors the bias report scores as "the engine describing one program differently depending on the language it is written in". For avg_func_loc that description is tautological: a per-function line count is length, and the corpus cannot plant it. For func_internal_density the same length sits in the denominator, so a language that writes the same 13 probes in fewer lines is reported as denser logic. For control_flow_ratio the branch count is planted at 3 while the denominator is a vocabulary tally that scales with the file — #2689 bucket B already established that planting structural_boundaries cannot equalise it.

On the real-world corpus the same thing holds one level up: two files with identical branch and argument structure score differently on all three purely by line count, which is the length-dependence #2655 removed from the risk densities but not from these recorder-side descriptors.

Options (for the design pass)

  1. Reclassify as length. Treat avg_func_loc as a size measure (it is), and the bias report demotes it to the context group beside coding_loc. Cheapest, but does nothing for the other two.
  2. Floor the per-function denominators the way UEF small-file normalization isn't achieving its intent — redesign scoring so identical intent scores identically regardless of file size (design pass for Fable) #2655 floored the per-file ones. func_internal_density = avg_comp / max(avg_loc, F) with a per-function evidence floor; control_flow_ratio over max(branch + boundaries, F'). Same doctrine as _mass_loc, moves golden masters for every function under the floor.
  3. Redefine control_flow_ratio against a planted-able denominator. Branch per function (avg_func_complexity already exists) or branch per structural boundary kind rather than per boundary token. Changes the metric's meaning; needs the wiki 08-xx pages updated.
  4. Leave the engine alone, ledger the three metrics corpus-side as length-derived. Honest for the rosetta gate, but leaves the real-world length dependence in place.

Recommendation to evaluate first: (2) for func_internal_density, (1) for avg_func_loc, and a decision between (3) and (4) for control_flow_ratio, since #2689 already showed its denominator cannot be planted.

Corpus side

keyword-rosetta PR (F.1/F.2) carries the check, the tiers map in docs/bias_data.json, and the table above regenerated on every corpus push. The three metrics' rosetta cells keep whatever verdict they have today; this issue does not change the gate.

Related: #2655 (evidence-mass floor — the per-file half of this), #2689 (bucket B: structural_boundaries is unplantable), #2653 (tier constants — the confound the check now holds equal), #2669 (Batch F).

Activity

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

    core-engineModifications to the central physics and parsing enginemetricsHeuristics, risk exposures, and topological math updates

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions