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.
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.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.
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:
- The period cycle is OBSERVE→ACCEPT→MAP→COMMIT→REACT→FILM, not Collect→Order→Reduce→Commit→Record.
- "Conflicts dissolve" is narrowed — single ownership stops generic cell races, but claim and control conflicts need explicit policies.
- Canonical ordering is policy-pinned, not a fixed hash formula.
- History splits into WorldFrame / EventLedger / BuildFilm.
- 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:
- Mailbox is
partial, notreserved. 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. *is narrowed. Only the replication-by-position meaning is implemented; wildcard matching remains reserved with no lowering. §4 is corrected.- The document boundary is normative (new §15). A WRL world document and
a scenario are different documents.
periodsand[epoch:N]claims are run inputs and are deliberately outside theSemanticArtifactID. The strict world parser is normative; the combined-document parser is an explicitly-named migration bridge. - 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:
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.
| Kind | Role (frozen) |
|---|---|
| Function | calculates — pure local computation |
| Actor | persists — durable identity + owned state |
| Route | communicates — a directed, textured transition edge |
| Wall (boundary) | authorizes — gate / commit / seal on propagation |
| Period | orders — logical-time step under which events settle |
| Fragment | carries code — quoted graph as movable data |
| Stencil | constructs graphs — parameterized fragment producer |
| Derive | transforms typed graphs |
| Film | proves what occurred — append-only replayable log |
| Hash | identifies 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
- Shape says what a thing is. Bracket shape identifies the kind of object.
- Line texture says how it moves. Route style identifies the operational guarantee.
- The colon introduces; the slash bounds. Both families cap at three strokes, ordered by permanence: transient · committed · sealed.
- Marks for architecture, words for computation. Process notation owns punctuation; expression notation defaults to words.
§3Container / shape-as-kind familyfrozen · 3 members
| Shape | Kind (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
| Symbol | Meaning-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:
| Meaning | Status |
|---|---|
replication-by-position ([relay:r*3]) | implemented — surface sugar, bounded, identity-equivalent (§17) |
| wildcard match | reserved — 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.
| Texture | Guarantee (frozen) |
|---|---|
--x--> solid | deterministic local transition; settles within the period |
~~x~~> async | asynchronous message; appended to a mailbox; observable next period |
==x==> verified | evidence-backed / committed transition under a named policy |
!!x!!> fault | crash / 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:
| Texture | Surface status | See |
|---|---|---|
-- solid | surface-grounded — declarable, emittable, round-trips | §14 |
~~ async | partial — IR/runtime-grounded, not surface-emittable, no structural edge declaration | §14b, §16 |
== verified | partial — acceptance machinery exists; the construct does not | §16.1 |
!! fault | partial — fault state exists; route and supervision do not | §16 |
§6Time familyfrozen
- Logical time is superdense: every event carries
(period t, microstep m). - A period is the settle-and-commit unit; same-period events are ordered by microstep.
- Wall-clock enters only through an explicit boundary and is recorded as a signed fact; wall-clock is never replayable state, logical time is.
Frozen: the period/microstep model and the wall-clock-as-fact discipline. Not frozen: the tick surface syntax details.
§7Memory-kind familyfrozen · 5 members
| Shape | Memory kind (frozen) | Lifetime rule |
|---|---|---|
(state) | volatile cell | mutable by replacement; single-owner |
{facts} | monotonic knowledge | grow-only; merges by lattice union; never retracts |
[archive] | durable store | persistent addressable actor; survives restart |
:: fragment // | code memory | quoted graph as portable data |
#hash | sealed identity | immutable, content-addressed |
{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)
- Single-owner cells — no shared mutable cell.
- 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
| Phase | Definition |
|---|---|
| OBSERVE | canonicalize and insert distinct claims |
| ACCEPT | create missing receipts per the pinned acceptance policy |
| MAP | newly-accepted successful ops → controls |
| COMMIT | apply control writes and fault resets to owned cells |
| REACT | deterministic within-period token cascade to fixpoint; latch current overflow |
| FILM | record 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:
- single-owner cells remove generic write-write races;
- append and fact-union structures merge deterministically;
- but equivocation, conflicting accepted operations, claim retransmission, receipt divergence, configuration ordering, fault-reset-vs-new-overflow, and capacity failure are not dissolved by ownership — each is resolved by an explicit pinned policy.
| Concern | Pinned law |
|---|---|
| recognition | distinct-CandidateKey count |
| acceptance | MIN CandidateKey / first receipt |
| application | canonical event order |
| fault | COMMIT clears, then REACT ORs |
| capacity | atomic 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.
| Artifact | Frozen role |
|---|---|
| WorldFrame | authoritative observable state sufficient for future behavior — the physical world snapshot (Film v0.6 bytes) |
| EventLedger | accepted events, receipts, effects, outcomes — the claim-aware layer (Film v0.7 adds this over v0.6) |
| BuildFilm | compiler / 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.
§10Boundary (wall) familyfrozen · gate / commit / seal
| Boundary | Reduction effect (frozen role) |
|---|---|
/gate | require capability; emit an effect-request node (no ambient I/O) |
//commit | canonicalize the fragment-so-far; assign a content id; freeze it |
///seal | commit, 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
- Meaning is normalized before content addressing. Comments, spacing, alignment, decorative line and route length, and presentation color never affect the semantic hash.
- Identity is content-addressed (
#hash); the same normalized graph yields the same hash on every host. - Builds and expansion are hermetic and host-blind: same source, same canonical bytes.
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.
| Family | Guarantee |
|---|---|
| Formatting invariance | presentation never changes the hash |
| Replay exactness | a live run and its replay share all observables |
| Build invariance | same inputs → identical film + hashes across hosts |
| Merge laws | fact union is commutative / associative / idempotent |
| Boundary safety | undeclared effects cannot cross walls |
| Scheduler invariance | worker ordering never changes the committed film |
| World round-trip 0.1.2 | a format→parse cycle preserves the complete canonical world projection, defined by exclusion (§15) |
| Identity round-trip 0.1.2 | a format→parse cycle preserves the canonical artifact bytes and the SemanticArtifactID |
| Document separation 0.1.2 | a world document excludes run inputs both semantically and lexically (§15) |
§13What Core 0.1 does NOT freeze
Deferred to later frozen revisions:
- Experimental: the entire expression notation (values, functions, types, generics, numerics), effects / capabilities / entropy, the supervision error ladder, streams and backpressure, actor behavior blocks, evolution and migration, podium / ranking, the static-check catalog, execution profiles.
- Proposed: metaprogramming (fragments, stencils, derives, reflection, sealed compiler tools) beyond their kind-role; modules and builds; build films; foreign functions; and all distributed settlement above fact-union.
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
| Family | TRVM grounding |
|---|---|
| The five surface-grounded world roles (Pulser / Relay / Door / Spinner / Orb) — Mailbox is not a sixth | fixture node kinds; lowered by the compiler |
| State + wiring | state schema (counters, wires, rotor-as-state, pose, sticky fault); sig-wire + socket edges |
Deterministic signal route (--) | sig-wire delivery; within-period REACT fixpoint |
| Periods | epoch-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
| Family | Status |
|---|---|
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 behaviors | reserved — 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:
| Term | Means |
|---|---|
| IR/runtime-grounded | a canonical IR declaration exists, the runtime executes it, and it is covered by a battery |
| surface-grounded | additionally, 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.
-- 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.
| Document | Carries | Enters SemanticArtifactID? |
|---|---|---|
| World document | profile, objects, edges, static config, wiring | yes |
| ScenarioV1 | periods, [epoch:N] claims, run inputs | no |
Frozen consequences
- Run inputs are not world content.
periodsand[epoch:N]claims are run inputs. Changing them does not move theSemanticArtifactID; the same world can be run by many scenarios. - 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 0would satisfy the semantic half while still writing scenario syntax into a world document. - 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 point | Accepts | Status |
|---|---|---|
parse_wrl_core(text) | world source only; rejects run inputs with a typed WRL_WORLD_SOURCE_HAS_SCENARIO | normative |
parse_wrl_legacy_document(text) | a pre-boundary combined document | explicit 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.
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.
| # | Step | Gate it must pass |
|---|---|---|
| 1 | L-0 closure | the round-trip and document-boundary laws restated and green — complete |
| 2 | ~~ async route | needs a canonical logical route declaration distinct from the structural edge declaration (§14b) |
| 3 | == verified route | see §16.1 |
| 4 | # references and & composition | — |
| 5 | //commit and ///seal | — |
| 6 | !! fault route | may only follow a minimal supervision floor actually existing |
| 7 | /gate capability gate | last |
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:
- the declaration
==would introduce is authorization structure; - the frozen texture
==denotes an evidence-backed transition.
== 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
| Layer | Carries |
|---|---|
| World document | declares a verified channel and its schema |
| ScenarioV1 | carries the claim instances |
| Receipts | prove 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:
- A sugared spelling and its explicit twin desugar to the same canonical
bytes and therefore seal to the same
SemanticArtifactID. Sugar introduces no identity of its own and no privileged path to the seal. - It is not the claim that editing sugar cannot move an identity.
sp*3→sp*4is a different program and moves the identity, exactly as the explicit spelling would.
Two required properties, gated by battery
- Bounded expansion. A one-to-many form must reject an absurd count with
a typed diagnostic before allocating the expansion.
sp*1000000000is a diagnostic, not an out-of-memory event. - 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.
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.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:
| Arrangement | An entity in n layers is | Consequence |
|---|---|---|
| Replica-based (general case) | n node-layer tuples, tied together by explicit inter-layer coupling edges | identity is a property of the coupling; the couplings can be wrong, missing, or asymmetric |
| Node-aligned proposed for WRL | one node, present in every layer by construction | identity 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.
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:
| Precedent | Rule | Why 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 kind | Admits | Meaning of a connection | Status |
|---|---|---|---|
| Owned | at most one writer | the writer's value becomes the reader's | frozen — this is today's rule, unchanged (§0) |
| Resolved | many drivers plus a named resolution function | all drivers contribute; the function determines the value | draft |
| Acausal | many participants, no drivers | potentials equal, flows sum to zero across the connection | draft |
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.
| Contract | Input | Required properties | What it buys |
|---|---|---|---|
| Canonical-map resolver | one map, source terminal → drive value, in §11's existing terminal order | pure, total, deterministic, plus a declared value for the empty map | the 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 resolver | the same map, folded in any order, partitioned, or resolved incrementally | associative, commutative, total, plus a declared value for the empty map | an 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 resolver | values arriving in unknown order and unknown multiplicity | associative, commutative, idempotent, with an identity | redelivery 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.
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.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:
| Axis | Range |
|---|---|
| Domain | spatial, informational, authority, energy, economic, evidential … |
| Directionality | directed · bidirectional · acausal |
| Cardinality | 1:1 · 1:n · n:1 · n:m |
| Timing | settles this period · settles later · continuous |
| Capacity & cost | unbounded · metered · priced |
| Reliability | guaranteed · lossy · adversarial |
| Geometry | abstract · metric · embedded in a coordinate frame |
| Schema | untyped · 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.
§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.
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:
| Engine | Determinism |
|---|---|
| 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.
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.
~~ (§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.
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.
| Property | Finite table | User-written function |
|---|---|---|
| Canonical bytes | the sorted entries — §11 already hashes exactly this shape | needs a canonical form for syntax, which is a new and contestable artifact |
| Totality | checked by counting: |T|n entries present | undecidable in general; requires a totality checker before any profile can be trusted |
| Termination | nothing executes | requires the language to be restricted anyway, at which point the restriction is the design |
| Associativity, commutativity | verified exhaustively over the type | proved, assumed, or checked by sampling — and §D8's one-pass settlement depends on it |
| Reviewable by a domain expert | yes — it is the table already in the standard | only 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:
- Resolution over an unbounded type. Summing currents on a
real-valued acausal net is not a table. That is correctly outside the kernel and lands with the solver wall (§D4), which is ladder step 9 — a solver was always going to be needed there, and routing it through a user-written resolution function would have put a numeric method inside a profile where §D4 cannot see it. - Derived or computed attributes —
cost = length ÷ speed. These wait for expressions at step 5, and until then an attribute is declared and supplied, never computed. - Conditional constraints — "capacity must exceed 2 when the segment is bidirectional". The built-in validators are unconditional. A profile needing more says so and waits.
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.
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
4 — resolution 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:
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.| # | Condition | What it would falsify |
|---|---|---|
| 1 | Real 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 |
| 2 | The 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 |
| 3 | Profiles 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 |
| 4 | Resolved-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 |
| 5 | Per-profile boundary safety across walls proves undecidable in practice | §D4. Heterogeneous execution would have to be dropped, leaving acausal connections nowhere to live |
| 6 | A 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 |
| 7 | Admission 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 |
| 8 | The 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 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 by | What it requires | Why Edge cannot carry it |
|---|---|---|
| §D3 attributes | capacity, cost, units, geometry on the connection itself | Edge has payload (what crossed) and policy (how it crosses), but no field for what the connection is |
| §D2 resolved ports | many drivers meeting at one terminal and settling by a named function | n 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 connections | three or more participants, no source and no destination, potentials equal and flows summing to zero | a source/destination pair asserts a direction that physically does not exist |
| §D1 multilayer | an occupancy relation between one drone and one segment that means something different from a message | label is a name, not a typed domain |
The three ways out, and the one taken
| Option | Verdict | |
|---|---|---|
| A | Broaden the entity. Rename Edge to Relation and generalise its shape. A directed binary route becomes the simplest specialisation of it. | taken |
| B | Add 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 |
| C | Encode 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.
| Field | Range | Replaces |
|---|---|---|
domain | a profile-declared namespace — signal, digital, mobility, electrical (§D6) | — (new) |
kind | the predicate within that domain — SignalWire, drive, occupies | label, promoted from a bare name to a domain-scoped kind |
endpoints | an unordered set of Endpoint, size ≥ 2 | source, destination |
orientation | directed · symmetric · acausal | — (was implicit and always directed) |
texture | the four frozen textures (§5). Required for directed; profile-defined for symmetric; absent for acausal, where a solver wall governs settlement instead (§D4) | texture |
attributes | typed, unit-bearing values validated by the profile | — (new; this is §D3's requirement) |
policy | unchanged | policy |
provenance | unchanged | provenance |
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.
| Role | Admitted by | Meaning |
|---|---|---|
source | directed | a member of the tail: it writes |
target | directed | a member of the head: it reads |
peer | symmetric | neither side distinguished; the profile says what the pairing obliges |
terminal | acausal | a 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:
| Rule | Admits | Verdict |
|---|---|---|
| once per role | {(x, source), (x, target)} | the same terminal is simultaneously in the tail and the head |
| once per relation | rejected | correct |
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:
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
object — a.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.
| Failure | Case | What breaks |
|---|---|---|
| Distinct relations collide | two 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 kind | content-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 name | operation 5, replace relation attributes — a road segment's capacity drops from 3 to 2 after a lane closure | the 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.
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:
| Relation | issuer | nonce |
|---|---|---|
| Present at period 0 — authored in the sealed world | the world itself | the 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 3 | the proposing claimant identity | a 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:
| Entity | Stable identity | Replaceable value |
|---|---|---|
| Actor | the object id — durable across every config change, which is what "durable identity" means in §1 | role config, and under draft §26 the behaviour |
| Relation | relation_id (this rule — previously missing) | RelationRevision |
| Boundary | the wall id | its open/closed state, changed by operation 7 |
| World | the sem- id, which §D9 keeps fixed across every topology change | the 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:
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:
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:
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:
| Field | Value for a frozen route |
|---|---|
domain | signal |
kind | SignalWire 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 |
orientation | directed |
attributes | empty |
| derived cardinality | 1:1 — computed, not written |
--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 artifact | General record | Why 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_out | the 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:
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
| Precedent | What 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
- A canonical form that leaves every existing
sem-id where it is (the note above). - 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 asir_version 1.0. Without that check, the first genuine multigraph world silently seals as something it is not. - An attribute type system.
capacity=3is meaningless without a unit and a range. This is the same requirement as typed ports (§D2), and neither should ship without the other. - 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
sourceendpoints reaching it is still an error, and must still be caught before the seal. A terminal declaringresolved(f)is the one exemption, and the exemption is visible on the terminal. - A well-formedness rule per orientation. Roles and orientation can contradict each other and the checker must say so:
directedrequires at least onesourceand at least onetargetand admits no other role;symmetricadmits onlypeer;acausaladmits onlyterminaland carries no texture. Each violation needs its own code. - A settlement rule per orientation.
directedsettles 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 aparallel_foldopt-in).acausaldoes not settle by passing values at all and requires a solver wall (§D4).symmetrichas no settlement rule yet, which is the honest state of it. - 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.
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 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.
| # | Operation | Changes | Ledger fact |
|---|---|---|---|
| 1 | create identity | adds an object with a role, config and ports | IdentityCreated |
| 2 | retire identity | marks an object non-participating; its history stays addressable | IdentityRetired |
| 3 | attach relation | adds a relation over existing terminals (§D8), minting its relation_id from a claimant-owned allocation | RelationAttached { allocation, revision_id } |
| 4 | detach relation | ends a relation's participation; the id stays addressable | RelationRetired { relation_id, expected_prior_revision } |
| 5 | replace relation attributes | changes capacity, cost, latency … without changing endpoints or the relation_id (§D8.1) | RelationRevised { relation_id, expected_prior_revision, next_revision } |
| 6 | transfer ownership | moves an owned terminal's single writer from one identity to another, atomically — no period has zero writers or two | OwnershipTransferred |
| 7 | open / close boundary | admits or refuses crossings at a wall | BoundaryStateChanged |
| 8 | expire lease | the timed form of 2, 4 or 7, scheduled at attach time | the corresponding fact, with the deadline as its cause |
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:
| Obligation | The general rule |
|---|---|
| Authorisation | which 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 identity | a 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 semantics | the 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 rules | what 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 representation | a 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 consequences | the 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 behaviour | which 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 behaviour | when 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.
| Class | Example | Coordination required |
|---|---|---|
| Monotone addition | attaching an unconstrained observation relation | none — §9's join is sufficient |
| Addition under a bounded resource | reserving a charger; occupying one of three capacity units on a road segment | an 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 transition | transferring ownership of a terminal | consensus, or a unique lease authority holding the right to transfer |
| Removal-like transition | retiring an identity; detaching a relation | none for the fact itself — append the tombstone and let the deterministic active-view rules settle the outcome |
| Scheduled transition | a lease expiring | none, 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 artifact | Appears later, in the ledger and the film |
|---|---|
| the trusted certificate roots | the concrete certificate a vehicle presented |
| the identity scheme (what counts as a name) | the vehicle's actual public key |
| the schema new participants must satisfy | the instance that satisfied it |
| the admission checker, by content address | its verdict, and the evidence for it |
| the quota and resource policy | the quota state at the period of admission |
| the namespace rules | the 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
- 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.
- A capability model. "Authorised by" presupposes capabilities, which are drafted (draft §23) and not built.
- Attributed relations (§D8). Operation 5 changes attributes that do not yet exist.
- 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. - A declared bounded-resource kind. The escrow class above cannot be applied to an attribute that is merely a number.
capacityhas 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. - An admission wall. The
admitrule 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
- [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.
- [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.
- [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.
- [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.
- [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.
- [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.
- [M] Modelica Language Specification, ch. 9, Connectors and Connections. — potential variables equated, flow variables summed to zero, acausal connection semantics.
- [V] IEEE 1164
std_logic_1164:std_ulogic(unresolved) vsstd_logic(resolved, with a resolution function). - [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.
- [R] ROS 2 documentation, About Quality of Service settings. — reliability, durability, history, depth, deadline, lifespan, liveliness as independent policies.
- [S] Eclipse SUMO documentation, Randomness. —
deterministic by default;
--seed/--random. - [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.