docs/position-pipeline.md
Position as a transformation graph
It was one sentence — "shortest-path metric, symmetrised, solved by MDS" — hiding six operations, each with its own losses. That is exactly where prose drift returns.
src/lib/projection/pipeline.ts, tests/pipeline.test.ts.
The criterion needed a qualifier — and I had it wrong
First pass: a step contributes convention when it collapses, creates, or selects
distinctions that were not present in its input. Applied literally, that made
co-change a stipulation for reducing commits to a count.
Wrong, and wrong in a way that empties the category. Almost every projection loses something; if loss alone is conventional then everything is, and the distinction stops distinguishing. The qualifier:
A stage contributes convention when it removes or invents distinctions relevant to the claim being made.
A thousand commits reduced to a count loses the commits, and the claim — "this
changed frequently" — survives intact. So the field is
preservesClaimDistinctions, not injective, and every stage now states the
claim its effect is assessed against.
The fold, corrected
co-change evidence —
weight evidence —
distance evidence —
shortest-path convention selects a reference its input does not contain
symmetrise convention removes distinctions relevant to its own claim
embed convention removes distinctions relevant to its own claim
→ 3/6 stages are semantic interventions
Position still appears the most evidential channel — every input an observed commit, no ceiling anywhere — and is the least. But the shape is sharper than the first count: the three interventions are the last three, the stages closest to the pixel. A chain is no more authoritative than its weakest stage.
shortest-path also moved from removes to selects: the minimum over routes
is a defensible metric and so are mean path length and resistance distance, and
nothing in the data chooses between them.
The losses the sentence was hiding
| Stage | Discarded |
|---|---|
| co-change | which commits coupled a pair, their order, their authors — two files coupled in 2020 and 2026 are indistinguishable from two coupled twice last week |
| weight | absolute counts |
| distance | nothing |
| shortest-path | which path realised each distance, and every longer route between the same pair |
| symmetrise | direction — a→b at 0.9 and b→a at 0.1 become one number, and the unreachable-pair factor is a chosen constant |
| embed | every dimension beyond the second — measured at 54.5% of proximity comparisons reversed |
Every one was invisible inside the sentence. distance is the only lossless
stage, and it says so — null is a claim, not an omission.
Why this is not a ProjectionRule
ProjectionRule folds a scalar through steps. Position consumes a graph,
changes its shape repeatedly, and emits coordinates. Forcing it into the scalar
vocabulary would have produced one giant op — a sentence with extra syntax.
Same discipline, different shape: each stage declares preserves, discards,
scope and the two checkable facts, and nothing declares a warrant.
The criterion, sharpened and now derived
Previously: a step contributes convention when it introduces a chosen reference that changes what the value means.
Now:
A step contributes convention when it collapses, creates, or selects distinctions that were not present in its input.
And it is no longer a judgement written per operation — which would have been the same defect one level up. Each operation states two checkable facts:
StepEffect = { injective: boolean; parametersFromInput: boolean }
warrant = injective && parametersFromInput ? "evidence" : "convention"
- collapses — not injective. A clamp, a bucket, a
minover paths. - selects — a parameter the input does not determine. Yesterday's peak, a declared ceiling, a top-N cutoff.
sqrt, scale and offset are injective and parameterless-from-choice, so they
cannot introduce a claim. This ties back to the distinction work: a
transformation is evidential exactly when it neither destroys nor invents a
distinction.
The two failure modes are reported separately, not merged:
floor → collapses distinctions its input had
stale → selects a reference its input does not contain
Standing
| Measurable defect exposed? | yes — position is 4/6 intervention, and looked evidential |
| Measurable improvement? | yes — the sentence is generated from six stages |
| Prose remaining in the manifest? | none for position, height, activity, hue or footprint |
Executable end to end
co-change 8 co-changed pairs over 8 subjects ran here
weight weights in (0, 1], peak-normalised ran here
distance 8 edge distances ran here
shortest-path a complete metric over 8 subjects ran here
symmetrise a symmetric metric; N pairs lost direction ran here
embed 8 coordinates in 2 dimensions ran here
→ 6/6 stages execute here
12 coordinates, identical to layoutFrom
layout.ts now exports symmetrise and embed, and layoutFrom composes the
same functions — one implementation, no copy. The pipeline is the mechanism
rather than a theory of it.
The load-bearing test is coordinate identity: running the stages yields exactly
what layoutFrom yields, asserted per coordinate. A divergence would mean the
explanation describes a different computation from the one that drew the scene.
stillDeclared is asserted empty.
The three-way loss taxonomy
A framework for trustworthy visualization cannot demand losslessness — visualization is compression, and demanding it makes the theory self-defeating.
lossless distance
compression co-change, weight removed distinctions irrelevant to the claim
convention shortest-path, symmetrise, embed chose among equally valid meanings, and said so
deception — removed claim-relevant distinctions, silently
Deception is the only class the engine must prevent, and what separates it from convention is declaration. Symmetrising away direction is a choice; symmetrising it away silently is a false claim of mutuality. The test proves the guard bites by hiding one declaration and watching the class flip.
A correction to the null rule
The previous round asserted "a stage with discards: null must fold to
evidence". Wrong — a stage can be perfectly lossless and still select a
reference its input does not contain. The invariant is about declaration, not
about warrant:
Discarded information must be declared whenever it could alter the viewer's interpretation of the claim.
What is still not established
- The stage metadata is still authored.
claim,preservesanddiscardsare written by hand; only the warrant and scope are derived, and only from two hand-set booleans. Executing the stages closed the gap between the pipeline and the computation, not between the metadata and either. preservesClaimDistinctionsis asserted per stage. It is checkable in principle — feed two inputs differing in the claim, see if the outputs differ — and nothing checks it.injectiveandparametersFromInputare asserted per operation. They are checkable in principle and nothing checks them; a wrong pair would silently change a warrant.- The 54.5% figure is carried from an earlier measurement, not recomputed here.
- Position remains the channel most likely to mislead, and this round did not change a pixel of it.
282 → 290 tests, 26 DOM, 40 mutations, tsc clean.