# BaoLife MVP — Session Handoff (2026-04-13)

**From:** Claude (autonomous MVP execution session)
**To:** Craig
**Branch:** main (19 commits ahead of origin/main)

---

## TL;DR

The automated portion of the MVP plan is **done**. Everything that could be verified without manual Apple Developer Portal access has been verified. One last mile of user-facing actions remains before a signed TestFlight build is in friends' hands. That last mile is documented below.

**What exists now that didn't at session start:**

- Backend test suite: **1127/1127 passing** (up from 1087/1090)
- Headless lifetime simulator: 34 e2e tests covering 40+ simulated years of gameplay across 25 distinct seeds
- **One real game bug found and fixed** (interactive events never deduped, player saw the same event every hour)
- iOS test suite: 19/19 passing (was 0/19, build was broken)
- iOS Release build: compiles cleanly (signing is a separate known issue)
- CI wired: new TypeScript job runs full suite + simulator on every push
- Dead code removed: legacy web frontend, legacy PHP API
- Docs reconciled: PROJECT_STATUS, CLAUDE.md, MEMORY.md all current
- TestFlight release notes written for your friends

## Current State

### Tests
| Suite | Pass | Total | Notes |
|-------|-----:|------:|-------|
| TS backend (unit + integration) | 1127 | 1127 | Includes 34 new e2e simulator scenarios |
| iOS unit | 16 | 16 | Was 0 — test target was broken |
| iOS UI | 3 | 3 | Just the default launch/performance tests |

### Code health
- `npx tsc --noEmit` — clean
- `npm run test:run` — green, ~35s
- `npm run test:e2e` — green, ~10s
- Xcode Debug build — clean
- Xcode Release build (without signing) — clean

### Git state
- 19 commits ahead of `origin/main`
- Working tree is clean apart from the Xcode user state file that was dirty at session start
- All commits use proper Co-Authored-By attribution

## The One Real Bug We Found and Fixed

### Symptom (as seen by the simulator)
A 1-year simulation with a 10-year-old character fired **8,759 event prompts** but resolved only **1 unique event id**. Same event, over and over, for an entire year. In the real game this would mean every player saw the same question every hour for their whole life.

### Root cause
`EventEngine.promptNext` calls `markAskedQuestion(player, eventId)` only when the selected event is `kind: 'passive'`. For interactive events (the common case — any event with a choice), `askedQuestions` was never updated. `selectNextEligibleEvent` dedupes on `askedQuestions.has(id)`, so the engine kept returning the first-eligible event every single time.

### Fix
Added `markAskedQuestion()` inside `EventResponder.respond()` right after `applyChoiceEffects`. Commit: `0b08443c`.

### Regression tests pinning the fix
- `tests/events-v2/engine.response-resolution.test.ts` — "marks interactive events as asked after resolution so they dedupe"
- `tests/e2e/lifetime-simulator.test.ts` — "1-year run fires at least 3 unique v2 events (no repeat-forever bug)"

Both fail loudly if the fix is reverted.

## What YOU need to do before TestFlight

These are the handful of manual steps that require your hands on the keyboard and your Apple Developer Portal login.

### Step 1 — Fix the provisioning profile (BLOCKER)

The Release build currently fails on device signing because the iOS Team Provisioning Profile for `baolife.baolifesim` doesn't include the Push Notifications capability.

**How to fix:**

1. Open `ios/lichunWebsocket.xcodeproj` in Xcode
2. Select the `lichunWebsocket` target (not Tests/UITests)
3. Go to **Signing & Capabilities** tab
4. If "Automatically manage signing" is checked, Xcode should offer to fix the profile when you hit Build for Any iOS Device. Click the "Fix" or "Update provisioning profile" button if it appears.
5. If not, click the **+ Capability** button at the top-left and add **Push Notifications**. Xcode will regenerate the profile.
6. Confirm the Signing team is **your** Apple Developer team (not mine, not anyone else's).
7. Set the active scheme destination to **Any iOS Device (arm64)** at the top of Xcode.
8. Do a test archive: **Product > Archive**. If it succeeds, you're unblocked.

If Xcode still complains:

- Check that your Apple Developer account at developer.apple.com has Push Notifications enabled for the `baolife.baolifesim` App ID under Certificates, Identifiers & Profiles.
- If needed, delete the local cached profile from `~/Library/MobileDevice/Provisioning Profiles/` and let Xcode regenerate.

### Step 2 — Archive and upload to TestFlight

Once Step 1 works:

1. In Xcode: **Product > Archive** (make sure scheme destination is "Any iOS Device" not a simulator).
2. When the Organizer opens, select the new archive.
3. Click **Distribute App**.
4. Choose **TestFlight Internal** (or **App Store Connect**, depending on your distribution setup).
5. Upload. Takes a few minutes.
6. Log into **App Store Connect > TestFlight**, confirm the build appears and passes processing (can take 10-30 min).
7. Add yourself + up to 10 friends to the Internal Testing group.
8. Share the TestFlight invite link.

### Step 3 — Share the release notes

Hand your friends `docs/MVP_TESTFLIGHT_NOTES.md` (it's written for them — no jargon). Copy-paste or send the markdown.

### Step 4 — Push this branch

```bash
cd /Users/craigvandergalien/Documents/GitHub/lichun
git push origin main
```

All 19 commits are ready. They're focused, atomic, and each has a clear commit message. You can review them with `git log --oneline 00b30eb5..HEAD`.

### Step 5 — Deploy the backend fix to production

The interactive-event-dedup fix (commit `0b08443c`) matters for live players. Deploy it:

```bash
ssh lichun-master
cd /var/www/lichun.app/lichun
sudo -u www-data git pull
sudo systemctl restart baolife-websocket.service
tail -f /var/log/baolife-websocket.log  # watch for errors
```

**No `npm run build` step needed** — production runs `tsx src/index.ts` directly (confirmed 2026-04-13).

## What we explicitly did NOT do

Scoped out of this session because they're either (a) not MVP-blocking or (b) require your hands on the keyboard:

- **Snapshot testing for iOS screens** — deferred to post-MVP. UI test coverage is limited to the existing launch/smoke tests. Once friends report real screens that are broken, we can snapshot-test those specifically.
- **Android work** — the Android app remains at ~85% parity with known issues. MVP is iOS-only. Android is v1.1.
- **1000-seed soak test** — we run 25 unique seeds (5 fixed + 20 in the soak block). 1000 is overkill for MVP confidence and runs too slow for CI. Deferred.
- **Content expansion** — 200+ events already exist. Do not add more until your friends play and tell you what's missing.
- **New features** — nothing in this session added product surface area. Everything was cleanup, testing, or bug fixing.
- **App Store public release** — MVP ends at TestFlight. Public release comes after friend feedback.

## What to pay attention to after friend testing

When the bug reports start coming in:

1. **Before fixing any bug, try to reproduce it in the simulator.** If you can, the fix gets a regression test for free. If you can't, the simulator's blind spot is itself a finding — extend the simulator's invariants to catch that class.
2. **Triage by crash-vs-stuck-vs-boring.** Crashes are priority 1. Stuck states are priority 1. "I got bored" is priority 2 but tells you the most about the content direction.
3. **Re-run the decade stress test and soak block after every bug fix.** They're cheap. They'll catch side effects.

## Commit Summary (session)

```
8ec1defc docs(mvp): add TestFlight release notes for internal testers
5e06bf62 build(ios): bump CURRENT_PROJECT_VERSION to 2 for MVP TestFlight
6c8116af test(ios): repair EventV2DecodingTests after 7-service split
1862891e test(e2e): add 20-seed × 2-year soak to simulator
06f420a5 test(e2e): add event-firing-rate + liveness invariants
0b08443c fix(events-v2): mark interactive events as asked after resolution ← real bug fix
aed7cf47 test(events-v2): pin C1 regression — positive stat rewards apply
76d7e6d5 test(e2e): add save/load round-trip invariant + messageTypeCounts
75f470eb ci: add TypeScript backend job + e2e simulator to CI + package-lock
187b4e16 test(e2e): add scenario suite — 11 lifetime simulator tests
e94194b2 test(e2e): add headless lifetime simulator harness + smoke test
e705efe2 docs(plan): record ADR-001 — simulator uses PlayerSession direct
38182850 docs(status): rewrite PROJECT_STATUS.md for 2026-04-13 reality
d5b05d6d chore(security): remove legacy PHP api/ directory
6ff4de45 chore: remove deprecated web frontend (index.html, main.js)
606c8d8b test(contracts): repair frontend contract tests after 7-service iOS split
b43942cf docs: verify and record production backend identity (2026-04-13)
a2a07bb9 docs(plan): MVP implementation plan with bite-sized tasks for Phase 0+1
39ec4dc9 docs(plan): MVP completion design (2026-04-13)
```

## Your move

1. Step 1–5 above. The bulk of the manual work is 30 minutes of Xcode clicking plus one SSH deploy.
2. Share TestFlight link with 10 friends.
3. Wait a week. See what comes back.
4. When you're ready for the next round, re-engage Claude with: "Here's the feedback from round 1, fix these." I'll take it from there.

Good luck. Ship it.
