Imported from ByteTerrace/Puck (
.claude/skills/puck-world/SKILL.md). Install upstream withnpx skills add ByteTerrace/Puck --skill puck-world. Copyright stays with the author.
Puck.World: the game of many games
For multi-body render validation, inhabited placements belong only to the body
stamp census, even when their creation is animated or attached. A simultaneous
animated-placement registration doubles the character. Verify body count and
world.budget together. Bounded flow/cloud media share 64 frame slots; disabled
volumes emit nothing, and the budget readback includes their submitted count.
The Moth courtyard
provides eight held poses and independent sky/cloud switches for repeatable GPU
captures. Density controls live in the World.Authoring README.
world.sdf.dump <path> copies the initialized renderer's live packed program
to little-endian uint32 words, replacing the destination. It excludes capacity
headroom, dynamic transforms and the frame grid; use the current SdfProgram
layout to inspect it. It is a CPU-side diagnostic copy, not a GPU readback or
loadable asset, and leaves simulation and rendering unchanged.
Creation shapes support type: "Prism": an XY profile extruded along Z.
scale gives bottom half-width, half-height and extrusion half-depth; optional
taper gives top/bottom width in [0, 1] (default 0.5). Zero makes a triangle,
one a rectangle. It uses the existing Trapezoid/Extrude VM path in both static
and animated emission and the deterministic contact field. Optional profile
selects RoundedRectangle (cornerRadius fraction), Polygon (3–32 sides),
Ellipse, ChamferedRectangle (cornerRadius fraction, mapped to a 45-degree
chamfer instead of a fillet), or Convex (3–8 clockwise vertices inside the unit square — the frame every profile is scaled from, and the frame the Prism's cull reach covers — cornerRadius
reused as a uniform corner-rounding fraction of the raw profile's own inradius —
the exact iq polygon SDF, refused by name when the vertices are not a
well-formed clockwise convex hull); null uses Trapezoid. Polygon/Ellipse are
renderable but refused by the deterministic field; RoundedRectangle,
ChamferedRectangle, and Convex are supported there. Optional lift (extrude default, revolve)
revolves the profile about local Y with scale.z as the radial offset (zero = a
solid of revolution); a solid placement refuses a revolve. Optional rounding
(Prism, Cylinder) fillets every edge by a world-unit radius, refused by name
past SdfSolidGeometry.MaxRounding — a trapezoid profile's ceiling is bound by
its narrower end, so a Cone (a sharp apex) has no room and refuses any rounding;
a Convex profile's cornerRadius already owns the profile's own rounding, so
this separate field reads a zero ceiling there too. Optional chamfer
(Box, Cylinder, and an extruded Prism only) bevels every edge at 45 degrees by a
world-unit radius instead of filleting it, refused by name past
SdfSolidGeometry.MaxChamfer and refused by name alongside a nonzero rounding
on the same shape; a Box emits as an extruded ChamferedRectangle and a Cylinder
as a revolved one, while a Prism's chamfer bevels its existing profile's cap
rims (RoundedRectangle/Trapezoid/Ellipse profiles only — a Polygon or Convex
profile's lanes are already full and reads a zero ceiling). Other types refuse
taper, profile, lift, rounding, and chamfer. A shape authored
type: "Superellipsoid" generalizes Ellipsoid with an exponent field, finite
in [2, 8] (null = 2, the ellipsoid limit — the two spellings agree bit-for-bit
there); larger exponents round the solid toward a box (a "squircle"). Exact
and 1-Lipschitz for the whole admitted exponent range — a proven fact, not
merely a claim: see SdfProgramBuilder.Superellipsoid's remarks for the
derivation and SuperellipsoidLawTests for the numeric proof. Other types
refuse exponent. The stamp budget is WorldPlacementPolicy.MaxShapesPerStamp = 367
shapes, including expanded glyphs — a panelled shape (below) charges 2; the matched CPU/HLSL
instance ceiling is 65536 (the stamp pool's own worst-case draw is
WorldPlacementPolicy.MaxStampRegistrations x MaxShapesPerStamp = 46976; the shipped overworld's
whole COMPOSED boot probe — the presenter's four emitters, the ten adjacency bands' reservations
included — measures 61392 instances, 4144 of headroom under the ceiling at 367, and drops below the
4096-instance floor at 368; the scene emitter alone reads 59712 and under-counts by the adjacency and
field reservations — see WorldRenderEnvelopeLawTests.ShippedWorldBootProbeInstancesFitTheEngineCeilingWithHeadroom).
Verify profile admission and surfaces with
AuthoredShapeAdmissionLawTests / SdfTrapezoidProfileLawTests, and capacity with
WorldRenderEnvelopeLawTests plus a real rendered world.
WorldStampPool keeps fixed dynamic-transform addresses but emits only live
registrations and authored shapes/groups. Empty capacity does not add parked
instances or widen live masks. The worst-case boot probe still emits every
reserved slot. WorldStampPoolCompactionLawTests covers removal, slot reuse,
and grouped geometry growing after a rebuild; world.budget shows the live
instance count separately from the reserved capacity.
A shape's panel (ShapePanelDocument) is a second-material inset face region: an eroded copy of
the same primitive, offset along a local face (a direction, normalized; null = +Z; zero-length
refused by name) and composed with its own material — depth positive recesses it (Subtraction,
the floor exactly depth below the plate's face) and negative raises it proud by exactly |depth|
(Union); inset erodes the copy on every local axis, refused by name past the shape's smallest
local half-extent (SdfSolidGeometry.HalfExtent), and depth is refused by name past ±2·h′
(h′ = the eroded copy's own half-extent along face). Both are creation units — the animated
pool scales them by the placement — and ShapePanelDocument.Resolve is the one placement
derivation both emission paths read; the static per-shape probe reserves two chains per panelled
shape (CreationStampEmitter.PerCopyInstanceCount). Render-only — the deterministic contact field never reads it, so a
panelled solid placement's collider is unchanged. Refused by name on a Plane, a domain-folded or
grouped shape, and a creation that otherwise needs its own field scope (a sibling's non-Union blend,
an engraved text run, or a noise facet) — a panel's own one-deep field scope has nowhere to nest
inside one a caller already opened. Verify with ShapePanelLawTests.
A creation may be authored in code instead of by hand, through the sculpting
library in Puck.World.Authoring/Sculpting (CreationBuilder/StateHoisting/
SculptPatch/ICreationSculpt/CreationSculptRegistry) — see that project's
README for the primitives; the shipped registry carries no sculpts, so one is
registered by a composition root or a test. The document
stays the source of truth either way: a sculpt only ever produces a patch a
caller applies, validates, and (for the live world) composes into ordinary
WorldMutation rows through the world.row.set/.remove section table under
the issuing principal (creation.sculpt <name>) or writes to disk offline
(puck creation sculpt <name> --world <path>) — it never bypasses
whole-document revalidation or the per-section Mutate grant check.
A shape carrying domain (Symmetry/Repeat/Polar…) admits a parent too: it
rides its own per-shape slot, packed with the rigid delta the parent's chain
imparts to creation space (identity with no parent, translation in placement
units), and its chain mirrors the static stamper's — Scale(placementScale),
the domain ops, then its own static rest pose — so a fold plane travels with
the parent's driver/effector motion at any look scale; its cull bound rides
that slot with the static stamper's RenderReach radius (rest offset plus
fold displacement plus primitive reach). It refuses an own swings/slides
(a fold rides its parent's frame, never its own swing), a named frames
entry (its geometry never reads a captured pose — and a frame posing its
PARENT moves the parent alone, since a frame replaces a base pose outside the
delta chain), an effector-chain bone, and a look partDynamics follower (no
pose of its own to ease). A parts entry may name it: both part-pose readers
(TryBodyPartPose, TryBodyPartAuthoredPose) compose its rest pose onto the
carried frame and agree. Pinned by CreationDomainParentLawTests.
Creation-driver transitions: blendInSeconds/blendOutSeconds are optional,
finite non-negative exponential time constants (null = 0.15 s; zero = immediate
on a positive render delta). They control gate weights independently of phase
cadence and the shared movement-speed filter. CreationAnimationLawTests checks
asymmetric response, frame-rate independence, zero time, and invalid values.
Creation contact tuning: CreationPlantDocument.SwingWeight (plant.swingWeight,
nullable float in [0, 1]) controls target influence outside the plant window while
its driver is active. Zero releases to the authored swing; omission keeps target
following. Driver rest restores contact influence. A false effector when gate
clears the latch, so reacquisition cannot reuse a pre-flight world target. The
pack-path controls live in CreationEffectorLawTests; Moth opts into zero swing
influence. These are presentation changes, not collision or simulation changes.
Keep this skill factual and procedural: record settled contracts, their exact seams, and how to verify them. Let the user's current instruction outrank this file. If the skill contradicts a demanded change, update it in the same change. Treat counts, inventories, quarantine status, and other repository-state claims as snapshots: verify them against the current tree before relying on them.
The model in one paragraph
Treat everything as data: versioned JSON documents (puck.world.def.v1 — the world
itself, and, seeded from it, one per owned identity) describe what runs; the
engine renders, composites,
validates, and replays them deterministically. The world is ONE bootable
experience — no sibling --flag modes; durable configuration is document
fields, live operation is console verbs, and there is no PUCK_*
configuration surface for this game. A baked C# constant is the same
violation as a flag, and the commonest one: the discriminator is whether Nexus, Dive, Kart, and Jump
would each want the value different — sensitivities, clamps, radii, timings,
speeds, which button arms a mode. If yes, it is a document field in its FIRST
commit, never a constant to migrate later. Before writing any feature carrying
a tunable number, search src/Puck.World.Schema for existing vocabulary: never build
a bespoke mouse-orbit with hardcoded sensitivity and
pitch clamps when the camera program's orbit/clampPitch ops and the
authored views.seatRig already exist. Legitimate constants: capacity bounds that size memory or the
wire, representation/determinism constants, and math. The console is the
control plane:
process stdin drives verbs, stdout/stderr echo results, and the on-screen
console is only a MIRROR of that pipe — nothing that draws (including a HUD
replace panel taking over the whole overlay) can take the control plane
away. Verify game behavior by RUNNING the game, never by a build gate
(CLAUDE.md rule 3).
A world document is authored in .puck source, not hand-written JSON.
Puck.World.Transpiler — the puck.world.def.v1 vocabulary, a peer of
Puck.GamingBricks.Transpiler's puck.cartridge.v1, both riding the
schema-agnostic Puck.Transpiler core — lowers a parsed .puck document to
the same JSON this file describes; JSON stays the wire form and the
checked-in shape of every shipped world. Puck.World's boot loader
(PuckWorldLoader.TryResolveWorld) transparently compiles a --world <x>.puck path in memory before composing and validating it exactly like a
JSON boot. The flagship puck.world.json itself has no .puck source today
— it remains hand-authored JSON, while the avatar/courtyard/tool worlds and
both shipped CGB cartridges are DSL-authored (git ls-files '*.puck' is the
current inventory; treat it, not this sentence, as the source of truth).
Grammar, let/template/modules, units, and diagnostics belong to
puck-dsl; this skill owns only the world vocabulary's own sugar and
semantics — the rule/gate/effect mapping in
references/mutations.md.
Every construct of that vocabulary is described once, in
src/Puck.World.Transpiler/Vocabulary/: keyword, members with their kinds and
defaults, the document member it lowers to, and what the printer requires
before it may print a node back as that construct. The parser's
embedded-language test, the decompiler's sugar guards, and the language
server's completion and hover read it, and puck vocabulary [--check]
generates the inventory from it.
Read the table before deciding a construct's spelling or its refusal, and add a
member there rather than at a reader.
The world project family
| Project | Owns | Key types |
|---|---|---|
src/Puck.State |
The state and rule engine beneath the document, with no world or presentation concept | IStateSection/StateRow/StateCell and the traits (StateAdvance/StateDynamics/StateCycle), StateDomain, StatePhase/PhaseGuard, StateVisibility, StateCatalog/StateHandle, StateReader, StateArena, LatticeTopology/CompiledTopology/TopologyCompilation, Draw/StateGenerator/GeneratorEngine, PatternRow/CompiledPattern, DynamicsRow, CompiledTable, ValueExpression/ValueToken/ExpressionSpelling, ExpressionOp/ExpressionArithmetic, TableDocument/TableCanonicalizer/TableRow, the StateTransform union, SafeName/CellName, CellKind, RuleFacts, ActionStateComparison/ActionTriggerMode, Search/SearchPlan (the resolved job a search runtime walks; Puck.State.Search owns the walk) — no World name; consumers reach them through a project-wide Using |
src/Puck.World.Schema |
What a world IS — the document model | WorldDefinition + section records (WorldStateSection/WorldStateRow extend the engine's section and row with the body lanes and the gatesDrive/field traits; WorldFieldTopology is the physical lattice case), WorldDefinitionValidator, WorldDefinitionSerialization (WorldJsonContext over the generated WorldJsonSourceContext, WorldJsonVocabulary adding the document's arms to the engine's polymorphic bases); authored-to-fixed collider compilation; document-embedded wire vocabulary that keeps the Puck.World.Protocol namespace (PlayerIntent, WorldGrant/WorldPrincipal, admission entries) |
src/Puck.World.Protocol |
What a world SAYS — the wire/tape vocabulary | WorldCommand, WorldMutation, SubmissionEnvelope, SessionRequest, WorldSnapshot, IServerLink/IClientSink/IWorldServerHost, LoopbackTransport, WorldAuthorityEndpoint/WorldSessionMirror, and the IWorldAdjacencySource family (WorldAdjacencyFramePair/WorldAdjacencyProjection/IWorldAdjacencyNeighbour) — all four namespaced Puck.World.Server still, moved here as files without a rename |
src/Puck.Networking |
The dialect-agnostic wire substrate | FrameCodec (the socketless frame grammar), WireReader/WireWriter, WireRefusal/WireFailure |
src/Puck.World.Server |
The authoritative sim | WorldServer (the tick, the journal), WorldGrants, WorldHandleTable, WorldPopulation/WorldBody, World-specific contact orchestration and policy, WorldEngagement, IWorldAddonHost/WorldAddonReceipt (the addon seam interface), IWorldMachineHost (the screen-machine seam — the concrete host lives in Puck.World.Machines), WorldOwnedWorlds (the owned-world identity catalog), WorldReplayTape, WorldOutputHub |
src/Puck.World.Console |
The server-only console command modules, moved out of Puck.World |
IWorldConsoleAuthority (resolves the addressed WorldInstance), WorldGrantCommandModule, WorldGroupCommandModule, WorldLookCommandModule, WorldNetworkCommandModule, WorldReplayCommandModule (the replay.* verb surface — the tape and its read-back stay in Server), WorldRowCommandModule, WorldStateCommandModule, WorldTimingCommandModule, WorldUpdateCommandModule, WorldWaitCommandModule + WorldConsoleWaitGate/IWorldWaitGateResolver |
src/Puck.World.Addons |
The addon guest host — scripting guests only, with no emulator surface at all | WorldAddonRuntime, WorldAddonMutationDecoder, WorldAddonWire, AddonMutateRefusal, AddonSimulationPump |
src/Puck.World.Machines |
The engine-neutral screen-machine host | WorldMachineHost (the IWorldMachineHost implementation — boot, per-tick stepping, cable-linking, memory peek/poke, the two-phase prepare/commit/finish lifecycle, cartridge symbol resolution), WorldMachineCatalog (immutable host-selected engines and neutral IMachineContentProvider registrations, also supplied explicitly to admission), WorldMachineExtensionLoader. References no Gaming Brick core or forge project |
src/Puck.World.Client |
The presentation-facing client seam, physically split out of Puck.World |
PlayerRoster/WorldClient/SeatController, the camera-program translation (WorldCameraRigCompiler, over the document-blind IR in Puck.SdfVm.Views), WorldFramePresenter (the composed-frame producer)/WorldSceneEmitter/WorldViewComposer, WorldSessionSceneEmitter/WorldAdjacencySceneEmitter/WorldSdfDocumentEmitter, the stamp/animation pool (WorldStampPool/WorldPlacementStamper/WorldScreenStamper), the SDF document intake (Sdf/SdfDocumentDecoder/SdfDocumentModel/SdfRefusal), IWorldAudioFrameFeed/IWorldAudioCueSink (the narrow seams the frame/scene producers hold the root's WorldAudioDirector through, the IWorldAudioLever pattern), and the binding-authoring layer (WorldSeatBindings/WorldAffordances/CommandVocabulary). References Puck.World.Protocol and Puck.Audio, never Puck.World.Server. |
src/Puck.World |
The sole composition root | Program.cs, WorldClientSeats (implements the Server seam IWorldEmbodiedSeats), WorldAudioDirector (stays here — imports Puck.World.Audio types directly; implements Client's IWorldAudioFrameFeed/IWorldAudioCueSink/IWorldAudioLever for the frame/scene producers and the session-lever sink), presentation and the screen-output binder, Audio/ (document intake, tune hosting, the render device — the mixer core and voice synth live in src/Puck.Audio), the command modules that stayed here (WorldCommandArguments, the free-text-tail reconstruction shared with Puck.World.Console, lives in Puck.World.Server since both need it), and the shipped world/scenario documents under Assets/ |
src/Puck.Physics owns the generic kernels the server drives: Navigation/ (the budgeted, checkpointed A* over surface, volume, and medium grids) and Fields/ (FieldLattice, the reaction integrator behind a state.lattices row). Server keeps pair selection, authority, and body-state writes. The static solid field bakes a distance grid at collision.gridCellSize (0 = none; the shipped world authors 0.5) and answers the exact program inside a contact band derived from the kit colliders, so a query never marches from scratch; world.collision.status echoes the grid. host.journalDepth bounds the undo journal (0 = unbounded; entries past the horizon fold forward into the base; world.status echoes it). Transfer leases, escrow rows, and parked entries expire off one sorted WorldDeadlineTable, never a per-tick sweep of a whole collection.
The one world and its districts. puck.world.json is the island; every district is a module under Assets/worlds/modules/ imported under an alias (imports[].as, names compose as <alias>_<name>, exports names the only rows a host may read, write, or bind; world.imports echoes every layer). The primitives the districts read: a placement deal facet deals one child placement per cell of a keyed row (<template>/<key>, laid out by the template's distribution region; PlacementDealLawTests); an observation writes rows of any cell kind and nothing else (WorldObservationLawTests); a respond[].when is a closed union of a lattice-field condition and a state-cell condition (PlacementResponseLawTests); an owned identity carries a keyed Int facts row mirrored into the reserved body-scope identity row, written by the setIdentityFact effect, echoed by identity.facts (IdentityFactsLawTests); a machine screen's content may be a puck.cartridge.v1 document compiled at bind by the brick's forge (MachineCartridgeLawTests); an inhabit facet's count may name an Int cell instead of an authored literal — the cell-driven placement starts every structural install at zero live bodies and is admitted/retired only by WorldPopulation.ReconcileInhabitCounts, called on every state-value mutation apply so a rule-written cell reconciles the same tick, clamped to the tighter of the peer capacity and the distribution's own sample count and never retiring a seat-driven body (InhabitCountLawTests). A world authoring no simulation.rateHz runs at 30 Hz; a stress fixture authors 240 by name. Three derived limits shape a district: sixteen drive-reach ordinals across channels and target registers, one physical field topology per document, and the two-million work-unit rule budget where a body-pair interaction costs capacity squared; modules/README.md records what each decided.
The agent projects are an optional extension family, not members of the base world dependency closure:
| Project | Owns | Key types |
|---|---|---|
src/Puck.World.AgentBridge |
The provider-neutral autonomous-participant extension | WorldAgentBridge, WorldAgentMailbox, IWorldAgentDispatcher, WorldAgentObservation, WorldAgentAffordances, WorldAgentActionReceipt; explicit opt-in composition through AddPuckWorldAgentBridge, bounded worker-to-pump dispatch through ISnapshotInputCapture, explicit-principal reads through IPrincipalServerLink, typed body actions, no model or Harness dependency |
src/Puck.World.AgentHarness |
The optional Microsoft Agent Framework adapter | WorldAgentHarness, WorldAgentHarnessOptions; constrained puck_* tools over the bridge, Harness approvals on mutations, caller-supplied skills and IChatClient, no provider credentials or lifecycle policy |
Puck.World, its core tests, Schema, Protocol, Server, Client, Console, and Addons must not reference either agent
project. An agent-capable composition root opts into them from above. The Operator MCP adapter lives in
Puck.Mcp, referenced by CLI, never base World or the standalone silo. CLI installs AddPuckMcp into the public
WorldSiloApplication.RunAsync composition for puck mcp --silo <silo.json> --http <remote.json>; the silo
exposes only Hosting's neutral IControlSessionHost. World installs the Puck.Hosting local control endpoint only
when the host Console issues world.control start; stop and status manage its live lifetime. Attach with
puck mcp --profile operator --attach <printed-file> on Windows or Linux x64. The file protects a mutual-authenticated
loopback capability for the current OS user, including its other processes/elevation levels. Each connection has
one bounded, dedicated Console session. Exec preserves ordinary result uncertainty; capture uses the same
session's InvokeAsync barrier and the exact render request's completion, with off-pump waiting and temporary
artifact cleanup. Host deadlines remain enforced even when an injected session ignores cancellation; invalid
host results are unknown. MCP results carry the same schema-backed metadata as JSON text and structured
content; invalid tool arguments return tool errors. The adapter bounds UTF-8 input and pending replies and
closes both stdio streams on shutdown; malformed input or stalled output exits with failure.
Cancellation/EOF close ingress without stopping World or its recordings. Remote MCP is an in-process silo
extension, never a gateway to a local Console capability. Reuse the existing user_impersonation scope.
The host matches validated issuer/subject to explicit OAuth admission and stamps a generation-bound Peer.
Replica disclosure authorizes text reads; ordinary World grants authorize state-cell writes. The remote
command allowlist must remain fail-closed for local admin verbs. Current target/owner binding is fixed;
distributed placement and portable handles are not implemented. Both transports select MCP 2026-07-28.
Remote HTTP uses caller-bound application handles, four attachments and four concurrent HTTP requests,
with two of each per subject and no waiting queue. Discovery uses admitted registry help, hides headless
capture and discloses only granted observation names. Downstream user-interaction challenges return
bounded claims in an HTTP 401 bearer challenge; clients obtain fresh authorization before retrying.
Revocation closes attachments and active requests. Optional Azure services reuse Function self-onboarding
and observation providers through request-confined OBO with federated managed identity client assertions;
never substitute host credentials. Deployment uses automatic Caddy TLS behind the existing load balancer,
persistent certificate state outside World mounts, and Azure expiry/readiness alerts. Durable delegated
cloud writes and richer participant tools remain uninstalled.
Run tests/Puck.Hosting.Tests, tests/Puck.Networking.Tests, tests/Puck.Cli.Tests and the real-host smoke described in
Puck.Mcp when changing this attachment seam.
src/Puck.Audio is a sibling engine-services project: the deterministic fixed-point mixer/voice-synth core
(Puck.Audio.Mixing — AudioMixer/VoiceSynth/
AudioSnapshot/MachineAudioRate) plus sim-state music
(Puck.Audio.Simulation — MusicClock/MusicDirector/
MusicSenseEdge, stepped from WorldServer.Step right after
WorldEventFeed.Collect()), referenced by Puck.World.Server (machine audio
rate; WorldAssetRowLoader resolves each WorldMusicRow/
WorldTune/WorldPatch reference's document off disk (puck.music.v1,
puck.audio.v1, puck.synth.v1 — the same name/source/hash
shape every world audio asset row carries), and
MusicDirectorFactory compiles the loaded document into the sim-side shapes
and projects WorldEventFeed.Edges into MusicSenseEdge) and Puck.World
(presentation glue). It parses no document. music.state is a
WorldAudioCommandModule query verb routed through seat 1's currently
claimed WorldSeatAuthorityRouter route — a transferred seat is followed the
same way PlayerCommandModule's drive-a-player verbs are. A rhythm hit
window is an authored compareState range over the world-rule operand
$clock:<music>:phaseError (the signed tick distance from MusicClock's
current position to the nearest beat), never a dedicated effect or section.
Dependency rules are enforced by the architecture gate (PUCKARCH
diagnostics from build/Architecture.props): Puck.World.Schema references
only its declared leaf/authoring closure plus Puck.Physics, which owns the fixed collider vocabulary, and
Puck.State, the state and rule engine it consumes and extends (expression syntax and tokens, the opcode enum
and its arithmetic, tables and their document, state transforms, the validated-identifier family, CellKind,
the reserved fact channels) —
structurally denied backends, presentation, Puck.Overlays, Puck.Input,
Puck.World.Protocol, and Puck.World.Server. Puck.World.Protocol adds
Puck.World.Schema and Puck.Networking (the transport-neutral frame/wire
grammar). Puck.World.Server
adds Puck.World.Schema, Puck.World.Protocol, Puck.Physics, Puck.Storage,
Puck.Hosting — and knows nothing about rendering or input; Puck.World.Addons carries
Puck.Scripting (the addon guest ABI) and its own AddonSimulationPump now, referencing
Puck.World.Server rather than the reverse. The optional Puck.World.AgentBridge adds Commands, Protocol, and Schema
while remaining independent of model runtimes; Puck.World.AgentHarness adds the bridge and Microsoft Agent
Framework packages. No base world project references either extension. Physics owns generic contact geometry; Server owns
pair selection, authority, walkability/grounding, obstruction reporting, and body-state writes. The two seams
that legitimately cross: BindingVocabularyHook (a [ModuleInitializer]
injection so Schema validators reach the input vocabulary; the sibling
MutationKindVocabularyHook crosses the identical seam so a
MutationKindMask field can round-trip its kind names against Protocol's
mutation-kind catalog), and the overlay capacity the composition root hands
Puck.Overlays as constructor data
(Puck.World.Client.WorldOverlayCapacity.FromSchema() — see
references/hud.md). Each project's README is the
current developer reference — start there for narrative depth this skill
deliberately does not duplicate.
Host provider boundary
Cloud-specific implementations belong in extensions. The silo selects persistence,
connection authentication, and retirement observers through WorldExtensionRegistry, using opaque provider
settings. WorldSiloHost consumes a supplied ObjectStorageTarget; its lifecycle
service consumes IWorldHostRetirementObserver. Metadata polling, event types and
credential handling stay in Puck.World.Azure, referenced only by composition.
The compiled-output architecture gate denies Azure SDK API use in Schema, Protocol, Server, and Client. Neither simulation nor replay executes physical host retirement. The silo README
owns provider-neutral configuration; the Azure README owns Azure provider keys.
Production silo verification
Endpoint naming and world/host alias conventions are owned by
CI and releases; deployment values belong in main.bicepparam.
Azure CI packages Assets/worlds/puck.world.json and its referenced neighbours
with puck world prepare; hosted references use canonical world file
names. The storage-neighbour resolver parses and migrates the composed document
with state-expression binding, then reduces it to seam facts; it does not validate
the neighbour's unrelated local settings or recursively prove its adjacencies.
Hosted activation awaits root and neighbour storage reads before validating the
loaded world. Failed drain saves can be retried; closed ingress stays frozen.
An activation's federation subject and WorldInstance.ListenEndpoint come from
the published definition, independently of checkpoint network fields. Reload
checks that activation binding; moving an endpoint requires a fresh activation.
puck azure test-world-container --image <image> boots the primary Puck row, verifies a durable
checkpoint and the expected QUIC key, then replaces the container against the
same store and repeats the checks. Linux requires libmsquic and UDP ingress.
Unmanaged pinned activation waits for host startup, establishes its initial checkpoint,
and reconciles changed published content through WorldSiloHost.ReloadAsync and
the ordinary rebuild submission. A release marker advances only after its
checkpoint; retrying failed persistence must not rebuild twice. Drain waits for
accepted reloads before freezing the pump. /healthz includes persistence health;
/livez checks simulation progress independently. WorldSiloLifecycleLawTests
owns reload and drain failure controls. WorldSiloDefinitionLawTests checks
serialized health defaults. Failure during startup stops the host.
Managed release startup skips ordinary reload, restores privately, and opens all
rows only through the deployment-group publication barrier. Source recovery
restores the protected operation roots before recording RecoverActivate; a
restart in that phase activates without restoring again. Run
WorldReleaseCutoverLawTests for the real-host coordinator, latest-state rollback,
and interrupted recovery, plus WorldReleaseArchiveLawTests for retained package
integrity. These same-binary laws do not qualify a pair of packaged engine images.
world release deploy and azure deploy-world retain exact inputs and enter this
coordinator after qualification; resume reads the pending operation and retained
configuration. azure prepare-world-release binds and pins all composed worlds.
Deploy and rollback automatically export current source state through loopback
POST /release/fixture/<request-id>; the host captures all rows at one pump boundary
without draining. WorldReleaseFixtureArchive publishes its inventory after the
immutable checkpoints. The CLI materializes exact captured state with test keys
and preserves machine identity. Run WorldReleaseFixtureArchiveLawTests, the
cutover laws, and WorldReleaseFixtureBuilderTests; set PUCK_TEST_WORLD_IMAGE
for the latter's unchanged-definition and metadata Docker controls. Empty no-kit population checkpoints
preserve only the zero selection sentinel; WorldEmptyPopulationCheckpointLawTests
also rejects nonempty population and invalid kit selections. The operator
rollback command selects the retained predecessor directly from an admitted
commit. WorldReleaseMetadataTransition prepares metadata-only checkpoint changes
and their undo bases; its preservation laws include continuation, conflicts, custom
null presence, and undo after rollback. The Azure activation adapter calls
WorldAuthorityBlobStore.PrepareReleaseMetadataAsync to publish the transformed
checkpoint and definition in one CAS against the protected drain root, retaining
receipts and recognizing retries without rewinding candidate progress. Run
WorldReleaseMetadataPublicationLawTests for that boundary. Changed definition
pins require the metadata coordinator contract and the runner's package-bound
preservation exercise. WorldReleaseQualificationTransition uses the real atomic
publisher on disposable copies; both images import the forward result, then both
import the reversed candidate continuation. Never reverse the original seed or
normalize away a checkpoint difference. Standalone qualify requires manifests
at their package roots for changed definitions; managed paths use the archive.
New package preparation includes WorldReleaseManifest.CurrentCoordinatorContract
(puck.world.release.restore.v1) in the canonical manifest. It requires
closed-group rewind enforcement, receipt-aware qualification and the prior metadata requirement.
ReceiptCoordinatorContract and MetadataCoordinatorContract remain readable. Metadata
publication requires a supported metadata-capable contract on one side of the pair so older coordinators
cannot silently skip the transformation on resume or rollback.
Preserve legacy identities by omitting an absent coordinator contract. Run
WorldReleaseCoordinatorContractLawTests; PUCK_TEST_PREVIOUS_WORLD_SERVER enables
the pre-contract reader check; PUCK_TEST_PREVIOUS_METADATA_SERVER enables the
metadata-only reader check. Both record the assembly hash and must refuse the
current contract after accepting their supported control.
Receipt export uses CaptureReceiptSnapshotAsync, which must
receive a root selected in the checkpoint's publication queue, never a later root
sampled after capture. WorldAuthorityReceiptSnapshot retains and validates the
original index and complete chain. CreateReleaseFixtureAsync creates only a new
disposable authority, retaining those references and the source sequence/journal
coverage under an unowned epoch. Run WorldAuthorityReceiptSnapshotLawTests and
WorldReleaseReceiptFixtureLawTests. WorldSiloHost.ReleaseFixture queues only the
root read at the capture boundary, then copies its immutable graph without holding
later publications. A canceled read must not poison the queue. WorldReleaseFixtureArchive
pins canonical receipt envelopes; the builder requires them, including explicit
empty history. Legacy captures without proof refuse. Run cutover, archive and CLI
fixture tests for this path. Exercise reports use puck.world.qualification-exercise.v2:
each image checks every original receipt after import and continuation, then
duplicate and conflicting retries on the drained fixture without changing its root.
The runner independently computes the expected receipt hash before each leg.
WorldReleaseReceiptProofTests covers this helper; set PUCK_TEST_PREVIOUS_WORLD_IMAGE
alongside PUCK_TEST_WORLD_IMAGE for the real older-exercise refusal control.
Do not equate the checkpoint hash with receipt proof.
The official image publishes Azure, both Gaming Brick forges, and MCP with their
dependency manifests under /app/worlds/extensions. Both shipped entry points
discover that directory. Optional dependencies absent from the bare host resolve
inside their extension; shared host contracts retain default-context identity.
Discovered providers use process-lifetime load contexts; the discovery API has no
unload owner, so collectible contexts would allow premature dependency unloading.
Keep .NET hosted-service supervision and lifecycle callbacks when composing an
extension, and return failure if its background task faults. Run
WorldReleasePackagedHostTests with PUCK_TEST_WORLD_IMAGE to verify actual image
startup, both machine types, Azure health, MCP discovery and failed-listener shutdown.
Read retained package definitions with the composed-document parser, allowing
unfilled boot draws; checkpoint live and undo documents keep strict rehydration.
Bootstrap retries compare LoadPublishedDefinitionBytesAsync with the archived
bytes, never LoadDefinitionAsync's initialized result. world prepare relocates
provider-declared machine asset paths from nested origins to the common worlds
directory, preserving the image's asset layout. Colocated silo rows with neither
authority nor listen endpoint sign with the stable instance name, matching the
server's authority identity; listening rows still require an advertised endpoint.
Run release bootstrap/preparation/publication and silo lifecycle controls for these seams.
NavigationRuntime.Domain.ValidateShared must leave empty shared-navigation slots
unbaked during checkpoint restore. Validate scheduler and empty-slot shape without
querying geometry; resident trees still bake and validate their recorded static
edges. Run Physics navigation laws and packaged full-inventory restore controls.
A retained pending identifier alone does not mean maintenance is unfinished;
use HasUnfinishedOperation, and cover this boundary with WorldReleaseRollbackTests
and the cutover law's loopback export before rollback. Explicit restore uses
WorldReleaseRestore and a pinned RestorePoint through the ordinary phases.
release.closedGroupRewind blocks remote federation/claims and constrains local
transfers to the fixed pinned inventory. Capture establishes RewindBoundary in
the publication queue; later hosting must retain its policy. Preserve current
receipts and journal numbering when restoring old gameplay; never replace them
with the point's receipt history. Qualification copies explicitly drop the live
boundary proof. The focused LocalDeployRollbackRewindAndInterruptedResumePreserveTheirDistinctStateContracts
probe drives actual hosts, metadata publication, an internal transfer, interrupted
rewind, and restart. It compares the entire checkpoint with the explicit reconnect
parking adjustment, and verifies reconnect, retained current receipts, and recovery
of the fresh drain after a rejected private rewind and interrupted recovery. It is
not a packaged-image or Azure acceptance substitute.
Local forwarding disposal must tolerate retired destinations without changing their
frozen checkpoint. An already completed admission under the same fence claim
still needs the group CAS and root census, but reuses fully published host effects;
do not add another simulation-boundary wait to a no-op publication retry.
Deploy, rollback, restore, resume, and
finalization use a renewable controller lease. Run the CLI
WorldRelease* and CheckedProcessCancellationTests laws for changes to retained
configuration, bootstrap, or controller ownership. The Azure lease law requires
Docker and the Azurite image documented in the CLI test README. Cancellation of
the local Azure CLI does not retract an already accepted remote operation.
build/Guard-WorldRelease.py reads the durable C# group wire format; keep its
numeric phase mapping synchronized with WorldReleaseOperationPhase. Bootstrap
and guest mutations share a VM flock and reject stale effects after acquiring it.
With no pending operation, bootstrap permits only the admitted active release,
so ordinary VM replacement can recover without reopening a release transaction.
The CLI WorldReleaseGuestGuardTests requires Python 3 (or PUCK_TEST_PYTHON).
world release qualify executes exact Docker images over a copied offline fixture
with networking disabled; see the CLI README for the fixture and evidence contract.
Each package must preserve the same source import and candidate-written reverse
import, advance simulation, and checkpoint successfully. A same-image control proves
the runner only. Named handheld machines support complete durable checkpoints;
run the real-core queued-host-time-travel probes and the world machine continuation
laws when changing this seam. Pumped addons, applied screen operations, live coupled
links, and enabled machine rewind history remain uncapturable and must reject
qualification rather than be omitted from its fixture.
--authentication-config-file selects an installed client provider and server
key pin; no token belongs in world content or checkpoints. Azure's provider
validates ByteTerrace API membership, while generic protocol code sees only the
verified session namespace. Run the real client against the deployed endpoint
for admission, authoritative interaction, and reconnect evidence; a QUIC key
probe alone does not prove these. docs/development/ci.md owns Azure deployment policy.
Cross-cutting contracts (every task)
Preserve determinism. Use no wall clock, RNG, or float in simulation state;
use fixed point from Puck.Maths and exact engine-tick durations throughout; the
simulation rate is an authored per-world document field
(WorldDefinition.Simulation.RateHz, MUST divide FixedTickConversion.TicksPerSecond
50400 exactly), defaulting to 30 Hz, the shipped game's own rate, for a world
that authors no simulation section; a world that wants the distinct
resident, non-stepping rate authors rateHz: 0 by name, and a fixture or law
that needs a higher stress rate authors its own rateHz (240 was the ceiling
every primitive had to survive, never a default). Every entity is advanced on the server from
a PlayerIntent — poses are never accepted from outside the simulation;
drivers only produce inputs, poses flow out through the tick snapshot. The
guarantee pins the MAPPING, not the values: a deliberate correction to math
or logic is EXPECTED to change replay hashes — make the correction and
re-record any persisted tape it invalidates in the same change. Client-side
(src/Puck.World/Client/) is presentation: floats are fine there, nothing
feeds back into the tick.
Navigation is authored world truth. navigation.domains owns bounded
surface, collision-free volume, and live-field-constrained medium grids.
A BodyTargetSource.Navigated producer points at one domain and one ordinary
authority-checked target register. Keep A* fixed-point, budgeted, stable-tied,
checkpointed, and hashed; bake static solid clearance once (a surface edge's
sweeps run from maxStepHeight above the foot to the head — a sweep skimming
the floor advances one contact skin per march step and exhausts on a
diagonal), but recheck a
medium field before traversing its cached edge — a medium's free surface
(value × heightScale) is bounded by the shared lattice's body-coupling
ceiling, not by its topology's own layer count, so a shallow layers: 1
topology under a deep medium still resolves correctly
(Puck.Physics.Fields.FieldLattice.IsInsideMedium/IsSegmentInsideMedium). Extend this
vocabulary for
engine-integral movement semantics; addons/agent extensions remain the home
for arbitrary policy and planning, not collision/path correctness.
Enforce the acting-principal rule. Make every mutating ingress consult the ACTING
principal before any mutation. The ingress stamps identity
(SubmissionEnvelope.Principal on the wire, CommandContext.Principal for
console text); handlers READ the stamp via context.ActingPrincipal() and
never construct a principal — constructing one is laundering an identity.
Client code never mutates local state before the server's verdict
(completions, not discarded replies). Details:
references/authority.md.
Rule writes land on the arena; the document installs once per tick. During
EvaluateWorldRules every state effect writes the host's StateArena
(WorldRuleHost.cs, WorldServer.Arena.cs) inside its own firing's journal
scope, and rules read through the same arena; one rule firing is one scope,
committed on success and rewound on the first refusal. What the arena
accumulated across the tick's firings is published into the installed document
at the end of the tick (WorldServer.PublishArena), so every other reader
(bodies, fields, search, the console) sees a rule's write only after that
publication. Only rows whose version moved are read back out of the arena; the
rest of the installed rows are kept. Publication ends in
WorldDocument.ReconcileStateConsumers, the same routine a value mutation ends
in, so anything outside the arena that caches a state value (drive gates, field
input, body scale, inhabit counts) is added there, never to one door. A
document-row arm is EffectNeeds.Transactional: a firing's rows are prepared
as one WorldMutation.Batch through WorldDocument.TryPrepareMutation before
the arena scope commits, where any gate's refusal rewinds the firing, and
installed by InstallPrepared after it. A gate added to the mutation door goes
in TryPrepareMutation; nothing in InstallPrepared may refuse. Every other world arm is delivered after
the commit, and a failed delivery is counted and undoes nothing. Row versions on the arena drive the rule scheduler and memoized
bindings (RuleSchedule, IStateReader.TryRowVersion); a rule whose read
rows are unchanged keeps its closed verdict. A text cell, a removal, a draw, a
shuffle, and a random or slice transfer take the same arena kernels
(WorldArenaTransforms.TryApply), which compose but still export once.
puck bench world measures the tick path on the fixture and the shipped
world.
Narrate through the hub, never the console. Server writes nothing to
System.Console; every line is a WorldNarration through
WorldOutputHub.Narrate(channel, text) under HasNarrationSink (a lambda that
captures locals allocates its closure on every call, sink or none), and a
composition root binds Puck.World.Console's WorldConsoleNarrationSink to
stderr, or stdout for the few lines a script reads as answers — Puck.World.Server
cannot hold that type itself, since build/Architecture.props's
PuckArchitectureDeniedApi denies it a System.Console reference and
PuckArchitectureDeniedApiGate fails the build (PUCKARCH008) on the compiled
output if one slips in. A fixture server narrates only if it is handed the
sink (Fixtures.FreshServer does), so a law that captures Console.Error
reads what the game prints.
A body sleeps after bodies.sleepAfterSeconds idle (0 never sleeps; a multiple of 1/800 s)
and wakes on intent, pose, transfer, admission, a designation, a dynamic
contact, or a contact-field version bump (WorldBody.Sleep.cs). A value write
delivers DeliverState; a shape change delivers DeliverDefinition; the
install marks which is pending and the step delivers once.
Add a read-back. Do not land a new decision surface without a verb that
echoes it, in the same change — a decision nothing can echo can only be
asserted through downstream inference. world.why, world.grants,
body.channels, world.hud, world.status, world.addons,
world.refusals, world.tables, world.rule.trace, world.rule.hazards,
world.budget.rules, world.binding-bar are the pattern.
Keep parsing strict and sweep shipped worlds. Refuse unmapped JSON members by name on
every nested row; only the document root's Extensions bag round-trips
reserved-prefix ($/_) keys. Adding a top-level section refuses at boot
until every shipped world carries it; adding a nested member silently
defaults at parse and (usually) refuses at validation — sweep the shipped
worlds in the same change either way. Precise direction:
references/documents.md.
Adding an authorable feature — the five steps, one change. The contracts above are each stated separately; a feature carrying tunable values owes all of them together, and skipping the binding is how a bespoke mechanism gets built beside an existing one:
- Search
src/Puck.World.Schemafor existing vocabulary first — the record, the$typearm, or the section that already says this. Extending what exists beats a parallel mechanism, and the existing one is usually invisible from the call site you started at. - Author the values as a document record (never C# constants — see the
model paragraph), with a
Defaultcarrying today's behavior so an unauthored world is unchanged. - Validate in
WorldDefinitionValidator, refusing by name in the style of its neighbors. - Sweep every shipped world in the same change (strict parse, above).
- Add the read-back verb (above) — the decision must be echoable.
- Echo the derived cost. A feature whose declaration carries a price (a
step clamp, an envelope reservation, a per-step loop) folds that price
into the
world.budgetcost sheet in the same change — a derived cost nothing can echo is a silent frame tax.
Doc hygiene, same commit. docs/game/design.md is the one document that says what we are collectively building; correct it in the SAME commit as any landing that changes its truth. NEVER write a status column — a status claim duplicates what the code answers better, so record the DECISION and let the code answer "is it done". Component READMEs are developer references (no doctrine
prose); if a change stales one, or stales a comment, fix it in the same
change. A doc that would produce wrong behavior today is hostile, not stale —
delete it.
Running and verifying
dotnet run --project src/Puck.World -c Release -- --exit-after-seconds N --state-dir <tmp> < script.txt > out.log 2> err.log
--exit-after-seconds 0(or absent) runs until the window closes. The full flag surface is parsed inProgram.cs(--backend,--width,--height,--exit-after-seconds,--present-mode,--world,--recording,--storage-uri,--user-id,--state-dir,--headless,--capture-dir,--schedule-dir,--listen,--connect); host-related flags are nullable deployment overrides. Absent host overrides leave the world document'shostsection in control.--worldaccepts a.puckpath directly —PuckWorldLoadercompiles it in memory before boot — or an ordinary JSON world document; point at a worked example instead of hand-writing JSON:src/Puck.World.Transpiler/Samples/*.synthetic.world.puck(fixtures) and the shippedAssets/worlds/avatars/moth.puck,moth-courtyard.puck, andtools/hgb-mirror.puck/hgb-compare.puck(real assets, each with a generated, ignored JSON twin;build/WorldAssets.targetsowns generation).host.presentationhas three values: windowed,none(HeadlessWorldSimulation— full authority, no GPU), andoffscreen(full authority + GPU composition to images, no window — whatpuck parityboots). A world may author acapturessection: tick-scheduled capture rows that arm theworld.screenshotpath at exact sim ticks, refuse when the camera is inside geometry (map(cameraPos) <= 0,cameraInside=true), stamp a per-station material census and aworld.state.hash-matching state hash, and write apuck.parity.manifest.v1intocaptures.directory(overridable by--capture-dir). The camera program'sselectop dispatches to named sub-programs keyed on a livestate.<row>value — the discrete sibling ofblend.- A world may author a
schedulesection, the command-side sibling ofcaptures: rows of(tick, principal, command)submitted at that completed-tick coordinate through a session bound to the authored seat, so admission, grants, phase guards and refusals behave as for a live actor. The section is INERT unless the boot passes--schedule-dir, which both arms it and names its output directory: any other boot submits no row, writes no export, and says so once on stderr.principalisseat1..seat4only —consoleis refused (it is trusted at every gate, so a step acting as it proves nothing about authority; author the step's power in the document's owngrants), as arepeer:/addon:.commandmust open with a verb inWorldScheduleCommands.Admitted— state mutations, guarded transforms, body intents/poses,player.join/player.leave— and every process, clock, file or authority verb (quit,world.rate,world.save,world.load,world.grant) is refused at validation with the row's index. The tick pins the SUBMISSION — anImmediateverb runs inline in it, aSimulationverb applies attick + 1. Atmax(rows[].tick) + settleTicksthe host writesWorldStateExport's canonicalstate-export.jsonplus apuck.world.schedule-manifest.v1schedule.jsoninto the armed directory. The manifest carries one entry per DECLARED row (a row whose tick the run never reached readsoutcome: "unreached", never an absent entry), every local edit echo including refusals, andauthoredExportTick/truncatedbesideexportTick— a run that ended early records a truncated run rather than presenting what it reached as the export, andpuck testrefuses such a leg with exit 2. Astaterow may carry averdicttrait (gateprose plus thestatuscell key; the row's other cells are the values the gate saw, status 0/1/2 = never-evaluated/pass/fail). A verdict row is kind Int, so what its gate saw of a Fixed or Bool row sits in a row of that kind carryingwitness: "<verdict row>", which the same doors refuse and which freezes with its verdict.world.scheduleandworld.verdictsare the read-backs;puck test <path>boots each such world twice through the real executable, refuses one whose two exports differ, and prints one line per verdict. This section and that trait are what a.pucktest "name" { given { } when { } expect { } }block at a world's root lowers to, one generated world per block:givenwrites boot cells,whenbecomes the schedule rows on a tick grid, and eachexpectline becomes a verdict row plus a rule gated on the export tick alone.puck testtakes the.pucksource directly and runs the worlds its tests generated — see Testing a world. --state-dir <dir>redirects the on-disk state root (profile catalog, replays) — use a temp dir for hermetic verification runs; parallel runs each need their own.- Capture BOTH streams. Read-back answers land on stdout; refusals,
server narration, boot origin lines, and
[world.mutation: …]echoes land on stderr. Reading one stream is reading half the conversation. - Blank lines and
#comments in the piped script are skipped — annotate your scripts. - The drain barrier: a following
Immediateverb is held until pendingSimulationtraffic applies, so write-then-read pairs need no polling.world.wait <ticks>holds only its issuing session, clocked by completed host-work ticks (see references/console.md). - Encoding, the two traps: a pwsh spawned from Git Bash reads captured
output under an OEM codepage and mangles the engine's em-dashes
(false-FAIL); pin
[Console]::OutputEncodingand$OutputEncodingto UTF-8 — but BOM-LESS ([System.Text.UTF8Encoding]::new($false)): a BOM'd pin writes its preamble into the piped stdin and silently corrupts the FIRST command. - Indexing:
body.*verbs andworld.grant body:<n>address the 0-based entity index (0..4095 at the engine ceiling); seat-scopedplayer.*verbs (join/leave/assign/mode/ bind/…) stay 1-based seat numbers.body:1is seat 2's entity. - Scenery boulders HAVE collision — zero displacement with no refusal means the physical path, not a dead command. A zero-input boot drifts p1 slightly (~(-0.04, 0, -0.82) over ~300 ticks) — do not assert exact rest poses without accounting for it.
world.screenshot <path.png>REQUESTS the next composed frame including the overlay — the cheap pixel assertion. It arms; it does not capture: the stdout echo sayspending, the file is announced on STDERR ([capture] unified overlay -> …), so fence a frame (world.wait) before reading it, and a second shot armed before the first composes is refused by name. The terminal console starts hidden; if a script opens its seat session (console [on|off] <player>from stdin), it may cover the frame — close it before judging pixels.- Two windowed captures are never byte-identical, even of identical
simulation state. Silhouette shading carries ±1-LSB variance across a
boot-time transition, so a byte comparison of two fenced captures reports a
difference about one run in three. The unified overlay also composites the OS
pointer's cursor (
WorldCursorFeed) whenever the pointer sits inside the window, and window placement varies per launch. Compare frames by CHANGED-PIXEL COUNT (CanaryFrameNoise, theframesAgreecanary assertion: pixels moving ≥2 LSB, budget 64), never by bytes. Do NOT reach forParityEnvelopehere — its whole-frame mean guard is for diffuse cross-backend noise, and a body relocation covering 0.06% of the frame measures ~0.03 LSB mean and slips under it. - Use the repository's content search. Run
puck search, nevergrep; the published project tool is the repository's supported search surface. - A verification that cannot fail is a lie. Pair every denial case with a control (actor holds the grant → succeeds), keep actor ≠ target (every seat is seeded wide, so self-targeting discriminates nothing), and prove a new assertion once by breaking it. This repo's recorded dominant failure mode is verification scripts that lie silently.
replay.verifyMATCH proves the explicitly hashed authoritative state-system trajectory, not the whole document, grant table, or HUD (references/replay.md).- Committed proofs:
puck canarymanifests undertests/Puck.World.Canaries/for every load-bearing seam, includingworld.grant-driven claims (a command claim's `s
Truncated - read the full file at https://github.com/ByteTerrace/Puck/blob/9c9014c94db75ba3de92a5e17e4d9065a7d53f7c/.claude/skills/puck-world/SKILL.md.