Skip to content
← Documentation

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 = fail on a building — overrides identity with the alert colour
  • blocked > 0 on 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 6Mesh.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 to ok
  • 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. emissive was cleaned; the identity/alert overload survives in the agent's Html label colour (WorldScene.tsx:439). The instrument captures no Html nodes, 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

  1. Fewer collisions — 16 → 10. Met.
  2. No channel gains hidden state-dependent meaningmet with a qualification. opacity is 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.
  3. A channel has a stable interpretationmet. emissive means identity, always, with no exceptions. It previously meant three things.
  4. Traceable to observed factsmet. Every carrier reads a World field; no invented values.

Remaining, measured

  • warn has no carrier. Still byte-identical to ok. The last health state with no encoding.
  • Building blocked has no carrier. Agent blocked now has a beacon; building blocked does not.
  • a.activea.steady; a.load invisible. 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.