Skip to content

Design (D.1): shape-descriptor comparability — the 160 cells decompose into five mechanisms, only 15 of which are a real design question #2689

Description

@squid-protocol

Batch D.1 of #2669. Design item — no code changed, and none should be until this is settled.

Measured against the post-Batch-C corpus (keyword-rosetta 3d94ca17, engine aacfe5da), not the
pre-A figures #2669's item text was written from.

The premise was wrong in a useful way

#2669 D.1 frames this as one family — "shape-descriptor invariance (avg_func_complexity,
control_flow_ratio, cog_raw, gini, internal density, structural_mass — 166 cells)" — with three
options that would apply to all of it: normalise against planted branch count, exclude as documented
design, or ledger as morphology.

The 160 out-of-band cells in that family today are five different mechanisms, and they want
four different answers. Only the last bucket is the design question the item was asking about.

cells mechanism
18 A — the language has zero functions, so every per-function descriptor is undefined
30 B — control_flow_ratio, a ratio against a denominator the corpus never controls
92 C — derived from an input metric that is itself out of band
5 D — the slicer segmented a different number of functions than were planted
15 E — residual: the actual open question

A — 18 cells: markdown and html have no functions

Both report functions_found = 0, so all nine per-function descriptors are not "deviant", they are
undefined. docs/GATING.md already has the right precedent one layer over: a cell is n/a
because the rule is None. The same reasoning applies to a per-function average when there are no
functions.

Recommendation: report as n/a, not as a red cell. Corpus tooling only — no engine change, no
rebless. This is the cheapest 18 cells on the board.

B — 30 cells: control_flow_ratio divides by something the corpus never plants

total_control_flow_ratio = branch_hits / max(branch_hits + linear_hits, 1)   # detector.py:1288

linear_hits is structural_boundaries. No manifest in the corpus asserts structural_boundaries
anywhere, and the bias report does not include it among its 57 metrics
— so the denominator of the
single largest out-of-band metric is neither planted, gated, nor displayed.

Measured per language (struct_linear, whole-corpus scan):

language branch structural_boundaries control_flow_ratio
solidity 3 45 0.063
ada 4 40 0.091
python 3 31 0.088
javascript 3 19 0.136
makefile 4 4 0.500
assembly 24 17 0.585

The numerator is pinned at 3–4 by design across all 46 languages. The entire spread is the
denominator
, which ranges 0–45.

This kills option 1 for this metric: "normalise against planted branch count" cannot help, because
branch is already controlled. The corpus simply cannot validate control_flow_ratio today.

Recommendation: plant and gate structural_boundaries in the corpus, or mark
control_flow_ratio incomparable until that happens. Prefer the former — the signal is real and
every language has the rule; it is the corpus that is silent, not the engine. Either way the bias
report should display the denominator, since a ratio cell is unexplainable without it.

C — 92 cells: composites of already-deviating inputs

cog_raw's formula (signal_processor.py ~L800):

effective_loc = min(loc, (branches + args + 1) * 10)
sum_function_impacts = ((branches + 1) * sqrt(args + 1) + 0.05 * effective_loc) * 10

Its inputs are branch, args and LOC — and args morphology and LOC normalisation are already
validated ledger entries
(args-no-parameter-surface-morphology from Batch A.4, and the tech-debt
LOC artifact noted in #2653). 15 of cog_raw's 26 out-of-band languages are also out of band in one
of its own inputs; across the whole family, 92 cells are this shape.

Recommendation: not a new deviation — mark derived. This is what E.1 was specified to do (a cell
counts as "explained" when a validated ledger entry names that metric + language); E.1 just needs to
follow the derivation edge from a composite to its inputs. No engine change and no new ledger
entries
— the entries already exist, the tooling does not yet connect them.

D — 5 cells: the slicer found a different number of functions

sqlite 30, dockerfile 17, livecode/lua/matlab/ruby/shell 16, makefile 14, against 13
planted everywhere else. Per-function averages move because the divisor moved.

Recommendation: ledger as morphology, per-language, naming the segmentation cause.

E — 15 cells: the real design question

Everything above is bookkeeping. This is not:

cog_raw          c, dart, go, java, javascript, kotlin, php, python, rust, typescript
structural_mass  c, embedded_python, python, scala, typescript

These are mainstream procedural languages whose every input is in band — branch, args and LOC
all green — yet the composite lands out of band. That points at non-linearity in the formula itself:
the (branches + 1) * sqrt(args + 1) product and the min(loc, (signals + 1) * 10) clamp amplify
differences too small to flag on their own.

Options:

  1. Accept and document — the composite is supposed to be non-linear (that is what makes it a
    cognitive-load proxy rather than a count), so a spread on identical intent may be correct
    behaviour to ledger rather than flatten.
  2. Re-band composites on a wider tolerance — derived metrics get a band accounting for the
    amplification of their inputs, instead of the flat 25%/50% used for raw counts.
  3. Decompose in the report — show cog_raw beside its three inputs, so a reader can see whether
    a deviation entered through an input or through the formula.

Recommendation: 1 + 3. Document the non-linearity as intended and make the inputs visible.
Option 2 is a threshold change that would mask real regressions later.

One hypothesis I tested and rejected

It is natural to suspect the median mixes incomparable populations — 46 languages, ~19 of them
config/DSL shapes — so that "deviation from median" flags the healthy procedural languages. This is
false.
Recomputing each metric's median over procedural languages only:

metric median (all 46) median (procedural only) procedural cells out of band: all-median → procedural-median
cog_raw 0.055 0.050 14 → 14
avg_func_complexity 0.188 0.188 5 → 5
func_internal_density 0.034 0.030 5 → 6

The two populations agree on the centre almost exactly. Recorded so nobody re-proposes
population-splitting as a fix.

Adjacent decisions this pass folds in

What I am asking for

Approval per bucket, before any edit — D.2 does not start until this is settled:

  • A → report n/a for per-function descriptors when functions_found == 0 (corpus tooling)
  • B → plant + gate structural_boundaries in the corpus, and show it in the report
  • C → E.1 follows derivation edges; no new entries, no engine change
  • D → per-language morphology ledger entries
  • E → document the non-linearity as intended + decompose composites in the report

A, C and D are corpus/tooling only. B is corpus work. E is the only one that touches
interpretation of engine behaviour, and none of the five requires an engine code change
— which is
itself the main conclusion: the shape-descriptor family is largely a reporting problem, not an
engine bias.

Child of #2669 (Batch D.1), epic #2560.

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 updatesquestionFurther information or community discussion requested

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions