# BaoLife — Release Hardening Campaign — PROGRESS

**Goal:** Run several high-leverage, player-felt release-hardening slices (waves), each discovered → root-cause-verified → fixed → proven → **shipped (commit+deploy+verify)** → audited, fully tied off before the next.

**Status:** ✅ DONE — `full_outcome_complete: true`. Campaign shipped 2 waves (straight_a `08065b1e`, offline-credit `72a4f0a8`), both prod-verified; vein mined to diminishing returns. Suite green 145 files / 1691 / 0 failed.

**Baseline:** HEAD `6a7d9c64`; tsc clean; vitest 143 files / 1683 / 0 failed.

**Authorization:** Craig pre-authorized commit+deploy per verified slice ("Continue to another slice" + "tie off each thread fully"). Pause only for risky/large/ambiguous/data-irreversible slices.

## Prior shipped slices (before this board)

| Slice | Commit | What shipped |
|-------|--------|--------------|
| baolife-economy | `db3dd13b` | Career progression was dead code; wired tier promotions/raises into both loops; money now meaningful + bounded |
| baolife-mechanics | `6a7d9c64` | Online players were missing weekly 5% romance events + GPA drift (offline-only); wired into online weekly tick |

## Carry-over constraints

- Green before deploy (tsc + full suite, no regression) + self-review diff + scope clean.
- No avatar WIP; no `ws/`; mirror loops online+offline; diamonds/IAP safe.
- Verify root cause + intent before fixing; don't resurrect cut features.
- Stage only wave files + goal docs; verify prod HEAD + service after deploy.

**(Note: an earlier "tool outage" scare was a false alarm — Bash outputs were just heavily delayed and all landed at once; everything executed fine. Also corrected: the mechanics slice shipped as `322b9c33`, not `6a7d9c64` (earlier hash misread from garbled output).)**

## Wave log

| Wave | Status | Slice | Commit / Deploy |
|------|--------|-------|-----------------|
| W1 | ✅ SHIPPED | **straight_a achievement** reachable (GPA threaded, scale reconciled at source) | `08065b1e` (deployed, prod-verified) |
| — | steered | Craig: offline survival = KEEP PAUSED (won't-fix, by design); NEXT = tackle #3 | — |
| W2 | ✅ SHIPPED | **Offline milestone credit** — idempotent lifecycle drain in the offline job (Option B) | `72a4f0a8` (deployed, prod-verified) |
| — | ✅ DONE | Campaign final audit — full_outcome_complete: true | — |

**Wave 2 outcome:** The offline event-catalog "gap" was investigated (Scout + 2 Judges). Findings: it's a REAL bug (a player offline ~a month can have their character's graduation window silently skipped → lost popup/diamonds/straight_a credit) but **rare** (offline advances ~1 in-game day/real day). Option A (offline career life-goal credit) is **self-healing filler — not shipped** (would be churn). Option B (reconnect-backfill of crossed milestone windows) is the real fix but a population-wide timing change for a low-frequency bug → **escalated to Craig** (B now vs accept as known edge case + wrap).

**Craig's decisions (2026-05-28):** ① Offline survival stays **paused by design** (recorded to memory `project_offline_survival_paused_by_design.md` — do not re-flag). ② Tackle the **offline event-catalog wave (#3)** next — scope carefully, check in before anything risky. Wave 2 scoping (T006 Scout) underway.

**Judge ruled (T002):** ship **#2 straight_a** (safest confirmed slice); **escalate #1** offline-survival to Craig (intentional/UX-risky — mirroring would make players "starve while away"); **defer #3** (offline doesn't run the event catalog at all — bigger than a drain fix). GPA landmine: stored 0-100 (default 50) not 0-4.0 — fix must reconcile scale or it unlocks for everyone.

### ⬆️ ESCALATION for Craig (#1 — offline survival, design call)
Offline players' survival is **frozen**: hunger/thirst can't rise while away (meal sink runs but no hourly rise), so no starvation/health-decay; offline death is **old-age only**. Mirroring online survival offline would risk a "you starved/died while you were away" regression + double-apply with the daily death roll. **Keep paused (current, arguably correct for a mobile life-sim) or simulate offline starvation?** Recommend keeping paused unless you want it as a deliberate mechanic.

## W1S Scout — findings (the prod offline engine is `LoopManager.initLifeSim`; `GameEngine.runGameTick` is orphaned/test-only)

1. **Offline players get NO hourly survival (`applyHourlySurvival`)** — hunger/thirst rise, starvation, energy penalty. Called only at `PlayerSession.ts:190` (online) + the orphaned `GameEngine.ts:571`. `LoopManager.initLifeSim` hourly block (236-285) lacks it. HIGH raw leverage BUT **design-ambiguous** (offline-survival-off may be INTENTIONAL — punishing players for being away is often bad UX; cf. v2 interactive events deliberately not firing offline). Also overnight energy recovery diverges (+1/day offline at LoopManager:368 vs +35 online). **Needs Judge: bug vs intentional?**
2. **`straight_a` achievement is unreachable on BOTH paths** — `schoolMilestones.ts:488` queues graduation with `{level}` only (gpa never put on the queue at the source); `achievements.ts:629` checks `eventData.gpa>=4.0`; `PlayerSession.ts:764 onGraduation(this, level)` also omits gpa. Concrete reward bug; spans ~2-3 files.
3. **Offline never drains `lifecycleQueue` / `updateLifeGoals`** — milestone achievements (graduation/promotion/marriage/child) + life-goal progress earned while away stall; offline only checks birthday+death directly (`LoopManager.ts:385,419`).

Scout caveat: two offline engines exist (LoopManager.initLifeSim = prod, GameEngine.runGameTick = test-only) with conflicting daily logic — the "correct" survival/energy code lives in the engine prod does NOT use.

## Receipts / notes

- W1S full receipt: see state.yaml.
