rimlike/memory.md
megaproxy a2da649404 memory.md: 2026-05-17 playtest-driven fix day
Captures the 5 fixes shipped today and 4 new patterns:
- "rename player-facing, preserve internal identifiers" (hyena reskin)
- "provider-priority eclipse" (CookingProvider split + Hauling bump)
- "input cost on routine work starves under contention" (sow free)
- "MCP probe context vs live sim can diverge" (reachability revert)

Plus deferred provider-audit findings for Phase 20 (tier-tie cases,
Cleaning eclipse, missing CombatProvider).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 23:32:42 +01:00

82 KiB
Raw Blame History

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: 36 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 Game design — core loop, simplifications, skills, statuses, mood, weather, stockpiles, production, combat, death/burial, storyteller corpus
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 Touch UX — work-priority matrix, stockpile/container screens, mood/lighting/rooms cues, combat banners, storyteller event UI, screens still to design
docs/art.md Owned assets (ElvGames bundle primary, Ventilatore secondary), license, autotile gotcha, audit list, candidates kept for record
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 515 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 36 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 35 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. ~8001500 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), 010 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 050 fresh / 50100 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.

  • [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.
  • [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.
  • [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:

  • [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.
  • [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.
  • ElvGames autotile audit — done. FG_Fortress.png IS autotile-solvable (tan stone, ~2030 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.
  • 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.
  • 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 + 24-frame walk × 4 directions; ~$3060 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 36 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 (34 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 111 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 12 vs design target 14 — 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.<modifier>_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 passtooltip_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 (https://elv-games.itch.io/terms). Commercial OK with credit; no NFT/crypto/resale.
  • Owned art bundle (secondary): Ventilatore Fantasy Tileset Complete Bundle — https://itch.io/s/117124/the-fantasy-tileset-complete-bundle
  • Mana Seed catalog (fallback if bundle gaps surface): https://seliel-the-shaper.itch.io/
  • Iconic Homestead (autotile-ready wall fallback, $19.99): https://seliel-the-shaper.itch.io/iconic-homestead
  • 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).