Skip to content

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

Closed

Description

@squid-protocol

Design issue, earmarked for a high-reasoning (Fable-class) session per the repo's model-tiering convention — this is an architecture-level scoring question, not a rule tweak. Context: docs/wiki/08-01..08-03 (the Universal Exposure Framework) and the #2560 rosetta classification sweep, whose control corpus (identical planted intent, tiny files) turned out to be exactly the adversarial case for the current small-file handling.

The original intent

Several UEF mechanisms exist to stop tiny files from artificially inflating risk (a 2-coding-LOC file with one hit is 50% "density"): the /(loc + 20.0) padding in _calc_documentation, the loc < 15 special path in _calc_cog_load, sigmoid gating to suppress low-density noise, and per-tier Irc/Fc constants.

Evidence it isn't working (rosetta corpus, 2026-09-02 sweep)

  • risk_tech_debt swings −50%..+100% across six config/DSL languages whose raw debt plants are byte-for-byte identical (1 planned + 1 fragile) — the swing is purely LOC-normalization shape.
  • fortran risk_state_flux reads +85% while its raw state_mutation is honest/below-median (−50%) — a direction inversion through the formula (the ledger's amplified_cascading_flux path).
  • The flat irc term is divided by safe_loc, so the smaller the file, the LARGER the implicit-risk floor — one small-file guard is counteracted by another mechanism in the same sum.
  • _calc_api_exposure hard-returns 0 when a file has zero api hits, discarding its encapsulation signal entirely (zig: main file's 4 encapsulation hits contribute nothing).
  • risk_documentation/risk_verification ambers on html/css trace to per-file denominator artifacts (func_start=0 or one-concept-per-file corpus layout), not their own signals.

Option space (deliberately not choosing here)

  1. Evidence-mass gating: below a minimum coding_loc / function count, emit a neutral tier + explicit "insufficient mass" flag instead of extrapolating density (per-file analog of the mass-weighted averaging 08-01 already uses at directory scope).
  2. Shrinkage / empirical-Bayes smoothing: score = (hits + k·prior_density·L̄) / (loc + k·L̄) — replaces the ad hoc +20 with a principled estimator; small files converge to the corpus prior, large files to their own density.
  3. Uncertainty-carrying scores: compute a Wilson-style lower bound on hit density and tier on that — a 2-LOC file mathematically cannot reach Red on one hit.
  4. Density-scale Irc rather than adding it flat, so the tier correction stops blowing up precisely on the small files the other guards try to protect.
  5. Whatever is chosen: adopt the rosetta corpus as the regression fixture — identical plants should yield near-identical §3 scores across languages and file lengths; today's deviation table becomes the acceptance test.

Related: #2653 (tier membership audit / bias-report treatment of Fc-Irc), keyword-rosetta#25 (census noise), and the sweep's cause-keys tech-debt-score-loc-normalization-artifact, api-exposure-zero-if-no-pub-hits, per-file-doc-api-imbalance.

Activity

  1. added
    enhancementNew feature, sensor, or structural signature
    metricsHeuristics, risk exposures, and topological math updates
    core-engineModifications to the central physics and parsing engine
    on Sep 2, 2026
  2. squid-protocol commented on Sep 2, 2026

    @squid-protocol
    OwnerAuthor

    Additional corroboration from the scripting-family lane: risk_cognitive_load swings from python −76% to powershell +91% across the family on structurally similar in-band raw signals — traced to _calc_cog_load's per-file LOC-density division with the hard <15 LOC special-path cliff. Shell's terse corpus idiom (b.sh packs an in-band state_mutation=6 into ~17-20 LOC) spikes density near the clamp ceiling with nothing raw-signal-anomalous. Same shape as the tech-debt swings in the issue body: identical/similar intent, wildly different §3 scores, purely from file-length shape. Also from the same lane: _calc_documentation scores a/b/c files 'undocumented' because the corpus documents itself once at its entry point while api mass lives in the imported files — the per-file formula has no notion of documentation living at a module boundary.

  3. squid-protocol commented on Sep 2, 2026

    @squid-protocol
    OwnerAuthor

    Design pass done (Fable session, 2026-09-02); implementation in #2667.

    Decision: an evidence-mass floor. Every per-file density equation now divides by max(coding_loc, 50). Below the floor a file is scored on its counts, so identical intent scores identically at any length; from 51 LOC up the density regime is byte-identical to before. This one mechanism replaces the six guards that were fighting each other (the <15 → 5.0 cog cliff, the two +20 pads as small-file guards, the loc/15 bureaucracy dampener, unbounded irc/loc, api's max(total_loc, 10)). Two consistency rules ride along: zero measured evidence scores zero in every tier (Irc corrects, never creates — documentation and cog load now follow the convention the other four Irc-bearing equations already had), and a branchless file carries no cognitive load at any length.

    Option space, resolved:

    1. Evidence-mass gating → the flag is adopted (mass_floored / evidence_mass in telemetry); the neutral tier is not (it would make every corpus file "insufficient" and neutralise half of real files — golden-master median is 51 LOC).
    2. Shrinkage → this is what safety/doc already did with the +20 pad; measured 2.4× residual swing; a pad large enough to matter re-tunes every large-file threshold.
    3. Wilson lower bound → does not deliver invariance: for fixed hits the bound still scales with 1/n (2 hits: 0.057 at n=10, 0.014 at n=40 — a wider swing than the raw ratio). Rejected on the math.
    4. Density-scale Irc → subsumed (the floor bounds irc/loc); unifying the convention for ≥50-LOC files is risk formulas: audit language-tier membership (embedded_python lands tier3) + decide how rosetta bias reporting treats documented Fc/Irc tier constants #2653's call.
    5. Corpus as regression fixture → engine-side first: tests/core_engine/test_uef_length_invariance.py feeds the SPEC's planted vectors through the equations (no scan, no cross-repo dependency); the scanned §3 rows become the acceptance test once state-flux ×3 branch-context weighting: undocumented, and phantom-triggered by branch keywords inside string literals #2546/orphan census counts non-function shapes on non-call-graph languages, making orphan→api conversion unauditable #2547 land, because saturation was masking those §2 effects.

    Two of the observations in the body turned out to be saturation on top of §2 effects, not formula inversions (per-file recomputation from the scan DB reproduces every stored score to 0.000): the config/DSL tech-debt swing is orphan/duplicate synthesis in main/b files (every c file scored exactly 100 in all 45 languages), and fortran's state-flux "inversion" is two saturated files averaged. Full attribution table, corpus evidence (identical-input groups: 0.00 output range on all seven metrics after the change) and the honest bias-report caveat are in the PR body.

    New finding for keyword-rosetta: the PATH_MODIFIERS "config" regex matches data/jcl/ and data/makefile/, applying cog ×0.70 to those two corpus folders — a bias source outside both the registry and the formulas; needs a ledger entry or a path rename.

  4. squid-protocol commented on Sep 6, 2026

    @squid-protocol
    OwnerAuthor

    The corpus's own directory name is still moving two languages' scores

    Re-measured 2026-09-06 on engine main a59ecf6b / corpus main 2038a86, during a cobol/jcl pass over the bias report. This issue is closed; the ledger entry it owns, path-and-extension-modifiers, is disposition: engine-semantic, still_reproduces: true, and it is the sole verdict on four out-of-band cells that nobody currently owns. Recording the residual here rather than reopening, since the behaviour is deliberate for real repositories — the question is what it does to the corpus that measures it.

    The entry's own survey names the actionable case:

    (2) the config rule (?:^|/)(?:configs?|settings|environments?|setup|makefile|\.env|cntl|jcl)\b matches the folder names data/jcl/ and data/makefile/ → cognitive_load × 0.70 on all 8 files of those two languages — the only case where the corpus's folder NAME, not its content, moves a score. … (2) is the actionable one for cross-language reading: jcl and makefile cognitive_load sits 30% low by construction.

    Two live consequences on the current cache:

    • jcl carries cognitive_load × 0.70 on all 8 files because the language's directory is called jcl, which the rule reads as a config directory. risk_cognitive_load still reads 1.62 against a 1.30 median — i.e. +25% after a 30% discount; without the modifier the cell would be roughly +79% and clearly red. The corpus is currently reporting a number that is neither the engine's honest reading of the content nor a comparable one.
    • cobol gets cog × 0.85 on a/b/c.cpy from the header/declaration extension rule, and its risk_documentation (+62%, red) and risk_safety_score (+26%, amber) are tagged to this entry with no other explanation on file.

    Both are structurally invisible to any corpus-side fix: the folder must be named after the language, and COBOL copybooks must be .cpy.

    What I think is worth deciding, in rough order of cost:

    1. Nothing — the modifiers are correct for real repositories, and the corpus simply cannot be banded on the affected risk columns. That is defensible but should be written down as a scope statement, because right now four cells read as unexplained-looking deviations with a ledger tag and no owner.
    2. Band the modifier-affected risk columns within a path-modifier stratum, the way #2669 F.3 bands the constant-reading risk metrics against their own strictness stratum. The multipliers are known per file and computable, so the stratum is mechanical.
    3. Make the modifier set inspectable in the scan output, so a reader can see that a jcl score carries ×0.70 rather than having to run _get_locational_multipliers by hand. This is the smallest change and probably the most useful one for anyone reading a real scan, not just the corpus.

    No action requested on this issue itself — flagging that its residual is load-bearing for cobol and jcl in #2669's sweep, and that nobody owns it since this closed.

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 engineenhancementNew feature, sensor, or structural signaturemetricsHeuristics, risk exposures, and topological math updates

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions