# Constraint layouts and Package wheel controllers

Status: accepted for local implementation; no deployment promotion.
Action: implement. Decision baseline: `e1dc2c0d5f38231054325dba67a9ea3344ea7ea5`
plus the pre-existing dirty working tree recorded in
[the architecture audit](VEHICLES_AND_CONSTRAINT_GENERATION_AUDIT.md).

## Context and evidence

The requested capabilities are hot-authored vehicles and constraint-driven
world generation. The product gate is the versioned Package loop in
[PRODUCT_GOAL](PRODUCT_GOAL.md); decision policy is
[ENGINE_DECISIONS](ENGINE_DECISIONS.md). Authorized work is local source,
examples and executable verification on Windows/Rapier. No production realm,
deployed binary or production Package digest is being promoted by this record.
The earlier audit is the pre-implementation decision, not a test receipt.

## Architecture decision

The two authoring surfaces share the existing Package lifecycle. They do not
share a runtime solver: layout constraints are solved in the cold content
compiler, while wheel suspension and traction run in the authoritative native
Physics owner. Luau composes policy through the existing `build_prefab` command.

```text
LayoutSpecV1 → bounded search → verified ResolvedLayoutV1 → PrefabDocV1
                                                               ↓
WheeledVehicleDocV1 → required prefab component ─────────────→ Package
                                                               ↓
cold staging → admission/dry-run/CAS → existing tick activation/checkpoint
                                                               ↓
ABI2 build_prefab → authoritative validation + whole-tick commit
                   ↓                                ↓
             static layout                    materialized vehicle pin
                                                    ↓
                                       Physics owner → chassis poses
                   └────────── replication/catalog ──┘
                                      ↓
                         client snapshot + presentation
```

`pajama-world` owns public documents and validation. `pajama-content::layout`
owns cold search, result verification and lowering to ordinary prefab
components. `pajama-server::package_authoring` exposes offline CLI commands and
uses the existing seal path. The server prefab compiler owns the materialized
vehicle recipe. The physics adapter translates it into the versioned Rapier
raycast controller. The client only renders the resulting chassis and wheels.

| Alternative | Preconditions and expected benefit | Cost and recovery consequences | Selection |
| --- | --- | --- | --- |
| Existing Luau/Prefab without new adapters | Reuse existing jeep and static components | Cannot author new wheel recipes or verify layout constraints | Retained baseline |
| Solve layouts inside Luau | One authoring language | Search competes with small tick/VM/state budgets; still needs durable result identity | Rejected |
| Cold compiler plus registered wheel controller | Existing Package lifecycle and native Physics ownership | Bounded schemas, admission adapter and client presentation; materialized instance recovery | Implemented |
| Universal constraint/assembly runtime or native plugins | New domain and lifecycle contracts | Spatial placement, mechanical integration and economic/network simulation have different state and convergence semantics | Deferred |

The cold compiler provides a distinguishing executable result with a small
authority surface. A new crate or solver adapter is warranted when a measured
workload outgrows this bounded compiler, not merely because both domains use
constraints. The nearest alternative, solving inside Luau, would spend live
simulation time on a workload whose result can instead be verified once.

No automatic world migration, live server deployment or production publication
is part of this change. Publication still requires the existing realm admission.

## Use the offline compiler

From the repository root, with a new destination directory:

```powershell
$env:CARGO_TARGET_DIR='target\vehicle-layout-validation'
cargo run -p pajama-server --bin pajama-package -- layout examples/authoring/constraint-layout.json <new-source-dir> studio.camp
cargo run -p pajama-server --bin pajama-package -- vehicle examples/authoring/wheeled-vehicle.json <another-new-source-dir> studio.rover
cargo run -p pajama-server --bin pajama-package -- seal <source-dir> <new-delivery-dir> https://your-cdn.example/blobs <world-id> <expected-generation> <request-id>
```

The first two commands write `package.json`, `assets.json`, and `prefab.json`.
They create revision 1 source; edit the manifest revision for a successor before
sealing. Existing output directories are refused. `seal` produces the ordinary
content-addressed release candidate. Upload/publish it through the existing
[Package authoring and publication workflow](CONTENT_PACKAGES.md); these commands
do not activate a world or create an authority grant.

After activation, an admitted ABI2 behavior with `build` capability can request:

```lua
{ kind = "build_prefab", prefab = "package:studio.camp:layout",
  x = 0, y = 0, z = 0, yaw = 0, scale = 1 }
{ kind = "build_prefab", prefab = "package:studio.rover:vehicle",
  x = 8, y = 0, z = 8, yaw = 0, scale = 1 }
```

These are command values returned from the existing ABI2 `tick`, not standalone
modules. The caller remains responsible for durable once-only/event-driven
policy. Placement Y is an authored anchor; the compiler does not sample live
terrain. Vehicle ground anchors receive recipe-derived initial clearance once.

## Layout language V1

[The example](../examples/authoring/constraint-layout.json) declares reusable
component prototypes, named instances with optional fixed cells, and rules.
There are always hard grid-boundary and pairwise non-overlap constraints.

| Rule | Meaning |
| --- | --- |
| `adjacent` | Two footprints share an edge of positive length; touching corners do not count |
| `separated` | Rectangle edges have at least `cells` gap on either grid axis |
| `connected` | The named group's induced edge-adjacency graph is connected |
| `exclude` | No footprint may intersect this reserved rectangle |

Grid rectangles are half-open, axis aligned, and expressed in integer cells.
The prototype `size` is the reserved footprint. Counts are explicit named nodes;
there is no implicit unbounded expansion. Elevation is in metres. Template
components cannot supply transforms: the compiler emits their positions,
prototype scale (default unit) and zero yaw. Native box colliders and wheel-controller envelopes must
fit their declared footprints; box offsets are refused. Other components still
undergo the owning Package adapter's checks. Footprint constraints do not prove
arbitrary mesh clearance, walkability, structural stability or terrain support.

Limits: input CLI document 1 MiB, grid 64×64, 32 nodes/prototypes, 128 rules,
16 components/prototype, and at most 1,000,000 attempted placements. No solver
executes under the authoritative world lock. `BudgetExceeded` means search was
incomplete; `Unsatisfiable` means the bounded finite search was exhausted.

Search order is fixed by node identity, fixed placements first, with seed-based
candidate order. Compiler ID, typed spec hash and exact placements are recorded
in the optional `pajama.layout-provenance` manifest extension, so Package identity
covers the provenance and generated prefab. The server consumes the generated
prefab, not this advisory provenance. Restarts restore committed entities rather
than rerunning generation. A standalone result verifier checks exact coverage,
fixed placements, bounds, overlap and all declared rules before lowering.

The result has a transform-only root plus one entity per node (at most 33),
inside the current 128-entity prefab limit. The compiler does not bypass the
256-entity build-transaction limit. Multiple builds are multiple transactions.

## Wheel-controller V1

The required `pajama.wheeled-vehicle` schema-1 component and required
`physics.wheeled-vehicle-v1` feature activate the new adapter. Both are necessary;
an optional component must not silently remove a vehicle on an older engine.
The definition specifies chassis size, mass, four wheel offsets/radius,
suspension stiffness/damping/travel, acceleration, steering limit, one seat and
primitive color. Units and ranges live in `WheeledVehicleDocV1::validate`.
Front wheels steer and rear wheels drive. The declared acceleration determines
total engine force (`mass × acceleration`); it is not a promise of measured
acceleration on every slope or contact surface. Existing speed-sensitive
steering, braking and arcade stabilization remain controller behavior.

The compiler owns the primitive chassis and hitbox. Combining this component
with a separately authored shape, body, structural support or tree
is rejected. Placement scale must remain 1; author new physical dimensions in
a successor recipe instead. The client draws generic wheels with the admitted
offsets/radius/travel and the driver uses the authored seat. Wheel animation is
derived presentation, not replicated solver wheel state. Multi-seat passengers
and arbitrary wheel counts are future component versions.

An optional `visual` block binds a body and four rigid GLB assets from the same
exact Package. It requires `presentation.wheeled-assets-v1` and presentation
admission. The compiler derives the entity's body render reference from this
binding; an independent mismatched render reference is rejected. Models use
metres and axle-centered wheel pivots, while collision still uses the recipe's
box. The client resolves all five parts before displaying the car. Package
retention and checkpoint recovery preserve old body and wheel versions together.
See [asset binding decision](VEHICLE_ASSET_BINDING.md) and the CC0 source/converter
in [the vehicle examples](../examples/authoring/vehicles/README.md).

Each entity stores `ResolvedVehicleDocV1`: exact package ID/revision/digest plus
the materialized recipe. New instances use the active registry; existing
instances retain their recipe after update or rollback. Native admission
validates shape/scale/hitbox consistency and forbids generic authoring from
injecting, replacing or removing the pin. There are at most 64 distinct active
vehicle recipes in a compiled catalog. Physics work uses the existing vehicle
lane and resource ledger, without a duplicate body collider.
Ground-collision residency covers a three-dimensional chassis/wheel envelope,
including suspension travel, so a tall vehicle remains covered when it rolls.

Rapier implements this recipe. Box3D rejects it before native allocation;
its legacy physical-wheel vehicles remain available. New controllers neutralize
their input each tick before applying authorized driving intent, so a departing
driver cannot leave a held throttle. Existing Interact, disconnect cleanup,
pose publication, AOI and composite-checkpoint paths remain authoritative.

Full chassis quaternion is used on recipe recovery. Velocities and solver
contact/suspension caches still rebuild/reset; seamless mid-flight continuation
is outside this version. The Rapier runtime recipe identity changes to
`pajama.physics.rapier-world-v2-wheel-recipes` (the historical wheel-recipe gate;
[Driving V1](DRIVING.md) subsequently advances the recipe to
`pajama.physics.rapier-world-v3-driving`). Old checkpoint runtime identities
fail closed and need a separately implemented explicit migration; **do not open
an existing deployment expecting this change to migrate it automatically**.
Legacy plain world documents/jeep tuning remain readable, but that is different
from accepting an old fenced checkpoint. The running desktop server is untouched.

## Acceptance and follow-up

The executable combined proof is
`vehicle_layout::generated_layout_vehicle_package_replicates_updates_rolls_back_and_recovers`
in the server `e2e` suite. It compiles constraints into a prefab containing a
vehicle, publishes an ABI2 build behavior, verifies two socket clients, drives
and exits the vehicle through player input, publishes a different mass, rolls
back and restarts against the same checkpoint, checking stable IDs and pins.
Socket-client equality does not constitute a real GPU pixel test.

The separate native recording workflow is documented in
[GENERATIVE_DEMO_RECORDING.md](GENERATIVE_DEMO_RECORDING.md). It runs the real
compiler and publication path, captures two GPU clients, verifies submitted
entity identities and recipes/positions, and decodes both resulting MP4s.
Visual framing and occlusion still require inspection; draw submission alone
is not pixel visibility evidence.

Other assertions cover finite schema bounds, unsatisfiable versus incomplete
search, altered/overlapping results, collider-footprint mismatch, exact recipe
admission, generic forgery/replacement, immutable scaling, actual native mass
and engine force, full quaternion recovery, tipped-vehicle ground coverage,
and source→seal compatibility.
The original package/physics/script/world suites are the retained-capability
checks. Actual local results on 2026-09-22:

| Check | Observed result |
| --- | --- |
| Final content / physics / script / world unit suites | 559 passed, 1 ignored (25 / 48 / 76 / 410 passed) |
| Final server Package integration tests | 4 passed, including the combined generation/drive/update/rollback/restart proof |
| Client content and render-frame suites | 42 + 15 passed; no GPU pixel assertion |
| Server unit suite | 1,209 passed, 10 failed, 1 ignored; all ten failures reproduced at the original baseline commit |
| Actual CLI examples | Both generated and sealed; both refused overwriting an existing source without changing it |
| Formatting and whitespace | `cargo fmt --all -- --check` and `git diff --check` passed |

[Raw logs, commands, source identity and caveats](evidence/vehicle-layout-20260922/README.md)
are retained. The initial Package regression run had one existing test time out
waiting for checkpoint storage to become idle; its unchanged rerun passed. Both
runs are preserved. Baseline unit reproduction had additional missing-game-fixture
failures, so the focused ten-test receipt establishes the comparison, not a
claim that the full baseline suite was green. This is a local implementation
verdict; it is not production promotion or a throughput measurement.

## Outcome and reuse

Retain this bounded compiler/controller implementation. Layout search and
runtime control are separate owners, while Package admission, exact revision
identity and authoritative commit remain the common lifecycle. The combined
test is the first in-repository composition of these two capabilities; no later
production reuse is claimed. Publication outcome reconciliation continues to
use the existing Package status/generation contracts. Runtime recovery uses
the committed materialized result and never reruns the solver implicitly.

Next architectural gates remain online input snapshots/stale-result rejection,
durable job idempotency, explicit multi-block visibility, and domain-specific
incremental systems. Generation-definition rollback is not deletion of player
world changes. Do not add these by allowing an external solver or Luau to write
world/physics handles directly. Invalidate this decision when the finite-grid
model cannot express the target workload or when measured search/commit/physics
budgets fail; retain the Package publication and authority boundary while
replacing the responsible compiler/controller.

One deliberate cost to measure next is replication: the current quaternion
pose path can emit Full entity updates, which include the materialized recipe.
This slice favors self-contained recovery over a new definition-distribution
protocol. Before increasing fleet size, measure bytes per client/tick and
consider the existing planned quaternion-bearing movement lane; do not claim
that the two-client proof establishes large-fleet bandwidth or tick capacity.
