---
phase: 06.2-gap-closure-d-50-d-53-uat-2026-05-13
plan: "07"
subsystem: client-render
tags: [fix, D-45, nameplate, e2e, tdd]
dependency_graph:
  requires: []
  provides: [D-45-closed, nameplate-offset-gate]
  affects: [apps/client/src/render/Nameplate.ts, apps/client/src/__test__/nameplate.test.ts, apps/client/test/e2e/cli-08-nameplate-offset.e2e.test.ts]
tech_stack:
  added: []
  patterns: [TDD RED/GREEN, DOM-mirror e2e pattern]
key_files:
  created:
    - apps/client/test/e2e/cli-08-nameplate-offset.e2e.test.ts
  modified:
    - apps/client/src/render/Nameplate.ts
    - apps/client/src/__test__/nameplate.test.ts
decisions:
  - "D-45: nameplate Y formula corrected from spriteY - spriteHeight/2 - 4 to spriteY - spriteHeight - 16 per CLAUDE.md canonical anchor"
  - "TDD flow: RED commit f20f0ad → GREEN commit 1d24c4e → e2e commit cc621ce"
  - "e2e path: apps/client/test/e2e/ (not apps/client/e2e/ as listed in plan — actual project e2e structure used)"
metrics:
  duration: "~15 min"
  completed: "2026-05-13"
  tasks_completed: 2
  tasks_total: 2
  files_changed: 3
---

# Phase 06.2 Plan 07: D-45 Nameplate Y Offset Fix Summary

**One-liner:** Fixed nameplate Y formula from center-anchor (spriteY - spriteHeight/2 - 4) to top-anchor (spriteY - spriteHeight - 16), locking via TDD unit test + Playwright e2e gate.

## Tasks Completed

| Task | Name | Commit | Files |
|------|------|--------|-------|
| 1 (RED) | Add failing D-45 tests | f20f0ad | nameplate.test.ts |
| 1 (GREEN) | Fix Nameplate.ts + update tests | 1d24c4e | Nameplate.ts, nameplate.test.ts |
| 2 | Author e2e gate | cc621ce | cli-08-nameplate-offset.e2e.test.ts |

## What Changed

### Old formula (D-45 root cause)
```typescript
const NAMETAG_OFFSET_Y = 4;
// follow():
const textY = spriteY - spriteHeight / 2 - NAMETAG_OFFSET_Y;
```
With `spriteY` = sprite FEET (origin 0.5, 1) and `spriteHeight` = 48:
- `textY = 100 - 24 - 4 = 72` (places text in the MIDDLE of the sprite — overlaps head)

### New formula (D-45 fix)
```typescript
const NAMETAG_OFFSET_Y = 16;
// follow():
const textY = spriteY - spriteHeight - NAMETAG_OFFSET_Y;
```
With `spriteY` = 100 and `spriteHeight` = 48:
- `textY = 100 - 48 - 16 = 36` (places text 16 px ABOVE the sprite top edge)

### Asserted (input → expected-y) tuples

| spriteX | spriteY | spriteHeight | expected nameplateY |
|---------|---------|--------------|---------------------|
| 160 | 100 | 48 | 36 (100 - 48 - 16) |
| 200 | 200 | 64 | 120 (200 - 64 - 16) |
| 100 | 200 | 32 | 152 (200 - 32 - 16) — updated old test |
| 100 | 100 | 48 | 36 (100 - 48 - 16) — updated old test |

### e2e gate tolerance

The Playwright spec (`cli-08-nameplate-offset.e2e.test.ts`) asserts:
```
nameplateY >= spriteTop - 16 - 1  (not too far above)
nameplateY <= spriteTop - 16 + 1  (not overlapping head)
```
±1 px rounding tolerance for Phaser `Math.round()` in the DOM mirror update.

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 1 - Path correction] e2e file path adjusted to match project structure**
- **Found during:** Task 2
- **Issue:** Plan specified `apps/client/e2e/cli-08-nameplate-offset.spec.ts` but that directory does not exist; all e2e tests live in `apps/client/test/e2e/` with `.e2e.test.ts` suffix
- **Fix:** Created at `apps/client/test/e2e/cli-08-nameplate-offset.e2e.test.ts`
- **Files modified:** cli-08-nameplate-offset.e2e.test.ts
- **Commit:** cc621ce

**2. [Rule 1 - Test file path correction] Unit test updated in existing test file**
- **Found during:** Task 1
- **Issue:** Plan referenced `apps/client/src/render/Nameplate.test.ts` which doesn't exist; actual test lives at `apps/client/src/__test__/nameplate.test.ts`
- **Fix:** Updated the existing test file at the correct path
- **Files modified:** apps/client/src/__test__/nameplate.test.ts
- **Commits:** f20f0ad, 1d24c4e

**3. [Rule 1 - Bug] Old follow() test assertions updated for corrected formula**
- **Found during:** Task 1 GREEN
- **Issue:** Two existing follow() tests used the old formula expected values (180, 72) — leaving them would have broken the test suite with the new formula
- **Fix:** Updated expected values to 152 and 36 respectively, with audit comments documenting the change
- **Files modified:** apps/client/src/__test__/nameplate.test.ts
- **Commit:** 1d24c4e

## TDD Gate Compliance

- RED gate commit: f20f0ad (`test(06.2-07): add failing RED tests for D-45 nameplate Y offset`)
- GREEN gate commit: 1d24c4e (`feat(06.2-07): fix D-45 nameplate Y offset — 16 px above sprite top edge`)
- REFACTOR: not needed (implementation was minimal and clean)

## Verification

- `pnpm --filter @rebno/client test -- nameplate.test` — 11 tests pass
- `grep -n "spriteY - spriteHeight - NAMETAG_OFFSET_Y" Nameplate.ts` — 2 matches (comment + code)
- `grep -n "NAMETAG_OFFSET_Y = 16" Nameplate.ts` — 1 match
- `grep -n "spriteY - spriteHeight / 2" Nameplate.ts` — 0 matches (old formula removed)
- REQ tags: `[impl->REQ-CLI-04]` + `[impl->REQ-CLI-08]` in Nameplate.ts; `[unit->REQ-CLI-04]` in nameplate.test.ts; `[int->REQ-CLI-04]` + `[int->REQ-CLI-08]` in e2e spec
- Playwright e2e: spec authored; staging run deferred to W3 UAT re-run (requires live staging server)

## Known Stubs

None — the formula fix is wired end-to-end from Nameplate.ts through the DOM mirror to the Playwright assertion.

## Threat Flags

None — this is a UI position offset fix; no new network endpoints, auth paths, or schema changes introduced.

## Self-Check: PASSED

- [x] `apps/client/src/render/Nameplate.ts` — FOUND
- [x] `apps/client/src/__test__/nameplate.test.ts` — FOUND (at correct path)
- [x] `apps/client/test/e2e/cli-08-nameplate-offset.e2e.test.ts` — FOUND
- [x] Commit f20f0ad — RED test
- [x] Commit 1d24c4e — GREEN implementation
- [x] Commit cc621ce — e2e gate
