# GoalBuddy v2 state.yaml
# Board truth lives here. goal.md is the editable charter; notes/ holds long receipts only.

version: 2

goal:
  title: "BaoLife — Hunt + Fix the Next Dead/Inert Gameplay Mechanic"
  slug: "baolife-mechanics"
  kind: open_ended
  tranche: "Discover the next high-leverage broken/unwired/declawed core mechanic (compounding the career-progression find), fix the biggest confirmed gap (mirrored online+offline), verify + prove it fires, until a final audit proves the outcome. Healthy-systems = valid stop."
  status: done
  oracle:
    signal: "For the fixed mechanic: a reproducible signal it now fires in live code (regression test / simulator / handler trace / in-app), plus tsc clean + vitest >= 1679 green."
    cadence: "after each Worker package and at final audit"
    final_proof: "Identified gap (or healthy verdict) + implemented & verified fix proven-to-fire (mirrored if loop-related) + final audit full_outcome_complete: true."
  intake:
    original_request: "Pick another high-leverage slice and continue autonomously."
    interpreted_outcome: "Find + fix the next broken/inert core gameplay mechanic (v2-migration-gap hypothesis); confirm money has a spend loop."
    input_shape: open_ended
    audience: "BaoLife players + Craig (pre-release confidence)"
    authority: approved
    proof_type: test
    completion_proof: "Next high-leverage gap fixed + proven-to-fire + green, OR systems proven healthy; final audit full_outcome_complete: true."
    likely_misfire: "Churning tweaks to working systems; resurrecting an intentionally-cut mechanic; scope-creeping into a rewrite; regressing the suite; touching avatar WIP."
    blind_spots_considered:
      - "A 'dead' mechanic may be intentionally deprecated — verify intent before wiring."
      - "Loop fixes must be mirrored online+offline (career-fix template)."
      - "Money may be meaningful to EARN but have nothing meaningful to BUY (sink problem) — a legit candidate."
    existing_plan_facts:
      - "Career fix (db3dd13b) is the proof case: job_manager.handleJob was dead; v2 port dropped salary raises."
      - "Constraints carry over from baolife-economy (no avatar WIP, no ws/, mirror loops, diamonds/IAP safe, green >=1679, deploy only on Craig's ask)."

rules:
  pm_owns_state: true
  one_active_task: true
  max_write_workers: 1
  no_implementation_without_worker_or_pm_task: true
  no_completion_without_judge_or_pm_audit: true
  planning_is_not_completion: true
  queued_required_worker_blocks_completion: true
  continuous_until_full_outcome: true
  missing_input_or_credentials_do_not_stop_goal: true
  preserve_and_validate_existing_plan: true
  intake_misfire_must_be_audited: true
  goal_pressure_requires_oracle: true
  no_completion_on_weak_proof: true
  slice_policy:
    max_consecutive_tiny_tasks: 2
    prefer_vertical_slices: true
    judge_picks_largest_safe_slice: true
    worker_completes_whole_slice: true

agents:
  scout: installed
  worker: installed
  judge: installed

visual_board:
  selected: none
  local:
    status: not_requested
    url: null
    command: "npx goalbuddy board docs/goals/baolife-mechanics"

active_task: null

tasks:
  - id: T001
    type: scout
    assignee: Scout
    status: done
    reasoning_hint: medium
    objective: "Hunt for the next dead/unwired/declawed CORE gameplay mechanic (the v2-migration smell that hid the salary-raise drop). For each player-facing system — relationships/dating, activities->skills->stat effects, education, health/habits, money sinks/shop, retention (quests/achievements/lifeGoals) — determine: is it advertised/expected by players? is it actually WIRED into a live tick/handler/event path, or defined-but-never-called? Cite concrete call sites (and dead ones). SECONDARY: does the now-meaningful money ($98k-$274k lifetime) have a satisfying SPEND loop? (shop price distribution vs achievable wealth; what's reachable vs gated vs unreachable). Rank candidates by player-felt leverage x confidence; do NOT pick yet."
    inputs:
      - "Career fix as the template: job_manager.handleJob was dead; events/v2/catalog/career.ts dropped salary raises"
      - "server/src/game/{PlayerSession.ts,engine/GameEngine.ts,engine/LoopManager.ts} (what each tick actually calls)"
      - "server/src/services/** (relationships/dating, education, health, jobs, shop, retention) — exported-but-uncalled functions"
      - "server/src/events/** (legacy class events vs v2 catalog — what the v2 port dropped/declawed)"
      - "server/src/services/shop/shop_manager.ts (price distribution vs wealth curve from baolife-economy)"
    constraints:
      - "Read-only. Do not edit."
      - "Prefer concrete file:line evidence; show the DEAD/unwired call sites explicitly (grep call sites, not just definitions)."
      - "Verify INTENT: flag whether a dead mechanic looks expected/advertised vs intentionally cut."
      - "Do NOT inspect/report the avatar WIP."
    expected_output:
      - "Ranked list of candidate broken/inert/declawed mechanics with call-site evidence (live vs dead) + intent read"
      - "Spend-loop assessment: is meaningful money meaningfully spendable?"
      - "Top 1-3 highest-leverage candidates + why"
      - "Candidate next tasks"
    receipt:
      result: done
      summary: "Found the SAME class as the career bug: the online weekly tick PlayerSession.processWeekTick (PlayerSession.ts:500-521) runs ONLY handleJob + applyWeeklyFinances, while the offline GameEngine.handleWeeklyUpdates (GameEngine.ts:783-813) runs the FULL set: processJobProgression, handleFinances, handleMoods, handleEducation, handleJob(happiness), handleRelationships + processWeeklyRelationshipEvents. So connected (online) players may never advance GPA/education (handleEducation offline-only) and never get the weekly 5% random romance events / weekly relationship decay. CAVEAT (Scout-flagged): online updateAge DOES run monthly/yearly affinity decay, so relationships aren't fully dead online — only weekly cadence + random events. Must verify each subsystem isn't handled at another online cadence before mirroring."
      ranked_candidates:
        - "#1 HIGH (real bug): online education/GPA progression. handleEducation (education_manager.ts:628) called ONLY at GameEngine.ts:806 (offline); PlayerSession has no handleEducation import (grep empty). GPA/graduation/college-gating advertised => expected, not cut."
        - "#2 HIGH/MED (real bug): online weekly relationship events + decay. handleRelationships + processWeeklyRelationshipEvents (5% random romance, randomEvents.ts:227) fire offline-only (GameEngine.ts:918-936). Online gets monthly/yearly affinity decay via updateAge but NOT weekly dynamics/random romance. Dating/romance is a headline system => expected."
        - "#3 MED (design ambiguity, NOT a clear bug): person.skills built by performActivity.ts:130 feeds no job/event gate; only flat intelligence reaches the job gate (job_manager.ts:707). BUT skills DO scale activity outcomes (performActivity.ts:119) so they're self-referential, not dead. Lower priority."
      not_bugs:
        - "Shop spend loop HEALTHY: prices $100-$5M, mid-tier $2k-$50k reachable on the $98k-$274k curve, weekly upkeep sink (finance.ts:73), $750k/$1M/$5M prestige-gated (minPrestige 100/250/500, enforced shop_manager.ts:502). Not a target."
        - "Retention quests/achievements/lifeGoals DO fire online (integration.ts:424 + handlers conversations.ts:254/purchases.ts:95/romance.ts:105 + daily updateLifeGoals). Offline-sim reward-cadence 0 is the known artifact."
        - "Habit quitting wired online (PlayerSession.ts:419-424 handleHabitChanges); habit formation event-driven (hobbies.ts:90). Live."
      top_picks: "Likely ONE coherent slice: mirror the missing offline-weekly subsystems (education [+ moods?] + weekly relationships) into the online processWeekTick — same one-line-mirror shape as the career fix — AFTER verifying none is already handled at another online cadence (avoid double-apply)."
      intent_read: "Education + relationships are advertised headline systems => real bugs, not intentional cuts. person.skills->gates is a design call, defer."
      evidence:
        - "server/src/game/PlayerSession.ts:500-521 (online weekly tick = handleJob + finances only)"
        - "server/src/game/engine/GameEngine.ts:783-813 (offline weekly = 5 subsystems); :918-936 relationship handling"
        - "server/src/services/education/education_manager.ts:628 handleEducation (offline-only caller)"
        - "server/src/events/relationships/randomEvents.ts:227 processWeeklyRelationshipEvents"
        - "server/src/stats/stats_manager.ts:449-475 updateAge (online monthly/yearly affinity decay)"
        - "server/src/handlers/performActivity.ts:119,130 person.skills (self-referential, no external consumer)"
        - "server/src/services/shop/shop_manager.ts:108-520 (healthy spend loop)"
      note_needed: false

  - id: T002
    type: judge
    assignee: Judge
    status: done
    reasoning_hint: high
    objective: "VERIFY then DECIDE. The Scout reports the online weekly tick (PlayerSession.processWeekTick) is missing offline-weekly subsystems (education/GPA, moods, weekly relationship handling + 5% random romance). FIRST independently VERIFY, for EACH of {handleEducation, handleMoods, handleRelationships, processWeeklyRelationshipEvents}: is it truly absent for ONLINE players, or is the equivalent already done at another online cadence (processHourTick/processDayTick/updateAge/event handlers)? A subsystem already handled elsewhere online MUST NOT be mirrored (double-apply risk). THEN pick the single highest-leverage CONFIRMED gap (or a coherent group of genuinely-missing ones) and define the exact Worker contract. OR rule healthy (valid non-churn completion). Confirm intent (advertised, not intentionally cut) before approving."
    inputs:
      - "T001 receipt"
      - "server/src/game/PlayerSession.ts (processWeekTick/processDayTick/processHourTick — what each online cadence calls)"
      - "server/src/game/engine/GameEngine.ts handleWeeklyUpdates + updateAge; LoopManager.ts"
      - "server/src/services/education/education_manager.ts handleEducation (what it actually does + whether online events cover GPA/graduation)"
      - "server/src/stats/stats_manager.ts updateAge (what relationship/mood work it already does online)"
    constraints:
      - "Do not implement. Independently verify each subsystem's ONLINE coverage at ALL cadences before calling it a gap — cite the call sites you checked."
      - "Explicitly rule out double-apply: if a subsystem already runs online at another cadence, EXCLUDE it from the fix."
      - "Reject resurrecting an intentionally-cut feature. Reject scope-creep into a rewrite — pick the largest SAFE slice."
      - "The Worker contract MUST require online+offline parity + a proof-it-fires test (career-fix template). Watch dedup/cadence: weekly handlers must fire once per in-game week online, matching offline."
      - "If systems are healthy, say so and route to final audit — no invented tweaks."
    expected_output:
      - "Per-subsystem online-coverage verdict (truly missing vs already-handled-elsewhere) with call-site evidence"
      - "Decision: fix <which subsystem(s)> | healthy-no-change"
      - "If fix: exact Worker objective, allowed_files, verify (tsc, vitest>=1679, proof-it-fires), stop_if, cadence/dedup guidance"
      - "Value justification + intent confirmation"
    receipt:
      result: done
      decision: approved
      full_outcome_complete: false
      per_subsystem_verdict:
        - "handleMoods: EXCLUDE (covered online — happiness moves via v2 event effects each hour + hourly survival; weekly random-walk would be churn/double-flavor)."
        - "Weekly affinity/familiarity decay: EXCLUDE (online updateAge already decays affinity monthly/yearly; offline per-NPC weekly loop double-applies — mirroring weekly -1 would be a regression)."
        - "processWeeklyRelationshipEvents (5% random romance beats): CONFIRMED MISSING online (fires only offline at GameEngine.ts:936). Headline system. PRIMARY fix."
        - "handleEducation (GPA drift): genuinely offline-only (GameEngine.ts:806) but near-inert (GPA consumed only by getGPA4Scale + straight_a achievement, and that path is ALREADY dead online because PlayerSession.ts:727 onGraduation omits the gpa arg). SECONDARY cheap parity mirror; low leverage."
      decision_detail: "Wire processWeeklyRelationshipEvents(player) + handleEducation(player.c) into PlayerSession.processWeekTick for the PLAYER CHARACTER ONCE/week (existing gate PlayerSession.ts:150). EXCLUDE handleMoods + per-NPC weekly decay loop (double-apply). Romance message rides existing player.messageQueue->messageEvent. No client work (results flow over existing WS channels). Deploy needs Craig's ask."
      intent_confirmed: "Romance + education are advertised headline systems => real bugs, not intentional cuts."
      flagged_followup: "Separate latent bug (NOT this slice): PlayerSession.ts:727 onGraduation(this, level) omits gpa, so the straight_a (gpa>=4.0) achievement can never fire online. Worth a future fix to make GPA meaningful online."
      client_work_needed: false
      evidence:
        - "PlayerSession.ts:500-521 (online weekly = job+finances only); :150 weekly gate; :166-308 processHourTick (updateAge hourly, no romance/education)"
        - "GameEngine.ts:783-813 (offline weekly = 5 subsystems); :936 romance; :806 education; :859 moods random-walk"
        - "stats_manager.ts:449-527 updateAge (online monthly/yearly affinity decay only)"
        - "events/relationships/randomEvents.ts:227 processWeeklyRelationshipEvents (offline-only caller)"
        - "education_manager.ts:628-658 handleEducation (offline-only caller); GPA near-inert + straight_a dead online via onGraduation gpa omission"

  - id: T003
    type: worker
    assignee: Worker
    status: done
    reasoning_hint: medium
    objective: "In PlayerSession.processWeekTick, after handleJob/applyWeeklyFinances, call (1) the player-character weekly relationship beat processWeeklyRelationshipEvents(this.player) — queueing any returned event/message onto player.messageQueue exactly as GameEngine.handleRelationships does — and (2) handleEducation(this.player.c) for student GPA parity. Mirror the offline GameEngine.handleWeeklyUpdates for the PLAYER CHARACTER ONLY, ONCE per in-game week. EXCLUDE handleMoods and the per-NPC weekly affinity/familiarity decay loop (double-apply). Prove it fires with a regression test that FAILS if the online weekly tick reverts to job+finances-only."
    allowed_files:
      - "server/src/game/PlayerSession.ts"
      - "server/tests/game/PlayerSession.weekly.test.ts"
    verify:
      - "cd server && npx tsc --noEmit (clean)"
      - "cd server && npx vitest run (>=1679 green, no regression)"
      - "PROOF-IT-FIRES: a test driving a connected PlayerSession to a weekly boundary (dayOfWeek===1, hourOfDay===0, minuteOfHour===0 => processWeekTick) that, with a Dating/Married partner + Math.random stubbed into the >95 branch, asserts a relationship messageEvent is queued/sent (relationshipScore/affinity changed); AND with a current_education student record asserts handleEducation ran (GPA record touched). Must FAIL if processWeekTick regresses to job+finances-only."
    stop_if:
      - "Need files outside allowed_files (esp. GameEngine.ts edits, avatar WIP, or ws/)."
      - "The romance/education subsystem turns out intentionally cut or ambiguous — STOP, surface to Judge."
      - "Wiring requires changing decay/balance semantics or turns into a rewrite — STOP, surface."
      - "Verification fails twice."
    cadence_dedup_guidance: "processWeekTick already fires once/in-game-week via PlayerSession.ts:150 gate (matches offline LoopManager:329). One call each to processWeeklyRelationshipEvents(this.player) + handleEducation(this.player.c) per week. Do NOT loop over player.r per-NPC. Do NOT mirror weekly affinity/familiarity decay (updateAge already decays affinity online — double-apply). Romance message rides existing messageQueue -> messageEvent drain in processHourTick."
    receipt:
      result: done
      changed_files:
        - "server/src/game/PlayerSession.ts"
        - "server/tests/game/PlayerSession.weekly.test.ts"
      commands:
        - cmd: "cd server && npx tsc --noEmit"
          status: pass
          detail: "PM independently re-ran: clean"
        - cmd: "cd server && npx vitest run"
          status: pass
          detail: "PM independently re-ran: 143 files, 1683 tests, 0 failed (1679 baseline + 4 new)"
        - cmd: "cd server && npx vitest run tests/game/PlayerSession.weekly.test.ts"
          status: pass
          detail: "4/4; Worker verified it FAILS 2/4 if the two calls are removed (assertion strength)"
      summary: "Wired exactly two calls into PlayerSession.processWeekTick after handleJob+applyWeeklyFinances, player-character only, each try/catch-wrapped: (1) processWeeklyRelationshipEvents(this.player) with message surfacing replicated EXACTLY from GameEngine.handleRelationships (iterate EventResults, push event.message onto player.messageQueue -> existing messageEvent drain); (2) educationManagerHandleEducation(this.player.c) for student GPA parity. EXCLUDED handleMoods + the per-NPC weekly affinity/familiarity decay loop (double-apply, per Judge). PM verified the diff is parity-exact vs GameEngine:918-936 and that only the 2 allowed files changed (avatar WIP untouched)."
      parity_confirmed: "Online now delivers the same weekly 5% random romance beat + student GPA drift the offline GameEngine.handleWeeklyUpdates already gave. Surfacing pattern byte-matches the offline caller."
      note_needed: false

  - id: T999
    type: pm
    assignee: PM
    status: done
    reasoning_hint: high
    objective: "Final audit (PM — PM independently re-ran tsc + full suite + reviewed the parity-exact diff; Judge already did the double-apply verification in T002): was the next high-leverage broken/inert mechanic found + fixed + PROVEN-to-fire (mirrored if loop-related), suite green (>=1679), avatar WIP untouched? Map the change to a verified player-felt outcome."
    inputs:
      - "All done task receipts"
      - "Last verification"
      - "Current dirty diff"
    constraints:
      - "Do not implement."
      - "Reject completion if a required Worker is still queued/active."
      - "Reject a fix that resurrected an intentionally-cut feature or wasn't proven to fire."
      - "Accept a data-backed 'systems healthy' as complete."
      - "Map every change to a verified outcome."
    expected_output:
      - "complete | not_complete"
      - "full_outcome_complete: true | false"
      - "value justification (or healthy verdict)"
      - "next task if not complete"
    receipt:
      result: done
      decision: complete
      full_outcome_complete: true
      rationale: "Found + fixed the next instance of the career-bug class (online/offline weekly-tick divergence): connected players never received the 5% weekly random romance events (offline-only) nor student GPA drift. Wired both into PlayerSession.processWeekTick for the player char, once/week, EXCLUDING the two subsystems already handled online (moods, affinity decay) to avoid double-apply — that exclusion was independently verified by the Judge in T002. PM independently re-ran tsc (clean) + full suite (143 files/1683 tests/0 failed) and reviewed the diff (parity-exact vs GameEngine:918-936; only 2 allowed files changed; avatar WIP untouched)."
      verified_outcomes:
        - "PlayerSession.ts: online weekly tick now fires processWeeklyRelationshipEvents(player) + handleEducation(player.c) -> connected players get weekly romance beats + GPA drift, parity with offline. Proof: diff + green suite + proof-it-fires test."
        - "PlayerSession.weekly.test.ts: 4 tests; FAIL 2/4 if the calls are removed (assertion strength confirmed) -> regression guard against reverting to job+finances-only."
        - "Double-apply avoided: handleMoods + per-NPC weekly decay correctly EXCLUDED (Judge-verified online coverage)."
      value: "Player-felt: romance is a headline system that was silently inert for connected players; now they get the weekly relationship beats the offline path always gave. GPA parity is minor but completes the weekly-tick mirror."
      owner_action_non_blocking: "Working-tree only (not committed/deployed). Awaiting Craig's deploy decision (same as economy slice). Flagged follow-up: PlayerSession.ts:727 onGraduation omits gpa => straight_a achievement can't fire online (separate small bug, not this slice)."
      evidence:
        - "tsc --noEmit clean; vitest 143 files/1683 tests/0 failed (PM re-ran)"
        - "PlayerSession.ts diff: 2 try/catch calls after finances, player-char only, parity-exact surfacing"
        - "GameEngine.ts:918-936 offline pattern matches; events/relationships/index.ts:11 re-export resolves"
        - "git status: only PlayerSession.ts + PlayerSession.weekly.test.ts changed; avatar WIP pre-existing only"

checks:
  dirty_fingerprint: "working-tree (online-parity fix uncommitted — awaiting Craig deploy decision, same as economy slice)"
  last_verification:
    result: green
    task: "T003 + T999 (PM independently re-ran)"
    commands:
      - cmd: "cd server && npx tsc --noEmit"
        status: pass
      - cmd: "cd server && npx vitest run"
        status: pass
        detail: "143 files, 1683 tests, 0 failed (1679 baseline + 4 new proof-it-fires tests)"
      - cmd: "cd server && npx vitest run tests/game/PlayerSession.weekly.test.ts"
        status: pass
        detail: "4/4; fails 2/4 if the two calls are removed"
