Skip to content
← Documentation

docs/types-classification.md


world/types.ts — field classification against three domains

Measurement only. No redesign, no file moves, no code. Every exported type in src/lib/world/types.ts, field by field, classified against three unrelated domains: software companies, hospitals, logistics networks.

  • universal — all three need it, unchanged
  • application-specific — at most one needs it, or each needs a different one
  • mixed — the concept is universal, the enumeration or encoding is not

1. Vec3

Field Class Evidence
x, y, z universal Abstract world units, explicitly renderer-scaled (file header rule 1). A ward floorplan, a depot map and a service topology all need position.

Type verdict: universal.


2. DistrictKind"civic" | "product"

Type verdict: application-specific. A hospital groups by ward or speciality; a logistics network by hub, region or lane. Neither has a "product". "civic" is HQ's own metaphor for its orchestration core.


3. District

Field Class Evidence
id, label universal opaque identity and display
kind application-specific inherits DistrictKind
center, radius universal spatial grouping extent
accent universal derived from identity (hueFor), not chosen
productId application-specific a typed foreign key to a product. A ward has no product id; a lane has no product id.
revenue.listedMonthly mixed the pattern (declared vs realised, null ≠ 0) is universal; "revenue on a district" is not. A ward has cost centres; a lane has tariffs.
revenue.listedOneTime mixed same
revenue.realisedMonthly mixed same. The comment's own justification — "null until telemetry proves it, never 0, because nothing has reported" — is a measure semantic, not a district semantic.
activity universal 0–1 aggregate
health universal see Health

4. BuildingKindhall | department | service | datacenter | library | security | conference | lot

Type verdict: application-specific. service and datacenter are software infrastructure; the doc comments bind library to hq_memory and conference to hq_meeting, naming HQ's own tables. A hospital needs theatre, ward, pharmacy; a logistics network needs depot, cross-dock, yard.

lot (allocated ground, unbuilt) is the one member that generalises — reserved-but-unrealised capacity exists in all three — but a single universal member does not make the enumeration universal.


5. Healthok | warn | fail | unknown

Type verdict: universal. All four states apply to a ventilator, a truck and a service. unknown is load-bearing: it separates not measured from measured healthy, which is the same distinction the whole provenance contract rests on.


6. Building

Field Class Evidence
id, label, shortLabel universal identity and display
kind application-specific inherits BuildingKind
districtId universal grouping reference
position, footprint, height universal abstract spatial extent
accent universal derived from identity
activity universal 0–1, renderer maps it
health universal see Health
capacity.used universal bed occupancy, trailer load, request concurrency
capacity.limit universal null = unenforced. The stated reason — "an unenforced limit is a guess" — holds identically in all three domains
capacity.basis universal free-text provenance of the limit
evidence universal why it looks as it does
detail[] universal {label, value, note}, uninterpreted by the renderer

Type verdict: mixed — one application-specific field (kind) in an otherwise universal record.


7. WorldAgent

Field Class Evidence
id universal ActorId = string, deliberately unbounded (Article V)
code universal short display code
accent universal derived
placement universal stationed / transit / attending / occupied / unstaffed — a nurse on a ward, in handover, off-rota; a driver in transit, at a depot
position, progress universal derived from placement and clock (header rule 2)
homeBuildingId universal assigned base
status universal active / steady / idle / unstaffed — operational classification, domain-free
load, queueDepth, blocked universal workload aggregates
focus universal current subject
queue universal WorkItemFact[] — work items exist in all three
evidence universal why it is where it is

Type verdict: universal. The only exported record with no application-specific field.


8. LinkKindroad | data | handoff

Type verdict: mixed.

  • road"department reports to City Hall": a reporting/structural relation. Universal.
  • handoff"a task moved between agents": universal (patient handover, load transfer).
  • data"a service calls another service": as written, software-specific. The generalisation (an information flow) is universal, but the member as documented is not.

Field Class Evidence
id, fromBuildingId, toBuildingId universal endpoints
kind mixed inherits LinkKind
flow universal 0–1 intensity
evidence universal

10. WorldEvent

Field Class Evidence
id universal
unrepresented? universal count of observations an aggregate stands for
kind application-specific alert / handoff / meeting are universal; deploy and commit are software-delivery concepts. A hospital has no commits. A mixed enumeration in a shared contract is application-specific, because a consumer must switch on members that cannot occur.
severity universal info / warn / critical
subjectId universal building or agent reference
label, detail, at universal

11. World

Field Class Evidence
version universal shape negotiation
generatedAt universal
districts, buildings, agents, links, events universal containers element types classified above
measurements universal MeasureFact[] with unit and evidence
answers universal {question, answer, detail?, agent?, tone} — the shape is domain-free; the questions are supplied by the application
stats.districts/buildings/agents mixed counts are universal; the field names hard-code the taxonomy
stats.agentsWorking/Idle/InTransit/InMeeting mixed same — a fixed partition of placement and status, named per HQ
stats.openItems/blockedItems universal work-item aggregates
stats.simulationLive universal "true once anything moves for a real reason" — an honesty flag
sources[].id universal substrate identity
sources[].ok mixed — measured defect see below
sources[].note universal but currently load-bearing, see below
focus.home universal camera hint
focus.alertSubjectId universal highest-severity subject

The sources finding

Measured: src/lib/hq/telemetry.ts carries a three-valued provenance vocabulary — live, unreachable, not-wired. world/types.ts encodes provenance as ok: boolean plus a free-text note.

The distinction between the source exists and cannot be reached and the integration was never built is therefore destroyed at the World boundary and survives only as prose. This is the one contract identified elsewhere as the strongest platform candidate, and the shared type is the lossy copy.


12. Minimal decompositions for the mixed types

Stated as decompositions, not designs. Each preserves expressive power.

12.1 The four taxonomy enumerations

DistrictKind, BuildingKind, LinkKind, WorldEvent.kind

Minimal decomposition: the platform keeps the slot — a kind tag plus a declared vocabulary — and the application supplies the members.

FALSIFIED BY IMPLEMENTATION 2026-08-03 — see decomposition-experiment.md. This claimed expressive power is preserved and exhaustiveness is not lost. It is not. Measured: a default type parameter loses closure outright (kind: "banana" compiles), and removing the default preserves closure only by propagating the parameter to 112 sites across 16 files. There is no zero-cost variant. The claim reasoned about the mechanism; the measurement contradicted it.

The repair is not to the vocabulary but to the location: exhaustiveness belongs to the consumer that owns the taxonomy, not to the shared contract — which is core's own runtime-validate/derive-locally pattern.

Evidence that this is minimal, and that the mechanism already exists: core/ingest.ts already does precisely this. ConnectorManifest declares what a connector emits, and ingest validates emissions against the declaration, rejecting undeclared ones. The platform already solves declare-then-validate for conditions and subject kinds. world/types.ts does not use it.

12.2 District.productId

Minimal decomposition: one generic subject reference in place of one typed foreign key.

core already has SubjectRef and urn, and a product is a subject. A ward and a lane are subjects too. No expressive power is lost — the district still points at exactly one thing — and the pointer stops naming one domain's noun.

12.3 District.revenue

Minimal decomposition: three measures instead of one embedded record.

World.measurements: MeasureFact[] already exists, and MeasureFact already carries metric, value, evidence, and optional limit/enforced.

The null-versus-zero distinction the comment defends is strengthened, not weakened: an absent MeasureFact is unambiguously not applicable, whereas a present one with value: 0 is measured as zero. That is the same distinction the comment says the world previously got wrong, expressed in the vocabulary that already exists for it.

12.4 Building.kind, Link.kind

Resolved by 12.1; no separate decomposition needed.

12.5 World.stats

Minimal decomposition: counts keyed by the declared vocabulary rather than by fixed field names.

agentsInMeeting is count(placement.kind === "attending"); districts is count(districts). Every field is already an aggregate over data present in the World. Keying them by vocabulary member preserves every current value and extends to a domain whose members differ.

12.6 World.sources[].ok

Minimal decomposition: adopt the three-valued vocabulary that already exists in hq/telemetry.ts.

This increases expressive power rather than preserving it: ok is derivable (live → true, otherwise false), while the reverse is not. The decomposition recovers a distinction the current contract discards.


Summary

Count
Exported types fully universal Vec3, Health, WorldAgent — 3
Exported types fully application-specific DistrictKind, BuildingKind — 2
Exported types mixed District, Building, LinkKind, Link, WorldEvent, World — 6

Four of the six mixed types are mixed for the same reason — a closed taxonomy enumeration written into a shared contract — and one decomposition (12.1) resolves all four. Of the remainder, District.revenue and World.stats are re-expressible in vocabulary the World already carries, and sources[].ok is a lossy encoding of a contract the codebase already has elsewhere.

No decomposition proposed here introduces a concept that does not already exist in core or in the current World.