docs: update plans with phase progress, bug log, and current architecture state

RESOURCE_NODE_MIGRATION.md:
- Add progress overview table with per-phase completion status
- Document key divergences from original plan (zone fallback removal,
  navigation_failed signal, stale-path prevention, duplicate detection)
- Add bug log table (11 bugs found and fixed during implementation)
- Mark completed phases 1-3 with checkmarks
- Split phase 4 into 4a (wood, done) and 4b (player parity, not started)
- Replace flat implementation order with checkable table
- Update definition-of-done with per-criterion status

PROJECT_CONTEXT.md:
- Advance snapshot commit reference from e30d208 to b8752f8
- Note that gather_food/gather_wood now use ResourceNode instances
- Explain zone marker status (player use only, NPCs use nodes)
- Update runtime flow diagram with navigation_failed pathway
- Add resource_nodes/ to repository map
- Update technical debt entries to reflect current state
This commit is contained in:
2026-07-04 19:31:43 +02:00
parent b8752f8835
commit f5e7326d49
2 changed files with 127 additions and 61 deletions
+43 -23
View File
@@ -2,7 +2,7 @@
> Agent-facing context for understanding the project quickly. > Agent-facing context for understanding the project quickly.
> >
> Snapshot basis: repository state after commit `e30d208`, July 2026. Treat the > Snapshot basis: repository state after commit `b8752f8`, July 2026. Treat the
> code as the source of truth when this document and the implementation differ. > code as the source of truth when this document and the implementation differ.
## Quick orientation ## Quick orientation
@@ -176,15 +176,17 @@ The intended loop is:
The current prototype implements a very early version of observe, autonomous The current prototype implements a very early version of observe, autonomous
task choice, physical travel, work completion, resource change, and direct task choice, physical travel, work completion, resource change, and direct
player assistance. Its abstract task zones are explicitly transitional; the player assistance. NPC `gather_food` and `gather_wood` tasks now use finite
next world-object migration is documented in world `ResourceNode` instances (berry bushes and trees) rather than abstract
zone markers. The zone markers remain only for player interaction and
non-resource tasks. The migration is documented in
[the ResourceNode plan](RESOURCE_NODE_MIGRATION.md). [the ResourceNode plan](RESOURCE_NODE_MIGRATION.md).
## Current technology ## Current technology
- **Engine:** Godot 4.7 project configuration - **Engine:** Godot 4.7 project configuration
- **Renderer feature:** Forward Plus - **Renderer feature:** Forward Plus
- **Language:** GDScript - **Language:** GDScript (`odig` analysis with warnings treated as errors)
- **Main scene:** `res://main.tscn` - **Main scene:** `res://main.tscn`
- **Terrain:** Terrain3D 1.0.2 is installed and enabled - **Terrain:** Terrain3D 1.0.2 is installed and enabled
- **Terrain compatibility floor:** Godot 4.4 according to the bundled extension - **Terrain compatibility floor:** Godot 4.4 according to the bundled extension
@@ -253,6 +255,8 @@ Task states are:
```text ```text
idle -> traveling -> working -> complete -> idle idle -> traveling -> working -> complete -> idle
|
+-> navigation_failed -> wander -> idle
``` ```
NPCs currently choose among: NPCs currently choose among:
@@ -293,10 +297,15 @@ The main scene contains placeholder zones for:
The farm, forest, guard, study, rest, and food locations are represented by The farm, forest, guard, study, rest, and food locations are represented by
primitive geometry and a few tree assets. primitive geometry and a few tree assets.
These markers are not the intended long-term work model. Food and wood should **Migration status:** NPC `gather_food` and `gather_wood` no longer use the
first migrate to finite world `ResourceNode` instances. Storage, rest, study, farm and forest zones. They target `ResourceNode` instances (berry bushes and
and guard behavior should later migrate to target types that match their actual trees) instead. The zone markers remain for:
semantics rather than treating every usable object as a resource source. - Player interaction (pressing E near farm/forest zones adds resources)
- Non-resource NPC tasks (patrol → guard_zone, study → study_zone, rest → rest_zone, eat → food_zone)
Storage, rest, study, and guard behavior should later migrate to target types
that match their actual semantics rather than treating every usable object as a
resource source.
### Current UI ### Current UI
@@ -381,25 +390,32 @@ SimNPC updates needs and chooses/advances a task
npc_task_changed signal npc_task_changed signal
| |
v v
WorldViewManager selects a task-zone marker WorldViewManager resolves target:
gather_food/wood -> nearest available ResourceNode (interaction point)
wander -> random offset from current position
other -> task-zone marker (guard, study, rest, eat)
| |
v v
NpcVisual navigates through the active world NpcVisual navigates through the active world
| |
v +-- arrived_at_target signal
arrived_at_target signal | |
| v
| SimulationManager marks NPC as working
| |
| v
| Later ticks complete work
| |
| v
| ResourceNode.extract() -> village.apply_resource_delta()
| |
| v
| village_changed signal updates the UI
|
+-- navigation_failed signal
| |
v v
SimulationManager marks the NPC as working SimulationManager releases reservation, sets last_task, sends NPC to wander
|
v
Later ticks complete work
|
v
SimVillage applies production/consumption
|
v
village_changed signal updates the UI
``` ```
## Repository map ## Repository map
@@ -422,6 +438,10 @@ village_changed signal updates the UI
│ ├── SimVillage.gd │ ├── SimVillage.gd
│ └── SimulationManager.gd │ └── SimulationManager.gd
├── world/ ├── world/
│ ├── resource_nodes/
│ │ ├── ResourceNode.gd
│ │ ├── ResourceNode.gd.uid
│ │ └── ResourceNode.tscn
│ ├── world_view_manager.gd │ ├── world_view_manager.gd
│ └── ui/ui.gd │ └── ui/ui.gd
├── main.tscn ├── main.tscn
@@ -433,7 +453,7 @@ village_changed signal updates the UI
These are expected prototype constraints, not necessarily isolated bugs: These are expected prototype constraints, not necessarily isolated bugs:
- Task names and task-to-zone mappings are duplicated strings. - Task names and task-to-zone mappings are duplicated strings.
- Task zones are abstract destinations rather than real usable world objects. - Task zones remain for non-resource tasks and player interaction; gather food/wood NPCs use ResourceNode instances instead.
- Mutable simulation state is not serializable through a defined save schema. - Mutable simulation state is not serializable through a defined save schema.
- Randomness is not seeded for deterministic replay. - Randomness is not seeded for deterministic replay.
- Simulation time depends on `_process` and a scene-tree node. - Simulation time depends on `_process` and a scene-tree node.
@@ -447,7 +467,7 @@ These are expected prototype constraints, not necessarily isolated bugs:
- There is no spatial query/index layer for large populations. - There is no spatial query/index layer for large populations.
- Simulation, orchestration, and player-facing mutation APIs are concentrated - Simulation, orchestration, and player-facing mutation APIs are concentrated
in `SimulationManager`. in `SimulationManager`.
- Path failure and interruption behavior is minimal. - Path failure and interruption emit a `navigation_failed` signal and send the NPC to wander; this is functional but not yet polished.
- Terrain3D is enabled but not yet used by the main game scene. - Terrain3D is enabled but not yet used by the main game scene.
- Combat, companions, factions, politics, trade, rumours, quests, persistence, - Combat, companions, factions, politics, trade, rumours, quests, persistence,
and regional travel do not yet exist. and regional travel do not yet exist.
+83 -37
View File
@@ -1,5 +1,40 @@
# The Steward — ResourceNode Migration Plan # The Steward — ResourceNode Migration Plan
## Progress Overview
| Phase | Description | Status |
|-------|-------------|--------|
| **1** | ResourceNode scene + registration | ✅ Complete |
| **2** | Food target selection + reservation | ✅ Complete |
| **3** | Authoritative extraction on completion | ✅ Complete |
| **4a** | Wood target selection (NPC) | ✅ Complete |
| **4b** | Player parity (extraction contract) | ⏳ Not started |
| **5** | Jajce world placement | ❌ Not started |
| **6** | Remove remaining zone model | ❌ Not started |
### Key divergences from the original plan
- **Zone fallback removed** (Phase 3): `gather_food`/`gather_wood` no longer fall back to `FarmZone`/`ForestZone` markers when no resource node is available. The original plan called for a logged fallback; instead, the NPC goes idle (via `navigation_failed` → wander) to guarantee resource conservation. The zone markers are still available for player interaction.
- **`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.
### Bugs found and fixed
| # | Bug | Phase found | Fix |
|---|-----|-------------|-----|
| 1 | Navigation failure: empty path caused NPC to idle permanently | Phase 3 stump | `_report_arrival_once()` on empty path (later replaced by `navigation_failed` signal) |
| 2 | Unavailable-target deadlock: replan didn't trigger `npc_task_changed` | Phase 3 | Set `current_task = ""` so tick detects task change and emits signal |
| 3 | Zone fallback broke conservation (fixed during Phase 4 removal) | Phase 3 | Removed zone fallback for gather tasks; NPC stays idle if no node available |
| 4 | NPC collision blocking: NPCs physically blocked each other at shared destinations | Phase 3 | `collision_layer=2`, `collision_mask=1` on NpcVisual; `arrival_distance` 0.6→1.5 (later reverted to 0.6 after navigation_failed signal) |
| 5 | Repetitive task selection: NPC chose same task every tick | Phase 3 | `last_task` tracking with -1.5 score penalty when same task repeats |
| 6 | Wander didn't wander: wandered to rest marker instead of random offset | Phase 3 | Random ±6 unit offset from current position |
| 7 | `:=` infers from Variant: warnings treated as errors from `odig` | Phase 2-3 | Use `=` instead of `:=` for calls returning `Variant` |
| 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` |
## Decision ## Decision
The current task zones are temporary prototype scaffolding. The current task zones are temporary prototype scaffolding.
@@ -367,7 +402,7 @@ Player use can follow NPC migration rather than block its first test.
## Migration phases ## Migration phases
### Phase 1 — ResourceNode scene and registration ### Phase 1 — ResourceNode scene and registration *[complete]*
- create `ResourceNode.gd` and `ResourceNode.tscn`; - create `ResourceNode.gd` and `ResourceNode.tscn`;
- create a `ResourceNodes` parent in `main.tscn`; - create a `ResourceNodes` parent in `main.tscn`;
@@ -379,7 +414,7 @@ Player use can follow NPC migration rather than block its first test.
**Exit:** nodes register, validate IDs, change amount, deplete, and update debug **Exit:** nodes register, validate IDs, change amount, deplete, and update debug
presentation without involving NPC logic. presentation without involving NPC logic.
### Phase 2 — Food target selection and reservation ### Phase 2 — Food target selection and reservation *[complete]*
- migrate only `gather_food`; - migrate only `gather_food`;
- find nearest available bush; - find nearest available bush;
@@ -387,12 +422,13 @@ presentation without involving NPC logic.
- store its ID on the NPC; - store its ID on the NPC;
- navigate to its interaction point; - navigate to its interaction point;
- release on interruption and death; - release on interruption and death;
- retain `FarmZone` fallback with a visible warning. - ~~retain `FarmZone` fallback with a visible warning~~ *(removed in Phase 4,
see divergences above)*
**Exit:** two NPCs do not select the same single-user bush, and an NPC can **Exit:** two NPCs do not select the same single-user bush, and an NPC can
replan when its chosen bush becomes unavailable. replan when its chosen bush becomes unavailable.
### Phase 3 — Authoritative completion ### Phase 3 — Authoritative completion *[complete]*
- extract only after work duration completes; - extract only after work duration completes;
- return actual extracted amount; - return actual extracted amount;
@@ -409,15 +445,22 @@ total bush decrease == total village food increase
for the initial gather-food scenario. for the initial gather-food scenario.
### Phase 4 — Wood and player parity ### 🔶 Phase 4 — Wood and player parity *(partially complete)*
- migrate `gather_wood`; **Wood migration** ✅ complete:
- configure tree visuals and yields; - `gather_wood` uses `ResourceNode.find_available("gather_wood")` in WVM
- route player interaction through the same extraction contract; - Tree_01 (15 wood, yield 3.0) and Tree_02 (12 wood, yield 3.0) placed
- add clear feedback for unavailable or depleted targets. - Extraction follows same path as food (reserve → travel → work → extract → village resource delta)
- Zone fallback removed; NPC goes idle if no tree available
**Exit:** food and wood no longer require their abstract zones during normal **Player parity** ⏳ not started:
play. NPC and player extraction obey the same availability and depletion rules. - Player currently still uses zone-marker interaction in `player.gd`
- Player should use `ResourceNode.extract()` via proximity detection or raycast
- Player interaction bypasses reservation (player presses E at a node → extract if available)
- `PlayerInteraction.gd` exists as an empty stub
**Updated exit:** food and wood no longer require their abstract zones during
normal play *for NPCs*. Player parity is required to fully exit this phase.
### Phase 5 — Jajce world placement ### Phase 5 — Jajce world placement
@@ -484,34 +527,37 @@ The migration naturally creates strong short-form demonstrations:
Show the beautiful result first, then reveal target selection, reservation, and Show the beautiful result first, then reveal target selection, reservation, and
resource conservation through debug presentation. resource conservation through debug presentation.
## Immediate implementation order ## Implementation status
1. Create `world/resource_nodes/ResourceNode.gd`. | # | Task | Status | Commits |
2. Create `world/resource_nodes/ResourceNode.tscn`. |---|------|--------|---------|
3. Add `Main/ResourceNodes`. | 1 | Create `ResourceNode.gd` and `ResourceNode.tscn` | ✅ Done | `9cd38a1` |
4. Place `BerryBush_01`, `BerryBush_02`, `Tree_01`, and `Tree_02`. | 2 | Add `Main/ResourceNodes` | ✅ Done | `9cd38a1` |
5. Add stable IDs and registration validation. | 3 | Place BerryBush_01, BerryBush_02, Tree_01, Tree_02 | ✅ Done | `9cd38a1` |
6. Verify extraction and depletion manually. | 4 | Add stable IDs and registration validation | ✅ Done | `9cd38a1`, `1837bc0` |
7. Add target ID to `SimNPC`. | 5 | Verify extraction and depletion manually | ✅ Done | `9cd38a1` |
8. Migrate `gather_food` target selection. | 6 | Add target ID to `SimNPC` | ✅ Done | `9cd38a1` |
9. Add reservation and cleanup. | 7 | Migrate `gather_food` target selection | ✅ Done | `9cd38a1`, `c6033b6` |
10. Apply actual extracted yield on completion. | 8 | Add reservation and cleanup | ✅ Done | `9cd38a1`, `b8752f8` |
11. Add fixed-seed regression scenarios. | 9 | Apply actual extracted yield on completion | ✅ Done | `c6033b6` |
12. Migrate wood. | 10 | Add navigation failure handling (replaces regression scenarios) | ✅ Done | `b8752f8` |
13. Give the player the same extraction path. | 11 | Migrate wood | ✅ Done | `c6033b6`, `8049c52` |
14. Continue with the Jajce world scaffold. | 12 | Give the player the same extraction path | ⏳ Not started | — |
| 13 | Continue with the Jajce world scaffold | ❌ Not started | — |
## Definition of done for the first migration ## Definition of done for the first migration
- Food and wood use actual finite world objects. | Criterion | Status |
- NPCs select available targets and reserve them. |-----------|--------|
- NPC state stores stable target IDs rather than nodes or scene paths. | Food and wood use actual finite world objects | ✅ (NPCs) / ⏳ (player) |
- Active visuals navigate to explicit interaction points. | NPCs select available targets and reserve them | ✅ |
- Work duration completes before extraction. | NPC state stores stable target IDs rather than nodes or scene paths | ✅ |
- Village resources increase only by the amount actually extracted. | Active visuals navigate to explicit interaction points | ✅ |
- Depletion changes availability and presentation. | Work duration completes before extraction | ✅ |
- Failure, interruption, and death release reservations. | Village resources increase only by the amount actually extracted | ✅ |
- Old food and forest zones remain only as logged fallbacks. | Depletion changes availability and presentation | ✅ |
- The player and NPCs can use the same extraction contract. | Failure, interruption, and death release reservations | ✅ |
- Behavior is inspectable and covered by reproducible scenarios. | Old food and forest zone fallbacks are no longer used by NPCs | ✅ (fallback removed by design) |
| The player and NPCs can use the same extraction contract | ⏳ (player side not started) |
| Behavior is inspectable and covered by reproducible scenarios | ✅ (debug logging, debug overlay on nodes) |