ADR-0018 -- Render-only office doctrine: no spatial fact becomes a gameplay input

Status: DRAFT (formal confirm at 2026-07-27 R3a block; review-by 2027-07-27) · Decided: 2026-07-27
These are the actual decision records the game is built from, published unedited except for internal process notes. They describe why the game works the way it does. They are not a strategy guide, and some of them argue with each other.

Context

The office floor (godot/scripts/ui/office_floor/office_sandbox.gd) is a dev-tool sandbox for placing props and previewing worker sprites on a 32px snap grid (GRID := 32.0, _snap_to_grid(), _cell_of() at lines 52-58 and 874-893). It already computes pixel coordinates, grid cells, and occupancy keys. Two spatial build lanes were provisionally on Tuesday's plan: a first-class tile-grid implementation and a camera/real-estate application of it to shipped gameplay.

docs/game-design/OFFICE_ECONOMY_PROPOSAL.md section 7 ("Out of scope") already named the target precedent: "Per-desk/per-room placement, pathfinding gameplay -- the office floor stays a RENDER surface, not a sim (Rams #10)." That line covered the office-economy proposal specifically. This ADR generalizes it into standing doctrine for the whole engine, and rules explicitly on the sandbox's grid, on click-driven interaction, and on Wednesday's decorating math.

Pip, 2026-07-27 ~0950 (verbatim intent): "No spatial fact will ever become a gameplay input."

The structural precedent is the 2026-07-25 modal soft-locks: bugs caused by a second, UI-side notion of state drifting out of sync with the sim's actual state (a dual-source-of-truth failure class -- two stores of "what is true," reconciled imperfectly, produce exactly the class of bug where the screen and the sim disagree and the player gets stuck). A spatial layer that fed gameplay would recreate the same shape of risk at a larger scale: coordinates as a second, render-side state store that the sim would have to trust.

Decision

The office view is a render surface driven one-way from the deterministic sim. The sim owns counts and quantities; the render owns coordinates.

Amendment 2026-07-27 (Pip ruling 1143 + facilitator reweight, "a(3)-lite"): a first-class integer grid type -- Vector2i col/row addressing, footprint occupancy, snap -- is approved as render-layer infrastructure. Its consumers are prop placement, decorating render, walk theatre, and click-targets. This is chosen over both rejected options it replaces: formalizing the sandbox's ad hoc string-keyed 32px snap (that debt is retired by this grid superseding it), and the original tier-integers-only clause (overturned 2026-07-27 by Pip: "having the sim be a useful part of the game is growing on me and this gives me room downstream to explore with less technical debt"). Scope guard, non-negotiable: this grid is explicitly not a pathfinding or simulation grid; nothing sim-side may read cell state -- the doctrine's arrow rule (sim owns counts and quantities; the render owns coordinates) governs it fully, unchanged. Tile addressing for anything sim-facing remains tier integers only (ADR-0014's presence/location tiers, office-era tiers) -- never (x, y) coordinates, never grid cells, never pixel positions, as sim-side input.

Three riders (part of the ruling)

(a) Review clause. This decision is re-reviewed within one year, by 2027-07-27, as part of standing decision reviews. If office gameplay has by then out-grown tier integers, that review is where a spatial model gets re-litigated -- not an ad hoc exception beforehand.

(b) Render-local cosmetic interactions are unrestricted. Clicking a coffee pot and having it boil, a worker idling into a new pose, ambient office flavor -- none of this touches the sim. This is not an exception to the doctrine; it is outside its scope. The doctrine governs the input -> sim boundary. A render-local effect is input -> render only: no sim read, no sim write, nothing serialized, nothing replayed. If an interaction never crosses into the sim, this ADR has nothing to say about it.

(c) Click-the-render as a command surface is permitted, under the entity-ID invariant. A click on a rendered object MAY dispatch a sim action -- but the action serializes as (action, entity_id), never as coordinates. Replays record entity-scoped actions, not click positions. Every render-click command must also have a non-spatial path (an action menu reachable without touching the render at all), so the render is always a convenience input method, never the only way to issue the command.

Two named rot modes are forbidden under this rider, because both smuggle a spatial fact back into gameplay through the side door:

  1. Clickability conditioned on placement -- e.g. "a worker is only assignable when their sprite is near a desk." The moment eligibility depends on rendered position, position has become a gameplay input.
  2. Command availability conditioned on placement -- e.g. "the coffee pot is only usable if it has been placed in the kitchen." Same failure: a spatial fact (which room a prop's sprite sits in) is gating a sim action.

Eligibility and availability may depend only on sim-owned state (tier, ownership, unlocked/owned flags, resource costs) -- never on where something is drawn.

Beacons served / violated

Interaction contract

Rejected alternatives

Consequences / open questions