Seal a world.
Type WRL Core world source on
the left. It is desugared, parsed, validated, canonicalized and hashed as you
type — by a JavaScript port of the same spine that runs inside TRVM Forge. The
sem- ids below are real: the pinned
conformance fixture seals here to the exact id every Forge battery folds
against. Nothing is sent anywhere.
Two encodings are read here,
and there is no switch to choose between them. A source that declares
ir 2.0 on the line after its profile is read as
Semantic IR 2.0, where every route carries a name; anything else is read by
the frozen IR 1.0 spine. The source decides — never a dropdown, never the
last button pressed. A world whose id depended on the interface would not
have an id.
self-check: running…
Buttons marked ✗ load a world the toolchain refuses — the red bar and its typed code are the demonstration, not a fault in the page. Unmarked buttons seal. Which is which is asserted by the conformance suite, button by button.
What this page is doing
The reference implementation's canonical bytes are
json.dumps(artifact, sort_keys=True, separators=(",",":")).
Every value in a sealed artifact is an ASCII string, an integer, a boolean, or
a container of those — so a recursive key-sorted serializer reproduces those
bytes exactly, and WebCrypto SHA-256 reproduces the id. That is why this
playground can be honest rather than illustrative.
The serializer is hand-rolled rather than
JSON.stringify for one reason: rotor lanes are
BigInt. At w=64 a
lane may legally reach 264−1, and a JS number cannot hold it —
an authored 9223372036854775807 would serialize as
9223372036854776000 and quietly mint a
different id from the arbitrary-precision reference. Lanes are exact
here at any width. Every other artifact scalar is bounded to ±(253−1)
and refused — never rounded — outside it.
What it deliberately does not do: lower to a backend, compile to interaction-calculus terms, or reduce to a Film. Those live in TRVM and are not a browser's job. This page covers the semantic half of the pipeline — everything up to and including identity.
Where this is stricter than the current Forge parser
Two mouths, one identity function. Every difference below is a rejection, so for every world both accept the canonical bytes are identical — no id can move across the gap. This is the intended Core 0.1.3 tightening, implemented in the browser first because this is the surface an author actually touches.
- The
profileline is required, singular, and first. The Python parser starts with the only profile pre-installed, so an empty document sealed successfully. - Unknown config keys and bare flags are refused
(
WRL_UNKNOWN_CONFIG_KEY), not dropped. - A key set twice is refused
(
WRL_DUPLICATE_CONFIG_KEY), not last-one-wins. - An edge's optional role prefix is a checked assertion
(
WRL_ROLE_PREFIX_MISMATCH), not decoration. - A repeated port name is a typo, not a set operation.
- Integers must be complete literals.
period=2xis a rejection here; a first cut of this port usedparseIntand read it as2, which was looser than Python'sint(). That one is a fidelity fix, not a tightening.
Things worth trying
- Load the starter world, then the pinned fixture. Different ids — they are different worlds, and neither may be shown wearing the other's.
- Load the pinned fixture, then load its explicit twin. Same id. Sugar has no identity of its own.
- Load the shuffled world. Same id. Declaration order is inert.
- Add a comment, or spaces, or a blank line. Same id.
- Change
rotor=quarter_turn_ztorotor=identity. Different id — the meaning moved. - Change the spinner's
n=8ton=4while keepingquarter_turn_z. Different id — an irrational rotor's integer projection depends on the geometry it lands in. - Wire a second pulser into
r0. A typedWRL_CONTROLLER_CONFLICT, with the line. - Delete the
profileline.WRL_MISSING_PROFILE— a world without a stated dialect is not a world. - Load a fault inside an expansion. The duplicate is reported at line 8, the line you typed, not at the generated line the collision actually landed on.
And in IR 2.0 (draft)
- Load a world with named relations. Two ids appear, labelled apart. The upper one is what the world is; the lower one identifies only the V1 bytes it would be run as. Nothing scoped to a world — a grant, a revision, a ledger event — may be scoped to the lower one.
- Delete the
ir 2.0line and leave the rest.WRL_UNSUPPORTED_FEATUREat the route — the V1 spine has never heard of a name prefix. Now delete[clock_feed]:as well. It seals, to a different id than the V2 world above — and that id is exactly the one the second row was showing you. The lower row was never this world's identity; it was the identity of the V1 world you just finished typing out by hand. - Rename
clock_feedto anything else. The world id moves and therel-moves with it — the name is the relation's whole preimage. Therev-does not move: a revision is standalone, and renaming changes what a relation is called, not what it connects. - Change
ir 2.0toir 3.0, or to bareir, or declare it twice. Four distinct refusals, and none of them falls back to V1 — every one of those sources is a perfectly good V1 world, which is exactly why a fallback would be the most convincing wrong answer available: it would seal, print a realsem-, and hand you the id of a world in an encoding you did not write. - Load an unnamed route.
WRL_MISSING_RELATION_NAME— and never a name derived from the two ends, which would make renaming an object silently re-mint the relation. - Watch the correspondence panel. A named world pairs with nothing in the pinned V1 fixture, because the fixture's relations are keyed by their endpoints and this one's are keyed by the names you wrote. That is the answer, not a gap.
Crossing between the two encodings
Two buttons, in the Migration & adoption panel, and they are the only two things on this page that mint an id you did not ask for. Neither runs because you typed.
- Load the pinned fixture and press Import V1 as V2. A new
sem-appears, every relation pairs, and ids preserved says no — the topology did not move an inch, and it is still a different world, because a different byte string is a different world. - Read what the panel says next. It asked the formatter to write the
world it had just made, and printed the answer:
WRL_UNWRITABLE_SEED. A V1 artifact stores{kind, src, dst}per edge and no field a name could have come from, so a migrated relation has never been named — which is a fact about its history, not a blank to be filled in quietly. A migration without adoption is a one-way door out of the language. - Fill in three of the four names and press Adopt these names.
WRL_INCOMPLETE_ADOPTION, and the names you did type are still there. The button is never disabled: a disabled button is a second copy of that rule, living in an interface state where nothing can test it. - Fill in the fourth and adopt. The formatter writes the world into the
editor, the spine above re-seals it, and the panel reports that the text it
produced re-admits to the very world it was written from. Every
rev-recurs and everyrel-is new — including the ones you did not rename. - Now look at the second id. Three world identities later — V1, migrated,
adopted — the V1 execution view is
sem-8ae91fe9…, the pinned fixture you started from, unmoved. What this world is changed three times. What it runs as never changed at all. - Import a world, then edit one character of the source. The pending migration is discarded and says so. It was an operation on a world, and that world is no longer the one in the box.