WallRiderLangWRL CORE 0.1.2 · FROZEN
PART I NORMATIVE · PART II DESIGN DRAFT

WRL Core 0.1.2 — Frozen Family Extract

This document commits the meaning families of WallRiderLang that the TRVM runtime and the Forge semantic graph have grounded. It freezes which kinds of things exist and what each kind means — not the detailed surface rules, which remain a design draft and may still change.

This file has two parts, and only one of them is normative. Part I (§0–§17) is the frozen family extract: everything in it is settled, implemented, and covered by a battery. Part II (§D1–§D7), new in 0.1.2, is a design draft — the architectural direction the frozen core is being aimed at. Nothing in Part II is frozen, nothing in it is implemented, and every construct it sketches is rejected by today's toolchain with WRL_UNSUPPORTED_FEATURE. Part II exists so that the direction is stated in the spec's own language and can be argued with, rather than living only in somebody's head. Where the two parts appear to disagree, Part I wins.
Part II is not the language's design draft. The authored Complete Design Draft — forty-six sections covering the expression notation, boundaries, effects, supervision, metaprogramming and the seven worked examples — is a separate and much larger document, published in full at Design draft. That is the primary forward map and the one whose section numbers the tier table cites. Part II below is a later, narrower extrapolation: nine sections on the questions the authored draft does not settle — how many topologies one identity set may carry at once (§D1–§D7), what a connection is when it is not a directed pair (§D8), and what happens when the topology itself changes (§D9). Read the draft first.

Status and errata

Status: frozen (families only). Extracted from the Complete Design Draft per the WRL ruling, Phase 1. This is the living Core-0.1.x document; the current revision is 0.1.2.

Freezing a family means: the set of members is closed, and each member's meaning-role is settled. It does not freeze exact glyphs, argument grammars, sugar, or edge-case rules — those live in the design draft and are Experimental or Proposed until a later frozen revision promotes them.
When the design draft and this document conflict about what is settled, this document wins. The draft remains authoritative for full rationale and forward design.

Errata 0.1 → 0.1.1

Before freezing Forge Semantic IR v1, five corrections were required so that the frozen core reflects the ADMIT and persistent-world results rather than the older actor execution model:

  1. The period cycle is OBSERVE→ACCEPT→MAP→COMMIT→REACT→FILM, not Collect→Order→Reduce→Commit→Record.
  2. "Conflicts dissolve" is narrowed — single ownership stops generic cell races, but claim and control conflicts need explicit policies.
  3. Canonical ordering is policy-pinned, not a fixed hash formula.
  4. History splits into WorldFrame / EventLedger / BuildFilm.
  5. The grounding language distinguishes grounded from reserved and partial.

Errata 0.1.1 → 0.1.2

Four corrections, none of which move a frozen family. They make the grounding claims honest and add the two structural facts the implementation established after 0.1.1 was written:

  1. Mailbox is partial, not reserved. The mailbox declaration is grounded in the canonical IR and the runtime, but the ~~ route construct is not surface-emittable and has no structural edge declaration. Mailbox is therefore not a sixth surface-grounded role — it is IR/runtime-grounded and surface-partial. §14 is corrected; §14b is new.
  2. * is narrowed. Only the replication-by-position meaning is implemented; wildcard matching remains reserved with no lowering. §4 is corrected.
  3. The document boundary is normative (new §15). A WRL world document and a scenario are different documents. periods and [epoch:N] claims are run inputs and are deliberately outside the SemanticArtifactID. The strict world parser is normative; the combined-document parser is an explicitly-named migration bridge.
  4. The Core 0.2 promotion order is recorded (new §16), together with the conditions each construct must meet before it may be promoted.

Part I · Normative — the frozen family extract

§0Architectural rulefrozen

WRL denotes the canonical Forge semantic graph. It is not lowered by compiling surface syntax directly to hand-written interaction-calculus text. The only sanctioned path is:

WRL source / canvas canonical semantic graph Forge Semantic IR TRVM facts · films · reductions

Forge Semantic IR v1 is a separate gated deliverable. Until it is frozen, no lowering target is normative.

§1The ten construct kindsfrozen · closed set

Every WRL construct is exactly one of these ten kinds. The set is closed.

KindRole (frozen)
Functioncalculates — pure local computation
Actorpersists — durable identity + owned state
Routecommunicates — a directed, textured transition edge
Wall (boundary)authorizes — gate / commit / seal on propagation
Periodorders — logical-time step under which events settle
Fragmentcarries code — quoted graph as movable data
Stencilconstructs graphs — parameterized fragment producer
Derivetransforms typed graphs
Filmproves what occurred — append-only replayable log
Hashidentifies what was built — content address

Of these, Actor, Route, Wall, Period, Film, Hash are additionally grounded by TRVM today (§14). Function, Fragment, Stencil, Derive are frozen as kinds but their detailed semantics remain draft.

§2The four reading lawsfrozen

  1. Shape says what a thing is. Bracket shape identifies the kind of object.
  2. Line texture says how it moves. Route style identifies the operational guarantee.
  3. The colon introduces; the slash bounds. Both families cap at three strokes, ordered by permanence: transient · committed · sealed.
  4. Marks for architecture, words for computation. Process notation owns punctuation; expression notation defaults to words.

§3Container / shape-as-kind familyfrozen · 3 members

ShapeKind (frozen)
[x]durable identity (actor, region, mailbox, named artifact)
(x)state — current, mutable-by-replacement cell
{x}wiring — permitted topology / capabilities / ports

Frozen invariant: (state) is single-owner and mutable by replacement; {…} is permitted, static structure, distinct from current state. Exact field grammars are draft.

§4Identity / addressing familyfrozen · 4 members

SymbolMeaning-role (frozen)
#canonical identity / content hash
@address / placement / stamp
?pattern variable introduced by matching
*wildcard match, or replication-by-position

Frozen invariant: executable identity is a hash; human aliases are words and never semantic.

Implementation status of * (0.1.2). The symbol's family role stays frozen as written, but the two meanings are at different maturities and the spec must not imply otherwise:

MeaningStatus
replication-by-position ([relay:r*3])implemented — surface sugar, bounded, identity-equivalent (§17)
wildcard matchreserved — no lowering exists

§5Route / texture familyfrozen · 4 core textures

The reduction relation is defined over exactly these four irreducible textures. All surface sugar canonicalizes down to one of them plus attributes.

TextureGuarantee (frozen)
--x--> soliddeterministic local transition; settles within the period
~~x~~> asyncasynchronous message; appended to a mailbox; observable next period
==x==> verifiedevidence-backed / committed transition under a named policy
!!x!!> faultcrash / cancel / reject / interrupt; engages supervision

Frozen: the set of core textures and their guarantee-roles. Not frozen: label, payload and guard grammar; the sugar table; branch and lane rules.

Surface status (0.1.2). Freezing a texture's meaning-role is not the same as grounding its construct. Only -- is surface-grounded today:

TextureSurface statusSee
-- solidsurface-grounded — declarable, emittable, round-trips§14
~~ asyncpartial — IR/runtime-grounded, not surface-emittable, no structural edge declaration§14b, §16
== verifiedpartial — acceptance machinery exists; the construct does not§16.1
!! faultpartial — fault state exists; route and supervision do not§16

§6Time familyfrozen

Frozen: the period/microstep model and the wall-clock-as-fact discipline. Not frozen: the tick surface syntax details.

§7Memory-kind familyfrozen · 5 members

ShapeMemory kind (frozen)Lifetime rule
(state)volatile cellmutable by replacement; single-owner
{facts}monotonic knowledgegrow-only; merges by lattice union; never retracts
[archive]durable storepersistent addressable actor; survives restart
:: fragment //code memoryquoted graph as portable data
#hashsealed identityimmutable, content-addressed
Frozen invariant: no shared mutable cell. Shared knowledge is {facts} (monotone union); shared identity is [archive]. This single-owner discipline is what makes execution deterministic without a runtime conflict resolver.

§8Execution-model family — the deterministic reactive floorfrozen

Grounded directly by TRVM's deterministic reduction, ADMIT reducer, persistent fold, and replayable films.

Configuration (frozen shape): the persistent runtime state — world state (owned state cells), monotone claim facts, immutable acceptance receipts, capacity-fault latches, clock state, poses, rotors, numeric faults; plus static topology and policies. Mailboxes, supervisor and behavior tables are Experimental and are not part of the frozen floor.

Two frozen invariants (enforced statically)

  1. Single-owner cells — no shared mutable cell.
  2. Disjoint deterministic guards — at most one deterministic rule enabled per actor-state; genuine choice must be an explicit scored branch.

Period cycle frozen · 6 phases

OBSERVEACCEPTMAP COMMITREACTFILM
PhaseDefinition
OBSERVEcanonicalize and insert distinct claims
ACCEPTcreate missing receipts per the pinned acceptance policy
MAPnewly-accepted successful ops → controls
COMMITapply control writes and fault resets to owned cells
REACTdeterministic within-period token cascade to fixpoint; latch current overflow
FILMrecord the entries needed to replay the epoch
For claim-free profiles the first three phases (OBSERVE/ACCEPT/MAP) degenerate to identity but must not disappear from the semantic model: the OBSERVE/ACCEPT/MAP distinction is what determines which claims were seen, which candidate was accepted, whether a retransmission retries an effect, which controls were committed, and whether a same-epoch configuration affects that epoch's reaction.

Canonical ordering is policy-pinned frozen as a family, not a formula

Events and accepted operations are ordered by a total canonical key whose schema and policy identity are part of the semantic artifact. Worker arrival order is never semantic. The current ADMIT profile pins the CandidateKey and the canonical accepted-event ordering; a future mailbox profile may pin a different key. The existence and policy-pinning of the key is frozen; the specific formula is not.

Conflicts narrowed in 0.1.1

Cell-write conflicts are structurally constrained; claim and control conflicts are resolved by explicit, policy-pinned recognition, acceptance and canonical application laws. Concretely:

ConcernPinned law
recognitiondistinct-CandidateKey count
acceptanceMIN CandidateKey / first receipt
applicationcanonical event order
faultCOMMIT clears, then REACT ORs
capacityatomic latch; no partial

Not frozen: the podium/ranking ceiling, scored-branch scoring functions, distributed termination detection.

§8bHistory family — WorldFrame / EventLedger / BuildFilmfrozen · 3 distinct artifacts

"Film" is not one undifferentiated object. Three conceptual artifacts are frozen distinct; their current serialization is the Film v0.6/v0.7 family.

ArtifactFrozen role
WorldFrameauthoritative observable state sufficient for future behavior — the physical world snapshot (Film v0.6 bytes)
EventLedgeraccepted events, receipts, effects, outcomes — the claim-aware layer (Film v0.7 adds this over v0.6)
BuildFilmcompiler / build provenance proposed

A replay package is frozen as:

ReplayBundle { initial_artifact, initial_state, event_ledger, frames, policy_ids }

Frozen: the three-way distinction and the ReplayBundle shape. Not frozen: byte layouts — they are the Film serialization, a backend concern.

§9Fact-merge familyfrozen · union law only

Replicated {facts} merge by a join-semilattice union that is commutative, associative and idempotent. This is the frozen distributed floor and matches TRVM's proven claim-fact set union.

Explicitly NOT frozen (Proposed in draft): acceptance-receipt settlement across logs, Byzantine and trust modes, sequencer or authority settlement, signatures and reputation. Conflicting receipts are recognized evidence for later settlement, never blindly merged.

§10Boundary (wall) familyfrozen · gate / commit / seal

BoundaryReduction effect (frozen role)
/gaterequire capability; emit an effect-request node (no ambient I/O)
//commitcanonicalize the fragment-so-far; assign a content id; freeze it
///sealcommit, then register the artifact under a content hash

Frozen: the three-tier ordering by permanence, and the no-ambient-authority rule — effects cross named walls only. Not frozen: exact policy grammars.

§11Canonicalization & content-identity familyfrozen

This matches TRVM's content-addressed objects and the digest / candidate-key discipline used in the ADMIT core.

§12Conformance families frozen

These observable properties are frozen as families of guarantee; their exact test matrices remain draft.

FamilyGuarantee
Formatting invariancepresentation never changes the hash
Replay exactnessa live run and its replay share all observables
Build invariancesame inputs → identical film + hashes across hosts
Merge lawsfact union is commutative / associative / idempotent
Boundary safetyundeclared effects cannot cross walls
Scheduler invarianceworker ordering never changes the committed film
World round-trip 0.1.2a format→parse cycle preserves the complete canonical world projection, defined by exclusion (§15)
Identity round-trip 0.1.2a format→parse cycle preserves the canonical artifact bytes and the SemanticArtifactID
Document separation 0.1.2a world document excludes run inputs both semantically and lexically (§15)

§13What Core 0.1 does NOT freeze

Deferred to later frozen revisions:

0.1.2: "mailboxes" was removed from the Experimental list — the mailbox is no longer merely experimental, it is IR/runtime-grounded and surface-partial (§14b). It is still not promoted.

Promotion path: a family moves from Experimental → Core only after it is surface-grounded (§14b) in a running Forge/TRVM implementation, at which point this file is revised. The steered order for 0.2 is recorded in §16.

Everything in Part II is, by definition, in neither list: it is not Experimental (nothing has been tried) and not Proposed (nothing has been designed to the point of a construct). Part II records the direction these deferrals are being resolved toward, so that a promotion can be judged against something other than convenience.

§14TRVM groundingcorrected 0.1.1

Grounding is stated honestly: grounded = an isomorphic TRVM lowering exists; partial / reserved = the concept is grounded but the WRL language construct is not, or the construct is reserved with no lowering.

0.1.2: the word "grounded" was doing two jobs. §14b now splits it into IR/runtime-grounded and surface-grounded. Every entry in the Grounded table below is surface-grounded.

Grounded

FamilyTRVM grounding
The five surface-grounded world roles (Pulser / Relay / Door / Spinner / Orb) — Mailbox is not a sixthfixture node kinds; lowered by the compiler
State + wiringstate schema (counters, wires, rotor-as-state, pose, sticky fault); sig-wire + socket edges
Deterministic signal route (--)sig-wire delivery; within-period REACT fixpoint
Periodsepoch-by-epoch persistent fold
WorldFrame / EventLedger (§8b)Film v0.6 (physical) / v0.7 (claim-aware); replay exactness proven
Content identity (§11)content-addressed rulepack hash; digest / CandidateKey packing
Fact union (§9)monotone claim-fact set union; immutable receipts kept separate
ADMIT acceptance core (§8)OBSERVE→ACCEPT→MAP reducer, single-epoch + persistent fold, reference == native
Commit / react sequencing (§8, §10)COMMIT (control writes + fault resets) → REACT → latch overflow

Reserved or partial — NOT a complete grounding

FamilyStatus
Async route / mailbox (~~)partial — the mailbox declaration is grounded in the canonical IR and the runtime, but the ~~ route construct is not surface-emittable and has no structural edge declaration (§14b)
Verified route (==)partial — acceptance machinery grounded, but the route construct is not a complete lowering (§16.1)
Fault route + supervision (!!)partial — numeric-fault state grounded; the route/supervision construct is not, and no supervision floor exists yet
Capability gate (/gate)reserved — no capability-gate node kind yet
Seal / artifact registry (///seal)partial — commit grounded; seal registry is Proposed
General actor behaviorsreserved — Experimental; no arbitrary-behavior runtime exists
Wildcard *reserved — only replication-by-position is implemented (§4)

§14bSurface-grounded vs IR-groundedfrozen distinction · new in 0.1.2

0.1.1 used a single word, grounded, for two genuinely different achievements. Conflating them let the Mailbox work read as if it had promoted a route texture, which it had not. 0.1.2 splits the term:

TermMeans
IR/runtime-groundeda canonical IR declaration exists, the runtime executes it, and it is covered by a battery
surface-groundedadditionally, WRL source can declare it, the formatter can emit it, and it round-trips through the canonical bytes

A construct is only eligible for promotion into a frozen Core family when it is surface-grounded. IR/runtime grounding alone is necessary but not sufficient.

Mailbox is the worked example. The mailbox declaration is IR/runtime-grounded: it exists in the canonical IR, the runtime honours it, and the networking battery covers it. It is not surface-grounded: there is no ~~ surface form to write, the formatter cannot emit one, and — decisively — there is no structural edge declaration for it.

Mailbox is not a sixth surface-grounded role. The five world roles (Pulser / Relay / Door / Spinner / Orb) are surface-grounded. Mailbox is IR/runtime-grounded and surface-partial.

The networking track is correct to call Mailbox a sixth IR role — that is exactly what it added, additively, without moving any pre-existing identity. What must not be inferred from it is that WRL now has six surface roles, or that the async route texture was promoted. It did not, and it was not.
The edge-declaration point is the one that constrains the next slice. An async route is not an ordinary structural edge — it does not settle within the period, so it cannot participate in the within-period REACT fixpoint the way -- does. Promoting ~~ therefore requires a canonical logical route declaration that is distinct from the structural edge declaration, not a new edge kind.

§15The document boundarynormative · new in 0.1.2

A world and a run of that world are different documents.

DocumentCarriesEnters SemanticArtifactID?
World documentprofile, objects, edges, static config, wiringyes
ScenarioV1periods, [epoch:N] claims, run inputsno

Frozen consequences

  1. Run inputs are not world content. periods and [epoch:N] claims are run inputs. Changing them does not move the SemanticArtifactID; the same world can be run by many scenarios.
  2. A world formatter emits a world document. It must exclude scenario syntax both semantically (reparsing yields no run inputs) and lexically (the emitted text does not contain scenario syntax at all). The lexical half is load-bearing: a formatter emitting a literal periods 0 would satisfy the semantic half while still writing scenario syntax into a world document.
  3. Round-trip laws split in two. The world round-trip preserves the complete canonical world projection; the identity round-trip preserves the canonical artifact bytes and the SemanticArtifactID. Neither subsumes the other.
The world projection is defined by exclusion — every canonical field except the run inputs — and never as a fixed tuple such as (profile, nodes, edges). A fixed tuple silently stops covering any field a later slice adds; §16 adds route semantics, which such a tuple would omit.

15.1 · The two parser mouths

The strict world parser is normative. Parameterizing the parser is sanctioned only as a migration bridge, and only through an unmistakable API — never a boolean flag on one function:

Entry pointAcceptsStatus
parse_wrl_core(text)world source only; rejects run inputs with a typed WRL_WORLD_SOURCE_HAS_SCENARIOnormative
parse_wrl_legacy_document(text)a pre-boundary combined documentexplicit compatibility path

The legacy path's destination is to split a combined document into a world document plus a scenario, rather than to keep parsing it forever.

Open question (flagged, not decided). The Phase-3C presentation surface CanvasGraphV1 was never migrated across this boundary: it still carries top-level periods / batches, making it the structural parallel of the legacy parser. This is not a live defect, because the production canvas path is CanvasLayoutV1, which is world-only by construction. Whether CanvasGraphV1 gets the same strict/legacy split or is retired is unruled.

§16Core 0.2 promotion orderrecorded · new in 0.1.2

The order below is steered, not merely preferred — each step supplies something the next one needs, so reordering them produces constructs that cannot be grounded honestly.

#StepGate it must pass
1L-0 closurethe round-trip and document-boundary laws restated and green — complete
2~~ async routeneeds a canonical logical route declaration distinct from the structural edge declaration (§14b)
3== verified routesee §16.1
4# references and & composition
5//commit and ///seal
6!! fault routemay only follow a minimal supervision floor actually existing
7/gate capability gatelast

16.1 · == is not "almost free"

It is tempting to read the verified route as nearly grounded, because ADMIT already accepts claims. That reading confuses two different things:

== is grounded only when ADMIT enforces all four of: the claimant, the target, the operation family, and a named policy. Anything less is a route that looks verified and is not.

16.2 · Permission / instance splitapproved

LayerCarries
World documentdeclares a verified channel and its schema
ScenarioV1carries the claim instances
Receiptsprove acceptance through that channel
Constraint. Do not introduce a principal-shaped role such as [worker:w1] as part of this work. How principals and writers exist in the role system is a separate question requiring its own sanction; smuggling one in as a side effect of the verified route would freeze a role-system decision that was never ruled on.

§17Surface sugar tierstatus · new in 0.1.2

Sugar is a source-to-source pre-pass that runs in front of the untouched canonical parser.

Status: IMPLEMENTED — CURRENT BATTERY GREEN / IDENTITY-EQUIVALENT / NOT CLOSURE-PROVEN / NOT FROZEN.

The law: no sugar-specific identity

Stated precisely, because the looser phrasing is false:

Two required properties, gated by battery

  1. Bounded expansion. A one-to-many form must reject an absurd count with a typed diagnostic before allocating the expansion. sp*1000000000 is a diagnostic, not an out-of-memory event.
  2. Original-source mapping. A one-to-many expansion must preserve the authored spans, so that diagnostics, completion, semantic diff and editor operations all refer to what the author wrote rather than to generated text.

Generated names are judged by the ordinary seal — a collision between a generated name and an explicit one is a normal WRL_DUPLICATE_ID, not a special sugar rule.

End of Part I. The next gated deliverable is §16 step 2 — the ~~ async route — which requires a canonical logical route declaration distinct from the structural edge declaration.


Part II · Topology extrapolation — non-normative

§D0Status of Part IInon-normative · new in 0.1.2

Part I answers what is settled. Part II answers a different question: what is this language for, such that the settled part is worth having? A frozen core with no stated destination gets extended by whoever asks loudest; writing the destination down turns that into an argument that can be lost.

Nothing in Part II is normative, implemented, or promised. Every world sketched below is currently rejected by the toolchain, and the site's conformance suite asserts that rejection on every run — each sketch also names the promotion capabilities it is waiting on. Marking one of those capabilities shipped in the registry makes the suite fail every block still waiting on it, by file and block number, and this section must then be rewritten before the build is green again. The sketches do not begin sealing by themselves — most are fragments with no profile line, so they would go on being rejected for a reason unrelated to the capability. The trip-wire is the registry row, where a person must act; that is the intended failure mode, and it is stated exactly in the promotion note.
And this is not the whole forward map. The authored Complete Design Draft already specifies most of what WRL intends to become — §10–§17 the expression notation, §18–§27 the process semantics, §28–§34 metaprogramming, §35–§38 programs at scale, §39–§46 the reference. All of it is on the Design draft page, marked with the author's own Core / Experimental / Proposed tiers. Part II here takes up three things that document leaves open: whether a world's identities may carry several distinct topologies at once (§D1–§D7), what a connection is once it stops being a directed pair (§D8), and what it means for the topology itself to change while the world runs (§D9). If you want the language's designed feature set, read the draft; if you want these architectural arguments, read on.

D0.1 · The reframe that produced Part II

WRL has been evaluated as an actor language — process, mailbox, behaviour loop, supervision tree — and by that measure it is weak: it has no runtime-spawnable process, no user-written behaviour, no supervision floor, and no surface mailbox (§14, §14b). Part I does not hide this.

That measure is not, however, the one this language was built for. What the implemented core actually delivers is unusual and hard: a content-addressed, replayable, statically-checked description of a network of identities, in which the description is a hash and the run is a film. Part II states the destination that follows from taking that seriously:

WallRiderLang is an executable topology language: a notation in which physical, computational, communication, authority, fault, and economic networks can be described over the same durable identities, checked before they run, and replayed exactly after they do.

The prose case for this framing, with the honest scorecard and the ordered list of what is missing, is on the Direction page. What follows here is only the part that has spec consequences.

§D1Multilayer topology over one identitydraft

A layer is a set of connections among the world's identities that answers one question about them: who is next to whom in space, who can message whom, who commands whom, who powers whom, who is blamed when whom fails.

The multilayer-network literature [K14] distinguishes two arrangements, and the distinction is the whole of this section:

ArrangementAn entity in n layers isConsequence
Replica-based (general case)n node-layer tuples, tied together by explicit inter-layer coupling edgesidentity is a property of the coupling; the couplings can be wrong, missing, or asymmetric
Node-aligned proposed for WRLone node, present in every layer by constructionidentity is structural; there is nothing to keep in sync

Draft rule D1. A WRL world is node-aligned. Layers are aspects of one identity, not replicas of it. There is therefore no inter-layer coupling construct, because there is no pair to couple: d7 in the road layer and d7 in the comms layer are the same d7.

This is not a new idea in the language; it is a consequence of §4. Identity is already a first-class construct kind whose whole purpose is that the same name means the same thing everywhere in the document. D1 says only: do not weaken that when layers arrive. The temptation will be to model a drone's radio as a separate node so it can have its own connections. Resist it — a radio that is not the drone is a different identity, and then the question "where is the radio" has two answers.

What node-alignment costs. It rules out the honest modelling of an entity that genuinely is several things in different layers — a consortium that is one legal person in the authority layer and three independently-failing datacentres in the fault layer. The draft's answer is that such a thing is three identities and a wall (§10), not one identity in three replicas. If that answer turns out to be unworkable in practice, D1 is wrong; see §D7.

§D2Three connection kindsdraft

Part I recognises exactly one way for two identities to be connected: a route from a writer to a reader, in which the writer's output is delivered to the reader's input and no cell has two writers (§0, §5). That rule is load-bearing and correct for signals. It is not correct for everything a layer might mean.

Two established precedents show what the frozen rule is a special case of:

PrecedentRuleWhy it exists
VHDL [V]std_ulogic is unresolved: two drivers is a compile error. std_logic is resolved: a resolution function reduces many drivers to one value.a three-state bus has many drivers and is not a bug
Modelica [M]at a connection point, potential variables are set equal and flow variables sum to zero. Connections are acausal — no direction is chosen.a wire has no writer; Kirchhoff's laws do not name a source

Draft rule D2. Single-writer-per-cell is not repealed. It is reclassified as a property of a port, and two further port kinds are proposed alongside it:

Port kindAdmitsMeaning of a connectionStatus
Ownedat most one writerthe writer's value becomes the reader'sfrozen — this is today's rule, unchanged (§0)
Resolvedmany drivers plus a named resolution functionall drivers contribute; the function determines the valuedraft
Acausalmany participants, no driverspotentials equal, flows sum to zero across the connectiondraft
The resolution function must be part of the sealed artifact. A resolved port whose resolution function is supplied by the runtime, or chosen by the host, breaks §11: two worlds with identical canonical bytes would then be able to disagree about a value. If a resolution function cannot be named in the document and hashed with it, resolved ports do not ship.

What this buys. Power buses, shared physical media, contention on a road segment, and summed loads all stop needing to be faked as chains of relays. What it costs. The within-period REACT fixpoint (§8) is currently a single-pass settle over owned cells. Resolved ports still settle in one pass, provided the resolution obeys the laws in §D2.1 below. Acausal connections do not settle by passing values at all: they require solving a system. That is a different execution model, which is what §D4 is for.

Three resolver contracts, and only one of them is a join

An earlier version of the paragraph above said resolved ports settle in one pass "provided the resolution function is associative, commutative and idempotent — the same three laws §9 already demands of fact-merge, which is not a coincidence". The three-law phrasing was borrowed from the CRDT literature because it was nearby, and the borrowing hid a distinction. It is a coincidence, at least in the third law, and reusing §9's laws here quietly forbids resolutions that are correct.

Idempotence exists to make redelivery harmless. That is its entire job. In §9's evidence merge, a replica cannot know how many times a fact will reach it — once, or twice by two paths, or again after a partition heals — so the merge must not be able to tell, and x ⊔ x = x is what guarantees it. Driver-set resolution has no such problem. The set of drivers reaching a terminal is fixed by the topology, the topology is in the sealed artifact, and §D9 makes even a changed topology a deterministic function of the ledger prefix. The multiplicity is known exactly. A law that pays for an unknown you do not have is not free — it is paid for in expressiveness.

The cost of the borrowed law is concrete rather than theoretical. An idempotent resolution cannot distinguish one driver from two — that is precisely what x ⊔ x = x says. So it can never express "two enabled drivers on this bus is a fault", which is an ordinary thing a hardware profile wants to say and a real bus-contention rule.

Dropping idempotence was the right correction and not the whole of it. The first version of this section kept associativity and commutativity, on the grounds that they let the canonical order "be chosen without changing the answer" — and that sentence refutes itself, because §11 already chose the order. If the input is one normatively-ordered map, there is nothing left for those two laws to protect either. They were retained by the same reflex that supplied idempotence: the laws came as a set, so they were kept as a set. Below they are separated by what each one actually buys.

ContractInputRequired propertiesWhat it buys
Canonical-map resolverone map, source terminal → drive value, in §11's existing terminal orderpure, total, deterministic, plus a declared value for the empty mapthe semantic contract. Sufficient for a defined, replayable result. Admits positional resolutions — "the strongest driver wins, ties are a fault" — that no fold expresses
Parallel-fold resolverthe same map, folded in any order, partitioned, or resolved incrementallyassociative, commutative, total, plus a declared value for the empty mapan implementation permit, not a meaning. A profile pays these two laws to buy the freedom to split the driver set across workers or reuse a partial result
Join resolvervalues arriving in unknown order and unknown multiplicityassociative, commutative, idempotent, with an identityredelivery safety. This is §9's evidence merge and §D9's grow-only fact set, and it is frozen

A profile declares which one it is writing — resolver_kind = canonical_map or parallel_fold — and that declaration is checkable against a finite table by §D6.1's validators rather than trusted. The two are not ranked: a parallel_fold is the stronger claim and the weaker expression, and a profile that will never partition a bus of four drivers has no reason to make it.

Every resolution table must answer the empty driver set. A binary operation does not define the value of a terminal with no enabled drivers, and this is not an edge case — it is a floating bus, which is the normal state of a tri-state line between transactions. std_logic answers 'Z'; another profile may answer absence, and another may answer that it is a fault. All three are legitimate and they are not interchangeable, so the value is a required declaration on the table, not a default the runtime picks. A table that omits it is rejected at profile-load, not at the first quiet period.
Nothing already true stops being true. std_logic resolution [V] is idempotent — resolve('1','1') = '1' — and remains a perfectly good member of the wider class, and it satisfies the parallel_fold contract as well. The change is that these become properties a particular table may have and may trade on, rather than admission requirements every table must satisfy. And because §D6.1 makes these tables finite and enumerated, whether a given one is associative, commutative, total or idempotent is decided by counting over the type, not by proof — so a profile claiming parallel_fold can be held to it mechanically, which is what makes the split worth having rather than merely tidy.

§D3Route axes are not route texturesdraft

The four frozen textures (§5) are guarantee classes: -- settles this period, ~~ does not, == requires evidence, !! carries failure. That is a small closed set, which is why glyphs work for it.

A recurring proposal is to add textures for domains — a road texture, a radio texture, a money texture. Draft rule D3 refuses this, on the following grounds.

ROS 2 [R] settles a similar question for message transport by exposing seven independent policies — reliability, durability, history, depth, deadline, lifespan, liveliness — any combination of which is meaningful. The general shape of a connection has at least these orthogonal axes:

AxisRange
Domainspatial, informational, authority, energy, economic, evidential …
Directionalitydirected · bidirectional · acausal
Cardinality1:1 · 1:n · n:1 · n:m
Timingsettles this period · settles later · continuous
Capacity & costunbounded · metered · priced
Reliabilityguaranteed · lossy · adversarial
Geometryabstract · metric · embedded in a coordinate frame
Schemauntyped · typed payload · verified payload

Eight axes of two to six values each is thousands of combinations. No glyph vocabulary survives that, and the attempt would either explode the textures or — worse — quietly overload them, so that -- means "synchronous" on one page and "physical" on another.

Draft rule D3. Textures stay four and stay timing-and-evidence only. Everything else about a connection is carried by attributes on the relation, validated against the world's profile (§D6). A connection is therefore a named relation with attributes, of which the texture selects only the settlement discipline.

The corollary is that attributed relations must ship before any domain profile does. A mobility profile without them would have to encode capacity and cost in identifiers, which is exactly the class of mistake §15 exists to prevent.

§D4Walls are where the model of computation changesdraft

§D2 ended with a problem: acausal connections need a solver, not a settle pass, and §8's period cycle is a settle pass. Introducing a solver into the period cycle would change the execution model of every world, including the ones that do not want it.

Ptolemy II [P] answers exactly this question. In Ptolemy a director implements a model of computation, and an opaque composite actor carries its own local director which need not match the director of the model containing it. Hierarchy is therefore the mechanism by which one model can contain a differently-scheduled model — discrete event outside, continuous time inside — without either one contaminating the other.

Draft rule D4. WRL already has that construct, and it is the wall (§10). A wall may enclose a sub-world evaluated under a different profile, and the wall is the only place where the model of computation may change. Within a wall, one discipline; across a wall, a declared boundary with declared effects.

This is the reason Part II proposes no new construct kind. The ten kinds of §1 already include identity, relation, boundary, and history. Multilayer topology is relations with attributes; heterogeneous execution is boundaries with profiles. A language that needed an eleventh kind for every domain it modelled would not be a topology language — it would be a collection of DSLs sharing a parser.

Consequence for §10. The boundary-safety law ("undeclared effects cannot cross walls", §12) becomes strictly more load-bearing: it is what stops a solver inside a wall from writing cells outside it. If walls are to carry profiles, boundary safety has to be enforced per profile pair, not globally — an open problem, not a solved one.

§D5Derives seal the answer, not the searchdraft

A topology language that describes a road network will be asked to route vehicles on it, and routing is an optimisation problem. WRL should not contain an optimiser. §13 already lists derives as Proposed; §D5 constrains what a derive is allowed to promise when it wraps one.

The two obvious external engines behave differently, and the difference is the rule:

EngineDeterminism
SUMO [S]deterministic by default; the RNG is seeded (default 23423) and the seed is an input. Same inputs → same run.
OR-Tools routing [O]not reliably reproducible: solution quality varies run to run, and the order in which variables and constraints are created can change the result.

Draft rule D5. A derive over an external engine seals its inputs, its solver profile, and its result. It does not seal the search. The sealed result must be accompanied by a deterministic checker that re-validates it against the world it came from. The solver is an untrusted oracle; the checker is the trusted part, and only the checker's verdict enters the artifact.

A sealed plan may record feasible. It may record an objective value. It may never record optimal. Optimality is a claim about the search, and by construction the search is not sealed. A film that asserted optimality would be asserting something no replay could check — which is precisely the class of claim §12's replay-exactness law exists to exclude.

This is why the rule is stated here rather than left to the derive design: it determines what a derive is. A derive whose output cannot be checked more cheaply than it was produced is not admissible under D5, and several optimisation problems have no such checker. Those problems stay outside the language.

§D6Profiles carry domains, not construct kindsdraft

Every world already opens with a profile line (§4), and today exactly one profile exists. The draft's answer to "how does WRL model drones, or circuits, or supply chains" is: a profile, and nothing else.

A profile fixes the vocabulary that the frozen kinds are instantiated with: which roles exist, which relation names are legal, which attributes each relation carries and what they mean, which resolution rules are available, and which model of computation applies inside a wall that declares it. §D6.1 narrows the third of those from functions to finite tables, for a reason that turns out to matter to the whole promotion order.

The sketch below is what a mobility profile might look like. It is not valid WRL, and the conformance suite asserts that it is rejected:

profile wrl.mobility.v1

; one identity, present in five layers by construction (D1)
[drone:d7](battery=82, payload=4){pose, telemetry, command}
[segment:a12](capacity=3){occupancy}
[station:west](range=900){uplink}
[cell:c3](watts=400){supply}
[policy:safety](mode=halt){fault_in}

; road layer  — attributed relation, not a new texture (D3)
[d7] --occupies(cost=1)--> [a12]
; comms layer — asynchronous, does not settle this period
[d7] ~~telemetry~~> [west]
; energy layer — acausal: no writer, flows sum to zero (D2)
[c3] --power--> [d7]
; fault layer  — requires a supervision floor first (§16 step 6)
[d7] !!fault!!> [safety]

Every line of that sketch is currently a diagnostic. The profile name is unknown, the five roles are unknown, ~~ and !! are unpromoted, and relation attributes have no grammar. Reading it as a promise would be a mistake; reading it as a specification of what §16 has to deliver, in order, is the intent.

The ordering hazard. A domain profile is the last thing to build, not the first. It depends on typed ports (§D2), ~~ (§16 step 2), attributed relations (§D3), == (§16 step 3), and a supervision floor (§16 step 6). Building the profile first would force each of those decisions to be made implicitly, in a hurry, to satisfy a demo — which is the same failure §16.2 already refuses for [worker:w1].

The profile mechanism cannot wait for expressions

The paragraph above lists what a profile declares, and one item in it is a problem: which resolution functions are available. A function needs a notation to be written in, and the expression notation is ladder step 5, while the profile mechanism is step 3. Stated plainly, that is a cycle in a document whose central claim is that its promotion order is a dependency order. Either step 3 secretly needs step 5 — in which case the ladder is wrong — or step 3 needs less than it says.

Draft rule D6.1 — the profile bootstrap kernel. Step 3 delivers ProfileSchemaV1, a restricted, purely declarative profile language containing no user-written expressions and no user-written functions. It admits exactly:

1. primitive and enumerated types; 2. unit declarations and the dimensional relations between them; 3. relation signatures — domain, kind, orientation, endpoint arity, admissible roles; 4. endpoint-role constraints (which object roles may occupy which endpoint role of which relation kind); 5. bounded-resource annotations naming an attribute as a capacity subject to §D9's escrow class; 6. finite resolution tables over enumerated types — enumerated, not computed; 7. a closed set of built-in validators (range, unit compatibility, cardinality, uniqueness, non-negativity), referenced by name and implemented by the toolchain; 8. canonical default values, so that an omitted attribute has one serialisation and not two.

Everything in that list is data. It canonicalises and hashes by §11's existing rules with nothing added, and it is total and terminating by construction rather than by analysis — there is no program in it to analyse.

Why a table is enough, and not a compromise

The instinct is that a table is the weak version of a function, accepted because the strong version is not ready. For the case that actually motivates resolution, it is the other way round. VHDL's [V] std_logic resolution — the canonical example, and the one §D8's sketch is written against — resolves a nine-valued enumerated type, and its resolution is a 9 × 9 table. Hardware engineers did not settle on a table because they lacked a language to write a function in. They settled on it because for a finite type it is the complete, inspectable, obviously-terminating specification, and a function would be a less direct way of denoting the same object.

PropertyFinite tableUser-written function
Canonical bytesthe sorted entries — §11 already hashes exactly this shapeneeds a canonical form for syntax, which is a new and contestable artifact
Totalitychecked by counting: |T|n entries presentundecidable in general; requires a totality checker before any profile can be trusted
Terminationnothing executesrequires the language to be restricted anyway, at which point the restriction is the design
Associativity, commutativityverified exhaustively over the typeproved, assumed, or checked by sampling — and §D8's one-pass settlement depends on it
Reviewable by a domain expertyes — it is the table already in the standardonly by one who also reads the expression notation

The last row of that table is the argument in one line: a resolution rule over a finite type has to be exhaustively checkable, and exhaustive checking over an enumeration is counting. Expressions do not make this easier; they make it a proof obligation instead of a loop.

What the kernel cannot do, said before someone finds out

A restriction that is only advertised by its benefits is a sales pitch. The kernel genuinely cannot express:

Each of those arrives as a strictly additive extension once expressions exist, because the kernel's obligations are the ones an extension must preserve rather than assumptions it may violate: a computed attribute still has to canonicalise, a conditional constraint still has to be total, and a real-valued resolution still has to be replayable. Building the declarative floor first is what makes those requirements explicit before there is a notation tempting enough to skip them.

What this does to the ladder, and to a falsification condition. Step 3 is now deliverable at step 3: nothing in ProfileSchemaV1 needs a step-5 notation, so §D8's attributed relations at step 4 have the type system they require and the cycle is gone. It also sharpens condition 4resolution functions cannot be expressed in the document and hashed with it — from an open worry into a split verdict: for finite types it is answered, because a table is data and §11 already hashes data. For unbounded types it stands, and it is now precisely located at the solver wall rather than distributed over the whole idea of resolution.

§D7Falsification conditionsdraft

A design draft that cannot be wrong is not a design draft. Part II is abandoned or substantially rewritten if any of the following turns out to be true:

This table is the normative statement of the conditions. Direction expands five of the eight at argument length and is a non-normative summary of them; where the two differ, this table governs. Each row carries a stable id="falsify-…" so that expansion can cite it, and the conformance suite requires every citing heading to link here and refuses any condition published there that is not declared in this table.
#ConditionWhat it would falsify
1Real domains require per-layer identity — the same entity genuinely needing distinct, independently-connected existence in two layers — often enough that "three identities and a wall" is unworkable§D1. Node-alignment would have to be relaxed to replica-based layers, and identity would stop being structural
2The results worth deriving have no checker cheaper than the search§D5. Derives would either seal unverifiable claims or become useless, and optimisation stays permanently outside the language
3Profiles need a central registry to interoperate — that is, two independently-written profiles cannot be composed without an authority arbitrating names§D6. The "profiles, not construct kinds" answer would collapse into a governance problem, which a language cannot solve
4Resolved-port resolution functions cannot be expressed in the document and hashed with it. Now split: §D6.1 answers it for finite enumerated types, where resolution is a table and §11 already hashes tables. It stands for unbounded types, and is located at the solver wall§D2. Resolved ports would break §11 content identity and must not ship
5Per-profile boundary safety across walls proves undecidable in practice§D4. Heterogeneous execution would have to be dropped, leaving acausal connections nowhere to live
6A broadened relation cannot serialise a plain directed route to today's exact bytes, so generalising the entity moves every published sem- id§D8. Relation would become a 0.2 breaking change rather than an 0.1.x extension, and the ordering of the entire ladder would change
7Admission decisions cannot be made from a sealed rule and recorded evidence alone — real federation turns out to require consulting live external state that no film can replay§D9. The admission answer collapses back into the dilemma it replaced, and a world has to choose between a closed roster and a replayable film
8The invariants worth enforcing on a changing topology cannot be escrowed — every useful bound needs global coordination at the moment it is checked, not an allowance divided in advance§D9. Topology change would be safe only in a single-writer deployment, and the replication story would quietly be a single-node story
Conditions 6, 7 and 8 were added with §D8 and §D9, which are drafted below and which each state their own remaining obligations in place. Conditions 7 and 8 replaced an earlier, weaker entry which said that unforeseen participants might put sealed identity and open topology in genuine conflict. They do not: §D9 seals the admission rule rather than the roster. What is genuinely uncertain is narrower and is stated above — whether admission can be decided from evidence, and whether bounded resources can be divided in advance. Every entry in this table is a way for the argument to be wrong that its author can recognise; the list is not claimed to be complete.

Conditions 1 and 2 are the load-bearing ones. Both are answerable by building the mobility profile for real, which is why the first genuine test of Part II is a single fleet described across road, communications, dispatch, energy and fault layers, run on TRVM, with routing supplied by an external solver and checked on the way back in.

§D8Relation, not Edgedraft

Sections §D2 and §D3 have been quietly accumulating a debt, and this section pays it. The authored design draft (draft §19) closes the runtime ontology at exactly six entity types — Actor, Value, Edge, Boundary, Fragment, Stencil — and gives the third of them this shape:

Edge { source, destination, label, texture, payload, policy, provenance }

That is a binary directed pair. Every proposal in Part II so far needs something it cannot hold:

Needed byWhat it requiresWhy Edge cannot carry it
§D3 attributescapacity, cost, units, geometry on the connection itselfEdge has payload (what crossed) and policy (how it crosses), but no field for what the connection is
§D2 resolved portsmany drivers meeting at one terminal and settling by a named functionn separate edges into one destination is exactly the multiple-writer error §0 forbids; the terminal, not the edge, is the object with the resolution rule
§D2 acausal connectionsthree or more participants, no source and no destination, potentials equal and flows summing to zeroa source/destination pair asserts a direction that physically does not exist
§D1 multilayeran occupancy relation between one drone and one segment that means something different from a messagelabel is a name, not a typed domain

The three ways out, and the one taken

OptionVerdict
ABroaden the entity. Rename Edge to Relation and generalise its shape. A directed binary route becomes the simplest specialisation of it.taken
BAdd a seventh entity type alongside Edge.rejected. §19's claim is "not five, not seven". An ontology that grows a member per unmet need is not an ontology, it is a changelog. Worse, two entities for one idea forces every later rule — canonicalisation, film schema, wall crossing — to be written twice
CEncode nets as actors: a bus becomes an actor with n ports and a behaviour that resolves.rejected. It works, and that is the problem — it works by making the connection into a participant, so a description of a circuit stops looking like a circuit. It also puts a solver inside an actor, where §D4 has no way to see it

Draft rule D8. The runtime ontology keeps six entity types. The third is renamed and broadened:

RelationAllocation { world_id, issuer, nonce }

RelationRevision   { domain, kind, endpoints, orientation,
                     texture?, attributes, policy, provenance }

Endpoint { terminal, role }

An allocation names the relation; a revision is its value. The split is the subject of §D8 identity below, and the revision is what replaces the old Edge. There is no stored record joining the two: the current revision of a relation is derived from the ledger, for the same reason cardinality is derived from the endpoints. texture is written texture? because it is genuinely optional — the table's own row says it is absent for an acausal relation, and a record that prints a field it does not always carry is a record that will be implemented wrongly.

FieldRangeReplaces
domaina profile-declared namespace — signal, digital, mobility, electrical (§D6)— (new)
kindthe predicate within that domain — SignalWire, drive, occupieslabel, promoted from a bare name to a domain-scoped kind
endpointsan unordered set of Endpoint, size ≥ 2source, destination
orientationdirected · symmetric · acausal— (was implicit and always directed)
texturethe four frozen textures (§5). Required for directed; profile-defined for symmetric; absent for acausal, where a solver wall governs settlement instead (§D4)texture
attributestyped, unit-bearing values validated by the profile— (new; this is §D3's requirement)
policyunchangedpolicy
provenanceunchangedprovenance

payload leaves the relation deliberately. What crossed a connection during a period is an event, and events already live in the event ledger and the film (§10). A relation is the standing fact that a crossing is possible; the payload is the record that one happened. Merging the two is what made Edge simultaneously a topology object and a message envelope.

Two fields, because a name is not a namespace

An earlier draft of this section carried domain alone and let it absorb the old label. That was wrong, and the sketch below shows why in its own syntax: a relation written --drive(domain=digital)--> is carrying two ideas, not one. digital is the namespace that says which profile validates the attributes and what the units mean; drive is the predicate that says what this particular relation asserts. A road relation needs both in the same way — domain=mobility, kind=occupies — and they are not interchangeable. Collapsing them means capacity on a road and capacity on a bus are the same word validated by nothing.

The surface spelling follows the split: a qualified relation is --digital.drive-->, and today's --sig--> is the unqualified form, resolved against the profile's default domain to signal.SignalWire. That is how the frozen syntax survives the generalisation unchanged.

Endpoints carry roles, because a hyperedge without them is ambiguous

A set of participants plus a 2:3 label does not say which two of the five are the two. This is a solved problem: a directed hyperedge in the standard formulation [H] is an ordered pair of disjoint vertex sets — a tail and a head — and the role-aware line of work on n-ary knowledge representation exists precisely because extending binary methods to n-ary relations without recording each entity's role loses information that cannot be recovered. So the role goes on the endpoint, where it is unambiguous, rather than being inferred from a position in a list.

RoleAdmitted byMeaning
sourcedirecteda member of the tail: it writes
targetdirecteda member of the head: it reads
peersymmetricneither side distinguished; the profile says what the pairing obliges
terminalacausala member of one connection set — potentials equal, flows sum to zero

Four roles, and two that were considered and left out. There is no driver role: a driver is a source whose target terminal happens to declare a resolution function, and giving it a role of its own would let the relation and the terminal disagree about whether resolution is in force. There is no resolved role or orientation either, for the same reason and with a precedent behind it — VHDL declares resolution on the resolved subtype of the signal, never on the drivers [V]. Resolution is a property of the terminal being written to. Exactly one object owns that rule.

Endpoint order is presentation, not meaning

endpoints is a set, and this is load-bearing rather than pedantic. The acausal connection in the sketch below joins n1.pin, vcc.pin and r1.pin. There are six ways to write those three terminals down and they are the same circuit. If endpoint order were semantic, two authors describing one physical net would seal two different sem- ids, and the identity would be measuring typing habits instead of topology.

So canonicalisation sorts endpoints by role, then terminal, with roles in the declared enumeration order above. This is the same discipline §11 already applies to sorted-key JSON and to omitted empty port groups — the canonical form decides an order so that the author does not have to have one.

For that sort key to be total, terminals have to be unique within a relation. An earlier draft said "at most once per role", which is the weaker rule and lets a contradiction through:

RuleAdmitsVerdict
once per role{(x, source), (x, target)}the same terminal is simultaneously in the tail and the head
once per relationrejectedcorrect

The hypergraph precedent [H] settles it: a hyperarc is an ordered pair of disjoint vertex sets. Tail and head do not overlap, so the rule is:

Normative. A terminal appears at most once in a relation, regardless of role. For a directed relation this makes the source and target terminal sets disjoint by construction, matching the hyperarc definition the roles were taken from — and it makes the canonical sort key total, since role alone can no longer be the tiebreaker.

This does not outlaw an identity relating to itself, which is an ordinary thing an actor does. A self-loop uses different terminals on the same objecta.out as source and a.in as target — and remains legal, because those are two terminals. What is now impossible is one terminal holding two contradictory positions in a single relation. If a profile has a genuine use for a reflexive terminal, it should declare that as an explicit feature with its own meaning, rather than receive it as an accident of a rule about uniqueness.

A relation needs a name that survives its value

Everything above describes what a relation is. It does not say how to refer to one, and until D9 that omission was invisible, because a fixed topology never needs to name a relation twice. The moment relations can be attached, revised and retired at runtime, the omission becomes the load-bearing defect in the pair of sections.

The tempting answer is the one the rest of this language already uses: address a relation by the hash of its content, as §11 addresses a world. Applied here it fails twice, and both failures are ordinary rather than exotic.

FailureCaseWhat breaks
Distinct relations collidetwo physical cables between the same pair of terminals; a redundant link deliberately provisioned as a second path; two lanes of one road segment carrying the same mobility.occupies kindcontent-identical relations are the same relation under a content address, so the model cannot say there are two, cannot fail one and keep the other, and cannot report which one a fault was on. §D8 claims WRL supports multigraph relations; a content address quietly withdraws the claim
Revision destroys the nameoperation 5, replace relation attributes — a road segment's capacity drops from 3 to 2 after a lane closurethe attributes are the content the address was computed over, so changing them changes the name. The ledger fact then cannot say "this relation was revised"; it can only say one relation vanished and an unrelated one appeared, which is exactly the history a replay needs to not lose

The second failure is the sharper one, because it is self-inflicted: nothing external went wrong, an authorised operation simply mutated the bytes being used as the identity. That is a category error, not a bug, and it has a standard resolution — separate the thing that persists from the thing that changes.

Draft rule D8.1 — relation identity. A relation has a stable relation_id, minted once from an allocation and never recomputed, and a succession of content-addressed RelationRevisions. The relation_id is the lifecycle handle: it is what a ledger fact, a diagnostic, a fault report and a film row name. The revision id is the value: it is what canonicalisation hashes and what a merge compares. Two parallel cables have two relation_ids and may share one revision id, and that is the correct description of two identical cables.

Where the name comes from, without a cycle

The first draft of this rule left the minting mechanism open and offered two options, one of which was "the creating event's canonical id". That option is not viable, and the reason is worth keeping rather than deleting, because it is a cycle that reads as a reasonable sentence:

relation_id      = H(attach_event)
attach_event_id  = H(operation, arguments…)     ; arguments include relation_id

Canonical event identity is a content address over the operation and its arguments (§D9), and the id being minted is one of those arguments. Neither hash can be computed first. An identity scheme needs an allocation source that is prior to the event, not derived from it:

RelationAllocation { world_id, issuer, nonce }

relation_id = H("WRL_RELATION" ‖ world_id ‖ issuer ‖ nonce)

RelationAttached { allocation, revision_id }

The cycle is broken because relation_id is a function of the allocation, and the allocation is an ordinary argument of the event. The event id may still be taken over all its arguments with no exception carved out, and the derived id adds no information to that preimage because it is recoverable from one of them. The alternative — defining the event id over a preimage that deliberately excludes the derived relation_id — is also sound, but it makes the sentence "event identity hashes the operation and its arguments" false, and a rule with one silent exception is a rule that will acquire a second.

RelationAttached carries the allocation, not both ids. Carrying relation_id alongside the allocation it is computed from would put one fact in two places — the defect §D8 rejects for cardinality and again below for revision history. A validator computes the id from the allocation; there is nothing to disagree with.

The nonce does not need to be random, and must not be: a random or clock-derived nonce is not replayable, and replay is the one property this language actually has today. It needs only to be unique within an issuer, and the two cases have different natural answers:

Relationissuernonce
Present at period 0 — authored in the sealed worldthe world itselfthe authored relation name, where the surface provides one. Where it does not, the triple (kind, src, dst) — legal only where that triple is unique, which is the same precondition the V1 projection already depends on. One condition, stated once, doing both jobs
Attached at runtime — §D9 operation 3the proposing claimant identitya claimant-owned monotonic sequence. Uniqueness is then local and needs no coordination, which matters because §D9 operation 3 must remain proposable under partition

Other issuers are admissible under the same shape and are named here so the mechanism is not read as narrower than it is: a deterministic spawn path, a capability-issued lease id, or a parent identity plus a local allocation index. What is not admissible is any nonce a replay cannot reproduce.

This is not a new mechanism invented for relations. It is the mechanism every other entity in the ontology already has, and writing the four out together shows that the third one was the anomaly:

EntityStable identityReplaceable value
Actorthe object id — durable across every config change, which is what "durable identity" means in §1role config, and under draft §26 the behaviour
Relationrelation_id (this rule — previously missing)RelationRevision
Boundarythe wall idits open/closed state, changed by operation 7
Worldthe sem- id, which §D9 keeps fixed across every topology changethe active configuration at period n

The world row is the argument in miniature. §D9's central move is that the world identity does not move when the world's structure changes, because identity and configuration are different things. D8.1 applies the same reasoning one level down. A rule that is right for the whole is not obviously wrong for a part.

What the ledger facts have to carry

Three of §D9's eight operations act on a relation, and each becomes expressible only once both ids exist:

RelationAttached { allocation, revision_id }
RelationRevised  { relation_id, expected_prior_revision, next_revision }
RelationRetired  { relation_id, expected_prior_revision }

Each still obeys §D9's eight obligations unchanged, and two of them get sharper rather than harder. Canonical event identity is a content address over the operation and its arguments — which now includes a stable relation_id (via its allocation), so two hosts proposing the same revision agree on the event id without having to agree on hashing the attributes first. Conflict rules become statable: two RelationRevised facts in one period naming the same relation_id with different expected_prior_revisions is a detectable conflict, not a race that the smaller hash wins. Without a stable id there is no way to notice that the two facts are about the same relation at all.

One fact, one home — including this one

The first version of this rule wrote history down in three places, and the mistake is instructive because each one looked individually justified: a current_revision field on an instance record (so the active value is cheap to read), a predecessor? field on the revision (so the chain survives a ledger read out of order), and the prior → next pair on the ledger fact (so revision is an edit rather than a coincidence). Three homes for one fact means they can disagree, and nothing said which one wins:

current_revision  = R3
R3.predecessor    = R1
RelationRevised { prior = R2, next = R3 }        ; which of these is true?

This is the defect §D8 rejects one section later for cardinality, committed in the section that rejects it. The rule is the same in both places, so the resolution is the same:

Draft rule D8.2 — one home for lifecycle. A RelationRevision is a pure immutable value with no reference to any other revision. Lifecycle history lives only in the ledger events. The active configuration is derived: current_revision(relation_id, ledger_prefix) is a fold over the prefix, and any materialised RelationInstance is a cache of that fold — derived, non-canonical, and never hashed independently.

Renaming prior_revision_id to expected_prior_revision is part of the same correction rather than cosmetic. The field is not a record of history — the ledger already is that. It is a precondition: the operation asserts what it believed the current value to be, and is rejected if the fold disagrees. That is what makes two concurrent revisions a detectable conflict instead of a last-writer-wins race, and naming it prior invited exactly the redundant back-pointer that produced the three homes.

Dropping predecessor? buys something beyond tidiness, and it is a property this language should want:

A revision id now describes a value, not a path to it. With a predecessor in the preimage, a capacity of 3 reduced to 2 and a capacity of 1 raised to 2 hash differently despite being the same relation in the same state — so two worlds that have genuinely converged cannot be recognised as having converged, and a diff between them reports a difference that is not there. Without it, they share one revision id. History is not lost: it is in the ledger and in the film, which is where §10 already says the record of what occurred belongs. The revision hash goes back to answering the question a content address should answer — what is this? — rather than how did it get here?

What it costs the frozen artifact

Nothing today, and the reason is worth stating precisely rather than assumed. The legacy projection above emits {"kind": K, "src": S, "dst": D} and carries no relation id. That remains correct for the frozen corpus, but only because of a property the corpus happens to have:

The projection's unstated precondition, now stated. The V1 edges encoding can omit relation_id only where the triple (kind, src, dst) is unique within the artifact — because that triple is then a key, and the id is recoverable from position. Every world published on this site satisfies that. A world with two parallel relations of the same kind between the same objects does not, and it is precisely the multigraph case D8 exists to admit. Such a world therefore cannot be encoded in ir_version 1.0 at all; it needs explicit ids under a newer ir_version. This is a real limit on V1, not a defect in D8.1 — and it is better as a stated precondition that a validator can check than as an assumption that holds until the first redundant link.

The uniqueness precondition above is doing double duty, which is the reason to notice it: it is also the period-0 nonce rule. A world whose relations are distinguished by (kind, src, dst) can both be encoded in ir_version 1.0 and have its relation ids minted without any new authored syntax, and a world that needs parallel relations fails both at once. That is one condition to check and one condition to explain, rather than two that happen to coincide until they do not.

Cardinality is derived, and therefore never stored

A stored cardinality field can disagree with the endpoints it describes: a record may claim 1:n while holding two sources. That is a second source of truth, and a validator that has to reconcile two sources of truth for one fact will eventually pick the wrong one. Cardinality is computed during validation and canonicalisation:

cardinality(R) = |{e ∈ R.endpoints : e.role = source}|
               : |{e ∈ R.endpoints : e.role = target}|     for directed
               = n-ary                                     otherwise

It is a derived view for diagnostics and profile constraints ("a mobility.occupies relation is 1:1"), never a field, and so never serialised and never part of the hash. This is the same judgement that removed payload: one fact, one home.

Today's route is a specialisation, exactly

The frozen directed route of §0 and §5 is recovered by fixing five of the eight fields:

FieldValue for a frozen route
domainsignal
kindSignalWire or SocketControl — the two frozen edge kinds are the two kinds of the signal domain
endpoints{(writer.sig_out, source), (reader.sig_in, target)} — size exactly 2, one of each role
orientationdirected
attributesempty
derived cardinality1:1 — computed, not written
The canonical-form obligation, which is the whole cost. §11 says a world's meaning is the hash of its canonical bytes. If broadening the entity changes how a plain --sig--> route serialises, every published sem- id moves, including sem-8ae91fe9…fe4a and every id printed on this site. So the broadening ships only with a canonicalisation rule that elides every defaulted field: a relation in the specialisation above must serialise to byte-identical output with today's edge encoding. Empty attributes must be omitted, not emitted as {} — the same discipline §11 already applies to empty port groups. If that cannot be arranged, D8 is a 0.2 breaking change and not an 0.1.x extension.

Two of the corrections above make this easier rather than harder, which is worth saying plainly. Cardinality is derived, so there is no field to elide. Endpoints sort by role then terminal, and source precedes target in the enumeration — so a two-endpoint directed relation orders its endpoints exactly the way today's src, dst pair is already ordered.

Elision is necessary and not sufficient

An earlier version of the note above claimed that eliding defaulted fields was enough to preserve every published id. That is wrong, and the gap is not a detail. Here is what a route serialises to today, from the starter world's own canonical bytes:

"edges": [{"dst": "r0", "kind": "SignalWire", "src": "p0"}]

Now remove every defaulted field from the general record — domain, orientation, default texture, empty attributes, policy, provenance, and the derived cardinality that was never a field. Three differences survive elision, because none of them is a field that could be elided:

Frozen artifactGeneral recordWhy elision cannot bridge it
container "edges""relations"a key is renamed, not omitted
"src" / "dst""endpoints"two scalar members become one array of records; that is a change of shape
"p0"p0.sig_outthe frozen form names the object; an endpoint names a terminal. Nothing is defaulted here — the general form is strictly more specific

So the compatibility mechanism is not elision but a legacy projection: a normative rule that a relation meeting the frozen profile's exact shape is encoded in the old form. Stated so it can be implemented and tested:

Normative — the ForgeSemanticArtifactV1 edge projection. When a relation has domain = signal; kind ∈ {SignalWire, SocketControl}; orientation = directed; exactly one source endpoint and one target endpoint; both terminals being the ports the kind implies for their objects' roles; and no attributes, policy or non-default relation metadata — then the canonical encoder MUST emit it into the edges array as {"kind": K, "src": S, "dst": D}, where S and D are the object ids of the source and target terminals, and MUST NOT emit a relations key at all. Any relation failing any clause needs either a relations encoding under a new ir_version, or a fully specified extension field that provably cannot appear in an artifact of the frozen shape.

The clause about implied ports is what makes the projection lossless in both directions: it is exactly the condition under which p0.sig_out is recoverable from p0, because the kind and the role determine the terminal. Where that is not true, the old form genuinely cannot represent the relation, and pretending otherwise would be the actual breaking change.

This is a testable claim rather than a promise, and it should be tested before D8 is called structurally closed. The golden test is a round trip with byte equality at the end:

frozen Edge      → canonical bytes  ─┐
                                     ├─ MUST be byte-identical
equivalent Relation → projection ────┘

and every published sem- id, re-sealed, is unchanged

Until that test exists and passes over the full pinned corpus, the correct status for exact compatibility is argued, not demonstrated — which is what falsification condition 4 already says, and this section narrows it from "can defaults be elided" to the sharper question of whether the projection above is total over the frozen corpus.

Why this shape and not another

PrecedentWhat it establishes
Bigraphs [B]A bigraph is a place graph (nesting) and a link graph (connectivity) over the same node set — which is §D1's node-alignment, arrived at independently — and the link graph is a hypergraph: a link may join any number of ports. Multi-participant connection is the general case in the best-studied formalism for exactly this problem, not an exotic addition to it
GQL (ISO/IEC 39075:2024) [G]The first new ISO database language since SQL standardises the property graph as a directed, labelled, attributed multigraph in which edges carry properties too. Attributes on the connection are settled practice with a standard behind them
Directed hypergraphs [H]A hyperarc is an ordered pair of disjoint vertex sets, a tail and a head. That is the settled formulation of a many-to-many directed connection, and it is where source and target come from: they are tail membership and head membership, recorded per endpoint instead of by position
Modelica [M]A connection of c1, c2, c3 generates c1.p = c2.p = c3.p and 0 = c1.f + c2.f + c3.f. Three participants, no direction, and no order — the generated equations are symmetric in the connectors, which is the direct precedent for endpoints being a set
VHDL [V]A resolution function takes a one-dimensional unconstrained array of all driver values and returns one value, and it is declared on the resolved subtype of the signal — not on any driver and not on any connection. That is why resolution is a property of the terminal here, and neither a role nor an orientation

What it would look like

Not valid WRL. The conformance suite asserts the toolchain does not accept it, and records which capabilities it waits on:

profile wrl.electrical.v1

; the RESOLUTION is declared on the terminal, not on the
; relations that reach it — one object owns the rule (VHDL)
[bus:b0](width=8){drive: resolved(std_logic)}
[driver:u1](enable=true){out}
[driver:u2](enable=false){out}

; two directed relations into ONE resolved terminal.
; domain.kind, not a bare label: `digital` says which profile
; validates it, `drive` says what it asserts.
; under §0 this is a multiple-writer error; under D8 it is
; legal precisely because b0.drive declares a resolution.
[u1] --digital.drive--> [b0]
[u2] --digital.drive--> [b0]

; an acausal relation: three endpoints, all role=terminal,
; no orientation and no texture. potentials equal, flows sum
; to zero (D2). the three may be written in any order —
; endpoints are a SET, so all six spellings seal the same id.
[node:n1]{pin}
[rail:vcc](volts=3.3){pin}
[load:r1](ohms=470){pin}
relation electrical.node acausal {n1.pin, vcc.pin, r1.pin}

What D8 owes before it can ship

  1. A canonical form that leaves every existing sem- id where it is (the note above).
  2. An allocation surface and its validator. The minting rule is now specified (§D8.1), but two things it depends on are not: a way to write an authored relation name in the surface, and a validator that enforces the V1 projection's precondition — that (kind, src, dst) is unique within any artifact encoded as ir_version 1.0. Without that check, the first genuine multigraph world silently seals as something it is not.
  3. An attribute type system. capacity=3 is meaningless without a unit and a range. This is the same requirement as typed ports (§D2), and neither should ship without the other.
  4. Terminal-level validation. Single-writer becomes a property of a terminal rather than a global law, so validation must move with it: an owned terminal with two source endpoints reaching it is still an error, and must still be caught before the seal. A terminal declaring resolved(f) is the one exemption, and the exemption is visible on the terminal.
  5. A well-formedness rule per orientation. Roles and orientation can contradict each other and the checker must say so: directed requires at least one source and at least one target and admits no other role; symmetric admits only peer; acausal admits only terminal and carries no texture. Each violation needs its own code.
  6. A settlement rule per orientation. directed settles by the existing REACT pass — including into a resolved terminal, in one pass, provided the resolution is a pure, total, deterministic function of the canonical driver map with a declared empty value (§D2.1. Algebraic laws are not required: idempotence belongs to §9's join, and associativity and commutativity are priced separately as a parallel_fold opt-in). acausal does not settle by passing values at all and requires a solver wall (§D4). symmetric has no settlement rule yet, which is the honest state of it.
  7. A film schema. A relation's attributes are state, and §10 requires every state variable that determines future behaviour to be serialised.

§D9Dynamic topologydraft

Every world on this site has a fixed topology: the objects and routes are what the document says, for all time. That is what makes sem- a meaning. It is also the single largest gap between Part II's thesis and any real fleet — drones are commissioned and retired, a vehicle joins a road segment and leaves it, a lease expires, a delivery is handed from one operator to another. A topology language that cannot describe the topology changing describes a photograph.

The identity question comes first, because everything else depends on the answer. If a world's topology can change at runtime, what does its sem- id mean? Three answers are possible: the id covers the initial topology only (and says nothing about later states); the id is recomputed on every change (and stops being a stable name for anything); or the id covers the initial topology together with the rules governing change, and each change is an event in the ledger. Draft rule D9 takes the third. It is the only one under which two hosts replaying the same film must reach the same topology, which is the property §10 exists to guarantee.

Draft rule D9. The sealed artifact is the initial configuration plus the authorised operations. A topology change is an operation applied during a period, recorded in the event ledger, and replayed exactly. The world identity does not move; the configuration at period n is a deterministic function of the sealed artifact and the ledger prefix. This is precisely the structure §10's ReplayBundle already has, extended from claim state to structure.

One boundary, not four

An earlier draft of this section gave each operation its own timing rule: creation took effect next period, retirement at the end of the current one after FILM, boundary changes immediately before the next OBSERVE. Every one of those descriptions named the same instant in different words, and four spellings of one rule is four chances for two implementations to read it differently. There is one rule:

The configuration boundary. A topology operation accepted during period n affects the active topology used by period n+1. Nothing takes effect mid-period, and there are no exceptions to qualify.

The reason is §8's REACT phase, which settles a cascade to a fixpoint by deterministic microsteps over a fixed configuration. If the configuration could change during REACT, the fixpoint would depend on microstep order and determinism would be lost at exactly the point §10 promises it. So every operation is proposed during a period, ordered canonically at COMMIT, and observed in the next one.

Nothing is lost by refusing the mid-period exceptions, because the thing they were reaching for is a different mechanism. Urgency is a safety question, not a structural one: a fault can suppress an identity's outputs within the current period through the supervision floor (draft §25) without deleting anything. Structural retirement still becomes visible at the next boundary. Silencing an actor now and removing it from the graph are two operations, and conflating them is what made retirement look like it needed its own timing rule.

The eight operations

Reaction rules in a bigraphical reactive system [B] reconfigure a bigraph locally and may create or destroy nodes. That is the general form; the list below is its closed, checkable specialisation. It is closed on purpose — an open set of topology operations is an open set of ways for two hosts to disagree.

#OperationChangesLedger fact
1create identityadds an object with a role, config and portsIdentityCreated
2retire identitymarks an object non-participating; its history stays addressableIdentityRetired
3attach relationadds a relation over existing terminals (§D8), minting its relation_id from a claimant-owned allocationRelationAttached { allocation, revision_id }
4detach relationends a relation's participation; the id stays addressableRelationRetired { relation_id, expected_prior_revision }
5replace relation attributeschanges capacity, cost, latency … without changing endpoints or the relation_id (§D8.1)RelationRevised { relation_id, expected_prior_revision, next_revision }
6transfer ownershipmoves an owned terminal's single writer from one identity to another, atomically — no period has zero writers or twoOwnershipTransferred
7open / close boundaryadmits or refuses crossings at a wallBoundaryStateChanged
8expire leasethe timed form of 2, 4 or 7, scheduled at attach timethe corresponding fact, with the deadline as its cause
Operations 3–5 depend on D8.1 and cannot be specified without it. An earlier version of this table wrote the three relation facts as bare verbs — RelationAttached, RelationRetired, AttributesReplaced — with no field saying which relation. That reads fine and is unimplementable: under a content address, the third fact changes the very bytes the first two used as a name, and two identical parallel relations cannot be told apart by any of them. §D8.1 supplies the missing stable relation_id, and the facts above are written in terms of it. The verb AttributesReplaced is renamed RelationRevised to match: what happens is a relation gaining a new revision, not a free-floating attribute set being swapped.

The ledger grows; the active view does not

Notice that the fourth column has no verb that means delete. Retiring an identity and detaching a relation append a fact; they never remove one. Nothing is ever physically taken out of the replicated store, and the active topology is a derived view:

active_topology(sealed_artifact, ledger_prefix, period)

This distinction is what lets §9's grow-only evidence model survive a non-monotonic world. The set of facts is grow-only and merges by an associative, commutative, idempotent join, exactly as before. The set of active identities and relations rises and falls, because it is computed from the facts rather than stored alongside them. A film from period 40 can still resolve every identity it names — as §10 demands — because the fact that named them is still there, with a retirement fact after it rather than in place of it.

What every operation must answer

An operation is not specified until all eight of these are answered for it. None of them is optional, and each one is a way for two hosts to diverge:

ObligationThe general rule
Authorisationwhich capability permits it, and which wall grants that capability. An unauthorised operation is a diagnostic, not a silent no-op — a world where a change failed and nothing said so is a world whose film lies
Canonical event identitya content address over (operation, arguments, period, proposer). Two hosts proposing the same change must produce the same event id, or the ledgers differ and replay fails
Timing semanticsthe configuration boundary above, stated normatively rather than by convention. There is one rule and each operation must be shown to obey it, not given its own
Conflict ruleswhat happens when two operations in one period touch the same object. Ordering by canonical event id makes the outcome deterministic; it does not make it right, so incompatible pairs (two transfers of one terminal, attach-and-retire of one identity) must be a diagnostic and not a race that the smaller hash wins
Historical representationa retired identity is not a deleted one. §10 requires that a film from period 40 still resolve every identity it names, so retirement appends a fact and never erases one
Film consequencesthe operation appears in the ledger with enough detail to reconstruct the configuration, and the film records the configuration digest at each period so a diverged replay is detected at the period it diverged, not at the end
Replication behaviourwhich of the five classes below the operation falls into — at the granularity of the relation kind and the invariant it is subject to, not of the verb
Archival behaviourwhen a retired identity's state may be dropped without changing any future observation. Nothing may be collected while a reachable film still requires it — which makes garbage collection a property of the evidence, not of the runtime's convenience

Replication is a property of the invariant, not of the verb

An earlier draft of the row above said attaching a relation is merge-safe; transferring ownership is not. The second half is right and the first half is only right for unconstrained attachment. Two partitions can each attach a perfectly well-formed relation and still produce a union that violates a domain invariant — both consuming the last unit of road capacity, both attaching a writer to a single-writer terminal, both reserving the same charger, one attaching to an identity the other retired, or together exceeding a degree bound. Every one of those merges cleanly as a set of facts and is wrong as a configuration.

ClassExampleCoordination required
Monotone additionattaching an unconstrained observation relationnone — §9's join is sufficient
Addition under a bounded resourcereserving a charger; occupying one of three capacity units on a road segmentan allowance split across replicas in advance — the escrow or bounded-counter construction [E]. Local while the share lasts; coordination only when a replica's share is exhausted
Exclusive transitiontransferring ownership of a terminalconsensus, or a unique lease authority holding the right to transfer
Removal-like transitionretiring an identity; detaching a relationnone for the fact itself — append the tombstone and let the deterministic active-view rules settle the outcome
Scheduled transitiona lease expiringnone, provided the lease and its deadline were accepted before the partition — the expiry is then a function of the clock, and both sides compute it

The escrow class is the interesting one, because it is the case a naive reading treats as needing global coordination and a careless one treats as merge-safe. It is neither. It is coordination arranged in advance: the allowance is divided while the network is healthy, so that the common path is local and only exhaustion is expensive. That construction is the reason capacity=3 in the sketch below is a claim a partitioned world can actually keep, and it is also the reason capacity cannot be an ordinary attribute — a bounded resource has to be declared as one, in the profile, before anything can be escrowed against it.

What it would look like

profile wrl.mobility.v1

[depot:west](bays=4){dispatch}
[segment:a12](capacity=3){occupancy}

; the operations a world authorises are part of what it seals.
; a change not named here cannot happen, however privileged
; the proposer — this is what keeps the id a meaning.
topology {
  allow create of drone by [west].dispatch
  allow attach mobility.occupies by [west].dispatch
       escrow [a12].capacity
  allow transfer occupancy by quorum
  allow retire of drone by [west].dispatch

  ; an ADMISSION rule, not an enumeration: the world does
  ; not know which vehicles exist, and does not need to.
  ; what it seals is the test they must pass.
  admit vehicle
    identity = public_key
    when certificate.root in #federation_ca
     and schema == #vehicle_v1
     and operator.quota_available
}

; a lease schedules its own expiry at attach time, so the
; release is in the ledger before the period that applies it
[d7] --mobility.occupies(cost=1, lease=6)--> [a12]

Sealed admission, not sealed enumeration

An earlier draft of this section ended by conceding that a world admitting genuinely unforeseen participants — another operator's vehicle joining mid-run — might be the point at which sealed identity and open topology genuinely conflict. That concession was too quick, and it mistook one way of closing a world for the only way.

A sealed artifact does not have to enumerate the identities that may exist. It has to fix the rule by which a new one is admitted. The distinction is routine outside this document: a SPIFFE trust domain [SP] is a trust root, and workloads are admitted by attested policy rules against that root rather than by having been listed in advance — which is exactly why two organisations can federate by exchanging bundles instead of exchanging inventories.

So what the seal covers is the admission rule and everything it depends on:

Sealed in the artifactAppears later, in the ledger and the film
the trusted certificate rootsthe concrete certificate a vehicle presented
the identity scheme (what counts as a name)the vehicle's actual public key
the schema new participants must satisfythe instance that satisfied it
the admission checker, by content addressits verdict, and the evidence for it
the quota and resource policythe quota state at the period of admission
the namespace rulesthe allocated name

Two hosts replaying the same film admit the same participants, because they run the same sealed checker over the same recorded evidence. The world identity never moves; what moves is the configuration, which §D9 already established is a function of the artifact and the ledger prefix. Novelty is admitted without being anticipated.

This matters more than it looks, because the interesting worlds are all federated ones: drone fleets crossing operators, public roads carrying anyone's vehicle, a utility interconnecting with a neighbouring utility, androids moving between owners. A language that could only describe a closed roster would have been unable to describe any of them. The remaining hard part is not identity — it is that admission is now a wall, and a wall that admits participants has to be as carefully specified as one that admits messages.

What D9 owes before it can ship

  1. Configuration digests in the film. Without a per-period structural digest there is no way to detect that two replays diverged, and the whole rule is unenforced.
  2. A capability model. "Authorised by" presupposes capabilities, which are drafted (draft §23) and not built.
  3. Attributed relations (§D8). Operation 5 changes attributes that do not yet exist.
  4. A conflict diagnostic set. Every incompatible pair needs a code, in the same family as WRL_CONTROLLER_CONFLICT, which is today's static analogue of exactly this problem.
  5. A declared bounded-resource kind. The escrow class above cannot be applied to an attribute that is merely a number. capacity has to be declared in the profile as a bounded resource, with a total, before an allowance can be split against it — which makes this a §D8 attribute-type-system obligation as much as a §D9 one.
  6. An admission wall. The admit rule above turns participant entry into a boundary crossing, and §D6's wall obligations apply to it in full: what evidence is required, what is recorded, what happens when the checker rejects, and what a partition does while it cannot reach the quota state.

References

  1. [K14] M. Kivelä, A. Arenas, M. Barthelemy, J. P. Gleeson, Y. Moreno, M. A. Porter. Multilayer networks. Journal of Complex Networks 2(3), 203–271, 2014. — node-layer tuples, aspects, intra- vs inter-layer edges, and the node-aligned special case.
  2. [B] R. Milner. The Space and Motion of Communicating Agents. Cambridge University Press, 2009. — a bigraph is a place graph (nesting) and a link graph (connectivity) over one node set; links are hyperedges; bigraphical reactive systems reconfigure structure by rules that may create or destroy nodes.
  3. [G] ISO/IEC 39075:2024, Information technology — Database languages — GQL. — the property graph as a directed, labelled, attributed multigraph in which nodes and edges carry properties.
  4. [H] G. Gallo, G. Longo, S. Pallottino, S. Nguyen. Directed hypergraphs and applications. Discrete Applied Mathematics 42 (2–3), 177–201, 1993. — a hyperarc as an ordered pair of disjoint vertex sets, a tail and a head; the standard formulation behind endpoint roles.
  5. [E] V. Balegas, D. Serra, S. Duarte, C. Ferreira, M. Shapiro, R. Rodrigues, N. Preguiça. Extending Eventually Consistent Cloud Databases for Enforcing Numeric Invariants. SRDS 2015. — the Bounded Counter: escrow splits a numeric allowance across replicas so that decrements commute locally and coordination is needed only when a replica's share is exhausted.
  6. [SP] SPIFFE, Trust Domain and Bundle and Federation specifications. — a trust domain is a trust root; workloads are admitted by attested policy rules rather than by prior enumeration, and domains interoperate by exchanging bundles.
  7. [M] Modelica Language Specification, ch. 9, Connectors and Connections. — potential variables equated, flow variables summed to zero, acausal connection semantics.
  8. [V] IEEE 1164 std_logic_1164: std_ulogic (unresolved) vs std_logic (resolved, with a resolution function).
  9. [P] Ptolemy II / System Design, Modeling, and Simulation using Ptolemy II, ch. on directors and hierarchical heterogeneity. — a director implements a model of computation; an opaque composite's local director need not match its executive director.
  10. [R] ROS 2 documentation, About Quality of Service settings. — reliability, durability, history, depth, deadline, lifespan, liveliness as independent policies.
  11. [S] Eclipse SUMO documentation, Randomness. — deterministic by default; --seed / --random.
  12. [O] Google OR-Tools routing issue reports on reproducibility. — solution quality varies run to run; construction order affects results.

End of WRL Core 0.1.2, Parts I and II. Part I is frozen. Part II is an argument, and is meant to be argued with — see Direction for the case in prose.