# Phase 06.7: Network model — client-trust + server illegal-position fall trigger - Discussion Log

> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.

**Date:** 2026-05-17
**Phase:** 06.7-network-model-client-trust-fall-trigger
**Areas discussed:** Scope, Wire shape + cadence, Anti-cheat envelope, Migration + Hard Rule 1

---

## Scope

### Q1: Phase A only or A+B bundled?

| Option | Description | Selected |
|--------|-------------|----------|
| A only — ship Phase B as 06.8 | Focused phase: flip self-player to client-trust + reconciler no-op for self. Closes the 10px diagonal-stop bug fast. Phase B (fall + legality check) becomes 06.8 — needs BNO legacy fall-spec extraction. | |
| A+B bundled in 06.7 | One phase delivers full safe-by-design model: client-trust + server fall backstop. Bigger phase, but the anti-cheat story is complete at ship. | |
| A only — B to backlog | Ship A; defer B until cheater abuse is observed. Reasonable for 50 CCU + private staging. Records the gap as a known anti-cheat TODO. | ✓ |

**User's choice:** A only — B to backlog.

### Q2: Other UAT findings to fold in beyond diagonal-stop drift?

| Option | Description | Selected |
|--------|-------------|----------|
| Just diagonal-stop drift | Single motivating bug. Any other movement glitch goes to its own ticket. | |
| Plus reconciler ghost-jitter D-57/D-58 anti-revert | 06.4 added regression tests for reconciler lerp + idle-state convergence. With self-reconciler removed, those regressions become moot. | |
| Plus inter-player collision arbitration | Two clients claim overlapping positions — server picks one. Seed flagged this as a risk. | |
| All of the above | Diagonal-stop + reconciler-test cleanup + inter-player arbitration. Bigger phase. | |

**User's choice (free-text):** "plus occasional movement hitching, seemingly from dropped packets. can't expect a perfect network connection, which the current system effectively does."

**Notes:** This reframed the wire-shape area — absolute-position-on-every-packet (not deltas) is the structural answer to packet-loss tolerance. Folded into Wire shape D-05.

### Q3: Inter-player collision arbitration — in scope or defer?

| Option | Description | Selected |
|--------|-------------|----------|
| Defer — not a current observed bug | Theoretical risk under client-trust; operator hasn't seen it. Keep 06.7 tight on movement-feel + dropped-packet tolerance. | ✓ |
| In scope — server picks arbitration rule | Lower account_id wins or latest-update-wins. Adds collision-detection-server-side scope + rule extraction from BNO. | |
| In scope — client-side visual nudge only | Server accepts overlap; client renderer applies a soft visual offset. | |

**User's choice:** Defer — not a current observed bug.

### Q4: D-57b/D-58b regression tests — retire as part of 06.7?

| Option | Description | Selected |
|--------|-------------|----------|
| Yes — explicit acceptance criterion | Self-reconciler removed = those tests become moot. Phase explicitly retires + replaces with a regression test proving self-position is NOT touched by snapshot apply. | ✓ |
| Retire silently — just delete during execution | Plans naturally remove dead tests. No acceptance criterion needed. | |
| Keep tests — they still guard remote-player reconciler | Self-reconciler is no-op but remote-player reconciler is unchanged. Existing tests may still apply with minor refactor. | |

**User's choice:** Yes — explicit acceptance criterion.

### Q5 (continuation): Continue or move on?

**User's choice (free-text):** "i want to consider one thing: how does Minecraft implement its semi-server-authoritative player movement model? that's another game where predictable movement is absolutely necessary, but it generally executes it well. is it something we can research and compare to our approaches?"

**Notes:** Triggered the Minecraft research directive. Surfaced as D-15 in CONTEXT.md.

### Q6: Add Minecraft network-model deep-dive as research directive?

| Option | Description | Selected |
|--------|-------------|----------|
| Yes — prioritize Minecraft + dropped-packet tolerance research | Research agent specifically investigates Minecraft Java Edition's PlayerPosition packet + server validation, plus packet-loss handling. Comparison table in RESEARCH.md. | ✓ |
| Yes — broad survey (Minecraft + others) | Wider sweep across multiple games. Slower research, broader perspective. | |
| Skip — seed plan is enough | Pattern is well-understood. Don't burn research time on comparison. | |

**User's choice:** Yes — prioritize Minecraft + dropped-packet tolerance research.

---

## Wire shape + cadence

### Q1: Position-update wire shape?

| Option | Description | Selected |
|--------|-------------|----------|
| `{x, y, facing}` — absolute, no velocity | Mirror Minecraft's PlayerPosition pattern. Server derives vx/vy by diffing snapshots. | |
| Full `{x, y, vx, vy, facing}` | Client also sends velocity. Useful for remote-player extrapolation. | partial |
| `{x, y, facing, seq, monotonic_at_ms}` | Absolute + monotonic timestamp + seq for ordering. Mirrors current cInput shape. | partial |

**User's choice (free-text):** "options 2 + 3. do we ever send sprite? maybe we should consider it if not. but probably only on sprite change event, and not every frame?"

**Notes:** Merged options 2 + 3 → `{x, y, vx, vy, facing, seq, monotonic_at_ms}`. Triggered the sprite-policy follow-up (Q2). Initial instinct was "sprite on event-only" but later corrected.

### Q2: Sprite_id wire policy?

| Option | Description | Selected |
|--------|-------------|----------|
| Separate `set_sprite` intent on change only | Mirrors `set_facing` precedent. Position_update stays lean. | partial |
| Include in position_update every tick | Larger packets, simpler client. | partial |
| Include in position_update, omit when unchanged | Optional field. | |

**User's choice (free-text):** "agent decides using best judgment. note that sprite updates are NOT rare. sprite changes when player starts moving, stops moving, changes direction etc. which happens very very often, sometimes many times per second."

**Notes:** Critical correction — first-pass instinct was wrong. Resolution: animation pose is **NOT derivable** from kinematics either (platforms / conveyors / hexport produce velocity-without-running) — so `anim_state` is a 1-byte enum shipped on every position_update. Special non-derivable poses (HexportIn/etc.) go on a separate `set_sprite_override` event-only intent. See D-08/D-09 in CONTEXT.md.

### Q3: Position-update cadence?

| Option | Description | Selected |
|--------|-------------|----------|
| Every tick (30Hz) regardless of motion | Steady stream. Mirrors Minecraft. Bandwidth tiny. | ✓ |
| Motion-only + idle heartbeat | Send on motion. 1Hz idle heartbeat. Saves bandwidth; could mask dropped packets at motion start. | |
| Adaptive — 30Hz during motion, 5Hz idle | Compromise. Adds complexity. | |

**User's choice:** Every tick (30Hz) regardless of motion.

### Q4: Position quantization?

| Option | Description | Selected |
|--------|-------------|----------|
| Int16 — pixel-snapped client-side first | Matches BNO snap-round model. Halves packet size. Removes float-comparison footguns. | ✓ |
| Float64 — full precision | Mostly wasted bits given pixel-snap rendering. | |
| Float32 | Compromise. No real benefit over int16. | |

**User's choice (free-text):** "Int16. --- a note on animation-sprite: server side deriving sprite from player x/y/vx/vy is not valid. there are many scenarios where player will have velocity but not be running, i.e. standing on moving platforms, conveyor platforms, or hexporting."

**Notes:** Confirmed Int16. The sprite-derivation correction is captured separately in D-08 of CONTEXT.md.

---

## Anti-cheat envelope

### Q1: What anti-cheat does 06.7 ship now (Phase B already deferred to backlog)?

| Option | Description | Selected |
|--------|-------------|----------|
| Cheap speed-cap only | Server validates `Math.hypot(x - prev.x, y - prev.y) <= MAX_DELTA_PER_TICK`. Discard on violation. | |
| Speed-cap + walkable-grid sanity | Validate position is on a walkable tile too. | |
| None — trust client fully (50 CCU + private) | Pre-launch staging = trusted operator pool. Skip entirely. | ✓ |
| Speed-cap with logging-only (advisory) | Logs every violation. Zero gameplay risk. | |

**User's choice:** None — trust client fully (50 CCU + private).

**Notes:** All cheap-anti-cheat options deferred to 06.8 alongside the fall reset.

---

## Migration + Hard Rule 1

### Q1: Migration strategy?

| Option | Description | Selected |
|--------|-------------|----------|
| Hard cut on staging branch | Staging is the only deployment target pre-launch. Land directly; if regress, `/gsd-undo`. | ✓ |
| Feature flag with operator toggle | Wire `NETWORK_MODEL` env. Operator A/B compares. Slower to ship. | |
| Feature flag with auto-promote | Default off, manual flip, promote-to-default, remove flag in cleanup phase. | |

**User's choice:** Hard cut on staging branch.

### Q2: CLAUDE.md Hard Rule 1 rewrite scope?

| Option | Description | Selected |
|--------|-------------|----------|
| Narrow movement carve-out | Rule 1 keeps server-authoritative thrust; one paragraph addition for movement exception. Lowest blast radius. | ✓ |
| Restructure into a trust-doctrine table | Per-domain authority matrix. More work. | |
| Defer doc update to Phase B / 06.8 | Ship undocumented divergence — risky. | |

**User's choice (free-text):** "narrow movement carve-out. note this game has no plans for scores or combat."

**Notes:** REBNO scope clarification — Rule 1 carve-out should list "chat origin, inventory, room transitions, persistence" only. Captured in D-13 of CONTEXT.md.

### Q3: Rollback strategy?

| Option | Description | Selected |
|--------|-------------|----------|
| `/gsd-undo` phase commits | Standard GSD rollback. Phase manifest lists every commit. | ✓ |
| Git revert to pre-06.7 SHA + force-push | Manual SHA revert. Bypasses phase tracking. | |
| Keep old reconciler code path branched off for quick re-enable | Dead-code branch. Conflicts with the "avoid feature flags" decision. | |

**User's choice:** `/gsd-undo` phase commits.

---

## Claude's Discretion

- Exact `anim_state` enum members + numeric byte values — pick during planning based on existing `SpriteStateMachine` states.
- Whether to keep `seq` echo / `last_input_seq` round-trip on the position_update path or drop it.
- Server-side `step()` retention strategy — whether server keeps running step() for platform/conveyor effect application or trusts client's reported position to include platform-carry.
- Whether to send remote-player broadcast diffs at full 30 Hz or downsampled.
- Whether `PredictionEngine` (`apps/client/src/prediction/predictor.ts`) stays for client-side local input responsiveness even with client-trust.

## Deferred Ideas

- **Phase B — server illegal-position detection + fall reset** → candidate Phase 06.8. Needs BNO legacy fall-spec extraction.
- **Inter-player collision arbitration** → defer until observed.
- **Speed-cap server-side validation** → backlog with 06.8.
- **Walkable-grid sanity check server-side** → backlog with 06.8 (requires server-side walkable_grid computation, currently client-only per T-06.1.02-01).
- **CLAUDE.md trust-doctrine table restructure** → defer until doctrine grows complex.
- **Server-side `step()` future role** for platform/conveyor effects → planner + researcher input needed.

### Reviewed Todos (not folded)

- `2026-05-16-split-playwright-smoke-into-3rd-workflow-job.md` (area: ci, score 0.9) — out of scope for 06.7 (CI workflow refactor, not network model). Stays in `.planning/todos/pending/` for a future CI-polish phase.
