---
phase: 06.7
title: Network model shift — client-trust + server illegal-position fall trigger
promoted_from: .planning/todos/pending/2026-05-16-shift-network-model-to-client-trust-with-server-illegal-posi.md
promoted_at: 2026-05-17
depends_on: Phase 06.6
requirements:
  - REQ-SRV-03
  - REQ-SRV-14
  - REQ-CLI-04
  - REQ-CLI-08
files_in_scope:
  - apps/server/src/RebnoRoom.ts
  - apps/client/src/prediction/reconciler.ts
  - packages/game-logic/src/collision.ts
  - apps/server/src/state.ts
  - apps/client/src/net/colyseus-client.ts
  - CLAUDE.md
---

## Problem

Current model is hybrid client-prediction + server-authoritative with reconciler
snap-back when divergence exceeds threshold. Operator UAT on staging surfaced
**movement feels choppy** because server is force-correcting predicted positions
back to positions the player "shouldn't" be at (small reconciler snaps mid-walk).
This game's feel depends on **predictable movement** — every input → exact same
on-screen response, no rubber-band.

Concrete symptom example from operator UAT (2026-05-16):
- Diagonal stop produces ~10 px horizontal shift before movement ends — likely
  reconciler snap from client-vs-server position drift.

Current hard rule in CLAUDE.md says "Server-authoritative. Clients send intent.
Server emits state. Never trust client positions." This rule needs revision for
movement specifically.

## Solution

Two-phase plan:

**Phase A (now / pre-launch hardening):** Flip the trust model for player movement.
- Client owns its position. Sends `position_update {x, y, vx, vy, facing}` to server
  every tick.
- Server stores client-reported position as authoritative state.
- Server still owns: chat origin (verify), inventory, scores, combat math,
  room transitions, fall detection (Phase B).
- Reconciler in `apps/client/src/prediction/reconciler.ts` becomes a no-op for
  self-player (only used for REMOTE players where server state IS authoritative).
- Update CLAUDE.md Hard Rule 1: clarify movement is client-trusted; non-movement
  state stays server-authoritative.

**Phase B (when "fall" feature lands):** Server detects illegal positions, triggers
fall as reset.
- Server keeps a tick-by-tick check:
  `isPositionLegal(player.x, player.y, room.walkable_grid)`.
- If position is illegal (e.g. unsupported over a void, inside a wall) for > N
  ticks (~250-500 ms grace), server triggers fall event.
- Fall = forced animation + position reset to last-known-legal-position or
  designated "respawn" point.
- This is the anti-cheat backstop: client can lie about position but only briefly
  before fall resets them.

**Why this works for BNO specifically:**
- Original BNO is a peer-to-peer 39dll networking game where clients DID trust
  each other's positions — REBNO can mirror that feel.
- 50 CCU target means abuse window is narrow; fall mechanic handles
  boundary-cheat case.
- Predictable movement is the core value (per CLAUDE.md Core Value: "walk around
  and chat ... bug-free").

## Risks (carry into discuss-phase)

- Cheaters can teleport visually (mitigated by fall-reset, but visible to other
  players briefly).
- Two players claiming same position → server arbitration model needed for
  collisions between players.
- Position-update spam → rate-limit to game tick rate (30 Hz per CLAUDE.md
  Extracted Constants).
- Backwards compat: existing reconciler code + tests need careful refactor or
  gate behind a feature flag during transition.

## Next

Run `/gsd-discuss-phase 06.7` to gather discussion context, then `/gsd-plan-phase 06.7`.
