# BaoLife — Feel & Pacing Tuning Pass

## Objective

Turn BaoLife's now-complete systems into a moment-to-moment experience that is genuinely **fun and rhythmically right** — events arrive at a digestible cadence, the economy and difficulty follow a satisfying curve over a life, energy/resources stay in a playable band, and rewards land on a motivating beat. Data-driven: **measure first with the headless simulator, then tune.**

## Original Request

"Feel & pacing tuning pass — turn the now-complete systems into a moment-to-moment experience that's fun and keeps players coming back. Use the soak/lifetime simulator to play many lifetimes and MEASURE the experience, define target curves, then tune the knobs in verified waves. Also fix the small follow-ups from the last in-app walkthrough." (Craig pre-authorized a big autonomous multi-agent GoalBuddy run; "I trust you, go as big as you like.")

## Intake Summary

- Input shape: `specific` (clear outcome + method) with `open_ended` data-driven tuning inside it.
- Audience: BaoLife players (the felt experience) + Craig (a game that's fun to keep playing).
- Authority: `approved` — pre-authorized big autonomous run.
- Proof type: `metric` + `demo` + `test` — simulator metrics in healthy bands AND in-app pacing feels right AND backend stays green.
- Likely misfire: (1) tuning numbers blindly without measuring first — DEFINE METRICS via the simulator before changing knobs; (2) over-fitting to the simulator while the real app still feels off — confirm in-app; (3) breaking existing tests by changing constants without updating their intent; (4) "feel" has no objective truth — anchor it in concrete measurable bands + an eyes-on session, not vibes.
- Blind spots: the prior run already set many constants (economyConstants, selection weights/cooldowns, energy 35/night, etc.) — tuning must build on those, not relitigate; pacing has TWO loop paths (online PlayerSession + offline GameEngine) that must stay consistent; event density is partly a function of catalog size + per-event trigger probability + speed thresholds; changing pacing must not starve content or create dead air either.
- Existing plan facts: preserved in state.yaml intake.existing_plan_facts (measurement dimensions, knobs, the 3 named follow-ups).

## Goal Oracle

`Across many simulated lifetimes, the measured experience lands in defined HEALTHY target bands — event-prompt cadence (digestible, neither a wall nor dead air), economy/money progression (a curve, not flat-broke or infinite), energy/resource band (playable, no soft-lock or trivial), difficulty/longevity (sensible death-age spread) — AND the 3 named feel-bugs are fixed, AND backend stays green (tsc clean + vitest >= 1621 no net regression), AND an in-app session demonstrates the new pacing feels right (events digestible, player can act between them).`

Concretely, completion requires:
1. A baseline metrics report from the simulator (current feel, quantified) — the "before".
2. Defined target bands for each pacing/economy/energy/difficulty dimension (the "good" definition).
3. Tuned knobs so the "after" metrics land in those bands (no dimension left pathological).
4. The 3 named follow-ups fixed: (a) event-prompt stream too dense at default speed; (b) mid-session death doesn't switch to DeathView until reconnect; (c) offline-digest money-delta content nuance.
5. Backend green after every wave (tsc + vitest >= 1621); loop changes mirrored online+offline.
6. An in-app session (sim + driven app) confirming the felt pacing — the final eyes-on check.

The PM keeps comparing receipts to this oracle. Numbers-in-bands alone is not enough — the app must be exercised. Done only when a final Judge/PM audit maps before/after metrics + the 3 fixes + green suite + the in-app session to the oracle with `full_outcome_complete: true`.

## Goal Kind

`specific` (data-driven tuning)

## Current Tranche

Continuous: (1) Scout measures current feel via the simulator + maps the tuning knobs; (2) Judge defines target bands + the first tuning package; (3) Worker tunes in verified waves (measure → adjust → re-measure), each green; (4) fix the 3 named follow-ups; (5) confirm in-app; (6) final audit. Keep going until the felt experience is right, not just until numbers move.

## Non-Negotiable Constraints

- **Never edit legacy `ws/`.** Source of truth = TS `server/`.
- **Mirror pacing/loop changes across BOTH paths** — online `game/PlayerSession.ts` AND offline `game/engine/{LoopManager,GameEngine}.ts` — keep them consistent.
- **Build on the prior run's constants** (economyConstants.ts, selector weights/cooldowns, energy restore 35/night, lifeGoals, etc.) — tune, don't relitigate, unless a constant is the actual pacing problem.
- **Measure before tuning.** Establish baseline metrics from the simulator first; define target bands; then change knobs and re-measure. No blind number-fiddling.
- After every change: `cd server && npx tsc --noEmit` then `npx vitest run` — stay green, total >= 1621. If a test encoded an old constant, update it to the new intent + explain.
- **Don't trade one pathology for another** — fixing event over-density must not create dead air; easing scarcity must not make resources trivial.
- iOS is the priority platform for in-app confirmation; Android parity for any client-side pacing changes.
- No production deploy unless Craig asks.
- Keep `notes/PROGRESS.md` updated as items complete.

## Stop Rule

Stop only when a final audit proves the felt experience is right: metrics in healthy bands + 3 fixes done + green suite + in-app confirmation, with `full_outcome_complete: true`. Don't stop after planning/measurement. Don't stop after one tuning wave if a dimension is still pathological. Missing device access blocks a specific in-app check, not the goal — use the simulator + a workaround and continue.

## Slice Sizing

Largest safe useful slice = one coherent tuning dimension end-to-end (e.g., "event cadence: add trigger probability + spacing, re-measure to target band, tests green") or one named follow-up fixed + verified. Group same-shape knob tweaks into one package. Re-measure after each wave.

## Canonical Board

Machine truth: `docs/goals/baolife-feel-pacing/state.yaml`. Human tracker: `docs/goals/baolife-feel-pacing/notes/PROGRESS.md`. If charter and state.yaml disagree, state.yaml wins.

## Run Command

```text
/goal Follow docs/goals/baolife-feel-pacing/goal.md.
```

## PM Loop

On every `/goal` continuation: read this charter, read state.yaml, re-check intake + likely misfire (esp. measure-before-tuning), work only the active task, assign Scout/Judge/Worker/PM, write a compact receipt, update board + notes/PROGRESS.md, advance to the next largest safe tuning wave, review at phase/risk/final boundaries, and finish only with a Judge/PM audit mapping before/after metrics + the 3 fixes + green suite + in-app session to the oracle, recording `full_outcome_complete: true`.
