Skip to content
← Documentation

docs/visualization-invariant.md


Design Invariant — Truth-Preserving Visualization

Standing constraint on all future architecture. Not a task; a test to apply to every architectural decision from here.

The visualization layer is not an illustration of the system. It is a deterministic projection of the system's semantics.

Facts → Properties → Metrics → Geometry → Rendering

Each layer derives only from the layer above it.

  • The renderer has no semantic authority. It must never invent geometry, infer meaning, cluster entities, or choose relationships. It consumes geometry already determined by the metric layer.

  • Metrics are renderer-independent. They must remain meaningful if no visualization exists. Visualization is one consumer among reasoning, simulation, search, optimization, and analysis.

  • Every visual primitive has a mathematically defined inverse. For any position, distance, size, colour, opacity, clustering, orientation, or motion, the system must answer "why does this appear exactly this way?" with a complete derivation:

    Visual Primitive ← Geometry ← Metric(s) ← Property(ies) ← Recorded Fact(s)
    
  • No visual element may exist without an evidence chain, and every metric must exist because it expresses a property of the system — not because it produces an attractive visualization.

The test for every future decision: will this still permit geometry to be derived entirely from metrics, with complete traceability back to recorded facts? If no, the architecture is wrong regardless of whether it functions.


The layer contract

The five layers are distinct and must not collapse into one another.

  • Property — an intrinsic fact about the system: ownership, dependency, entropy, trust, causal influence, load, volatility. It is true whether or not anyone measures or draws it.
  • Metric — a mathematical function over one or more properties.
  • Geometry — derived only from metrics.
  • Rendering — projection only. The renderer never computes meaning. Its contract is intentionally dumb.

Every metric declares four things

A metric definition must answer all four, and the renderer may answer none of them:

  1. What facts produce it?
  2. What mathematical function computes it?
  3. What units does it inhabit?
  4. Which geometric degrees of freedom may it control?

Question 4 is directional and the direction matters: the metric declares which geometry it may drive. Geometry claiming a metric is the same edge pointing the wrong way, and it lets any geometric property help itself to any measurement.


Milestone — semantic closure precedes metric design

Metric definitions may not begin until the Legend completely and correctly describes the World contract. Until then the metric layer would be built on shifting semantics, and metrics would encode a moving target rather than an enduring property.

Semantic closure means, at minimum: every visual property the World produces is declared in the Legend; every property the Legend declares is produced; and each declares its source. Gaps 3 and 6 below are the measured distance from it.


Baseline — measured, not assumed

The invariant is stated above as given. What follows is where the current system stands against it, measured before any design work, so the metric layer is designed against reality rather than against an assumption.

Gap 1 — the renderer invents geometry

src/components/hq/CityMap.tsx:40

const SPIRE_H = 252;

City Hall's spire height is a renderer constant. It has no metric, no property, and no fact behind it. Asked "why is the spire that tall?", the system's only answer is "a renderer chose 252".

The boundary worth keeping precise: computing the vertices of a box whose height was given is projection and is permitted. Choosing the height is invention and is not. SPIRE_H is the second kind.

Gap 2 — visual properties with no inverse

accent appears on District, Building, and WorldAgent in the World contract. It originates as a hardcoded hex in src/lib/hq/roster.ts (accent: "#8ea2ff"), reaching products via accentForOwner. health is the same shape.

Neither appears anywhere in the fact vocabulary (src/lib/world/facts.ts). So colour — which the eye reads as meaning — has no derivation. It cannot be inverted, because there is nothing to invert to.

Gap 3 — the constitutional legend and the World have drifted apart, in both directions

src/lib/world/constitution.ts carries the LEGEND, and Article VIII.1.2 declares it exhaustive: "A visual property absent from it may not be introduced."

Declared in the legend, never produced by the World campusTiers
Produced by the World, absent from the legend accent, health, radius, center, load, progress

Nothing checks either direction. This is the general form of the defect that W5 found in miniature: the legend's quantised: true flag is declared on all nine rows and read by nothing. The legend is a constitutional file describing a system that does not exist — in one direction it promises what is absent, in the other it is silent about what is present.

Gap 4 — there is no properties layer

src/lib/world/facts.ts exports fact types and nothing else. Between recorded facts and geometry the system has an undifferentiated middle: no type, no module, and no vocabulary corresponds to a property.

Gap 5 — property and metric are collapsed inside one function

deriveLoads in src/lib/world/build.ts performs both steps in one pass:

load.weight += priority * (item.state === "blocked" ? 2 : 1);   // PROPERTY
// "Normalise against the busiest DEPARTMENT"                    // METRIC

The first line is an intrinsic claim about the system — an agent's queue weight, true whether or not anything draws it. The second is a function over that property producing a 0–1 value for geometry.

The cost of the collapse is visible in the first line: why is blocked work worth double? That is a semantic judgement about the domain, and it is currently buried inside what reads as a normalisation utility. Under the layer contract it belongs in the property definition, where it can be stated, questioned, and cited.

Gap 6 — the metric ↔ geometry binding points the wrong way

src/lib/world/structure.ts:43

| { kind: "measure"; metric: string; base: number; scale: number }

Geometry names the metric it consumes. The layer contract requires the reverse: the metric declares which geometric degrees of freedom it may control. As built, any geometric property may claim any measurement, and the measurement has no say — which is how code-size came to drive building height (II.2) without anything objecting.

Not a gap — units are already enforced, in core

Question 3 of the contract is already satisfied upstream. core/ingest.ts requires every manifest metric to declare a unit and rejects the manifest outright if it does not:

if (!m.unit || m.unit.trim().length === 0) {
  throw new ManifestRejected(`${manifest.id}: metric '${m.metric}' declares no unit.`);
}

The legacy MeasureFact carries subjectId, metric, value, limit?, enforced?, evidenceno unit. So the unit declaration exists and is mechanized in core, and was lost on the way into the legacy stack. This is a migration gap, not a design gap, and it means one of the four questions needs no new machinery — only the migration already underway.


Design implication

The metric layer's purpose, stated in the invariant's own terms, is to supply the middle of the chain so that every visual primitive becomes invertible. The legend is where that inverse is declared — it is the mapping from visual property to what it represents and which source it derives from.

So the metric layer cannot be designed against the current legend, because the legend does not describe the system. Reconciliation is prerequisite, and it decomposes into three decisions rather than one:

  1. Which visual properties exist at all. accent and health are either promoted into the legend with declared sources, or removed as visual properties. Under VIII.1.2 the legend is exhaustive, so leaving them undeclared is not a third option.
  2. What units geometry is quantised into. Already pending — the floor and tier units blocking II.2 and II.3 (see tripwires.md W5).
  3. Whether identity-only properties are exceptions or metrics. Footprint is already a declared exception to Article I.1 — assigned by kind, never derived. accent is plausibly the same kind of thing, but it has never been declared as such. The invariant permits declared exceptions; it does not permit undeclared ones.

Decision 2 is already with the ratifying authority. Decisions 1 and 3 are new and follow from this invariant.

Status

Recorded, not implemented. Per the instruction accompanying it: the metric layer is to be designed once the ontology, constitutional semantics, and mechanization are stable enough that metrics represent enduring properties rather than moving targets. Gap 3 is direct evidence that they are not yet stable — a legend that disagrees with the system in both directions is the definition of a moving target.

The three gaps above are the measured baseline any future metric design must close, and they are recorded here so that design starts from measurement.


North star

The visualization is not an illustration of the system. It is the system projected into geometry.

Every visible primitive must admit an exact inverse:

pixel ← geometry ← metric ← property ← fact

If that chain cannot be reconstructed precisely, the visual element either lacks sufficient evidence or belongs outside the metric-driven visualization.

That is the standing test for the entire visualization architecture, and it is deliberately stronger than "looks right". A chain that reconstructs is auditable, explainable, and reproducible; one that does not is decoration with better manners.


Elimination attempt — the Properties layer

Applied under foundation.md §2.0, which requires an abstraction to earn its existence. Recorded with the rigour of an admission, because discovering a layer is unnecessary is worth more than successfully adding one.

Method. Take the seven properties named when the layer was proposed — ownership, dependency, entropy, trust, causal influence, load, volatility — and ask of each whether it is a recorded Fact, a function over Facts (a Metric), or genuinely neither.

Candidate Verdict Evidence
load Metric build.ts:181load.weight += priority * (blocked ? 2 : 1), a function over WorkItemFacts
entropy, volatility, causal influence Metric functions over facts by construction
trust absent not present in this system; no evidence either way
ownership neither estate.ts:39owner: AgentId, declared in a connector register
dependency neither estate.ts:48dependsOn?: string[], likewise declared

Five of seven collapse into Metrics outright. The layer holds nothing for them.

The two that do not collapse — and why they still do not justify the layer

ownership and dependency are declared relations between subjects. They are not functions over facts, so they are not Metrics. But they are also not Facts in core's sense: FACT_KINDS is ["condition", "measure", "exchange", "placement"], and a standing relation is none of those. exchange is movement between subjects over time; placement is location. Neither expresses "X belongs to Y" as a durable relation.

So there is a genuine expressive gap. It is not a missing layer. Under §2.0 rule 3, an abstraction must reduce overall complexity rather than redistribute it, and adding a layer above the fact vocabulary to hold what the fact vocabulary cannot express is redistribution by definition. The minimal fix, if one is needed, is at the fact layer — whether core's four families can express a standing relation. That is a lower-layer question and belongs to the core ontology, not to visualization.

Three steelmen, all failing

"A property is true whether or not it is observed; a fact is a recorded observation." This is the strongest case for the layer, and it fails against existing constitutional text. Article I.2: "Nothing exists in the world that cannot be explained by data." Article I.5: absence is represented, never hidden. The architecture deliberately refuses to model unobserved truth — an unmeasured condition is a Gap or an absence, not a hidden property. A layer holding unobserved truth would contradict Article I rather than extend it.

"Properties is where semantic judgement lives" — e.g. why blocked work counts double. It fails because the metric contract already owns that: question 2 requires a metric to declare what mathematical function computes it, and priority × 2 if blocked is exactly such a declaration. Gap 5's finding stands — deriveLoads collapses two layers — but the repair is to declare the function in the metric, not to add a layer above it.

"Metrics must be meaningful without visualization, so something must hold renderer-independent truth." Facts are already renderer-independent. This distinguishes nothing.

Result

The Properties layer does not currently earn its existence. The minimum model is:

Facts → Metrics → Geometry → Rendering

and the provenance chain shortens accordingly:

pixel ← geometry ← metric ← fact

The north star is the chain's completeness, not its length. A shorter chain that reconstructs exactly is strictly better than a longer one with a layer that holds nothing.

Provisional in both directions. This elimination is itself evidence-bound. If a measured ambiguity appears that Facts and Metrics cannot express between them, §2.0 admits the layer on the same terms as any other abstraction. What is recorded here is that no such ambiguity has yet been measured — not that none exists.

Open question, recorded rather than answered: can core's four fact families express a standing relation between subjects? ownership and dependency currently live in a connector register and are invisible to the fact vocabulary. That is a core-ontology question, and it is the one thing this elimination could not dispose of.


Gap 7 — the provenance chain is broken at two points

Found by re-testing the Properties elimination against explanatory sufficiency rather than minimality. Under the older criterion this was recorded as an open ontology question; under the newer one it is a live violation of the north star, because the test is no longer "does anything land in this layer?" but "can every rendered primitive be inverted back to evidence?"

Both ownership and dependency reach the render layer without passing through a Fact or a Metric:

stroke colour ← accent ← accentForOwner(product.owner) ← connector register
                         structure.ts:248                estate.ts:39

rendered link ← Link{kind:"data"} ← product.dependsOn  ← connector register
                structure.ts:296-302                     estate.ts:48

Consumed at CityMap.tsx:143, :144, :235 as gradient stops and stroke.

Asked "why is this district that colour?", the chain terminates at a hardcoded hex in a register — not at evidence. Asked "why is there a line between these two services?", it terminates at a hand-written array. Neither inverts.

This does not resurrect the Properties layer. §2.0 rule 3 requires an abstraction to reduce complexity rather than redistribute it, and a layer placed above the fact vocabulary to hold what the fact vocabulary cannot express is redistribution by construction. The repair is at the fact layer: whether core's four families can express a standing relation between subjects.

What changed is the finding's status. It is not a curiosity about where a concept sits; it is two rendered primitives with no inverse.


Re-test of recorded verdicts under explanatory sufficiency

Every §2.0 verdict was reached under "smallest architecture". Re-running them against the refined criterion, because a changed measure should not be assumed to preserve prior conclusions.

Verdict Survives? Under the refined criterion
K eliminated yes, with a better reason Classification is a governance act, not an observation of the estate. It lies outside the chain's domain, so removing it costs no reconstructability. The old reason — "the trace reading absorbed it" — was true but weaker.
X.3 withdrawn yes Added no explanatory power; constrained process only. Independently refuted by the Article IX counterexample.
Liveness admitted yes, strengthened It expresses "the process never terminates", which no other class could state at all. Explanatory power gained; cost is one lattice cell. This is what "indispensable" looks like.
CapacityFact failing yes, stated more precisely Removing it preserves complete reconstructability — MeasureFact{limit, enforced} expresses every field. It therefore redistributes responsibility without increasing explanatory power, which is the exact definition of an abstraction to eliminate.
Properties eliminated yes, but the residue is upgraded The layer still holds nothing that Facts and Metrics cannot. But its two non-collapsing candidates turn out to reach rendering, which makes them Gap 7 rather than an open question.

No verdict reversed. One was strengthened, one gained a better justification, and one exposed a defect that minimality could not see — which is itself evidence that the refined criterion is the more discriminating of the two.


Exhaustive reconstruction audit

Every prior gap was found by looking at something specific. This enumerates every field the renderer reads and traces each chain, so the baseline is complete rather than opportunistic.

Measured by extracting all property accesses in CityMap.tsx:

Primitive Reads Chain Status
accent 24 none not in Legend; no chain
blocked 5 ← WorkItemFacts complete
depth (queueDepth) 2 ← WorkItemFacts complete
workload (load) 1 ← WorkItemFacts complete
x, y (position) 8 ← plot register (plots.ts) declared exception, Legend source structure
kind, code, domain, glyph 14 ← roster identity labels
id 19 identity, not a visual primitive

accent accounts for 24 reads — more than the three complete chains combined (8) — and is the only primitive that is neither chain-complete nor Legend-declared. The most-used visual property in the system is the least grounded one.

The decisive finding — accent duplicates what position already declares

The Legend's own rows:

{ visual: "footprint", represents: "identity and architecture", source: "structure" }
{ visual: "position",  represents: "ownership and topology",    source: "structure" }
{ visual: "height",    represents: "cumulative operational output", source: "output" }
{ visual: "illumination", represents: "current activity",       source: "activity" }

The Legend assigns ownership to position. And accent derives from accentForOwner(product.owner) — so colour represents ownership as well, undeclared.

That makes accent not merely ungrounded but duplicative: two visual properties carrying one meaning.

This is decidable under existing articles — no amendment required

  • Article I.4"One fact has exactly one representation. If two things can disagree, one of them is wrong by construction." Ownership is represented twice, by position and by colour. I.4 rejects it.
  • Article VIII.1.2 — the Legend is exhaustive; "a visual property absent from it may not be introduced." accent is absent. VIII.1.2 rejects it.

Two independent articles reject accent in its current form. The Constitution has an opinion, and it needs no new text to express it. This moves accent from unresolved gap to resolved by existing article, repair pending.

The repair is partly blocked. Ceasing to derive accent from ownership is an implementation act. Retaining colour as a declared identity property — the footprint treatment, an explicit exception to I.1 — requires a Legend row, which is a constitutional file and therefore blocked while an amendment is pending.

Terminal condition: neither A nor B

Not Constitutional Falsification (B). Every measured primitive is accepted or rejected by an existing article. Nothing yet requires new constitutional text.

Not yet Constitutional Closure (A). Chains remain open, and the repairs are blocked behind governance rather than undecided:

Primitive Decided by Repair blocked on
accent I.4 + VIII.1.2 (rejected) Legend row → constitutional
data links (dependsOn) I.1 (rejected) fact-family question
SPIRE_H I.1 (rejected) Legend row → constitutional
height from code-size II.2 (rejected) floor/tier units decision

Four open chains, four existing articles deciding them, zero requiring an amendment. The distance to closure is decision and repair, not theory.


Audit revision — two self-corrections, and a located impossibility

Re-testing the audit against the complete Legend (all nine rows, not the four a truncating grep returned) overturns two findings recorded above.

{ visual: "roadExistence", represents: "declared relationship", source: "structure" }

Link existence is in the Legend, and its declared source is structure — an explicit exception of the same kind footprint and position hold. The data link's evidence string reads "declared dependency in the estate register", which is exactly what the Legend says it should be. Data links are constitutional and honestly sourced. Gap 7's claim about them is withdrawn.

Correction 2 — position does not represent ownership; the I.4 finding was wrong

The Legend claims position represents "ownership and topology". Measured:

export function assignSlots(ids: string[], capacity: number,
                            recorded: Readonly<Record<string, number>> = {})

assignSlots structurally cannot see ownership — it receives ids, a capacity, and a register. Products are placed by FNV-1a hash of their id. That owner z's two products landed on plots 3 and 4 is coincidence, not design.

So accent does not duplicate an existing representation of ownership. It is the only representation. The Article I.4 finding recorded above is withdrawn.

What replaces it is different and still decidable: the Legend's position row is false. Position does not represent ownership. That is an Article II semantic-correctness violation, and it is decided by existing text.

The reduced picture

Against all nine Legend rows, exactly one rendered primitive is undeclared: accent. (health is undeclared in the World contract but is not read by the renderer, so it is a contract defect rather than a rendered one.)

Measured constitutional impossibility — precisely located

accent's chain has two ungrounded terminals:

  1. The ownership relationestate.ts:39, a declared register entry. Groundable by emitting it as a fact.
  2. The colour value itselfroster.ts:76, accent: "#8ea2ff". It appears in no fact vocabulary, in core or legacy. A hue is not a measurement of anything; it is an identity assignment.

Grounding terminal 1 does not close the chain, because terminal 2 remains. Therefore a relation fact family — the abstraction that first suggested itself — fails §2.0 rule 2 for this purpose: it would not resolve the measured ambiguity. It is rejected on measurement, not taste.

Terminal 2 has exactly one constitutionally-valid repair: declare accent as an identity-only property in the Legend, the treatment footprint already receives as "a deliberate exception to Article I.1, explicitly declared as such". The Constitution also already accommodates presentational values through Article VI's ASSUMED_CONSTANTS, where walk-duration-ms is recorded with the basis "presentational: how a journey is shown, not measured".

Both mechanisms live in src/lib/world/constitution.tsa constitutional file under Article VIII, which Article X forbids modifying while an amendment is pending.

This is not lack of implementation. The implementation is identified, precedented, and constitutionally forbidden at this moment. The blockage is governance, and it is located at a single file and a single pending decision.


Exhaustive rejection proof — no existing mechanism terminates the accent chain

The claim "accent is the only primitive without a chain, and only a Legend amendment can close it" is treated here as a falsifiable hypothesis. Every existing mechanism is enumerated and rejected with evidence.

A. Core fact families

Candidate Rejected because
condition A closed 20-member vocabulary of judgements. A hue is not a judgement about state. Extending it is the subject of the pending amendment.
measure core/ingest.ts throws ManifestRejected if a metric declares no unit. A hue inhabits no unit. Rejected mechanically, not by argument.
exchange {moved, from, to} — an event of movement. Colour does not move.
placement {subject, at} — location. Colour is not a location.

B. Legacy fact families

PlacementFact, ActivityFact, IncidentFact, ExchangeFact, MeasureFact, CapacityFact, WorkItemFact. Each narrows a core family and inherits its rejection. MeasureFact is the only near-miss and fails on semantics: its field is value: number, a quantity, and a hue is not a quantity of anything.

C. Identity vocabularies

SUBJECT_KINDS, ConditionId, FACT_KINDS all participate in persisted identity (conditionIdentity, urn, factIdentity). Admitting colour would place it inside every identity string and fork every stored key — the precise hazard Article XIII exists to prevent. subtype is open and connector-declared, but names what kind of thing a subject is, not how it appears.

D. Presentation constants — the closest existing mechanism

Article VI's ASSUMED_CONSTANTS genuinely accommodates presentational values:

{ id: "walk-duration-ms", value: 6000, where: "substrate/runtime",
  basis: "presentational: how a journey is shown, not measured", assumed: true }

It nonetheless fails twice. Type: AssumedConstant.value is number; a hex colour fits only by coercion. Semantics: its own doc comment scopes it to "every normalisation ceiling, threshold and band" — a hue is none of those. And decisively, ASSUMED_CONSTANTS lives in src/lib/world/constitution.ts, so even a successful fit would be the blocked constitutional act.

E. Constitutional exceptions

Article II.1 grants footprint an exception: "a deliberate exception to Article I.1 and is explicitly declared as such." The operative words are explicitly declared — footprint's exception exists because it holds a Legend row with source: "structure". The exception mechanism is not available independently of the Legend; invoking it is the Legend amendment.

F. Deriving hue from an already-grounded primitive

Deriving accent from activity would close the chain — and change what colour means, from ownership to activity. illumination already represents activity, so the result is an Article I.4 duplication. health is itself undeclared. Rejected: a closed chain to the wrong meaning is the failure already recorded against reconstructability.

G. Subsumption under an existing Legend row — and a new defect

The remaining candidate: eliminate accent by making position represent ownership, which the Legend already claims it does. That would need no Legend change and would classify as implementation under IX.3.

It is unsatisfiable, and the evidence is in the declarations themselves:

{ visual: "position", represents: "ownership and topology",
  source: "structure", quantised: true, changeRate: "never" }

Article III.3 — "Buildings never move. Their plot is assigned once and held
for life."

Position is declared to change never. Ownership carries no immutability guarantee anywhere — it is a mutable field in a connector register, protected by no article and no test. Two things with different change rates cannot remain in faithful correspondence. Position therefore cannot represent ownership without violating Article III.

That is a second Legend defect, distinct from the first: the position row does not merely fail to represent ownership today, it declares a correspondence that Article III makes impossible to maintain. Decidable under Article II, and requiring the same blocked file.

Conclusion

All seven categories exhausted; every candidate rejected on measured evidence. The accent chain requires a Legend amendment — specifically a row declaring colour an identity-only property, the treatment footprint holds.

This is not Constitutional Falsification. The Constitution adjudicates accent without difficulty — VIII.1.2 rejects an undeclared visual property, and the remedy is an ordinary amendment rather than new theory. What is blocked is the act, not the reasoning.


Reconstruction completeness — achieved for the SVG renderer

The rejection proof above concluded a Legend amendment was necessary. That conclusion was wrong, and the error was in its premise: it treated accent as a terminal semantic object. Modelled instead as a deterministic transform of identity, the chain closes with no constitutional action.

The derivation that survived

Seven deterministic derivations were enumerated. Six were rejected:

Rejected because
f(activity) illumination already represents activity — I.4 duplication
f(health) health is itself undeclared
f(kind) footprint already represents "identity and architecture" — I.4
f(load) department height is heightRule {kind:"load"} — I.4
f(owner) the World carries no owner field, measured
fixed palette the palette hexes remain hand-chosen; the terminal survives

One survived: hueFor(id), a pure function of the entity's own identity.

The precedent is already in the repository. code (4 renderer reads), domain (3) and glyph (2) are rendered, identity-derived, and hold zero Legend rows — uncontroversially. So VIII.1.2's "visual property" means a channel that encodes state, not everything visible. Colour that claims only "this is that entity" joins the label category and needs no source declaration. hash32 was reused rather than reinvented; it already maps identity to plots and was chosen there for cross-runtime stability.

Audit re-run

Primitive Reads Chain
accent 24 hueFor(id) ← idclosed
blocked, depth, workload 8 WorkItemFacts — closed
x, y 8 plot register, Legend source: "structure"closed, declared
kind, code, domain, glyph 16 identity labels — label precedent
id, key, queue not visual primitives

Zero hex literals remain anywhere in src/lib outside the constitutional file. 69 tests green, 9 architectural mutations caught.

Gap 1 resolves by the same principle

SPIRE_H = 252 was recorded as renderer-invented geometry. The sharpened test: a visual value needs a provenance chain iff it VARIES with its subject. SPIRE_H is constant across every composition — it encodes nothing, exactly as stroke width and the SATURATION/LIGHTNESS constants in hue.ts encode nothing. accent needed a chain precisely because it did vary per agent.

Gap 1 is withdrawn.

What this deliberately gave up

Colour previously encoded ownership — two products of one agent shared a hue. That mapping had no evidence chain, no Legend row, and no available carrier: Article III declares position changeRate: "never" while ownership is mutable. The signal is not hidden; ownership remains in the estate register and in the inspection panels. It is no longer asserted by a channel that could not justify it.

What remains open — none of it reconstruction

Article Blocked on
height derives from code-size II.2 floor/tier units
Legend position row claims ownership falsely II Legend row
campusTiers declared, never produced II Legend row
health undeclared in the contract (unrendered) VIII.1.2 Legend row

All four are semantic correctness, not reconstructability. The provenance chain is complete; what remains is whether the chains terminate at the right evidence.


Renderer-level audit — the methodology required revision, and the revision found more

The previous completeness claim rested on an assumption now disproven: that reconstructing the World object reconstructs rendered output. This audit enumerates renderer decisions by syntactic position — every JSX visual attribute whose value is an expression — across every rendering surface, assuming no layer is authoritative.

Method comparison, measured. The old method found 9 conditionals, all colour, in one file. The new method finds 26 across two renderers, and the second renderer (WorldScene.tsx, the R3F path) had never been audited at all.

Classification by provenance

Class Count Discriminator Verdict
A — interaction 9 selectedId (CityMap:156–187) Asserts nothing about the estate. See ambiguity below.
B — operational state → hardcoded value 5 node.blocked > 0, building.health === "fail", agent.status Article I.3 violation
C — undeclared normalisation band 3 activity, flow Article VI violation
D — structural 9 kind === "reserved" | "lot" | "handoff" Permissible under Article II.1's precedent

Class B — Article I.3

"No renderer may create, classify, infer, aggregate or interpolate state. A renderer maps world values to pixels and does nothing else."

CityMap:436     fill={node.blocked > 0 ? "#ff8a6b" : node.accent}
CityMap:704     fill={reserved ? "#5a5a66" : node.blocked > 0 ? "#ff8a6b" : node.accent}
WorldScene:280  emissive={failing ? ALERT : building.accent}
WorldScene:389  emissive={agent.blocked > 0 ? ALERT : agent.accent}
WorldScene:390  emissiveIntensity={busy ? 1.5 : 0.35}

Each classifies operational state inside the renderer and selects a value from that classification. Two of them override accent, the field the previous proof declared closed.

Class C — Article VI, a violation class no colour-focused scan could find

"Every normalisation ceiling, threshold and band in the system" must be declared with its basis.

WorldScene:174  0.12 + district.activity * 0.3
WorldScene:239  0.1  + link.flow      * 0.45
WorldScene:282  0.12 + building.activity * 0.85

Three normalisation bands, in the renderer, mapping grounded world values onto visual intensity. ASSUMED_CONSTANTS declares eight entries; none of these is among them. This is a different article, a different mechanism, and invisible to every prior pass — which searched for colour.

Remaining ambiguity — the scope of Article I.3's "state"

Class A derives from selectedId, which the renderer creates. Read literally, I.3 forbids a renderer creating state at all. Read against Article I's scope — "the operational world is a projection of one canonical operational state" — selection is view state and asserts nothing about the estate.

The Constitution does not explicitly scope I.3. Nine visual decisions rest on the narrower reading. Recorded as a tripwire, not resolved by assertion; it is the one place in this audit where an existing article is genuinely ambiguous rather than merely violated.

Verdict on the methodology

Revision was required, and it is specified: enumerate by syntactic position across every rendering surface, classify each decision's discriminator by provenance, and never validate a rendered invariant against a data-layer object.

Applied once, it produced an independent counterexample class (Article VI) that the colour-scoped method could not have found. That is evidence the repair is real, and equally evidence that no completeness claim is yet justified.