---
phase: 06.7-network-model-client-trust-fall-trigger
type: human-uat
status: pending-operator
requirements: [REQ-CLI-08, REQ-CLI-04]
---

# Phase 06.7 Human UAT

[doc->REQ-CLI-08]

This script verifies the client-trust movement flip on staging. Tests 1, 2, 3, 5, and 6 are operator-executed. Test 4 is automated coverage only.

## Preconditions

- Staging deployed with the Phase 06.7 server and client commits.
- `PROTOCOL_VERSION = 4` confirmed live.
- Two Chrome browser sessions are available with two distinct authenticated operator accounts.
- Chrome DevTools Network conditions and WebSocket frame panels are accessible for packet-delay simulation and idle-cadence observation.
- Operator IP is allowlisted on staging.

## Pre-flight (run before any test)

[doc->REQ-SRV-14]

After ANY edit to `packages/protocol/src/*`, rebuild the protocol
package before running integ tests OR manual UAT against staging.

**Canonical operator command for protocol-handshake testing (UAT Test 4):**

    pnpm -C apps/server test:protocol-v4

This script (added in Plan 06.7-07) invokes the `pretest:protocol-v4`
npm-lifecycle hook FIRST, which runs `pnpm --filter @rebno/protocol
build`, THEN runs `vitest run test/protocol-v4-handshake.integ.test.ts`.
The same pre-flight applies to `pnpm -C apps/server test:integration`
and `pnpm -C apps/server test:full` (via their respective `pretest:*`
hooks).

**Do NOT use the direct `pnpm exec vitest` path:**

    # DISCOURAGED — bypasses pretest hooks, can produce stale-protocol-build false positives
    pnpm -C apps/server exec vitest run test/protocol-v4-handshake.integ.test.ts

npm lifecycle hooks only fire on the script invocation (`pnpm <script>`),
NOT on `pnpm exec` direct binary invocations. If you must use the
direct path (e.g., debugging with additional vitest flags), run
`pnpm --filter @rebno/protocol build` MANUALLY first.

**Why this matters (stale-protocol-build false positive):** UAT Test 4
(PROTOCOL_VERSION 3 client rejection) first-run reported `expected: 4400
actual: 524` + 'seat reservation expired' failures that were diagnosed
as a stale-protocol-build artifact (stale `@rebno/protocol` dist), NOT
a real defect. After `pnpm -C packages/protocol build`, all 3 tests
passed at HEAD `9816275`. See
`.planning/debug/v3-rejection-wrong-close-code.md`.

`test:full` incremental tsc rebuild adds ~1-2 s of latency to that
script's runtime — acceptable trade-off for eliminating the
stale-protocol-build false-positive class.

### Test 1: Diagonal-stop drift bug closed (D-02a)

Reproduction steps from the 2026-05-16 UAT:

1. Log in and navigate to BNCentral spawn.
2. Press W+D for up-right diagonal movement for 2 seconds.
3. Release both keys simultaneously.

Expected: sprite stops at its current pixel position with no horizontal shift and no approximately 10 px snap right or left.

Observed: ___

Pass/Fail: ___

### Test 2: Dropped-packet hitching closed (D-02b)

1. Open Chrome DevTools, then Network, then Throttling. Use Slow 3G or a custom profile near 5% packet loss / 150 ms RTT.
2. Hold W to run north for 5 seconds.

Expected: continuous smooth movement. Each `position_update` is self-describing under the Phase 06.7 model, so a single dropped packet should be invisible at 30 Hz.

Observed: ___

Pass/Fail: ___

Note: if hitching appears, capture HAR and a screenshot. This is the regression signal for this bug.

### Test 3: Two-player smoke (REQ-CLI-08 hard milestone regression confirm)

1. Operator A logs in on Browser 1. Operator B logs in on Browser 2. Both join BNCentral.
2. A and B see each other's sprites.
3. A walks in a square: W, D, S, A for 1 second each. B observes A moving smoothly, with no rubber-band and no visible diagonal-stop drift.
4. B walks in a square. A observes the same smoothness.
5. Both send chat messages.

Expected: movement observations pass, and chat appears with correct origin attribution.

Observed: ___

Pass/Fail: ___

### Test 4: PROTOCOL_VERSION 3 client rejection — AUTOMATED COVERAGE ONLY (D-12)

No operator browser action is required. The v3-client-rejection path is covered exclusively by automated integration coverage:

- File: `apps/server/test/protocol-v4-handshake.integ.test.ts`
- Assertion: a client joining with `protocol_version: 3` receives close-code 4400 / `PROTOCOL_VERSION_MISMATCH`; a client with `protocol_version: 4` succeeds.
- Operator verification at sign-off time: confirm this test exists and record the latest result from `pnpm -C apps/server test:protocol-v4` (this script auto-builds @rebno/protocol first — see Pre-flight section).

Expected exit: 0; reject and accept cases both passing.

Observed (test-suite exit code): ___

Pass/Fail (automated): ___

Rationale: this is a deterministic server-side handshake contract and is better verified by integration coverage than by a manual browser override. Adding a dev-only downgrade control is out of scope for the Phase 06.7 trust-flip work.

### Test 5: Rollback drill (D-14)

1. Capture the current staging commit SHA.
2. Do not actually run `/gsd-undo`.
3. Verify the rollback target references the four Phase 06.7 plan commits.

Expected: documented rollback target is the four Phase 06.7 plan commits.

Observed: ___

Pass/Fail: ___

### Test 6: Idle-cadence steady 30 Hz network confirm (D-06)

[doc->REQ-CLI-04]

This closes the manual half of the D-06 validation row from `06.7-VALIDATION.md`. The automated half is covered by `apps/client/src/__test__/position-dispatcher.test.ts`.

1. Log in to BNCentral and do not press any movement keys.
2. Open Chrome DevTools, then Network, then the WebSocket frame panel for the Colyseus connection carrying `position_update`.
3. Leave the player idle for 5 seconds while watching the WebSocket frames.

Expected: approximately 150 `position_update` frames over the 5-second window. The acceptable range is 120-180 frames to allow browser scheduler jitter. Each frame carries the same x/y with `vx=0` and `vy=0`. The cadence is steady, with no gaps longer than approximately 100 ms.

Observed (rough frame count over 5 s): ___

Observed (any gaps > 100 ms?): ___

Pass/Fail: ___

Note: if frame count is dramatically lower, such as fewer than 30 frames over 5 seconds, the dispatcher is likely gating on movement. Capture the WebSocket trace and route the issue to gap closure.

## Sign-off

- Operator: ___
- Date: ___
- Overall pass/fail: ___
- Phase 06.7 ready for `/gsd-verify-work`: yes / no
- Open issues: ___
