docs/visual-channel-separation.md
Visual Channel Separation — measurement
Measurement only. No implementation, no Legend proposal, no visual changes.
Observable object: scene-graph.
Method: hold every fact fixed, vary one at a time, and record which rendered channels move and to what value. Two matrices — buildings and agents — plus a byte-level distinguishability check on the states that moved the same channels.
1. What each informative channel represents
| Channel | Fact(s) it responds to |
|---|---|
Mesh.position.y (building) |
height; also agent status |
Mesh.emissiveIntensity |
activity; also building kind (lot); also agent status |
Mesh.emissive |
identity (accent); also building health=fail; also agent blocked |
Mesh.color |
building kind; agent identity |
| Marker mesh (appears) | building health=fail |
No channel maps to exactly one fact. Every informative channel is shared.
2. Does each channel have a single stable meaning?
No. Two channels are overloaded, and the overloading is state-dependent.
Mesh.emissive carries three different facts:
- identity, via
accent, when nothing is wrong health = failon a building — overrides identity with the alert colourblocked > 0on an agent — overrides identity with the alert colour
Measured: an agent at blocked=0 renders emi=7bd57b (its identity hue); the
same agent at blocked=5 renders emi=ff8a6b. The channel's meaning changes
according to a fact the channel does not itself carry — so a viewer seeing a
colour cannot know which question it is answering.
Mesh.emissiveIntensity carries three facts: activity (0.12 + activity × 0.85), building kind (lot → 0.05), and agent status (idle/unstaffed → 0.35, otherwise 1.5).
3. Can independent facts produce the same visual state?
Yes — measured, byte-identical, four collisions.
Buildings
| Change from baseline | Channels moved |
|---|---|
blocked 0 → 5 |
0 |
health ok → warn |
0 |
health ok → unknown |
0 |
Three distinct organizational facts are indistinguishable from "nothing happened." Building-level blocked work, a warning, and an unobserved building all render exactly as a healthy idle building.
Agents
Byte-identical signatures:
status=steady y=3.20 col=7bd57b emi=7bd57b int=1.5
status=active y=3.20 col=7bd57b emi=7bd57b int=1.5 ← identical
status=idle y=2.20 col=7bd57b emi=7bd57b int=0.35
status=unstaffed y=2.20 col=7bd57b emi=7bd57b int=0.35 ← identical
load=0.3 y=3.20 col=7bd57b emi=7bd57b int=1.5
load=0.9 y=3.20 col=7bd57b emi=7bd57b int=1.5 ← identical
unstaffed is visually identical to idle. These are unrelated
organizational facts: one is no one holds this role, the other is someone
holds it and has nothing to do. A viewer cannot tell a staffing gap from spare
capacity.
active is visually identical to steady, so the status vocabulary's four
members render as two states.
load and queueDepth are entirely invisible. Varying either changes
nothing at all.
4. Can one fact change multiple channels unintentionally?
Yes — one fact moves six channels.
| Fact varied | Channels moved |
|---|---|
health ok → fail |
6 — Mesh.emi, and a marker's posY, scale, col, emi, int |
height 6 → 20 |
2 |
kind dept → lot |
2 |
| identity (accent) | 2 (agent col and emi) |
| activity | 1 |
health=fail is the most coupled fact in the system: it changes the building's
emissive and summons an entire marker object. Whether that redundancy aids
perception is unmeasured — but it means health failure is the only fact with
two independent carriers, while load has none.
5. What is lost between scene graph and interpretation
Partly unmeasurable — interpretation requires observers. What is measurable is what never reaches the scene graph to be interpreted:
| Fact | Reaches the scene? |
|---|---|
building health = warn |
no |
building health = unknown |
no |
building blocked |
no |
agent load |
no |
agent queueDepth |
no |
agent status active vs steady |
no (collide) |
agent status unstaffed vs idle |
no (collide) |
source provenance (live/unreachable/not-wired) |
not in the 3D world — text panel only |
The four specific investigations
Identity — does colour represent identity alone?
No. Identity occupies two channels (col and emi on agents), and emi is
seized by alerts: health=fail on buildings and blocked>0 on agents both
replace the identity hue. Identity is neither exclusive to a channel nor
reliably present on one.
Health — can a viewer distinguish healthy / unhealthy / unknown / unobserved?
No — only fail versus everything else. ok, warn and unknown are
byte-identical. Four states render as two, and the two that vanish are precisely
the ones carrying uncertainty.
Alerts — separate signal, or overloaded onto identity?
Overloaded. Alerts have no channel of their own; they take identity's. The
marker mesh is the only alert-specific carrier, and it appears for building
health=fail only — not for agent blocked, which relies solely on the
seized emissive.
Uncertainty — can a viewer distinguish the three uncertainty states?
No, and the three fail in different ways:
- no issue observed (
health=ok) — renders as baseline - observation unavailable (
health=unknown) — identical took - source not connected (
not-wired) — never enters the 3D world; it exists only as text in a side panel
The distinction the provenance work restored at the World boundary is
destroyed again at the renderer.
Summary against the four channel criteria
| Criterion | Status |
|---|---|
| Observed — represents a measured fact | Holds. No channel was found rendering an invented value. |
| Traceable — source reconstructible | Holds mechanically; the scene-graph witness reconstructs each value. |
| Stable — meaning does not change with hidden state | Fails. emissive and emissiveIntensity each carry three facts, switched by state they do not expose. |
| Interpretable — observers apply the meaning consistently | Cannot be assessed, and would fail on stability alone: a channel with three state-dependent meanings has no consistent rule to learn. |
The current visual language is not yet truthful enough to document. Two of
four criteria fail on measurement, and the failures are not gaps in coverage —
they are collisions, where distinct organizational realities produce identical
renderings. Declaring a Legend row for emissive today would ratify a channel
that means three things.
Six facts reach no channel at all; four pairs of distinct facts are byte-identical. Those are the measurements a separation experiment must reduce before any Legend amendment is proposed.
Separation experiment — result
INCOMPLETE — see
carrier-purpose.md.emissivewas cleaned; the identity/alert overload survives in the agent'sHtmllabel colour (WorldScene.tsx:439). The instrument captures noHtmlnodes, so the experiment measured the channel it fixed, could not measure the channel the defect moved to, and reported success.
Implemented three minimal separations, each reusing an existing convention rather than inventing a carrier, and measured the same 11-state matrix before and after.
| Before | After | |
|---|---|---|
| Distinguishable states (of 11) | 5 | 7 |
| Collision pairs | 16 | 10 |
| Indistinguishable groups | Before | After |
|---|---|---|
{baseline, warn, unknown, b.blocked, a.active, a.load} |
6 states | — |
{a.idle, a.unstaffed} |
2 states | — |
{baseline, warn, b.blocked, a.active, a.load} |
— | 5 states |
What was changed, and why each carrier
Identity keeps emissive unconditionally. Measured after: emi=7bd57b
under ok, fail, unknown, lot and lot+unknown — five states, one value.
The channel now answers exactly one question. Failure kept its beacon, which
was already a dedicated alert carrier; agent blocked gained the same beacon
rather than seizing identity.
Uncertainty reuses the lot convention. Lots already render translucent +
wireframe, meaning allocated, nothing there. An unobserved building is the
same family — we cannot see it — so it takes translucency without wireframe.
Measured: (op=1, wire=false) observed · (op=0.5, wire=false) unobserved ·
(op=0.35, wire=true) lot. Three distinct pairs.
Unstaffed reuses it too. A role with nobody in it renders as outline without
substance, the same reading as unbuilt ground. unstaffed and idle are no
longer byte-identical.
Against the success criteria
- Fewer collisions — 16 → 10. Met.
- No channel gains hidden state-dependent meaning — met with a
qualification.
opacityis now shared between lot-ness and observation status. It is not ambiguous, because the(opacity, wireframe)pair is injective across all three cases — but opacity alone is not a stable channel, and that should be stated plainly rather than claimed as clean. - A channel has a stable interpretation — met.
emissivemeans identity, always, with no exceptions. It previously meant three things. - Traceable to observed facts — met. Every carrier reads a
Worldfield; no invented values.
Remaining, measured
warnhas no carrier. Still byte-identical took. The last health state with no encoding.- Building
blockedhas no carrier. Agent blocked now has a beacon; building blocked does not. a.active≡a.steady;a.loadinvisible. Workload remains unencodable without a new dimension, so "recognizing overload without reading metrics" is still impossible.- An unobserved lot is identical to an observed lot. The absence convention is fully consumed there and cannot express two absences at once. A real boundary of the chosen encoding, not a defect introduced by it.
Instrument corrections forced by this experiment
The fidelity metric was wrong. It counted channels that vary, and
de-overloading emissive dropped that count 6 → 5 while distinguishable states
rose 5 → 7. An overloaded channel varies for many facts because it is
ambiguous, so channel-variation rewards the defect it should punish. The ratchet
now counts distinguishable states.
The instrument was blind to the channels used. Its capture sampled position, scale, colour, emissive and intensity — not opacity or wireframe, which are exactly the carriers the separation used. It reported the uncertainty separation as no change. An instrument blind to a channel reports its use as absence.
Both were caught by the tests failing, not by review.