# memory — rimlike Durable memory for this project. Read at session start, update before session end. Date format: `YYYY-MM-DD`. A 2D, tile-based **cute-farming-RPG-meets-colony-sim** — Rimworld DNA, Going Medieval × Stardew lodestars — shaped for mobile and handheld. Promoted from `~/claude/ideas/rimlike` on 2026-05-10 after a single deep brainstorm session. Realistic MVP timeline: **3–6 months solo**. ## How to read this project `memory.md` is the index of decisions and open questions. The deep specs live alongside in `docs/`: | File | Contents | |---|---| | [`docs/design.md`](./docs/design.md) | Game design — core loop, simplifications, skills, statuses, mood, weather, stockpiles, production, combat, death/burial, storyteller corpus | | [`docs/architecture.md`](./docs/architecture.md) | Tech — pawn AI / job system, time/tick model, Godot 4 engine layout, TileMap split, all subsystems (mood, lighting, rooms, hauling, production, combat, storyteller) | | [`docs/ui.md`](./docs/ui.md) | Touch UX — work-priority matrix, stockpile/container screens, mood/lighting/rooms cues, combat banners, storyteller event UI, screens still to design | | [`docs/art.md`](./docs/art.md) | Owned assets (ElvGames bundle primary, Ventilatore secondary), license, autotile gotcha, audit list, candidates kept for record | | [`docs/implementation.md`](./docs/implementation.md) | **Phased build plan** — 21 phases (audit → P0 scaffold → … → P20 export). Checklists, acceptance demos, scope-cut levers, de-risking spikes. Track progress here. | When working on a feature, read `memory.md` first, then the relevant `docs/` file(s). For "what do I build next?" check the **Status** row at the top of `docs/implementation.md`. ## Decisions & rationale Distilled from the brainstorm. Each lock has a "why" — change with deliberate intent. ### Pillars | Decision | Choice | Why | |---|---|---| | **View** | Top-down grid for gameplay (pathfinding, designation, floor) **+ 3/4-perspective rendering for vertical structures** (walls, doors, furniture). | Re-decided 2026-05-10 after exhausting the asset library: every wall pack we own is RPG-style perspective (Stardew / Going Medieval style), not Rimworld-style top-down. Pivoting the renderer (Y-sorted entity sprites for walls) makes the entire library usable as-is and replaces the "we need to author or commission" bottleneck. Gameplay grid + pathfinding stay identical; only the rendering of vertical structures shifts. | | **Wall layer rendering** | Walls are **entity sprites** (Sprite2D / Y-sorted Node2D), not TileMap cells. `Wall` TileMap layer (Layer 2) becomes data-only — used for pathfinding-impassable + room-detection BFS, but doesn't render. | Same source as the view-style pivot above. Consequence: doors, crates, furniture all live as entities with Y-sort; floor and designation-paint still tile-based. Architecture.md TileMap-layer section needs an annotation. | | **Primary platforms** | iOS + Android touch, then Steam Deck / ROG Ally gamepad. Desktop falls out for free. | Mobile is the hard constraint; Deck inherits. | | **Ambition** | itch.io + TestFlight release. Real artifact, small audience. | Drives engine choice; no app-store polish-tax. | | **Engine** | Godot 4 (GDScript) | 2D-first, free, exports everywhere we need, fast iteration. | | **Tile size / style** | 16×16 pixel art, **cute-farming-RPG primary** (ElvGames bundle), Ventilatore as medieval accent | ElvGames "Ultimate Farming RPG" Humble bundle owned (~2.8 GB, 70+ packs). Tone: Stardew × Going Medieval × Rimworld. | | **Setting** | Medieval fantasy with cute palette | Owned-art alignment + clearer scope (no electricity / hydroponics / energy weapons) + underexplored on mobile. | | **Run shape** | Open-ended sandbox, autosave, persistent world | Player picks up where they left off. | | **Session length** | 5–15 min target | Drives every UI and pacing choice. | | **Default speed** | Fast (5×), with auto-pause on event | 1 in-game day ≈ 5 min real time. Solves the "watch a pawn walk for 60s" problem; 1× exists for combat / fine work. | | **Goal scaffolding** | Light storyteller prompts | Soft, dismissible nudges give each short session shape without forcing scenarios. | | **Combat** | Realtime with auto-pause on threat | Rimworld-feel; touch-friendly. | | **Health** | Single HP per pawn + status effects (Bleeding/Sick/Tired/Hungry/Wet/Cold/Downed/...) | ~80% of the drama for ~5% of Rimworld's health-system code. | | **Scale** | 3–6 pawns, **80×80** MVP map (architecture sized to ~120² ceiling) | Roughly Stardew-farm size; readable when zoomed in, doesn't fit a phone screen — forces the world-view camera (pinch / pan / jump-to-alert) rather than strategic-overview-on-phone. | | **Priority levels** | 5 (Critical / High / Normal / When idle / Off) | Matches Rimworld + Going Medieval contract. | | **Failure state** | Ghost colony — no game over; storyteller drops wanderer in 3–5 days | Mobile-friendly; preserves player investment. | | **Engine version** | Godot **4.6.2 stable** (Win64 binary at `D:\godot\Godot_v4.6.2-stable_win64.exe`) | Locked for reproducibility; pinned in `project.godot` features. | | **Renderer** | GL Compatibility (mobile + desktop), **not** Forward+ | Max device reach; Forward+ would lock out older phones. | | **Repo location** | Physical: `/mnt/d/godot/rimlike/` (D: drive, fast for Windows-side editor). Symlink: `~/claude/projects/rimlike` → physical. | Mirrors tavernkeep's pattern. Both WSL and Windows access without crossing the WSL net bridge. | | **Player walls** | **Wood + stone via Pixel Crawler `Walls.png`** (entity sprites, Y-sorted; no autotile in Phase 5). Single-sprite-per-material is the Phase 5 ship; per-direction variants are a polish item. | After the rendering pivot to 3/4 perspective, the Pixel Crawler Walls.png pack becomes directly usable. It has 4 wood materials + a sandstone variant with clear corner/edge pieces. Phase 5 ships with one sprite per material (uniform-looking walls); Phase 17 can add per-direction variants if playtest reveals the visuals feel flat. Stardew-cabin warmth restored without authoring or commission. | ### Architecture (tech) - **Sim tick 20 Hz, render 60 Hz, decoupled.** Pawn positions lerp between sim ticks. (`docs/architecture.md` Time / tick model) - **Pawn AI: 5-layer pipeline** (Decision → WorkProvider → Job + JobRunner → Status interrupts → Player overrides). Slimmer than Rimworld's ThinkTree. ~800–1500 LOC GDScript for full MVP. - **TileMap layers**: 0 Terrain · 1 Floor · 2 Wall · 3 Designation · 4 Roof · 5 Fog. Furniture / Pawn / Item / EffectFX as scene-instanced entities (not TileMap). - **Pathfinding**: `AStarGrid2D` (built-in), updated on wall/door/furniture changes. - **No background simulation** — app backgrounded = sim paused. Avoids "lost colony to a raid while at work." - **Save format**: between sim ticks only; JobRunner mid-toil state round-trips from day one. ### Game design - **5 skills** (Manual Labor / Crafting / Cooking / Medicine / Combat), 0–10 each, level by use, multiplicative speed/quality bonus. Skills modify duration and quality, never permission. - **9 work categories** (Construction / Mining / Hauling / Cleaning / Crafting / Cooking / Plant / Doctor / Combat). 5-level priority matrix per pawn. - **Storage**: floor zones AND independent crate furniture (4 stacks each), unified by `StorageDestination` interface. **16 filter chips** (Wd/St/Ir/Cu/Ag/Au/Cl/Veg/Mt/Gr/Ck/Md/Tl/Wp/Ar/Co), 5 priorities with Rimworld flow semantics. One stack per tile, one type per tile. - **Production**: 2-step where medieval-sense (Iron→Ingot→Weapon, Grain→Flour→Bread); 1-step otherwise. 5 workbenches (Carpenter, Smelter, Smithy, Cooking hearth, Millstone), ~22 recipes. Full Rimworld bill semantics (one-shot count / forever / until-N + ingredient-quality filter + skill threshold). - **Quality system** (Shoddy/Normal/Excellent/Masterwork/Legendary) on every crafted item; multiplicative stat bonus. Quality from crafter skill + RNG only (inputs are just resources). - **Mood**: ~13 thoughts (data-driven registry, mix persistent + event-driven). Soft breaks at sustained mood < 25 for 30 in-game min — Sulking or Wandering, recover at mood ≥ 35. - **Lighting** (real shader at night), **auto-detected rooms** (named by furniture, scored for beauty + dirtiness), **dirtiness + Cleaning** (8th work category), **beauty score** with Quality multiplier. - **Roofing**: indoor = Layer-4 Roof flag, sim-data only (no rendering, just an indoor tint on floors). Auto-roof when walls enclose ≤8 cells (BFS); No-Roof designation for courtyards. Plants don't grow indoors. - **Weather**: 4 types (Clear / Rain / Storm / Cold snap), daily roll, season-weighted. Wet status accumulates outside in rain, decays indoors. 4 seasons × 12 days = 48-day year. - **Combat**: 3 weapons (sword/axe/bow), 3 armor slots (helm/cuirass/boots). Walls + trees provide cover. Two-roll resolution (hit, then damage with armor reduction). Downed-then-rescue death model; doctors auto-prioritize. Combat=Off "defends if cornered, won't volunteer." Friendly fire ON. - **Death / corpses**: Both burial AND cremation. Graveyard = special stockpile (Corpses-only); pawns dig graves, place permanent grave markers (tap → deceased pawn-detail). Cremation pyre = furniture with single recipe (1 corpse + 5 wood). Corpses decay 0–50 fresh / 50–100 rotting (no butcher) / 100 rotted. - **Storyteller**: 25 prompts written in `docs/design.md`. Daily 6am roll, weighted pool, per-category cooldowns, tension model alternates quiet/threat per Tynan Sylvester pacing. Ambient banner for low-stakes, modal auto-pause for choice events. ### Touch UX - **Bottom-sheet menus** instead of right-side panels. **Long-press** = inspect/context. **Tap world** = select. Speed/pause buttons fixed top. - **Work-priority matrix**: 9 columns × N pawns, sticky pawn-name column, horizontal scroll on phone. Tap-to-cycle priority, long-press for 5-chip picker, swipe column for bulk-set. Per-pawn and per-job views layered on top. - **Stockpile/container UI**: 4×4 chip grid for the 16 filter categories; same UI for floor zones and crates. - **Storyteller events**: ambient banners for nudges/seasonal/lore; modal auto-pause for wanderer/threat/disease/milestone. Events log + "while you were away" digest at resume. - **Indoor tint** marks "this is inside" without needing roof rendering — matches Stardew/Rimworld convention. - **World-view camera**: pinch-zoom + drag-pan + double-tap-to-center; selected pawn does **not** force-follow. Storyteller alerts/banners include a *Go there* tap that pans the camera to the event tile. **No minimap in MVP** — revisit if playtest shows people getting lost on the 80² map. ### Art strategy - **ElvGames "Ultimate Farming RPG" Humble bundle** is the primary art (`/mnt/d/godot/assets/humble set new/`, ~2.8 GB, 70+ packs, all 16×16, ElvGames license: commercial OK with credit). - **Ventilatore Fantasy Tileset Complete Bundle** stays as medieval accent for biome variety / decorative props / animated water. - Mana Seed series considered but **dropped** from buy plan (the bundle covers what we'd planned). - **Authoring still required**: designation overlays (~2 hrs custom), possibly autotile bits on bundle wall sheets, possibly wolf sprite, possibly grave marker. ## Known bugs / triage backlog Reported from playtest. Triaged but not yet fixed. Plan: knock out as a bug-triage patch out-of-phase before Phase 18 (Audio) — same shape as the 2026-05-12 PC-controls patch. - [x] **[HIGH] Torches don't build when placed.** Reported 2026-05-15, fixed 2026-05-15. Root cause: Torch/Bed/Crate/Workbench/CremationPyre didn't call `World.register_build_site(self)` in `_ready` (only Wall/Floor/Door/GraveSlot did). ConstructionProvider iterates `World.build_queue` so the unregistered entities were never offered as jobs. The seeded cabin pre-built everything via `_spawn_complete_*` helpers, masking the gap until a player painted a fresh furniture designation. Fix: added register_build_site / unregister_build_site to all five entity types. - [x] **[HIGH] Two of three pawns sit idle, one does all work.** Reported 2026-05-15, fixed 2026-05-15. Root cause: pawns get stranded on tiles that became impassable mid-walk. Sequence: pawn-A walks a path computed pre-wall; pawn-B builds a wall on a tile in pawn-A's path; pawn-A keeps stepping along the now-stale path and lands on the wall tile; pathfinder requires a walkable START, so all subsequent find_path calls from pawn-A return empty → pawn-A is idle forever. The Phase 6 wall-trap-bug fix only protected the BUILDING pawn (adjacent-stand); bystanders + walk-through cases weren't covered. Two-part fix: (a) Wall._complete dislodges any pawn standing on the tile via new `Pathfinder.find_nearest_walkable` BFS helper; (b) Pawn._advance_walk re-checks `next_tile` walkability before snapping `tile`, aborts walk + cancels job + lets Decision reroute. Defense in depth. - [x] **[MED] Pawns render behind floor tiles.** Reported 2026-05-15, fixed 2026-05-15. Root cause: Floor entity anchored origin at tile-center (`tile.y*16+8`), same Y as Pawn — Y-sort tiebreak fell to scene-tree order, Floor (spawned later) drew over Pawn. Fix: moved Floor origin to top-of-tile (`tile.y*16`) so Floor.y < Pawn.y under Y-sort; _draw offsets compensate (rect spans y=0..TILE_SIZE_PX instead of -half..+half). Older bugs noted in passing but never fixed: - [x] **[LOW] Bed-claim failure for 2/3 pawns when beds are free** (logged 2026-05-11, fixed 2026-05-16). Audit showed claim mechanism was already race-free; visible symptom was greedy nearest-neighbor convergence (3 pawns picking the same nearest bed). Fixed in commit `2f76ae1` by extending the existing `Job.is_target_taken_by_other` deconfliction (used by ConstructionProvider) to SleepProvider — `j.target_node = best_bed` at job proposal so other pawns skip the same bed. - [ ] **[LOW] Save mid-INTERACT/mid-BUILD restarts toil from 0 on load** (Phase 16 known acceptable gap). Walk toil round-trips; multi-step interact does not. Tolerable per Phase 20 tuning note. - [x] **[MED] Drag-paint eaten by camera with paint tool active** (logged 2026-05-16, fixed 2026-05-16). Camera_rig was last in `world.tscn` so Godot's reverse-tree `_unhandled_input` dispatch gave it first crack at drag events. Fixed in commit `cf43ef9`: camera_rig's drag-start and ScreenDrag handlers early-return when `World.designation_ctl.active_tool() != TOOL_NONE`. ## Open questions / TODOs ### Audit / unblock-the-prototype action items Total ~75 min. **3 of 5 closed on 2026-05-10**; see session log + `docs/art.md` for findings. Two open. - [ ] **Aesthetic harmony test** — needs your eye. ElvGames Forest tile vs Ventilatore tile, side-by-side. Decides whether Ventilatore stays as accent or gets shelved. ~15 min. - [x] **ElvGames autotile audit** — done. **`FG_Fortress.png` IS autotile-solvable** (tan stone, ~20–30 modular pieces); **`FG_Houses.png` is NOT** (pre-built decorative house compositions, not modular wall variants). Iconic Homestead $19.99 fallback **not needed**. New decision required — see Open questions below. - [x] **Wolf sprite source** — done. **No wolf in the bundle anywhere.** Need a custom commission, a CC0 sprite, or a Ventilatore find. New decision required — see Open questions below. - [x] **Grave marker source** — done. `Retro Graveyard 16x16 Tileset [Kingdom Explorer]` confirmed in Tier 3, full graveyard suite. Direct use, no custom work. - [ ] **License compilation** — maintain credits string for every pack used (ElvGames + Ventilatore + Kingdom Explorer + any others). Display in game's credits screen. Confirm specific terms per pack before any commercial release. ### Design topics still open **Resolved: wall material strategy.** Lock = **custom-author wood + stone walls** (option b). Player builds wood-cabin walls from day one (corner/T/cap variants authored on top of `FG_Houses.png` wood-and-blue-roof material, ~½ day work) then upgrades to stone fortress walls later (`FG_Fortress.png` autotile-solvable as-is, ~few hours). Phase 5 slips ~3 days but the Stardew-cabin-warmth aesthetic survives, which was the original anchor. See `docs/art.md` Wall-material decision. - [ ] **Wolf sprite acquisition** (NEW from 2026-05-10 audit): bundle has nothing canine-predator; **Ventilatore checked 2026-05-10, also nothing** (Ventilatore is character + terrain + decoration; no animal/creature sprites at all besides player + slimes). Remaining options: (a) commission a 16×16 wolf (idle + 2–4-frame walk × 4 directions; ~$30–60 paid commission); (b) source a CC0 wolf from OpenGameArt; (c) reskin a bundle animal placeholder for MVP (e.g. recolor a dog from `Animal Sprites Pixelart`) and replace later. Phase 10 blocker — can defer until then; placeholder from option (c) unblocks the combat/AI work meanwhile. - [ ] **Auto-roof big-room UX**: the ≤8-cell BFS cap silently fails on rooms larger than 8×8. Decide whether to (a) keep the cap and surface "this area is too large to roof — split with an interior wall" hint when the player encloses one, (b) bump the cap to ~16 with the same hint at the new threshold, or (c) detect any enclosed area regardless of size. Affects `EnclosureDetector` + a new room-feedback UI. - [ ] **Onboarding / first 60 seconds**: mobile-specific. Don't copy Rimworld's tutorial. - [ ] **Touch UI for non-priority screens**: world view, build drawer, alerts, day-summary, pawn detail. See `docs/ui.md` "Screens still to design." - [ ] **Background time / "while you were away" mechanic** — locked to no background simulation in MVP; revisit if it feels bad in playtest. - [ ] **Audio direction** — who/where for SFX + ambient track? Bundle has 11 music + 8 SFX packs covering most needs. - [ ] **Steam Deck input parity** — gamepad-driven cursor, or full menu navigation by D-pad? Probably both. - [ ] **Localization stance** — English-only for MVP, but architect strings for i18n (already locked in CLAUDE.md). - [ ] **Pawn name/backstory generation** — hand-curated list vs simple generator. - [ ] **Naming the game** — "rimlike" is a working title. - [ ] **Monetization stance** — free? PWYW? Premium? - [ ] **Tech / research progression** — medieval tech tree shape. - [ ] **Map / world generation** — fixed seed for slice; procgen later. ### Tunable in prototype (not real "open Qs", just numbers to playtest) - Sleep mood gradient values (`+5/+0/−2/−5/−8`) - Wet status thresholds (25 / 60) and accumulation rates - Season weather weights (Spring/Summer/Autumn/Winter distributions) - Hit-chance bonuses (skill ×5%, range ×5%, cover 40/20%) - Bleed-out timer (6 in-game hours) - Various mood thought magnitudes and decay times ## Vertical slice (MVP target) Same scope as locked in `~/claude/ideas/rimlike/plan.md`. Realistic timeline 3–6 months solo for an MVP this rich. - **1 biome** (temperate forest, ElvGames Forest Tileset 4 Seasons; Ventilatore foliage accents). - **1 map**, **80×80 tiles**, fixed seed for now. - **3 starting pawns** ("settlers" / "villagers"), each with name + portrait + one-sentence backstory. - **Verbs**: chop wood, mine stone & ore, build walls/floors/furniture/crates, plant/harvest crops (3–4 types), cook meals (recipes), haul, repair, clean. - **Needs**: hunger, sleep, mood (~13 distinct thoughts, soft breaks at sustained mood < 25). - **Status effects**: Hungry, Tired, Bleeding, Sick, Downed, Wet (Damp/Soaked), Cold. - **Storage**: floor zones AND containers, 16 filter chips, 5 priorities. - **Quality system** on all crafted items. - **Lighting** — torches + hearths emit light; visual darkness at night. - **Rooms** — auto-detected, named by contents, beauty + dirtiness scored. - **Cleaning** as 8th work category. - **Day/night cycle**, ~5 min per day at default speed. **Seasons** (Spring/Summer/Autumn/Winter, 12 days each). - **Weather**: Clear / Rain / Storm / Cold snap. - **One disaster type**: wolves at night (bandit raids deferred). - **One storyteller**: random quiet/threat alternation, 25 prompt corpus. - **Save/load**, autosave on suspend, single slot. - **Touch UI** end-to-end (no desktop-only gestures). - **Sound**: minimal — UI clicks, ambient day/night loop, alert sting. ## Session log ### 2026-05-10 - Promoted from `~/claude/ideas/rimlike/` (single multi-hour brainstorm session). - Scaffolded `projects/rimlike/` from `_templates/project/`. Customized `CLAUDE.md`. Distilled `plan.md` into this `memory.md`. Moved companion files (design / architecture / ui / art) into `docs/`. `git init`, first commit "Initial scaffold". Created Forgejo repo `rimlike` (private), set HTTPS remote, pushed `main`. Archived idea folder to `~/claude/archive/ideas/rimlike/`. - Wired Godot MCP Pro: `.mcp.json`, `.claude/settings.local.json` allowlist, three project-local subagents (`quick-edit`, `researcher`, `gdscript-refactor`). Added the MCP-Pro and tiered-delegation sections to `CLAUDE.md`. Editor plugin / `addons/godot_mcp/` re-copy still pending Godot project scaffold. - Reviewed `docs/` against `memory.md` and `CLAUDE.md`. Decisions made: - **Map size bumped from 40² → 80²** for the MVP slice; architecture sized to ~120² ceiling. Pawn count stays at 3 start / 6 cap (frontier feel was rejected in favor of "split-the-difference" sizing — Stardew-farm scale, not Going-Medieval scale). - **World-view camera** = pinch-zoom + drag-pan + double-tap-to-center; storyteller alerts include a *Go there* tap. No minimap in MVP. No follow-cam (avoids fighting the player when issuing build orders across the map). - Storyteller cooldown semantics: **per-event AND per-category, both gates must pass** (resolves the design.md vs architecture.md disagreement). - `MAX_STACKS_PER_THOUGHT = 5` (was named-but-unset). - Hauling-loop fallback: items that fail to find any valid destination after 3 retry passes drop on the floor and surface as a passive "No stockpile accepts X" alert. - Cleaned up doc rot in `docs/design.md` and `docs/architecture.md` — both had stale "8 work categories" intermediate sections still next to the canonical "9" lists. Removed the (8) snapshots. - Updated `docs/architecture.md` perf assumptions (60² → 80² with 120² ceiling). - `docs/ui.md` got a new **World view camera (locked)** section; removed the world-view bullet from "Screens still to design". - Open: auto-roof big-room UX (added to TODOs above) — the ≤8-cell BFS cap silently fails on bigger rooms; player feedback path needs a decision before EnclosureDetector lands. - Wrote `docs/implementation.md` — 21-phase build plan with checkboxes, acceptance demos, scope-cut levers, and de-risking spikes. Status row at the top of that file is the canonical "where are we now" pointer. - **Project location moved** from `/home/megaproxy/claude/projects/rimlike/` to `/mnt/d/godot/rimlike/` (D: drive). Symlink at the original WSL path preserves the home-CLAUDE.md layout convention. Set `git config core.filemode false` to silence DrvFs's everything-is-0777 false-positive. Mirrors tavernkeep's pattern; both WSL and Windows access without crossing the WSL net bridge. - **Phase 0 scaffold landed.** `project.godot` + 7 autoloads + smoke-test scene + addons re-copy + folder layout. Used **GL Compatibility renderer** (not Forward+) for max mobile reach. Folder layout matches tavernkeep (`autoload/` at root, scripts co-located in `scenes/`) — *not* the `scripts/autoloads/` mirror layout originally sketched in `implementation.md`. Headless verification: `godot --headless --path . --quit` exits 0 with the smoke-test message. Editor-side green-dot check pending — needs you to open the editor once. - **Asset audit ran via researcher subagent (Haiku).** 3 of 5 items closed. Findings: - `FG_Fortress.png` autotile-solvable (tan stone). `FG_Houses.png` NOT autotile-solvable (pre-built decorative compositions). Iconic Homestead fallback not needed. **Opens a new wall-material decision** (see Open questions). - No wolf sprite anywhere in the bundle. **Opens a wolf-acquisition decision** (see Open questions). - `Retro Graveyard 16x16 Tileset [Kingdom Explorer]` confirmed in Tier 3 — full graveyard suite, ready for direct use in Phase 14. ### 2026-05-11 - **Phases 1–11 all shipped between 2026-05-10 and 2026-05-11** (the in-context session log lagged behind the commits). See `docs/implementation.md` Status table for canonical phase-by-phase state; below is just the headline. - **Phase 9 (Status + Medicine) + Phase 10 (Combat + Wolves) shipped as the "drama pair"** via 3-agent fan-out (Agent A: HP/Status on pawn, Agent B: DoctorProvider + medical bed + Rescue/Treat toils, Agent C: Wolf entity + WolfSpawner). Opus integrated into `scenes/world/world.tscn` + `world.gd` (middle bed at (47,24) marked `is_medical=true`). - **MCP runtime verified both phases.** Phase 9: injected 75 dmg + Bleeding(2) into Bram → went Downed (hp 25) → Edda+Cora both volunteered `doctor → 'Rescue Bram → bed at (47, 24)'` → treated → Bram healed to 94.2 hp, statuses cleared, returned to work. Phase 10: `[wolf] RAID: 1 wolf(ves) spawned at [(53, 2)]` fired at day 3 22:00 (darkness ≥ 0.8); 4 wolves alive across raid cycles by day 4 01:51. Screenshot confirms medical-bed red-cross marker and wolf silhouettes at night. - **Phase 10 deliberately partial** — wolf-side combat (two-roll, bleed on hit) shipped, but pawn-side weapons/armor/cover/friendly-fire deferred. Acceptance demo's full chain (wolf → bites → pawn bleeds → doctor saves) awaits player weapons. WolfSpawner pack size 1–2 vs design target 1–4 — tune up post Phase 20 numbers pass. - **Bleed-out timer shipped at demo value** `BLEED_OUT_TICKS = 1200` (~ minutes) instead of design value `432000` (6 in-game hours). Documented in `status_catalog.gd`; flip on first time-balance pass. Recorded so it doesn't ship to a release at the demo value. - **Bed-claim bug noticed in passing** during ULTRA-speed run: `Bram bed claim failed at /root/Main/World/Bed — sleeping on floor` for two of three pawns even when beds free. Doesn't gate Phase 12; logged for separate triage. - **Phase 12 (Seasons + Weather) shipped same day.** Three-agent fan-out (Agent A = clock seasons + season top-bar + terrain palette; Agent B = Weather autoload + procedural rain overlay + storm flash; Agent C = Wet/Cold statuses + mood thoughts + shelter check). Opus prepped contracts up front (event_bus signals, Clock season constants, Weather autoload stub) so all three could run fully parallel. - **Pre-fan-out contract pattern was the key.** By writing the public API surface to disk *before* dispatching agents, each agent's slice compiled standalone and integrated on first try. Worth repeating for any future "multi-system phase" — pay the 5-minute scaffolding cost, save the round-trip merge fixup. - One quick-edit fixup needed: Variant inference errors on `var old_sev := s.severity` (untyped Array loop var). Same trap as the Phase 7 crop fix. Pattern: always explicit-type any `:=` declaration assigned from a non-typed-Array element. - **MCP runtime verified all paths.** Top-bar shows "Spring 1/12", rain droplets render across screen, storm white-flash caught mid-animation, wet status flipped 0→Damp(26)→Soaked(65) with mood thought sync, cold status fired on cold_snap with -4 mood. `_is_sheltered()` proxy (has floor) works for v1; Phase 13 Room BFS replaces it. - Next: Phase 13 (Rooms, roofing, beauty, dirtiness, cleaning) is the natural follow-up — it pays the `_is_sheltered()` debt and unlocks beauty/dirty mood thoughts. - **Phase 13 (Rooms + Beauty + Dirtiness + Cleaning) shipped same day.** Three-agent fan-out reusing the Phase 12 contracts-first pattern (`Room` class, `World.rooms`/`room_at_tile`/`is_indoor`, 4 EventBus signals pre-written by Opus before dispatch). - **DECISION: Big-room UX = bump cap to 16, banner above.** Auto-roof activates for rooms ≤16 interior tiles; rooms above that emit `room_too_large` with no roof. Cabin (24 tiles) intentionally exceeds cap so the warning path is exercised at boot. Test shed (3×3 = 9 tiles) exercises the roof path. - **Agent C wiring loss + recovery.** Agent C reported wiring `BeautySystem` / `DirtinessSystem` / `CleaningProvider` / `IndoorTintOverlay` into `world.gd` but the instantiation code never landed — only the autoload field declarations and the entity-side `register_furniture` hooks survived. Opus added the missing 4 instantiations + work-provider registration + pre-built furniture seed in a follow-up edit. Recovery time ~5 min via runtime probe. Pattern: **trust but verify agent reports against actual file state**, especially for "I wired this into the scene/autoload" claims. Don't repeat by asking for "show me the diff" alongside the report. - **Demo seed extended.** Added `_prestamp_test_shed_for_room_detector` + `_spawn_complete_wall` / `_spawn_complete_floor` helpers — instantiates entities in completed state for a 5×5 walled shed at (34, 23). Demonstrates the auto-roof path side-by-side with the cabin's over-cap path. - **Sheltering proxy debt paid.** `Pawn._is_sheltered()` now reads `World.is_indoor(tile)` first, falling back to the floor-has-cell proxy for graceful degradation while RoomDetector populates. - Phase 10 wolves' raid cooldown is set to 4800 ticks (1 in-game day). Combined with `darkness_factor ≥ 0.8` trigger gate, wolves continue to spawn nightly. No tuning required this phase. - Next: Phase 14 (Death + corpses + burial) — closes the death loop. Pairs naturally with Phase 13's `DirtinessSystem.bump()` API for combat-blood spikes. - **Phase 14 (Death + corpses + burial) shipped same day.** Three-agent fan-out (A: death+corpse spawn, B: graveyard+grave_slot+marker+haul, C: cremation pyre+ash+4 mood thoughts). Opus pre-wrote Corpse class with `setup()`/save round-trip + 5 EventBus signals + World.corpses/grave_markers registries before dispatch. Pattern proven for the third time — this is the way. - **Bleed-out timeout shipped at design value** (`BLEED_OUT_TICKS = 432000` = 6 in-game hours at 20 Hz). Previous memory entry's claim of "demo value 1200" was based on a misread; the constant has been at the design value since Phase 9 status_catalog.gd landed. - **Throughput tuning surfaced as a real concern.** At ULTRA speed (12×), corpses decay (DECAY_PER_TICK=0.05) hit the 100-rotted threshold before HaulingProvider's priority-3 corpse-haul scheduling could chain pawn→pickup→graveyard. Pipeline is correct; numbers need a Phase 20 pass. Workaround for testing: pause + manual job assignment, or boost corpse-haul to priority 4+. - **`recipe.gd` extended with `ingredient2_type/count`** for the cremation recipe (1 corpse + 5 wood). CraftingProvider's pickup step still only enforces ingredient1 — documented gap. Cremation pyre works as a corpse-only recipe today. - **HaulingProvider corpse path uses Node metadata** (set_meta on the carrying pawn) to track the carried corpse. Agent B did this to keep Corpse class (Agent A's territory) untouched. If a proper `Pawn.carrying_corpse` field lands later, replace the metadata. - **MCP runtime verified:** DEMO_PHASE14_AUTOKILL toggle force-kills first pawn at tick 50 — Bram DIED → corpse spawned → Cora's saw_corpse (-3) thought fired. Grave dig + designation paint chain verified to completion. Bleed-out + Wet status overlap firing correctly during ULTRA runs. Screenshot shows fresh corpse silhouette at cabin doorway. - Next: Phase 15 (Storyteller) — the world prods the player. 25-event registry, daily 6 AM roll, per-event + per-category cooldowns (both gates locked), banner/modal UI, ghost-state recovery. - **Phase 15 (Storyteller) shipped same day.** Three-agent fan-out (A: EventCatalog with all 25 events authored from design.md, B: Storyteller autoload daily roll + cooldowns + tension + ghost state, C: BannerUI + ModalUI + go-there pan). Opus pre-wrote EventDef class + 5 EventBus signals + Storyteller autoload stub before dispatch. Contracts-first pattern proven for the 4th time. - **Code-as-data choice for events.** EventCatalog is GDScript factories rather than `.tres` resources. Trade-off: faster iteration in MVP (no Inspector dance, no resource UID drift), but `.tres` is on the table for Phase 17 if hot-reload becomes a workflow need. - **MCP runtime verified across two boots** (different daily rolls per boot — RNG fresh per session): - Boot 1: `lone_wolf` THREAT → modal "A starving wolf circles your livestock." with Prepare/Dismiss buttons + sim auto-paused. Resolve → tension bumped 27→42, sim resumed. - Boot 2: `an_old_map` LORE → top-center banner, non-blocking, 6-sec auto-dismiss. - **Quick-edit fixup mid-flight:** modal didn't auto-hide when `Storyteller.resolve_current` was called externally (only on internal button click). Added a `storyteller_event_resolved` subscriber to the modal — one method. Phase 17 polish will sweep similar UX gaps. - **Wolf-spawn integration deferred to Phase 17** — Agent A noted `EventBus.request_wolf_spawn(count)` signal not yet declared; threat events that spawn wolves currently log-stub. Pattern recorded: when a Phase-N agent surfaces a missing cross-Phase signal, declare it on EventBus in the next contracts-prep pass rather than spreading the fix across agents. - **All categories used** in the 25-event corpus: nudge×4, seasonal×4, wanderer×4, threat×4, disease×3, resource×3, lore×2, milestone×1. Total cooldowns lock the per-day pool to roughly 3-6 eligible events on a typical day. - Next: Phase 16 (Save/load full coverage) — pays the partial-save debt accumulated since Phase 3. All entity types (pawn, item, furniture, container, corpse, wolf, plant, grave_marker), TileMap layers via `get_used_cells_by_id`, Storyteller state round-trip, Bill mid-fetch states. Phase 16 is the integration phase — fewer new files, more save-seam plumbing. - **Phase 16 (Save/load full coverage) shipped same day.** Three-agent fan-out (A: class_id tagging + missing to_dict/from_dict on 6 entities + tilemap helpers + beauty/dirt map serialization; B: SaveSystem v2 rewrite with per-class factory registry + clear-and-respawn apply_save + slot API; C: Autosave autoload + Save/Load TopBar buttons + LoadMenu + ResumeToast). Opus pre-wrote World.clear_all + 4 EventBus signals before dispatch. Pattern proven for the 5th time. - **User explicitly wanted autosave + manual save/load UI** — both shipped: Save (💾) + Load buttons in TopBar; Load opens slot picker; Autosave fires periodically (every 6000 ticks = ~5 in-game min) and on app pause / focus loss. - **clear-and-respawn pattern works.** `World.clear_all()` wipes all entity registries + queue_frees the Nodes; SaveSystem.apply_save then iterates `payload.entities` and dispatches to per-class factories. Verified: 113 entities saved at tick 1137, sim advanced to tick 4600 at ULTRA, load restored tick=1137 + all 3 pawns + all furniture in correct positions with 0 errors. Round-trip clean. - **Wolf target re-resolution** — wolf.from_dict stores target_pawn as a name string; Agent A's note says Agent B's apply_save should re-resolve names against `World.pawns` after all pawns are restored. Verify on next save-load cycle. - **Save version bumped to 2.** v1→v2 mismatch shows a warning dialog in LoadMenu; player can continue or cancel. Future bumps follow the same pattern. - **Known acceptable gaps:** Pawn JobRunner mid-INTERACT/mid-BUILD restarts from toil 0 on reload (walk toil round-trips; multi-step interact does not). Workbench bill mid-craft fetch state isn't fully serialized. Both are tolerable — pawns just redo a few seconds of work. Document as Phase 20 tuning. - **Three new autoloads now: Autosave (Phase 16) + Storyteller (Phase 15) + Weather (Phase 12).** All registered in project.godot in dependency order (Sim/Clock first, then Weather/Storyteller/Autosave). - Next: Phase 17 (Touch UX completion). The biggest deferred-polish bucket — work-priority matrix UI, bills UI, alerts log, pawn detail panel, build drawer, settings menu, all the touch-first interaction layer that's been stubbed. Several Phase 14/15 effects (wanderer recruit, resource buffs, wolf-spawn signal) wire in here too. - **Phase 17 (Touch UX completion, MVP-cut) shipped same day.** Three-agent fan-out: A = PawnDetailPanel + SettingsMenu, B = BuildDrawer (with 12 new Designation tools), C = WorkPriorityMatrix + AlertsLog + Decision Layer 4 per-pawn priority refactor + EventBus.request_wolf_spawn wiring. Opus pre-wrote 6 EventBus signals + Pawn.work_priorities Dictionary stub before dispatch. Pattern proven for the 6th time. - **TopBar now has 10 buttons:** ‖ / 1× / 5× / 12× / Save / Load / Settings / Build / Work / Log[N]. + a floating ⊕ FAB at bottom-right (BuildDrawer quick-open). Crowded but functional; mobile-polish pass can group under a hamburger. - **Decision Layer 4 refactor.** Pawn.work_priorities (Dict cat→int 0..4, 0=OFF, default 3=NORMAL) is now respected. Needs categories (rest/eat/sleep) BYPASS the filter so a pawn can't accidentally starve from misconfiguration. Doctor IS in the matrix (players can opt a pawn out of doctor duty). Audit log now prefixes work decisions with `(pri=N)`. - **Mouse drag-paint works as-is.** User specifically asked about this. Existing Selection + Designation tools read `_unhandled_input` events for mouse motion + button-held state, so drag-painting walls/floors/designations works in the editor without further work. - **Pattern proven 6th time:** "pre-write contracts to disk before fan-out". This session has been a single 1-day sprint shipping 6 phases (12 through 17) end-to-end via this pattern. Cost discipline: every phase = 3 Sonnet agents in parallel + Opus pre-write contracts + Opus integration + Opus MCP runtime verify. - **MCP runtime verified all 4 new UI surfaces:** Bram's detail panel shows Crafting=8 / Cooking=2 / Manual=0 matching seed; BuildDrawer Designate tab shows 4 tools with procedural icons; WorkPriorityMatrix shows 3 pawns × 8 categories grid with default "3" cells; AlertsLog shows 4 entries with mixed severity icons + "Spring Awakens" from the boot storyteller roll. - **Deferred to Phase 17.5 polish pass:** Per-pawn/per-job view layers, stockpile 4×4 chip grid, Bill UI, "no stockpile accepts X" / "bill blocked" emit wiring, DaySummaryCard visual. - Next: Phase 18 (Audio). Music + SFX + ambient + volume sliders + mute-on-suspend. Smaller scope than 17 — 1 week target. ElvGames + Ventilatore bundles include music/SFX packs we can source from. ### 2026-05-12 - **PC controls patch shipped** (out-of-phase, for desktop testing + Steam Deck prep). New input actions: WASD/arrow pan, `=`/`-` zoom, `C`/`Home` center, `Tab`/`Shift+Tab` pawn cycle, `B`/`L`/`P`/`,` panel toggles, `Escape` priority stack (cancel tool → close topmost panel → deselect pawn), right-click cancel/deselect (RTS convention). `F` is the new `speed_cycle` binding (handler still TODO). Touch paths untouched. Commit `0b2e0fc`. - **Pattern note: pawn cycling Shift+Tab.** First attempt used `Input.is_key_pressed(KEY_SHIFT)` to detect modifier on `pawn_next` action — this works with real keyboard but fails for MCP synthetic input (the singleton keystate isn't updated by injected events). Fix: read `event.shift_pressed` on the InputEventKey directly. More reliable AND works in tests. **Use `event._pressed`, not `Input.is_key_pressed(KEY_*)`, when reading modifiers tied to a specific key event.** - **Latent pre-Phase-17 bug surfaced and fixed in same commit.** WorkPriorityMatrix, AlertsLog, StorytellerModal, LoadMenu, SettingsMenu all had `MOUSE_FILTER_STOP` Backdrop/Dim Controls that stayed input-active when the panel was "closed". Their open/close only toggled `_root.visible` / `_panel.visible` — never `CanvasLayer.visible` (self). World mouse events (right-click deselect, left-click pawn-select) silently eaten. Each `_set_visible`/`open`/`close` now toggles `self.visible` so input dispatch shuts off. **Why it went undetected since Phase 17:** Tab-select and direct script invocation bypass `_unhandled_input`; the runtime verify in Phase 17 used those paths. First proper mouse-world test (this session) exposed it. - **Pattern recorded:** for any CanvasLayer-based UI panel with full-screen backdrop, always toggle `self.visible` (CanvasLayer) in addition to inner Control visibility, OR use `mouse_filter=PASS` on the backdrop. Apply when adding future modals. - Delegation report: PC controls drafted by `gdscript-refactor` (Sonnet, 2 dispatches — spec implementation + polish pass). Backdrop-bug discovery + fix done on Opus during MCP runtime verification (already in context). Headless + runtime verify all green. - **Visual sprite upgrade pass (play → fix → play loop).** Three procedural draws replaced with ElvGames atlas sprites this session: - **Walls** — stone-fill was at FG_Fortress (1,1), which is a tan stone *floor* tile (no brick texture, no cap). Swapped to (13,4) — middle column of a 3-tile-wide capped autotile, used as a non-autotile single sprite. Reads as a proper brick wall with a depth cap. Commit before context summary. - **Items on ground** — five most-spawned types (stone, iron_ore, gold, wood, plank) now render from FG_Abandoned_Mines coords with a quality border + stack-count badge composited on top in `_draw()`. Other types still use the original hue-hashed square fallback. Commit `7274ada`. - **Doors** — first attempt used FG_Fortress 32×32 closed-gate at (4,19); user rejected as "a door for a entrance to a castle" (commit `ac21443`). Pivoted to **FG_Village (3, 24)**, a 1-tile-wide olive-wood cabin door with U-handle extracted from the red-roofed cottage template. Commit `745ab29`. FG_Village.png copied into `art/tiles/`. - **Tileset survey notes (for future visual passes).** FG_Fortress = ALL doors are 2-tile-wide castle gates. FG_Houses = component-style 2-wide doors (cottage but still big). **FG_Village** = full-house templates with 1-tile-wide cabin doors baked into the bottom row (the one we used) — good source for residential furniture, signs, windows. FG_Interior = wardrobes/dressers/wallpaper, no standalone door entities. FG_Marketplace = no doors at all. - **MCP `execute_game_script` quirk:** scripts with 3+ statements fail `Parse error`. Workaround: pack with `;` or use `for x in y: a; b; c` (compound for-body line). Affected this session's visual probes. - **Pattern recorded:** when copying a new texture into `art/tiles/`, run `/mnt/d/godot/Godot_v4.6.2-stable_win64.exe --editor --headless --quit` to force generation of the `.import` file before any scene can `preload()` it. Skipping this step yields a silent null preload at runtime. - Delegation report this session: **No delegation — handled on Opus** for sprite-atlas surveys, GDScript refactors, MCP runtime verification. Pure visual / MCP-tool work; subagents would have re-read the same files repeatedly. ### 2026-05-15 - **Bug-triage patch shipped** out-of-phase, before Phase 18 (Audio) starts. Same pattern as the 2026-05-12 PC-controls patch. Three playtest-reported bugs investigated, root-caused, fixed, and MCP-runtime-verified end-to-end. See **Known bugs / triage backlog** above for per-bug detail. - **Pattern recorded — "pre-built seed masks downstream wiring gaps".** Five entity types (Torch / Bed / Crate / Workbench / CremationPyre) had been missing `World.register_build_site(self)` since Phase 17 landed. Nobody noticed because the cabin demo seed calls `_spawn_complete_*` helpers (insta-builds via direct on_build_tick loop), so no player ever painted a fresh furniture designation. Lesson: when adding a new entity type to a build-queue iterating system, always playtest the FRESH-PAINT path, not just the pre-built seed. - **Pattern recorded — "pathfinder mutation can strand walking pawns".** When set_cell_walkable(false) fires, pawns already mid-walk on a path through that cell will keep stepping along the stale path and land on the now-impassable tile. AStarGrid2D requires walkable START, so the pawn idles forever. Defense-in-depth fix: Wall._complete dislodges-on-tile + Pawn._advance_walk re-checks walkability before stepping. Generalises to any future system that mutates pathfinder walkability (e.g. doors, big_rock, future fortifications). - **Pattern recorded — "Y-sort equal-Y ambiguity".** Two entities at same Y-sort root with same position.y fall back to scene-tree order, which is fragile. Always set distinct Y-positions for distinct visual layers (ground vs. above-ground). Ground-tier entities use top-of-tile anchor; above-ground entities use bottom-of-tile anchor (Wall/Bed/Torch/Workbench pattern). Pawn at mid-tile sits cleanly between. - Delegation report: `researcher` (Haiku, 1 dispatch) mapped three bug surfaces (~600 word digest with file:line citations). All fixes + MCP runtime verification handled on Opus — fix sites were already in context from the researcher report, so re-dispatching to Sonnet would have been pure overhead. - **Job system audit + designation-gate fix shipped same day** in the same bug-triage patch session. Player report: "I set some trees to be cut but no one is doing it". Root cause via `researcher` (Haiku, 1 dispatch, ~900 word digest mapping all 11 WorkProviders): ChopProvider and MineProvider iterated `World.trees` / `World.rocks` unconditionally — `is_choppable()` / `is_mineable()` only check progress, not designation. The `&"chop"` and `&"mine"` cases in `world.gd._on_designation_added` stashed a `null` sentinel and never touched the entity, so paint was purely cosmetic. Pawns were auto-chopping the nearest unfelled tree regardless of what the player painted. - **Fix**: added `chop_designated: bool` to Tree, `mine_designated: bool` to Rock + BigRock (BigRock flag covers entire 2×2 footprint via `footprint_tiles()` membership check), gated both providers, wired designation paint/cancel to flip the flag, auto-designated SAMPLE_TREES / SAMPLE_ROCKS / SAMPLE_BIG_ROCKS at boot so the demo still runs end-to-end. Rimworld parity restored — pawns no longer auto-chop / auto-mine. - **Also from same audit**: added reachability pre-checks (mirroring HaulingProvider) to DoctorProvider and EatProvider — they were issuing jobs to unreachable targets, then relying on JobRunner walk-cancel to clean up. The pre-check avoids the busy-spin between Decision and JobRunner. - **Pattern recorded — "iteration-source bugs hide behind boot-seed shortcuts".** The Phase 4 seed `SAMPLE_TREES` / `SAMPLE_ROCKS` was always being chopped/mined because providers auto-picked. Player only noticed when they painted designations and the behavior didn't change. Boot seeds that mirror "default OK" state can mask gate logic; when adding any new gate (designation, skill, capability), playtest the FRESH state, not just the seeded state. - **Other audit findings (deferred, not fixed this session)**: CraftingProvider has an ingredient re-scan race (ingredient may disappear between lines 73 and 91); PlantProvider only handles harvest (sow stubbed for Phase 17); RestProvider is a Phase 3 smoke-test leftover that could be deprecated. All low priority. - **ConstructionProvider reachability gate** added later same day after player report: chop designations weren't being honored. Root cause: a leftover `build_door` designation at the test shed wall (36, 27) was unreachable (wall in the way), but ConstructionProvider (priority 6) kept offering it to every pawn every tick, and Decision picked it over chop (priority 5). JobRunner cancelled the doomed walk each tick — busy-spin starved chop. Same fix shape as Doctor/Eat: pre-check `pathfinder.find_path` before issuing a job; for blocking sites (walls) probe from an adjacent walkable cell. - **Door-replaces-wall replacement build** added by user request. Painting `build_door` on a tile occupied by a Wall (ghost OR completed) atomically demolishes the wall in place and spawns the door ghost. Reverses Wall._complete cleanly: unstamps wall_layer, marks pathfinder walkable, recomputes rooms. Source of truth is World.build_queue so the rule covers both designation-painted walls AND pre-built seeds (cabin, test shed) which self-register via Wall._ready. Other replacement combos (floor-on-floor, wall-on-door) NOT in scope; would need separate refund/destroy rules. - **Phase 18 (Audio) shipped same day** — bug-triage sprint extended into Phase 18 once the job system was solid. Scope: - 8 curated OGG files copied into `audio/{music,sfx}/` from the ElvGames bundle (Retro Farming Music 1, Cozy Melodies Pack 1, UI Pack 1, Woodcutting & Mining 1, Sword Pack 1). Two source zips (Cozy Melodies Pack 1, Retro Farming Music 1) were extracted in-place inside the bundle's Tier 1 dir. - New `autoload/audio.gd` singleton: 3 buses (Master, Music routed to Master, SFX routed to Master), lazy stream loading with per-key cache, rate-limited play_sfx (80ms cooldown to prevent chop-tick spam at ULTRA), single persistent AudioStreamPlayer for the music director, automatic music swap on `Clock.phase_changed` (day_loop ↔ night_loop), mute on `NOTIFICATION_APPLICATION_PAUSED` + `FOCUS_OUT`. - SettingsMenu sliders (master/music/sfx) live-bound via `value_changed.connect(Audio.set_*_linear)`. Ambient slider intentionally unwired — no ambient bus this pass. - SFX wiring: `Tree.fell` → tree_fell, `Rock.mined` / `BigRock.mined` → mine_tick, EventBus.pawn_took_damage → combat_hit, storyteller_event_fired → ui_confirm sting, alert_added → ui_click. - MCP runtime verified all paths: buses online, music swap day→night→day round-trips, volume sliders correctly map linear→dB (0.5 → -6.02dB, 0.0 → -80dB silent), SFX trigger on demand. - **Pattern recorded — "rate-limit per-key SFX or the mixer chokes at fast sim speed".** First instinct was per-chop-tick SFX from `Tree.on_chop_tick`; at ULTRA (12× speed, ~12 chop ticks/sec/pawn × 3 pawns) that would saturate the audio mixer. Moved to completion-only SFX (Tree.fell, Rock.mined) plus an 80ms per-key cooldown in `play_sfx`. Future ambient/tick-based SFX should reuse this throttle. - **Pattern recorded — "extract zipped asset packs into their own named folder, in place".** The bundle ships some packs as zips (Cozy Melodies Pack 1, Retro Farming Music 1) and others as already-extracted folders. Each zip's top-level entry was a folder matching its name, so `unzip -q` in-place created the expected `Cozy Melodies Pack 1/` directory alongside the .zip without flattening. Don't extract to a generic name like `audio_extracted/` — preserve the pack identity so licensing/credits stay traceable. - **Hover-inspect tooltip shipped** in the same out-of-phase polish sprint as the audio + designation-gate work. New `scenes/ui/inspect_tooltip.gd` CanvasLayer at z=50, `PROCESS_MODE_ALWAYS` so it works during pause. Priority order is: pawn → wolf → corpse → grave marker → crop (exact tile) → tree (4-tile canopy) → big rock / rock → furniture at exact tile → furniture sprite-canopy (bed +1 tile up) → item → stockpile region → floor. Iterated three times against player feedback: trees not showing (needed canopy box), beds covered by floor (needed sprite-canopy fallback because bed sprite spans 16×32), crops not showing on the exact-tile pass. - **Pattern recorded — "tall sprites need sprite-canopy fallback in pickers".** Bed sits 32px tall but its `tile` is only the foot row; hovering the headboard row hit the floor first because exact-tile match prefers furniture-on-this-tile. Fix: after the exact-tile pass fails, retry with `tile + Vector2i(0, 1)` for sprite-tall entities. Same trick will be needed for any future 2-tile-tall furniture (wardrobe, double bed, statue). - **Bed sprite rebuilt procedural after atlas misidentification.** The 2026-05-12 visual pass used FG_Interior atlas coords for "beds" — those coords were actually chairs-with-cushions. User caught it as "not good and confusing where the bed is". Replaced with a procedural top-down draw: wood frame + striped blanket + pillow + foot board, 16×32, three deterministic colour variants seeded by tile coord. The cabin was 8×6 = beds slipped into the north wall; bumped to 8×7 at origin (44, 22) so the headboard row is interior floor. - **Pattern recorded — "visually verify atlas coords during survey, not just by filename hint".** FG_Interior has a band of chair sprites at the same atlas band where beds would naturally live; previous pass logged them as beds without zooming in. Future atlas surveys should ALWAYS render a `_zoom.png` with red gridlines and visually confirm before committing to a coord constant. - **Designation tile-highlight leaked across job completion.** Painting chop / mine / build left the orange/blue overlay on the tile forever, even after the job finished. Root cause: nothing called `designation_ctl.clear_cell()` from entity completion handlers. Fix: added `World.designation_ctl` field + `clear_designation_at(tile)` helper, wired into completion handlers of Wall, Floor, Door, Bed, Torch, Workbench, Crate, Tree, Rock, BigRock (iterates footprint), GraveSlot. Commit `f67c12c`. World boot wires the controller (`World.designation_ctl = designation_ctl`). - **Crop sprites rebuilt with ElvGames atlas + four growable kinds.** Replaced procedural stem-and-circle draw with per-kind 16×32 Sprite2D anchored bottom-of-tile (matches tree pattern). Four kinds wired: wheat, potato, corn, strawberry — first two from 4-stage sheets, last two from 5-stage sheets (we slice the first 4 cols; the 5th is a post-harvest regrow frame we ignore). Stage-to-sprite mapping: TILLED=no plant, SOWN→col 0, G1→col 1, G2→col 2, G3 + READY both→col 3 (harvest highlight is the cue, not the sprite). Boot demo plants one column per kind east of the cabin. Harvest outputs: wheat/corn → grain, potato/strawberry → vegetable. Commit `c93f889`. - **Pattern recorded — ".import auto-generation via `godot --headless --import`".** Writing `*.png.import` companion files by hand with placeholder paths works but Godot rewrites them with proper UIDs on the first import scan. Reliable workflow: copy PNG into `art/`, write minimal .import (UID will be ignored), then run `/mnt/d/godot/Godot_v4.6.2-stable_win64_console.exe --headless --import` once to generate the .ctex binaries. Headless boot fails until the import scan runs. Same pattern from 2026-05-12 confirmed. - Delegation report (whole-day 05-15 polish sprint): **No delegation — all on Opus.** Sprite-atlas surveys, MCP runtime verification (camera positioning, dialog dismissal, stage forcing), and the iterative play→fix→play loop kept the same files hot in context; Haiku/Sonnet handoffs would have re-read every time. The `researcher` (Haiku) dispatch from earlier in the day for the job-system audit is the only delegated work. - **Workbenches rebuilt procedural after second atlas-misidentification.** The 2026-05-12 atlas-sprite pass for Carpenter/Smelter/Hearth/Millstone again hit the same trap as Bed: the picked tiles read as chest-of-drawers (Carpenter, FG_Interior 24,20), candle base (Smelter, FG_Marketplace 8,30), 2-burner stove (Hearth, FG_Interior 16,32), and cushion stack (Millstone, FG_Interior 17,40). User: "the carpenter is a chest of draws? no idea what the mill is, looks like a bucket". Survey of all 7 plausible source tilesets (Interior / Marketplace / Houses / Village / Fortress / Mines / Dark Castle / Extras) found a 2×2 medieval stone fireplace at FG_Interior (12,13)..(13,14) for Hearth but **2-wide tiles don't fit the current single-column sprite system**, and the other three had no clean replacement. Decision: go procedural for all four, following `CremationPyre._draw_pyre` as precedent. New methods `_draw_carpenter / _draw_smelter / _draw_hearth / _draw_millstone` in workbench.gd; `_VARIANT_SPRITES` table + `_build_sprite()` Sprite2D path removed; `_complete()` no longer touches a sprite child. Hearth is the only h=2 variant — draws stone surround + mantle + arched opening + log fire + flame teardrop extending into the tile north (Y-sort handles occlusion). Commit `c97ada8`. - **Pattern recorded — "atlas-misidentification trap, second occurrence — prefer procedural for named furniture".** Pixel-art tile atlases pack hundreds of squat 16×16 items at low resolution; without zoomed-in inspection it's easy to mistake a furniture silhouette for what its name implies. Both Bed (2026-05-12) and the four workbenches (2026-05-15) hit this failure mode. Going forward: for furniture whose silhouette is **definitional** (workbench-with-tools, bed-with-pillow, millstone-as-grindstone), **start with procedural draws** and only swap in an atlas tile after verifying via 16× zoom that the tile actually depicts the named object. Atlas-sourced sprites are still the right call for generic decorative variation (trees, walls, floors, crops with growth stages). - **Tree variety added via 3-season palette swap.** Copied `FG_Tree_Summer.png` + `FG_Tree_Fall.png` from the Grasslands tier-1 pack into `art/sprites/` (Spring was already there). 4 silhouettes × 3 palettes = 12 visual variants; `tree.gd` hash mixes silhouette and season independently so neighbouring tiles don't all share the same palette. Winter omitted — snowy trees would look out of place in the current biome. When a season-cycle system lands (future phase), the active texture can be swapped globally by season instead of per-tree. No growth stage / sapling system yet — bundle has no growth frames for trees, and the user's "regrow / auto-grow" ask is explicit future scope (deferred to a later phase that also covers planting designation). Commit `c97ada8`. ### 2026-05-16 - **Pre-made SW stockpiles + crates removed** so items sit where they're produced until the player paints storage (matches Rimworld parity). Deleted `_spawn_sample_stockpiles()` (two zones at (15,55) and (15,62)) and the two SW haul-target crates at (17,60)/(18,60). Kept the interior cabin crate at (50,23) as a starting amenity. Bills' `UNTIL_N` mode counts items anywhere in world (not just stockpiles) so the Smelter's "5 stone blocks" bill still terminates. All `World.stockpiles` consumers iterate the list — empty-list no-op, no crashes. Commit `c81e817`. - **Workbench Bill Editor plan drafted** for the deferred Phase 17 item ("Bill UI for workbenches"). 10-step plan saved to user-memory at `~/.claude/projects/-mnt-d-godot-rimlike/memory/plan_bill_ui.md` (resilient to fresh sessions). Without this UI, every player-built workbench is functionally dead — build drawer can paint them but bills can only be set in code. Recipe.id field existence needs verification in Step 4. Starting execution 2026-05-16. - **Workbench Bill Editor shipped same day** (commit `bdd4352`). Tap a workbench → right-side panel with bill rows (mode toggle, target spin, pause, remove) + Add-bill popup filtered by `accepted_skill`. Mirrors `PawnDetailPanel` exactly: layer 18, right-anchored 360 px, procedural `_build_ui()`, sim_tick refresh of the current-bill status only (bill list rebuilt on add/remove/select to preserve scroll). Selection chain extended in `selection.gd`: pawn-first then workbench, mutual-exclusion via new `EventBus.workbench_selected/deselected` signals. Closes Phase 17 deferred item. Code-level verification: headless boot clean, no runtime errors. Visual MCP verification still pending (need editor running). - **Delegation report — bill UI sprint.** Steps 1, 3, 4, 6 (~5 mechanical edits across 5 files) → `quick-edit` (Haiku, 1 dispatch). Step 5 (new ~432-LOC WorkbenchPanel script needing full mirror of PawnDetailPanel + 6 source files) → `gdscript-refactor` (Sonnet, 1 dispatch). Steps 2, 7 (selection chain + main mount, needed design judgment about mutual-exclusion and main.gd's typed-var pattern) handled on Opus. Strings table follow-up (`ui.bill.until_count` key missed by Sonnet, fallback was a hardcoded literal) caught and fixed on Opus before commit. - **Pattern recorded — "main.gd typed-var pattern requires CanvasLayer.new() + set_script(), not SCRIPT.new()".** First mount attempt used `WORKBENCH_PANEL_SCRIPT.new()` — Godot 4's parser refused with "Cannot infer the type" because `Script.new()` returns generic `Object`. Switched to `var x := CanvasLayer.new(); x.set_script(SCRIPT)` matching the rest of main.gd. Cheap parse error to surface via `--headless --quit`, but worth noting: subagents writing UI mount glue need this idiom explicitly. - **Pattern recorded — "never free a widget from within its own signal callback".** Bill editor crashed when user changed mode FOREVER → UNTIL_N. Root cause: OptionButton.item_selected lambda called `_populate_bills()` directly, which clears + rebuilds all bill rows — freeing the very OptionButton whose signal was still emitting. Same pattern in the Remove button. Fix: `call_deferred("_populate_bills")` so the rebuild runs on the next idle frame after the signal frame completes. Commit `4e09dea`. Applies to any UI where a child Control's signal handler mutates a parent container — always defer rebuilds. - **Pawn reskin Slice 1 shipped** (commit `b4c9541`). Pawns now render as AnimatedSprite2D children sourced from ElvGames Farming Characters Pack (Pack 1, chars 001-015). 9 anims per pawn: idle×4dirs + walk×4dirs + dead. Atlas pick is `(absi(pawn_name.hash()) % 15) + 1` — Bram=004, Cora=013, Edda=001, all visually distinct. New `_atlas_for_pawn(pawn)` helper is the **single extension point** for the future Slice 2 (armor → atlas swap when equipment+combat phase ships). Slice 1 plan archived to user-memory at `~/.claude/projects/-mnt-d-godot-rimlike/memory/plan_pawn_reskin_slice1.md`. 45 PNGs + .import companions copied to `art/sprites/characters/`. New `scenes/pawn/pawn_sprite_frames.gd` builds the SpriteFrames from a {idle, walk, dead} atlas trio. Code-level verification clean; visual MCP verification still pending (needs editor running). - **Pattern recorded — "class_name lookups aren't reliable during cold-start parse; use preload() for sibling scripts".** First attempt referenced `PawnSpriteFrames.build()` in pawn.gd via the global class_name registry. Headless parse failed with "Identifier 'PawnSpriteFrames' not declared in the current scope" because pawn.gd was loaded before pawn_sprite_frames.gd registered its class_name globally. Fix: `const _PAWN_SPRITE_FRAMES = preload("res://scenes/pawn/pawn_sprite_frames.gd")` at the top of pawn.gd, then call `_PAWN_SPRITE_FRAMES.build(atlases)`. Class_name is fine for public API documentation; preload is the reliable runtime resolver. - **Pattern recorded — "deferred-init data must be wired AFTER setup(), not in _ready()".** First sprite mount happened in `_ready()` and read `pawn_name` — but pawn_name is empty at _ready time (assigned later in setup()). All three pawns got atlas idx 1 (hash of empty string). Fix: moved sprite mount to `_mount_sprite()` called from setup() AND from_dict(), both of which assign pawn_name first. Idempotent (frees prior sprite). Same shape will recur for any future render that depends on per-instance saved state — always check whether the data the renderer reads is available at _ready vs after setup/from_dict. - **Delegation report — pawn reskin Slice 1.** `quick-edit` (Haiku, 1 dispatch) handled the mechanical edits across pawn.gd (facing field, save/load, `_canonical_facing`, `_atlas_for_pawn`). `gdscript-refactor` (Sonnet, 1 dispatch) wrote the new `pawn_sprite_frames.gd` helper (~50 LOC) and wired the AnimatedSprite2D into Pawn._ready (z_index, anim switching, _facing_suffix). Opus handled the asset copy + headless --import, the two parse/runtime fixes (preload + setup-not-ready), the hash-distribution audit, and the commit. The two patterns above were caught on Opus during verification. - **BuildDrawer redesign — 2-pass iteration on Opus.** First pass made the panel auto-size and anchor to bottom-center (commit `c63926a`) plus added section headers in the Build tab. User rejected: panel "jumps around" between tabs. Second pass (commit `88e3fa9`) reverted to full-width fixed-size tray (PRESET_BOTTOM_WIDE, PANEL_HEIGHT 280 down from 600), and restructured the Build tab as side-by-side section *columns* (Structures / Furniture / Production) filling the wide tray horizontally instead of stacking vertically. Lesson saved to user-memory as `feedback_ui_trays.md`: bottom-tray panels stay full-width fixed-size; "smaller" means shorter, not narrower; sections lay out horizontally inside the tray. - **Pattern recorded — "tray UIs: short and wide, fixed size, sections fill horizontally".** Bottom-anchored UI panels in this project are framed as trays, not cards. Player wants the tray as a visual anchor; auto-resizing it per-tab feels jumpy. Future workbench / inspector / day-summary panels should follow this rule. Help-modal and DaySummaryCard (later this session) are *modals*, not trays — different rules apply. - **Phase 17/18 closure sprint shipped same day** (commit `bba1ce4`). Three-agent fan-out (gdscript-refactor × 3) ships the deferred polish: **StockpilePanel** (layer 18 right-anchored; 21-chip 4-col filter grid + 5-priority segmented control + Select-all / Clear-all; mirrors WorkbenchPanel; selection chain extended to pawn → workbench → stockpile mutual-excl); **DaySummaryCard** (layer 19 modal auto-opens at dusk→night via `EventBus.day_ended`, auto-pauses sim, weather row + stats grid + green/yellow/red tension bar; Settings opt-out persists via `GameState.settings["show_day_summary"]`); **atmospheric audio** (rain-ambient loop on weather rain/storm with 0.5s fade-out, thunder sting on rain→storm transition, raid-warning sting on `EventBus.wolf_spawned`; OGGs sourced from ElvGames Cozy Melodies Pack 6 + Magic Spells Pack 6 + Sword Pack 1; reuses SFX bus so existing slider + suspend-mute inherit). Earlier worry that the `no_stockpile_accepts` + `bill_blocked` alerts needed wiring was wrong — both signals already emit end-to-end from HaulingProvider/CraftingProvider with rate-limiting and AlertsLog translators; replaced that wedge with DaySummaryCard. - **Pattern proven 7th time — contracts-first fan-out.** Opus pre-wrote 3 EventBus signals (`stockpile_selected/deselected`, `wolf_spawned`) and the WolfSpawner emit-call before dispatch. All three agents integrated on first try. - **Phase 19 (Onboarding) shipped same day** (commit `59ca6ba`). Decision: **hint system (option a)**, not guided-day or tutorial-scene. Three-agent fan-out: **HintSystem autoload + HintOverlay (layer 22)** 7-step tour gated on `EventBus` triggers (welcome → pawn select → build drawer → stockpile painted → work matrix → day_ended → tour_complete), per-hint dismissal persisted as `Array[String]` in `GameState.settings["dismissed_hints"]`, FIFO queue depth 3 (drop oldest), reduce-motion snap path, `reset_tour()` public API; **HelpModal (layer 20)** 5-tab static reference (Controls / Verbs / Priorities / Storyteller / Tips) opened via `EventBus.help_requested`, dim-backdrop dismiss; **SettingsMenu "Onboarding" section** with Show-hints checkbox + Help button + Reset-hints button; **tooltip pass** — `tooltip_text` via `Strings.t` on every TopBar button (with shortcut hints), BuildDrawer FAB, and every tool button (21 tools, `_add_tool_btn` extended with optional `tooltip` param). 67 new strings keys total. - **Pattern recorded — "generic `ui_panel_opened(panel_id)` signal beats per-panel-open signals".** HintSystem needs to know when BuildDrawer + WorkPriorityMatrix open (for tour gating). Instead of adding `build_drawer_opened` + `work_matrix_opened`, added one generic `EventBus.ui_panel_opened(panel_id: StringName)` that each panel emits with its own ID. New panels just call emit; HintSystem (or any future subscriber) needs one connection. Cleaner than N per-panel signals. - **Pattern proven 8th time — contracts-first fan-out.** Opus pre-wrote 3 EventBus signals (`help_requested`, `hint_dismissed`, `ui_panel_opened`) + 2 GameState settings defaults (`show_hints`, `dismissed_hints`) + the emit calls in `BuildDrawer.open()` and `WorkPriorityMatrix.open()` so HintSystem had clean subscribe points. One Agent A self-noted "ui_panel_opened not yet wired" but the wiring was already in place — agents sometimes report based on partial state. Trust-but-verify: I confirmed via the actual files and the runtime probe showed the chain firing correctly (welcome → dismiss → build_drawer hint). - **MCP runtime verified Phase 19 end-to-end.** Welcome banner fired 2s after boot, dismissed → persisted in `dismissed_hints=["welcome"]`, build_drawer hint chained on next `ui_panel_opened` emit; HelpModal opened via `help_requested` with tab switching (Controls → Tips) verified. Status table in `docs/implementation.md` marked **🟡 done (pending playtest review)** — Phase 19's acceptance demo ("cold tester productive in <60s") still owed; needs a real human, not MCP. - **Bug logged for Phase 20 polish — drag-paint eaten by camera (MED).** When a Designate/Build/Stockpile tool is active in BuildDrawer, drag-paint should rectangle-fill, but camera_rig's drag-pan consumes the drag instead. Contradicts the 2026-05-11 memory note (that was selection drag-paint, not camera-vs-paint priority). Fix shape: Selection or Designation marks drag events handled when a paint tool is active, or camera skips pan when `Designation.active_tool != TOOL_NONE`. - **Delegation report — whole-day Phase 17 closure + Phase 19 sprints.** Six `gdscript-refactor` (Sonnet) dispatches total across the two 3-way fan-outs. Opus handled all pre-write contracts, integration glue, MCP runtime verification, and the post-fan-out cross-check + small fix-ups (W→P keybind text in HelpModal, integration spot-checks, commits). No `quick-edit` (Haiku) dispatches today — every wedge was big enough to warrant Sonnet's reasoning. - **Pre-Phase-20 audit + 5-sprint clean-up day.** Fan-out of 5 parallel `researcher` (Haiku) agents produced a code-review surfacing ~30 items across save/load gaps, mechanically-inert features, dead-ends, drift hazards, and Phase 20 tuning candidates. Synthesised into 4 critical-bug categories + cleanup follow-ups. Six commits land the response: `d9638a4` (6 critical bugs in parallel — save/load 4-tuple, sow, ingredient2, hauling fallback, smelter wired-but-not-checked, storyteller effects); `2afca16` (A+B+C — buff consumers, sick penalty, multi-count cremation); `cc6d60d` (D+E+F+stale comment — corpse_cremated null, dead Recipe.ingredient_count, save_system simplification + workbench from_dict missing `_complete()` side-effects); `cf43ef9` (drag-paint priority + gitignore); `2f76ae1` (bed deconfliction via existing Job.target_node); `fd6f958` (Sprint A cleanup — accessibility flags wired, InspectTooltip long-press, dead arrived_at_destination signal wired to DirtinessSystem, CraftingProvider ingredient race cached, RestProvider Phase-3 debris deleted, DIRTY_THRESHOLD extracted to DirtinessSystem). - **Pattern recorded — "trust-but-verify agent reports".** Two cross-agent contradictions surfaced during the critical-bug fan-out: (1) one researcher claimed Smelter/Millstone/Hearth weren't player-buildable; the gdscript-refactor agent dispatched to fix it verified they ALREADY are (build_drawer.gd:234-248, world.gd:767-772, strings, draws — all present), no code change needed. (2) save/load researcher claimed crop save was broken due to field-name mismatch (`crop_kind` vs `kind`); on direct verification the field names were correct, but the bug WAS real for a different reason — `Crop.from_dict` is static returning a Dict, and `save_system._spawn_crop` discarded the return value. Verifying conflicting agent claims against the actual code caught both before fan-out dispatch. - **Pattern recorded — "Job.target_node deconfliction is the standard mechanism".** ConstructionProvider already filtered build sites via `Job.is_target_taken_by_other`. Sprint extended it to: (a) SleepProvider (beds) and (b) CraftingProvider (per-job `ingredient_item` cache + `is_instance_valid` re-check at pickup). Any future scarce-resource provider should mirror this — set `j.target_node = X` on proposal, filter candidates by `Job.is_target_taken_by_other(X, pawn)`. - **Pattern recorded — "workbench.from_dict must call `_complete()`, not bare `_completed = true`".** The agent doing F (save_system._spawn_workbench cleanup) caught that workbench.from_dict was setting `_completed = true` directly — skipping side-effects: light source enabling, beauty register, designation clear. Any restored workbench would render but not emit light or contribute to room beauty. Fixed in commit cc6d60d. Same trap shape will recur for any entity whose completion has registered side-effects (Wall, Floor, Door, Bed) — `_completed = true` is never sufficient; always call the `_complete()` path. - **Pattern recorded — "ingredient2 inversion: primary is carried, secondary is buffered".** The two-trip craft refactor (commit d9638a4) settled on: `recipe.ingredient_type` = the carried (primary) ingredient, `recipe.ingredient2_type` = the buffered (secondary, may be multi-count) ingredient. For cremation this is corpse (carried) + 5 wood (buffered across multiple pickup/deposit trips). Workbench.deposited_inputs Dictionary holds the buffered count. JobRunner._tick_craft validates buffer at full count, consumes both at completion. Pawn carry slot stays single-stack. `Workbench._last_consumed_ingredient` transient field captures the carried item before queue_free so signal consumers (cremation_pyre.on_craft_complete → corpse_cremated) get a non-null ref. - **Delegation report — audit + 5-sprint day.** Five `researcher` (Haiku) for the initial audit fan-out. Across the five fix-sprints: 12 `gdscript-refactor` (Sonnet) dispatches, 1 `quick-edit` (Haiku) for the J+M mechanical batch. Opus handled all pre-write contracts, integration verification, the cross-agent conflict resolution, and all 6 commits. Tiered system fired naturally across both axes (audit + execution). ### 2026-05-17 - **Hyena reskin shipped** (commit `f30c7a8`). Replaced procedural wolf draw with CraftPix Free Desert Enemy Sprite Sheets — 48×48 side-view, 4-frame idle + 6-frame walk. AnimatedSprite2D mounted in Wolf.setup() + from_dict() (same pattern as pawn reskin Slice 1). Player-facing copy renamed wolf→hyena in strings.gd + event_catalog.gd EventDef titles/bodies. Internal class names (Wolf, World.wolves, EventBus.wolf_spawned, event IDs like `lone_wolf`) preserved for save compat — explicit comment in wolf.gd. MCP-verified: hyena AnimatedSprite2D mounts on spawn, idle anim plays, storyteller modal renders "Lone Hyena — A starving hyena circles your livestock." Memory.md asset audit had said "no wolf in bundle" — this session's fresh scan via researcher (Haiku) found the CraftPix-DesertEnemies pack sitting next to the Humble bundle. Lesson: original asset audit was incomplete. - **Pattern recorded — "rename player-facing, preserve internal identifiers".** When changing entity flavor (wolf → hyena, future renames), edit display strings only (strings.gd + EventDef labels). Keep class names, registry fields, signals, save class_ids, event IDs the same to avoid breaking saves + cross-system wiring. Header comment in the entity file explains the historical naming. - **Cooking pipeline starvation fix shipped** (commit `87a7beb`) after playtest report "pawns starve even with harvested crops." Root cause: `CraftingProvider` (category=&"crafting", priority=4) handled BOTH crafting-skill bills AND cooking-skill bills. Decision tiebreaker (lower pawn-priority wins; tie → higher provider.priority wins) ranked Plant=5 / Chop=5 ABOVE Crafting=4, so pawns endlessly harvested fresh crops instead of cooking ones already on the floor. Raw +25 vegetable couldn't outpace HUNGER_DECAY × 3 pawns; colony starved on a pile of uncooked food. - **Fix**: split `CookingProvider` out of `CraftingProvider`. Same find/score logic, including ingredient2 buffer flow. CookingProvider has `category=&"cooking"`, `priority=6` (above Plant/Chop), filters bills by `required_skill == &"cooking"`. CraftingProvider now filters bills by `required_skill == &"crafting"`. Added `&"cooking": 3` to `pawn.work_priorities` defaults (was missing — 8 keys vs design spec's 9). WorkPriorityMatrix gains a "Cook" column. MCP-verified: 2 bread items appeared by tick 261 of fresh boot. - **Pattern recorded — "provider-priority eclipse"**. When multiple providers share a category (like crafting+cooking did) AND default pawn-priorities are equal, the decision tiebreaker uses provider.priority descending. Always-available higher-priority work eclipses batch-style lower-priority work. Same root cause hit cooking (eclipsed by plant/chop) and hauling (eclipsed by everything). Phase 6 placeholder priorities flagged for full Phase 20 retune. Going forward: any new provider needs to land at a priority that doesn't starve under realistic always-busy load. Audit ALL providers when adding one. - **Sow + crop zone designation shipped** (commit `c6c88ac`) after playtest report "pawns aren't replanting crops + need a way to designate new crop areas." Two issues: (a) `_find_sow` required a TYPE_GRAIN item as seed but Millstone's flour bill FOREVER consumed all grain before sow could claim it (especially with CookingProvider now priority 6 creating constant flour demand); (b) no UI for player to designate new crop tiles. - **Fix**: removed grain requirement entirely. Sow is now Rimworld-style — the designation triggers work; no input consumed. _find_sow returns a 2-toil walk → interact job. Crop.on_sow_tick flips stage to SOWN. Plus 4 new BuildDrawer paint tools (Wheat/Potato/Corn/Strawberry) in a new "Farm" section column. Painting a grass tile spawns a TILLED Crop entity; PlantProvider sow then picks it up. Tile validity: grass + walkable + no existing crop/tree. MCP-verified: 12 forced-TILLED crops fully cycled TILLED → SOWN → growth → READY → harvest. Paint tool spawned wheat at (35, 30); wall tile at (44, 23) correctly rejected. - **Pattern recorded — "input cost on a routine work toil starves under contention"**. Original sow design consumed 1 grain (matching the design-doc feel of "seed cost"), which seemed fine in isolation. Under multi-provider load (cooking demanding grain too), the input cost became a contention bottleneck. Rimworld's "designation triggers work, no input cost" pattern avoids the contention entirely. Apply for future work types that compete for the same item pool. - **Hauling priority bump 3 → 5 shipped** (commit `ab53808`) after playtest report "where is all the bread and meals going? Not in the crate." Cooking was working (verified); output spawned at the Hearth tile and never hauled. Same eclipse pattern: HaulingProvider priority 3 was below every gathering/production provider (Plant=5, Chop=5, Cooking=6, Construction=6, Crafting=4, Mine=4). With 3 pawns + constant production, no one ever reached "idle enough to haul." EatProvider (7) also ate food directly off the workbench tile. - **Fix**: 1-line bump to priority 5 (same tier as Plant and Chop). MCP-verified: 1 grain + 1 wood arrived in cabin crate within 3000 ticks; pawns visibly mid-haul. Cooking still fires in parallel. - **Reachability pre-check sprint partial** (commit `16e04e4`). Researcher audit (Haiku) found 6 WorkProviders missing reachability gates before returning jobs: cooking, crafting, plant-harvest, sleep, chop, mine. Gap class: pawn offered doomed walk-job → JobRunner cancels each tick → Decision re-offers same job → 20Hz busy-spin starves lower-priority work. Same root pattern as the cooking starvation. Dispatched Sonnet to add all 6. - **4 landed, 2 reverted**. PlantHarvest, Sleep, Chop, Mine all got safe reachability gates (mirroring Pattern A from HaulingProvider/EatProvider for walkable targets, Pattern B from ConstructionProvider for impassable ones). Cooking + Crafting changes had to be reverted: they intermittently returned null in live sim despite all manual MCP-probe conditions passing — likely an edge in the adjacent-walkable computation under sim-tick contention. Filed for a smaller, more careful follow-up. - **Pattern recorded — "MCP probe context vs live sim context can diverge"**. Manually called `cooking_provider.find_best_for(pawn)` showed all checks pass (ingredient reachable, workbench approach reachable). But the LIVE sim returned null for the same call shortly after. Items get carried, claims toggle, paths invalidate within ticks. For stateful providers, don't fully trust isolated probes — verify by observing aggregate outcomes (items produced, jobs fired in audit log) over many ticks. - **Provider audit findings preserved** (deferred to Phase 20): - Construction = Cooking = 6 tie → Construction always fires first (stable sort + input order). Player won't notice unless both have queued work simultaneously. - Plant = Chop = Haul = 5 → harvest > chop > haul in tiebreaker order. Wood briefly piles up at tree tiles during early game. - Cleaning (priority 2) → never fires while higher-priority work exists. Rooms get grimy. Phase 20 tuning territory. - No CombatProvider (Phase 10 partial — pawn-side combat deferred per earlier memory). - **Delegation report — 2026-05-17 playtest fix day.** Five `gdscript-refactor` (Sonnet) dispatches across 5 sprints (hyena reskin, CookingProvider split, sow + crop zone, reachability sprint, audit-driven fixes). Two `researcher` (Haiku) dispatches: wild-beast sprite hunt across all asset packs (found CraftPix Hyena), provider audit. Opus handled all live MCP probes, pre-write contracts, integration verification, the cooking/crafting revert decision, and all 6 commits. Trust-but-verify proved its keep AGAIN: the Smelter/Millstone/Hearth audit yesterday was wrong (workbenches already wired); today the agent's claim "cooking/crafting reachability works" was wrong (live sim regressed). Always verify in MCP if the change touches scheduling. ## External references - **Forgejo repo:** https://git.rdx4.com/megaproxy/rimlike (private) - **Owned art bundle (primary):** ElvGames "Ultimate Farming RPG" Humble bundle, local at `/mnt/d/godot/assets/humble set new/`. License: ElvGames terms (). Commercial OK with credit; no NFT/crypto/resale. - **Owned art bundle (secondary):** Ventilatore Fantasy Tileset Complete Bundle — - **Mana Seed catalog (fallback if bundle gaps surface):** - **Iconic Homestead (autotile-ready wall fallback, $19.99):** - **Lodestars:** Rimworld (Tynan Sylvester / Ludeon Studios), Going Medieval (Foxy Voxel), Stardew Valley (ConcernedApe). - **Original brainstorm history:** archived to `~/claude/archive/ideas/rimlike/` after promotion. The session log there captures every decision's rationale. - **Brainstorming-mode preference:** see `~/.claude/projects/-home-megaproxy-claude-ideas/memory/brainstorm-ask-dont-decide.md` (not directly applicable to projects but captures interaction style).