# T008 — In-App Confirmation: Feel / Pacing Fixes

**Date:** 2026-05-27
**Task:** T008 (verification / driving — no product-code edits)
**Goal:** `baolife-feel-pacing`
**Oracle:** eyes-on confirmation in the running iOS app

## Environment

- Backend: `cd server && npm run dev` on `:8001`, MySQL `lifesim` on localhost (root / no password, matching `server/.env.example`).
  - NOTE: there was no `server/.env` at session start — only `.env.example`. A temp `.env` (copied from the example) was created to boot the server, then **removed** at the end. The repo tree is left as found.
  - NOTE: `server/src/services/character/avatar.ts` was already modified (`M`) at session start (pre-existing avatarLibrary WIP). NOT touched by this task.
- iOS: scheme `BaoLife`, built Debug for simulator `iPhone 17` (UDID `C0663EED-464C-41A7-8C03-9FF499A1C696`, iOS 26.4). Launched with `--ws-url ws://localhost:8001`.
- Driven with `idb` (logical-point coords; the screenshots are 2x physical, but `idb ui tap`/`describe-all` use logical points).
- Test player: device-vendor UUID maps to existing DB player `C03C00E1-0AE2-499F-9D7F-50A10AE4A752` ("Test hero"). DB seeding (local only) was the primary lever for the time-gated checks.

## Results

| # | Check | Result | Evidence |
|---|-------|--------|----------|
| FU2 | Mid-session death flips to DeathView immediately (no reconnect) | **CONFIRMED-IN-APP** | `12-after-okay.png`, `13-deathview-clean.png` |
| FU3 | Offline digest shows NON-ZERO money delta over multi-day absence | **CONFIRMED-IN-APP** | `14-welcomeback.png` (+$60), `15-after-digest.png` ($3000 -> $3060) |
| SURVIVAL | Meal sink drops hunger/thirst; no starve-to-death; oscillates in healthy band | **CONFIRMED-IN-APP** | DB sample 90/90 -> 18/24 across meals; `23-survival-confirmed.png` (alive, health 91) |
| CADENCE | Events at a digestible rhythm — not a wall, not silence | **CONFIRMED-IN-APP** | `16/17/18/19` (health), `21` (friendship), `22` (work), `24` (travel) — distinct single events spaced across the day; server log: 6 `event_prompt`s spaced over many `u` minute-ticks |

## Detail

### FU2 — immediate DeathView (the key new check)

Seeded the connected test player to `ageYears=121` (> 120 = guaranteed death in
`checkDeath`) with `hourOfDay=23, minuteOfHour=55` so the next day tick fired within
a few in-game minutes of Instant play. Relaunched the app (clean connection) and set
Instant speed.

- The character died **while connected**. Server log: `Sending message type: lifeSummaryEvent, size: 482 bytes`.
- DB at that moment: `status='dead'` while `connection_status='connected'` — death set mid-session, NOT on a reconnect.
- The app switched to the DeathView ("In Loving Memory / Test hero / 121 years lived /
  LIFE SCORE 1,213 / Net Worth $3,000 / Begin a New Journey") immediately — no manual
  reconnect. This is the fix the prior session flagged (death previously only showed on reconnect).
- Code path verified: `PlayerSession.processDayTick()` -> `checkDeath` -> `handleDeath` (sets
  `status='dead'`) -> `this.send({type:'lifeSummaryEvent', ...})` over the live socket.

### FU3 — non-zero offline digest money

Restored an alive/playing age-30 character ($3000), then backdated DB `updated_at` by 3
days (`lastOnline` is derived from `updated_at` in `loadPlayer`). Relaunched the app.

- Connect path ran `processOfflineTime` (server log: `Processing 4320 minutes of offline
  time for player`) and sent `offlineDigest` (153 bytes).
- The app showed the **"Welcome Back / 3d away"** card with **Money +$60** (NON-ZERO) — the FU3
  fix (`applyProratedFinances` proration of the leftover sub-week). Previously this was a flat $0.
- After dismissing, the dashboard balance read **$3,060** (= $3,000 + $60), corroborating the delta.
- The digest's "While you were away" line also said "You have died!" — a notable-event message
  captured during the offline iteration (a rare death roll mid-sim); the authoritative end-state
  saved as alive. This is cosmetic to FU3; the money-delta proration is what FU3 fixes and it is clearly non-zero.

### SURVIVAL — meal hunger/thirst sink

Constants (verified): `HUNGER_RATE_PER_HOUR=3`, `THIRST_RATE_PER_HOUR=4`,
`MEAL_HUNGER_REDUCTION=50`, `MEAL_THIRST_REDUCTION=65`. Single shared sink
`applyMealEffect` is called from `getIntradayActivity` (the daily-plan executor both online and
offline loops run), at breakfast/lunch/dinner slots.

- Seeded `hunger=90, thirst=90` at 11:50 (lunch fires at hour 12), then played at Instant
  across a full in-game day. The saved DB state advanced to the next day (0:02) with
  **hunger=18, thirst=24** — a large drop from 90/90, exactly the meal-sink behavior.
- Final state: age 30, **alive**, hunger 18, thirst 24, **health 91** (recovered). The character
  thrived rather than pegging hunger/thirst at 100 and starving. SURVIVAL fix confirmed.

### CADENCE — digestible event rhythm

Over a single fast-forwarded in-game day I encountered a sequence of distinct, single
event prompts — a health-tests dilemma, a drifting-friendship dilemma, a work-feedback
review, and a long-distance-travel dilemma — each one a meaningful one-at-a-time choice,
spaced across the day (server log shows 6 `event_prompt`s separated by long runs of `u`
minute-tick messages, not a simultaneous burst). No wall of prompts, no decades of silence.
The choice-resolution path also worked end-to-end (e.g. "Get all the tests done ($500)" applied
-$500 / -10 energy / Stress +15 / Happiness -10 / Health -5 and updated the running balance),
which incidentally re-exercises the C1-resolved reward/effects path.

## Blockers / Notes

- No hard blockers. Live raw fast-forward observation of hunger/thirst is awkward because
  event prompts pause the loop on nearly every connect, and the DB only reflects in-memory
  state on periodic/hour-boundary saves — so DB sampling looked "frozen" mid-hour. The
  decisive SURVIVAL evidence came from the post-meal save (90/90 -> 18/24), which is
  unambiguous.
- The live-reload (tsx watching the WIP `avatar.ts`) caused a couple of server restarts early
  in the session; it settled and did not affect the confirmations.

## Bottom line

**All 4 checks CONFIRMED-IN-APP**, including the two priority items: FU2 (mid-session death
flips to DeathView immediately, no reconnect — verified by `status='dead'` while
`connection_status='connected'` + the immediate DeathView screen) and FU3 (offline digest
shows a non-zero **+$60** money delta over a 3-day absence). SURVIVAL (meals drop
hunger/thirst into a healthy band; character thrives at age 30) and CADENCE (digestible,
spaced single events) both confirmed during live fast-forward play.

### Screenshots (`/tmp/baolife-feel/`)

- `01`–`08` — launch, character-creation flow, dashboard, reconnect
- `09`–`13` — FU2: seeded age-121, Instant, immediate DeathView
- `14`–`15` — FU3: Welcome Back +$60, dashboard at $3,060
- `16`–`20` — CADENCE/event-resolution: health dilemma + result
- `21`–`24` — SURVIVAL + CADENCE: friendship/work/travel events; survival-confirmed dashboard
