diff --git a/docs/BUILD_IN_PUBLIC_PLAN.md b/docs/BUILD_IN_PUBLIC_PLAN.md index 44869d9..8ab2570 100644 --- a/docs/BUILD_IN_PUBLIC_PLAN.md +++ b/docs/BUILD_IN_PUBLIC_PLAN.md @@ -361,7 +361,7 @@ learning roadmap: - deterministic clock and seeded randomness; ✅ - fixed-seed headless scenarios and final-state checksum; ✅ - versioned serializable NPC, village, resource, clock, and RNG records; ✅ -- resource amount and reservation authority outside scene nodes; +- resource amount and reservation authority outside scene nodes; ✅ - stable action/profession IDs and initial definitions; - separate action selection, execution, target resolution, and visual travel; - explicit active-position synchronization. diff --git a/docs/LEARNING_ROADMAP.md b/docs/LEARNING_ROADMAP.md index a754518..32f6abd 100644 --- a/docs/LEARNING_ROADMAP.md +++ b/docs/LEARNING_ROADMAP.md @@ -54,7 +54,7 @@ The gate is complete when the same scenario produces the same checksum without loading `main.tscn`, and loading or unloading a visual does not change authoritative NPC or resource state. -**Current progress:** the first two architecture slices are implemented: +**Current progress:** the first three architecture slices are implemented: - `SimulationClock` advances explicit fixed ticks while preserving remainder; - NPC decisions and visual wander requests use controlled per-NPC random @@ -65,10 +65,15 @@ authoritative NPC or resource state. clock, and RNG state; - a save-at-tick-24 continuation test matches an uninterrupted 48-tick checksum and rejects unsupported schema versions. +- `SimulationManager` owns resource records and all amount/reservation + mutation; ResourceNode binds by stable ID as presentation and interaction + geometry; +- an unload/rebind regression proves resource state remains authoritative while + its scene node is absent. -The gate remains open. Scene-independent resource authority, stable -definitions, responsibility separation, and visual load/unload invariance are -still required. See [the simulation state schema](SIMULATION_STATE_SCHEMA.md). +The gate remains open. Stable definitions, responsibility separation, and NPC +visual load/unload invariance are still required. See +[the simulation state schema](SIMULATION_STATE_SCHEMA.md). ## Three vertical slices @@ -615,23 +620,21 @@ Completed foundations: The practical next sequence is: -1. Move resource amount/reservation authority out of `ResourceNode` scenes and - make unloaded resources remain in state. -2. Replace free-form task and profession strings with stable IDs and +1. Replace free-form task and profession strings with stable IDs and definitions. -3. Separate action selection, execution, target resolution, and visual travel; +2. Separate action selection, execution, target resolution, and visual travel; remove decision mutation from `WorldViewManager`. -4. Define active-position synchronization and prove visual unload/reload. -5. Pass the architecture gate's deterministic headless and unloaded-visual exit +3. Define active-position synchronization and prove visual unload/reload. +4. Pass the architecture gate's deterministic headless and unloaded-visual exit tests. -6. Build location-based food storage, inventory, and transactions. -7. Emit structured economic events while making one NPC visibly gather, carry, +5. Build location-based food storage, inventory, and transactions. +6. Emit structured economic events while making one NPC visibly gather, carry, store, retrieve, and eat food. -8. Add save-slot persistence and explicit schema migrations before schedules +7. Add save-slot persistence and explicit schema migrations before schedules or social state expand. -9. Resume Terrain3D sculpting, runtime integration, and the simulation-garden +8. Resume Terrain3D sculpting, runtime integration, and the simulation-garden beauty pass. -10. Add stylized character and profession readability plus the reason +9. Add stylized character and profession readability plus the reason inspector. This order strengthens the simulation while regularly producing visible diff --git a/docs/PROJECT_CONTEXT.md b/docs/PROJECT_CONTEXT.md index e1bcf4a..9daa6aa 100644 --- a/docs/PROJECT_CONTEXT.md +++ b/docs/PROJECT_CONTEXT.md @@ -434,7 +434,7 @@ NpcVisual navigates through the active world | Later ticks complete work | | | v - | ResourceNode.extract() -> village.apply_resource_delta() + | ResourceStateRecord.extract() -> village.apply_resource_delta() | | | v | village_changed signal updates the UI @@ -498,10 +498,10 @@ These are expected prototype constraints, not necessarily isolated bugs: - Task names and task-to-activity-marker mappings are duplicated strings. - Temporary activity markers remain for eating, rest, study, and patrol; NPC and player food/wood gathering use `ResourceNode` instances with no fallback. -- Current NPC, village, loaded resource, clock, and RNG state serialize through - schema v1, but there is no save-slot/file UX or backward migration yet. -- Resource records currently mirror loaded ResourceNode state; the scene nodes - still own live amounts and reservations. +- Current NPC, village, resource, clock, and RNG state serialize through schema + v1, but there is no save-slot/file UX or top-level backward migration yet. +- Simulation-owned resource records retain live amounts, reservations, and + usage definitions while ResourceNode scenes are unloaded. - An explicit fixed-step clock converts frame delta into simulation ticks, but orchestration still lives on the scene-tree `SimulationManager`. - Automated coverage includes deterministic same-seed and save/restore @@ -716,21 +716,21 @@ Food and wood migration and the deliberately minimal `JajceWorld` scaffold, greybox, navigation spike, and stable-ID placement proof are complete. Do not proceed directly from that proof into open-ended beauty production. -The architecture gate is now the active milestone. Its first two slices are +The architecture gate is now the active milestone. Its first three slices are complete: - explicit fixed-step simulation clock; - seeded per-NPC decision and wander random streams; - headless fixed-seed scenario with a final-state checksum; -- versioned JSON records for NPC, village, loaded resource, clock, and RNG - state; -- deterministic save/restore continuation with exact 64-bit RNG preservation. +- versioned JSON records for NPC, village, resource, clock, and RNG state; +- deterministic save/restore continuation with exact 64-bit RNG preservation; +- simulation-owned resource amount/reservation state with ResourceNode + presentation binding and unload/rebind coverage. The remaining gate work is: - data-defined action and profession IDs; - separated action selection, execution, target resolution, and visual travel; -- resource amount and reservation authority moved out of scene nodes; - explicit synchronization of active NPC position. The coherent visual slice can then aim for: diff --git a/docs/RESOURCE_NODE_MIGRATION.md b/docs/RESOURCE_NODE_MIGRATION.md index f5dfd9b..1fa4e47 100644 --- a/docs/RESOURCE_NODE_MIGRATION.md +++ b/docs/RESOURCE_NODE_MIGRATION.md @@ -18,6 +18,9 @@ - **`navigation_failed` signal** (discovered during Phase 4): Added a separate signal pathway to distinguish genuine arrival from navigation failure. The original plan treated all "arrivals" uniformly. - **Stale-path prevention** (discovered during Phase 4): Added `path_request_id` + `path_pending` to reject async path results that arrive after a new target was set or the NPC died. - **Duplicate node_id detection** (discovered during Phase 4): Added startup validation that disables nodes with duplicate IDs and pushes an error. +- **Simulation-owned resource authority** (architecture gate): Resource amount, + reservation, and enabled state now live in `ResourceStateRecord`; ResourceNode + binds by stable ID and can unload without deleting authoritative state. ### Bugs found and fixed @@ -33,8 +36,8 @@ | 8 | `Variant as StringName`: invalid cast | Phase 2-3 | Check for `null` on Dictionary `.get()` instead of casting | | 9 | Empty-path re-entrancy: immediate arrival report caused re-entrant signal chain | Phase 4 | Defer to `stuck_counter`/`stuck_time` in `_physics_process` instead | | 10 | Stale async path: `await physics_frame` result used after target or death changed | Phase 4 | `path_request_id`/`path_pending` guard | -| 11 | Stolen extraction: NPC extracted from node after reservation was lost | Phase 4 | Guard extraction with `node.reserved_by == npc.id` | -| 12 | Depleted target retained an NPC reservation after player harvesting | Phase 4 | `ResourceNode.extract()` now releases its reservation on depletion | +| 11 | Stolen extraction: NPC extracted after reservation was lost | Phase 4 | Guard extraction with the authoritative resource record's reservation | +| 12 | Depleted target retained an NPC reservation after player harvesting | Phase 4 | Authoritative extraction now releases its reservation on depletion | ## Decision @@ -451,7 +454,8 @@ for the initial gather-food scenario. ### ✅ Phase 4 — Wood and player parity *[complete]* **Wood migration** ✅ complete: -- `gather_wood` uses `ResourceNode.find_available("gather_wood")` in WVM +- `gather_wood` resolves loaded presentation candidates through + `SimulationManager.acquire_resource_target()` - Tree_01 (15 wood, yield 3.0) and Tree_02 (12 wood, yield 3.0) placed - Extraction follows same path as food (reserve → travel → work → extract → village resource delta) - Zone fallback removed; NPC goes idle if no tree available @@ -460,7 +464,8 @@ for the initial gather-food scenario. - Player interaction resolves the nearest player-usable resource node by its interaction point. - `SimulationManager.harvest_resource_node()` uses the same authoritative - `ResourceNode.extract()` contract and applies exactly the returned amount. + `ResourceStateRecord.extract()` contract and applies exactly the returned + amount. - Player harvesting can contend with an NPC reservation; depletion releases that reservation so the NPC can recover cleanly. - Depleted and disabled targets provide explicit feedback. diff --git a/docs/SIMULATION_STATE_SCHEMA.md b/docs/SIMULATION_STATE_SCHEMA.md index e0bb755..b57ab42 100644 --- a/docs/SIMULATION_STATE_SCHEMA.md +++ b/docs/SIMULATION_STATE_SCHEMA.md @@ -11,7 +11,8 @@ simulation. Schema v1 captures: starvation state, and decision RNG stream; - village resource counters; - controlled per-NPC wander RNG streams; -- loaded ResourceNode amount, reservation, and enabled state. +- scene-independent resource state plus the definition facts needed while its + ResourceNode is unloaded. The top-level identity is: @@ -42,19 +43,26 @@ its future random sequence. 4. running the remaining 24 ticks; 5. requiring the complete final-state checksums to match. -It also verifies clock remainder, ResourceNode amount/reservation/enabled -round-tripping, and rejection of unsupported schemas. +It also verifies clock remainder, resource amount/reservation/enabled +round-tripping, presentation unload/rebind, and rejection of unsupported +schemas. -## Transitional resource boundary +## Resource authority -Schema v1 can capture and restore loaded `ResourceNode` state, but the scene -node still owns the live amount and reservation. That is transitional support, -not the final authority model. +`SimulationManager` owns `ResourceStateRecord` instances independently of the +scene tree. Amount, reservation, enabled state, action/resource identity, +yield, and usage permissions remain available and serializable while no +ResourceNode is loaded. -The next architecture-gate phase moves those values into scene-independent -resource records. ResourceNode will then bind to a record by stable ID and act -as presentation plus an active-world interaction surface. An unloaded resource -must remain simulated and serializable. +ResourceNode binds to a record by stable ID and provides presentation, +interaction transforms, and active-world discoverability. Unloading a node does +not delete its record. Loading another node with the same stable ID rebinds to +the existing record and cannot overwrite its amount or reservation with scene +defaults. + +ResourceStateRecord v2 adds the definition facts needed while unloaded. Nested +v1 resource records are migrated explicitly and hydrate their missing +definition from a matching presentation node when one becomes available. ## Deliberately out of scope @@ -64,7 +72,7 @@ This phase does not yet provide: - backward migrations beyond schema v1; - inventory, relationship, schedule, or history records; - a player-facing load flow; -- scene-independent resource authority. +- save-slot persistence for scene-independent resource state. Those features should build on this boundary rather than inventing parallel serialization paths. diff --git a/docs/decisions/0001-simulation-authority-boundary.md b/docs/decisions/0001-simulation-authority-boundary.md index 7d11b22..c2729f2 100644 --- a/docs/decisions/0001-simulation-authority-boundary.md +++ b/docs/decisions/0001-simulation-authority-boundary.md @@ -46,9 +46,9 @@ random streams, and no dependency on a loaded gameplay scene. 1. ✅ Add a deterministic clock, seeded random source, and scenario runner. 2. ✅ Introduce serializable village, NPC, and resource-state records. -3. **Next:** Bind `ResourceNode` presentation to resource records instead of - owning authoritative amount and reservation. -4. Move target choice and reservation coordination out of +3. ✅ Bind `ResourceNode` presentation to resource records instead of owning + authoritative amount and reservation. +4. **Next:** Move target choice and reservation coordination out of `WorldViewManager` into an action/target system with an active-world query adapter. 5. Split action selection, execution, and presentation travel.