docs/worldscene-audit.md
WorldScene against the grammar
The grammar run against the actual renderer. Executable:
tests/worldscene-audit.test.ts.
Outcome: one illegal visual claim, and it is position. The repair class is
representation — the canonical source already exists and the renderer does
not consume it. Two other candidates were investigated and withdrawn as
unreachable, which is recorded because withdrawing them was the harder call.
The finding
Visual claim. Two buildings drawn near each other are near each other.
Violated rule. R2 proximity-invented — a categorical space placed on a spatial channel with no declared metric.
Missing canonical source. None is missing.
slots.ts:98places every unrecorded subject athash32(id) % capacity. Rendered position is a hash of the subject's name.Repair class: representation gap. Not an observation gap and not a semantic ownership gap.
Why it is a representation gap and not the others
layout.ts:118 exports layoutFrom(exchanges), which derives placement from
couplingMatrix — proximity computed from exchange facts, exactly what R2
requires. It is consumed by CouplingField.tsx and by nothing else.
Two placement systems exist. The metric-derived one renders a different component; the ring-slot one renders the city.
So the facts are observed, the semantics are canonical and singly-owned, and the metric is derived. Only the renderer's input is wrong.
Corroboration
In the scale-10 fixture, over all 4,950 building pairs:
mean rendered distance linked = 500.0 unlinked = 333.3 ratio = 1.500
Buildings that exchange are drawn 1.5× farther apart than buildings that do not. Position is not merely uninformative about coupling — in this fixture it is anti-correlated with it.
The mechanism is topological, and I should not overstate it: every fixture
link is hub-to-spoke, and a radius is longer than an average chord. So the 1.5×
is a property of this fixture, not a law. The finding does not rest on it. The
finding rests on hash32(id), which is in the source and needs no fixture.
What was already declared, and what was not
projection.ts:131 already carries an ids→plots entry declaring that
assignSlots loses which plots came from the register and which were allocated
by hash.
That is an honest declaration about assignSlots's own output. It does not
cover this finding. The declared loss is internal to the function; the finding
is that the rendered spatial channel asserts a relation to a viewer that no
fact licenses. The Information Ledger tracks what a function discards. It does
not track what a projection claims.
That is a gap in the ledger's scope, not a failure of its honesty — and it is worth stating plainly, because the ledger's existence made it easy to assume this had been covered.
Channels audited
Six, read from source with line references, all in the test.
| Channel | Licensed by | Canonical? | Meanings | Verdict |
|---|---|---|---|---|
| ring hue (171) | district accent | identity | 1 | ✓ |
| ring opacity (174) | district.activity |
yes | 1 | ✓ |
| emissiveIntensity (292) | building.activity |
yes | 1 | ✓ |
| box height (336) | a declared measure | yes | 1 | ✓ |
| link dash (240) | link.kind |
yes | 1 | ✓ |
| position (275) | hash32(id) |
no | — | R2 |
Two channels carry activity — ring opacity and emissive intensity. That is
sufficiency, not overload: one channel still carries one meaning, and the
closure/sufficiency distinction says a fact with two carriers survives losing
one. R6 is not triggered and should not be.
The link animation is legitimate
period = 9 - min(1, flow) * 6.5 (line 217). Travel time is derived from flow,
so the motion channel is a real readout, not decoration. This is the one place a
temporal channel carries an ordered state honestly, and it passes.
Two candidates investigated and withdrawn
Both looked like findings, and reporting them would have been wrong. Recorded because the withdrawal is the substantive part.
1. Building opacity carries isLot and unobserved (line 299).
opacity={isLot ? 0.35 : unobserved ? 0.5 : 1} — the lot branch wins, so a lot
that is also unobserved renders identically to an observed lot.
Measured: atScale(1) produces 0 lots. The state is not reachable in any
fixture available to me, and this project's own rule — never vary fields
independently unless the generator can emit that state — applies to findings as
much as to fixtures. Withdrawn as unreachable, not as false. It may be
reachable against the live substrate, which I cannot exercise here.
2. Link opacity carries handoff and flow (line 239).
opacity={handoff ? 0.75 : 0.1 + link.flow * 0.45} — when handoff is true,
flow is unrepresentable, and handoff already has its own channel (colour,
line 236). That would be R3 with no compensating gain.
Measured at scale 10: only road links exist; no handoff link is produced.
Withdrawn as unreachable.
Both would have been plausible findings supported by a source read. Neither survived a reachability check. That is three times in this investigation that a reachability check has overturned a reading of the source, and it is the reason the check is not optional.
Standing
| Question | Answer |
|---|---|
| Are all visual claims licensed? | no — one is not |
| Which? | position, on R2 |
| Missing canonical source? | none — layoutFrom exists and is unconsumed by the scene |
| Repair class | representation gap |
| Does the ontology need extending? | no — nothing was added to make anything pass |
| Purity | unchanged; no canonical logic was duplicated into the renderer |
Not repaired. The directive was the smallest falsifiable finding, not a
refactor. The audit test asserts the exact violation set, so wiring layoutFrom
into placement will fail this test and retire the finding — the same
mechanism the blocker tests use, where a change in reality breaks the
expectation rather than waiting for someone to remember.
What is still not established
- The audit covers six channels.
WorldScene.tsxis 457 lines and carries more — beacons, agent elevation (busy ? 3.2 : 2.2), labels, wireframes. The table is a start, not a census, andbusy-as-elevation is the next candidate. - Reachability was tested against fixtures only. The two withdrawn findings need a live substrate to settle.
- Whether the ring layout is deliberately non-semantic — a stable civic map that never claimed to encode coupling — is a design question the grammar cannot answer. The grammar says the channel makes a claim; whether the claim was intended is not a measurement.
165 tests → 168, tsc clean.