From e1c1b53bf40987d89210adae1c96a4562e084b35 Mon Sep 17 00:00:00 2001 From: Joe Esquibel Date: Fri, 4 Sep 2026 08:33:10 -0400 Subject: [PATCH] fix(recorders): floor func_internal_density's denominator at 12 lines per function (#2705) The per-file densities got an evidence-mass floor in #2655; the recorder-side per-function density did not, so avg_func_complexity / avg_func_loc read function LENGTH where structure was held equal: the keyword-rosetta corpus (the same 13 functions at 46 lengths) measured it at Spearman -0.96 against coding_loc with branch/args/func_start held, and in the golden master five find*.sql files whose functions average one line and one branch each read a flat 1.00 -- the densest files in the corpus -- for being the tersest. FUNC_EVIDENCE_MASS_FLOOR = 12 beside EVIDENCE_MASS_FLOOR = 50, derived the same way: 50 is the golden-master median coding_loc (51); 12 is the golden-master median avg_func_loc (12.0), flooring the same 49% of the population. Below the floor the column is an exact rescale of avg_func_complexity (branch structure); at or above it, byte-identical to before. Rosetta: leak rho -0.96 -> 0.000, consistency 54% -> 67%, no --gate verdict changes. Real code: 574/2074 files with functions move (median drop 0.060), 72.3% untouched. No ML blast radius: the metric is in neither pre-trained vector, and its only other consumer (security_auditor's telemetry read) has always seen 0.0 because signal_processor never writes the key -- filed separately as #2714. Tests pin the four #2655 properties for the per-function analog (invariance below the floor, no cliff at it, parity above it, zero with no functions) through RecordKeeper.record_mission. ruff baseline regenerated for pure line-shifts. Co-Authored-By: Claude Fable 5.1 --- docs/wiki/08-03-transforming-regex-counts.md | 1 + gitgalaxy/recorders/record_keeper.py | 15 +- gitgalaxy/standards/analysis_lens.py | 10 ++ tests/ruff_audit_baseline.json | 30 ++-- .../test_func_internal_density_floor.py | 145 ++++++++++++++++++ 5 files changed, 183 insertions(+), 18 deletions(-) create mode 100644 tests/tools_recorders/test_func_internal_density_floor.py diff --git a/docs/wiki/08-03-transforming-regex-counts.md b/docs/wiki/08-03-transforming-regex-counts.md index 47c37a354..efe5ada60 100644 --- a/docs/wiki/08-03-transforming-regex-counts.md +++ b/docs/wiki/08-03-transforming-regex-counts.md @@ -18,6 +18,7 @@ Applies four stabilizing principles: 3. **Sigmoid Gating:** Uses a logistic sigmoid function to filter low-density noise (0-5%) and scale exponentially as risk crosses thresholds. 4. **Quantized Tiering:** Scores are binned into qualitative tiers (Unshielded to Fortified). 5. **Evidence-Mass Floor:** Every per-file density divides by $\max(LOC, 50)$, never by raw LOC. Below 50 coding lines a file is scored on its *counts* (a two-hit file is a two-hit file whether it is 5 or 49 lines long), so identical intent scores identically regardless of file length; at or above the floor the density regime is untouched. This is the per-file analog of the mass-weighted averaging used at directory scope, and it is the *only* small-file mechanism -- it replaced six independent guards (a `<15 LOC` cognitive-load cliff, two `+20` paddings, a `loc/15` dampener, an unbounded $Irc/LOC$ floor, a `max(total_loc, 10)` API guard) that each fired on a different LOC range and fought each other (#2655). Files below the floor carry `mass_floored: true` and their `evidence_mass` in telemetry. Two consistency rules follow from it: $Irc$ *corrects* measured risk and never creates it (zero measured evidence scores zero in every tier), and a file with no branches carries no cognitive load at any length. + The per-function descriptor has the same floor, derived the same way (#2705): `func_internal_density` $= \text{avg\_func\_complexity} / \max(\text{avg\_func\_loc},\ 12)$, where 12 is the golden-master median lines-per-function exactly as 50 is the median coding LOC per file. Below the floor the column is a pure rescale of `avg_func_complexity` -- it reports branch structure, and two files with the same branches per function read the same density however tersely one of them is written; at or above it the column is unchanged. That means for roughly half of real files the two columns say the same thing, by design. Language Confidence Tiers (1 to 3) apply Fidelity Coefficients ($Fc$) and Implicit Risk Corrections ($Irc$) based on language strictness. General Risk Equation: diff --git a/gitgalaxy/recorders/record_keeper.py b/gitgalaxy/recorders/record_keeper.py index 12d5d7480..490b713a9 100644 --- a/gitgalaxy/recorders/record_keeper.py +++ b/gitgalaxy/recorders/record_keeper.py @@ -19,9 +19,12 @@ import sqlite3 import statistics from pathlib import Path -from typing import Optional, TypedDict +from typing import Optional, TypedDict, cast -from gitgalaxy.standards.analysis_lens import RECORDING_SCHEMAS +from gitgalaxy.standards.analysis_lens import ENGINE_CONSTANTS, RECORDING_SCHEMAS + +# #2705: per-function evidence-mass floor (see analysis_lens.ENGINE_CONSTANTS). +FUNC_EVIDENCE_MASS_FLOOR = float(cast("int", ENGINE_CONSTANTS["FUNC_EVIDENCE_MASS_FLOOR"])) class FolderStats(TypedDict): @@ -484,7 +487,13 @@ def record_mission( pct_z_above_5 = (sum(1 for c in complexities if c >= 5) / func_count) * 100.0 pct_z_above_15 = (sum(1 for c in complexities if c >= 15) / func_count) * 100.0 - func_internal_density = (avg_comp / avg_loc) if avg_loc > 0 else 0.0 + # #2705: evidence-mass floor on the per-function denominator, the + # analog of signal_processor._mass_loc. Without it a file whose + # functions average one line and one branch each read 1.00, and the + # rosetta corpus measured the column as file length (rho -0.96 with + # coding_loc, content held equal). The max() also absorbs the old + # avg_loc > 0 guard: no functions -> avg_comp 0 -> density 0. + func_internal_density = avg_comp / max(avg_loc, FUNC_EVIDENCE_MASS_FLOOR) logic_loc_denom = max( int(file_data.get("coding_loc", 1) * tel.get("control_flow_ratio", 0.0)), diff --git a/gitgalaxy/standards/analysis_lens.py b/gitgalaxy/standards/analysis_lens.py index 05c871b2d..0ad626118 100644 --- a/gitgalaxy/standards/analysis_lens.py +++ b/gitgalaxy/standards/analysis_lens.py @@ -79,6 +79,16 @@ def get_policy(mode="baseline"): # each other. 50 is the engine's existing unit of mass (file_impact = loc/50, # graveyard's safe_mass_floor) and the golden-master corpus median. "EVIDENCE_MASS_FLOOR": 50, + # Per-function analog (#2705): func_internal_density = avg_func_complexity / + # max(avg_func_loc, FUNC_EVIDENCE_MASS_FLOOR). Same derivation as the file + # floor -- 50 sits at the golden-master median coding_loc (51); 12 sits at the + # golden-master median avg_func_loc (12.0) and floors the same 49% of the + # population. Below it the column is an exact rescale of avg_func_complexity + # (branch structure), so functions that say the same thing in fewer lines stop + # reading as denser logic; at or above it the column is byte-identical to + # before. Only consumers: the file_data column and the reports -- the metric is + # in neither pre-trained vector (#2714 is why security_auditor never saw it). + "FUNC_EVIDENCE_MASS_FLOOR": 12, } FIDELITY_TIERS = { diff --git a/tests/ruff_audit_baseline.json b/tests/ruff_audit_baseline.json index ae1e39889..5f0aeac68 100644 --- a/tests/ruff_audit_baseline.json +++ b/tests/ruff_audit_baseline.json @@ -23,25 +23,25 @@ "gitgalaxy/recorders/audit_recorder.py:358: PERF401": "Use `list.extend` to create a transformed list", "gitgalaxy/recorders/gpu_recorder.py:285: C414": "Unnecessary `list()` call within `sorted()`", "gitgalaxy/recorders/llm_recorder.py:1049: SIM102": "Use a single `if` statement instead of nested `if` statements", - "gitgalaxy/recorders/record_keeper.py:187: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:188: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:189: W291": "Trailing whitespace", "gitgalaxy/recorders/record_keeper.py:190: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:289: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:290: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:291: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:191: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:192: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:193: W291": "Trailing whitespace", "gitgalaxy/recorders/record_keeper.py:292: W291": "Trailing whitespace", "gitgalaxy/recorders/record_keeper.py:293: W291": "Trailing whitespace", "gitgalaxy/recorders/record_keeper.py:294: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:765: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:769: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:771: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:772: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:818: RUF005": "Consider iterable unpacking instead of concatenation", - "gitgalaxy/recorders/record_keeper.py:887: RUF005": "Consider iterable unpacking instead of concatenation", - "gitgalaxy/recorders/record_keeper.py:924: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:925: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:956: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:295: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:296: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:297: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:774: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:778: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:780: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:781: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:827: RUF005": "Consider iterable unpacking instead of concatenation", + "gitgalaxy/recorders/record_keeper.py:896: RUF005": "Consider iterable unpacking instead of concatenation", + "gitgalaxy/recorders/record_keeper.py:933: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:934: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:965: W291": "Trailing whitespace", "gitgalaxy/recorders/sbom_recorder.py:221: PERF401": "Use `list.extend` to create a transformed list", "gitgalaxy/security/security_auditor.py:359: RUF046": "Value being cast to `int` is already an integer", "gitgalaxy/security/security_auditor.py:426: PERF203": "`try`-`except` within a loop incurs performance overhead", diff --git a/tests/tools_recorders/test_func_internal_density_floor.py b/tests/tools_recorders/test_func_internal_density_floor.py new file mode 100644 index 000000000..4578311e5 --- /dev/null +++ b/tests/tools_recorders/test_func_internal_density_floor.py @@ -0,0 +1,145 @@ +# ============================================================================== +# GitGalaxy +# Copyright (c) 2026 Joe Esquibel +# +# This source code is licensed under the PolyForm Noncommercial License 1.0.0. +# You may not use this file except in compliance with the License. +# A copy of the license can be found in the LICENSE file in the root directory +# of this project, or at https://polyformproject.org/licenses/noncommercial/1.0.0/ +# ============================================================================== +""" +Per-function evidence-mass floor (#2705): func_internal_density must not read +function length where the branch structure is held equal. + +The file-level equations got this property from #2655 (EVIDENCE_MASS_FLOOR, pinned +by tests/core_engine/test_uef_length_invariance.py). The recorder-side per-function +density was left out: avg_func_complexity / avg_func_loc with no floor, so the +keyword-rosetta corpus -- the same 13 functions at 46 lengths -- measured the column +as file length (Spearman -0.96 against coding_loc with branch/args/func_start held). +This module pins the same four properties for the per-function analog: + + 1. INVARIANCE -- below ENGINE_CONSTANTS["FUNC_EVIDENCE_MASS_FLOOR"], identical + branch structure scores identically at any function length. + 2. CONTINUITY -- the value at the floor equals the value just below it. + 3. PARITY -- at floor+1 and above, the column is byte-identical to the + pre-#2705 definition avg_comp / avg_loc. + 4. ZERO -- a file with no functions still records 0 (the max() absorbed the + old `avg_loc > 0` guard; nothing divides by zero). + +Driven through RecordKeeper.record_mission, the only place the column is computed. +""" + +import sqlite3 +from unittest.mock import patch + +import pytest + +from gitgalaxy.recorders.record_keeper import RecordKeeper +from gitgalaxy.standards.analysis_lens import ENGINE_CONSTANTS + +FLOOR = int(ENGINE_CONSTANTS["FUNC_EVIDENCE_MASS_FLOOR"]) + +# The rosetta shell shape: a fixed branch/args profile per function; only `loc` sweeps. +ROSETTA_BRANCHES = [3, 0, 0, 0] # main.* plants 3 branches in probe_branch, siblings 0 +ROSETTA_ARGS = [1, 1, 1, 1] + + +@pytest.fixture +def keeper(): + schemas = {"RISK_SCHEMA": ["cognitive_load"], "SIGNAL_SCHEMA": ["branch", "io"]} + with patch("gitgalaxy.recorders.record_keeper.RECORDING_SCHEMAS", schemas): + return RecordKeeper() + + +def _file(func_locs, branches=ROSETTA_BRANCHES, args=ROSETTA_ARGS): + functions = [ + { + "name": f"probe_{i}", + "type_id": "function", + "loc": loc, + "branch": b, + "args": a, + "impact": 1.0, + "hit_vector": {}, + } + for i, (loc, b, a) in enumerate(zip(func_locs, branches, args)) + ] + return { + "path": "main.py", + "name": "main.py", + "lang_id": "python", + "directory_group": ".", + "lock_tier": 0, + "total_loc": sum(func_locs) + 5, + "coding_loc": sum(func_locs) + 2, + "doc_loc": 1, + "file_impact": 1.0, + "raw_imports": [], + "hit_vector": [3, 1], + "telemetry": {"control_flow_ratio": 0.1, "network_metrics": {}, "domain_context": {}}, + "classes": [], + "functions": functions, + } + + +def _density(keeper, tmp_path, parsed): + db = tmp_path / "out.db" + if db.exists(): + db.unlink() + keeper.record_mission(parsed, [], {}, {"target": "t", "git_audit": {}}, str(db)) + conn = sqlite3.connect(db) + try: + return conn.execute("SELECT func_internal_density, avg_func_complexity, avg_func_loc FROM file_data").fetchone() + finally: + conn.close() + + +@pytest.mark.parametrize("avg_loc", [1, 2, 3, 5, FLOOR - 1]) +def test_identical_structure_scores_identically_below_the_floor(keeper, tmp_path, avg_loc): + """Property 1: the rosetta shells average 1-6 lines per function; every one of them + must read the same density as the same structure at the floor.""" + at_floor = _density(keeper, tmp_path, [_file([FLOOR] * 4)]) + shorter = _density(keeper, tmp_path, [_file([avg_loc] * 4)]) + assert shorter[0] == pytest.approx(at_floor[0]) + # and that shared value is the rescaled branch structure, nothing else + assert shorter[0] == pytest.approx(shorter[1] / FLOOR) + + +def test_no_cliff_at_the_floor(keeper, tmp_path): + """Property 2: floor and floor-1 agree; floor+1 is the first point on the old curve.""" + below = _density(keeper, tmp_path, [_file([FLOOR - 1] * 4)])[0] + at = _density(keeper, tmp_path, [_file([FLOOR] * 4)])[0] + above = _density(keeper, tmp_path, [_file([FLOOR + 1] * 4)])[0] + assert below == pytest.approx(at) + assert above < at # the density regime resumes, monotonically + + +@pytest.mark.parametrize("avg_loc", [FLOOR + 1, 20, 50, 200]) +def test_density_regime_unchanged_above_the_floor(keeper, tmp_path, avg_loc): + """Property 3: at or above the floor the column is the pre-#2705 avg_comp / avg_loc.""" + density, avg_comp, got_avg_loc = _density(keeper, tmp_path, [_file([avg_loc] * 4)]) + assert got_avg_loc == pytest.approx(avg_loc) + assert density == pytest.approx(avg_comp / avg_loc) + + +def test_one_line_one_branch_functions_no_longer_read_as_maximal_density(keeper, tmp_path): + """The golden-master shape that motivated the floor: five find*.sql files whose + functions average one line and one branch each read a flat 1.00 -- the densest + file in the corpus -- for having the shortest functions.""" + density = _density(keeper, tmp_path, [_file([1, 1, 1, 1], branches=[1, 1, 1, 1])])[0] + assert density == pytest.approx(1.0 / FLOOR) + + +def test_no_functions_records_zero(keeper, tmp_path): + """Property 4: the max() absorbed the old `avg_loc > 0` guard.""" + parsed = [_file([])] + parsed[0]["functions"] = [] + assert _density(keeper, tmp_path, parsed)[0] == 0.0 + + +def test_floor_matches_the_file_floor_derivation(): + """Both floors sit at their population's golden-master median (#2655: coding_loc 51 + -> 50; #2705: avg_func_loc 12.0 -> 12). Pin the ratio so a future retune of one + without the other is a deliberate act, not drift.""" + assert ENGINE_CONSTANTS["FUNC_EVIDENCE_MASS_FLOOR"] == 12 + assert ENGINE_CONSTANTS["EVIDENCE_MASS_FLOOR"] == 50