STASIS
Docs / runtime

One model, one tick order.

The host owns the loop. Stasis owns persistent state, simulation decisions, and the boundary where new code can become live.

01 / Lifecycle

Four entry points.

main()

Set initial values and request resources once.

tick()

Consume the input snapshot and update model state.

render()

Read state and emit drawing commands only.

on_code_swap()

Restore invariants after compatible state migration.

function on_code_swap(): void {
  if (state.selected >= state.item_count) {
    state.selected = 0;
  }
  return;
}
02 / Determinism

Input becomes intent, then state.

Read a frame snapshot at the start of tick(). Turn it into intent, apply systems in a documented order, and let render project the committed model.

input snapshotintentsystemsstaterender projection
function tick(): i32 {
  let move_x: i32 = 0;
  if (is_key_down(Scancode.Left))  { move_x -= 1; }
  if (is_key_down(Scancode.Right)) { move_x += 1; }
  state.player_x += move_x * state.speed;
  if (should_quit()) { return 1; }
  return 0;
}

Rendering observes state; it is not an authority that changes gameplay.

03 / Live publication

Candidate builds are all-or-nothing.

On the desktop JIT development target, stasis play makes the edit → save → hot-swap loop the center of iteration. It watches the current import graph, compiles each saved .stasis candidate in the background, and attempts publication at a safe point between ticks. Compatible running state is preserved, so a gameplay session can continue without a process restart.

The background compiler builds a candidate generation. Between ticks, the runtime checks lifecycle ABI, target compatibility, and global layout; it migrates only through the bounded plan, runs on_code_swap(), then publishes one host-entry table.

edit source
  ↓
background compile + semantic checks
  ↓
between ticks: validate → migrate → on_code_swap()
  ↓
publish complete generation

// any failure: keep previous code and state
Partial commits are not observable by gameplay. A failing diagnostic, incompatible layout, or failing swap hook rejects the candidate. This live loop describes desktop JIT development; packaged web and mobile targets use their documented build flows.
04 / Targets

JIT for development, AOT for release.

Development uses an in-process Cranelift JIT so the runtime can compile changed code without a process restart. Release builds use Cranelift AOT. Both share the direct-call lowering contract and lifecycle boundaries.

Development

Edit, save, and keep stasis play running: background candidates publish between ticks when compatible. The session keeps its compatible state and rejects unsafe candidates.

Release

Complete AOT finalization for a packaged program; no live-edit service required.

Packaging keeps this runtime contract while changing only the host shell. See the platform targets guide → for desktop archives, WebAssembly, Android, and iOS/iPadOS requirements.