docs: add current developer feature index

This commit is contained in:
Rijad Zuzo
2026-08-13 00:26:55 +02:00
parent 149e043f86
commit 9872b986b6
21 changed files with 1296 additions and 45 deletions
+2
View File
@@ -66,6 +66,8 @@ troubleshooting.
## Project guide
- [`docs/DEVELOPER_INDEX.md`](docs/DEVELOPER_INDEX.md) — current developer
onboarding index, feature map, extension workflow, and remaining work;
- [`AGENTS.md`](AGENTS.md) — development loop, architecture rules, validation,
documentation, and Git expectations;
- [`docs/PROJECT_CONTEXT.md`](docs/PROJECT_CONTEXT.md) — vision, current
+5
View File
@@ -5,6 +5,11 @@ Serializable simulation records are authoritative; loaded Godot nodes present
that state and contribute active-world facts such as positions and navigation
results.
For onboarding and feature-specific implementation guides, use the
[developer index](DEVELOPER_INDEX.md). This file remains the concise ownership
and dependency reference; it intentionally does not repeat every field and
test case from the focused guides.
## Emergent content and narrative boundary
Definitions, authored placements, mutable records, systems, and presentation
+12 -1
View File
@@ -1,5 +1,14 @@
# The Steward — Build-in-Public Visual Slice Plan
> **Current-status note (2026-08-13):** This document remains the scoped Jajce
> visual-production plan. The simulation has since added authored content
> packs, shared player/NPC activity commands, generated situations/dialogue,
> regional records, and reversible High/Balanced/Low presentation profiles.
> Use the [developer index](DEVELOPER_INDEX.md) and
> [presentation guide](FEATURE_PRESENTATION_PERFORMANCE.md) for the current
> implementation map. The visual plan's phase history is retained for capture
> provenance and does not claim that regional gameplay is production-wired.
## Purpose
This plan adapts the proposed “Jajce simulation garden” to the repository as it
@@ -630,7 +639,9 @@ The adjusted first milestone is complete when:
- River, waterfall foam, mist, warm lighting, and first foliage are present.
- A beauty camera produces a strong 1020 second shot.
- A rough navigation test proves the main route is viable.
- The existing simulation has not yet been duplicated or moved.
- The existing simulation remains in one `SimulationManager`; regional records
and the caravan proof are additive and have not replaced the local Jajce
facade.
## Definition of “Simulation Garden 01”
+207
View File
@@ -0,0 +1,207 @@
# The Steward developer index
This is the shortest reliable route into the current codebase. It describes
what is implemented today, where the implementation lives, which document owns
each contract, and what remains intentionally incomplete. It is written from
the Godot 4.7 source and the current headless test/benchmark suite, not from
the original prototype plan.
If a statement here disagrees with code or a focused test, update this index
after fixing the code. Do not make this document a second implementation.
## What the project is
The Steward is a deterministic, embodied village-simulation prototype set in a
Jajce-inspired valley. The player, NPCs, animals, enemies, resources, storage,
events, memories, relationships, situations, and commitments use the same
authoritative world state. Godot scenes are optional loaded-world adapters:
they provide geometry, navigation, visual feedback, and input, but they do not
own persistent quantities, travel completion, or quest progress.
The long-term direction is one regional simulation containing settlements,
routes, caravans, named people, aggregate cohorts, and polities. The current
repository has the regional records, deterministic scheduler, one exact
caravan transfer service, chunked persistence, and a 20-caravan structural
benchmark. It does not yet run that regional service as the production
`SimulationManager` facade or provide a complete market, settlement-growth, or
kingdom gameplay loop. That distinction is important when extending the game.
## Start here
| Goal | Read first | Then inspect |
| --- | --- | --- |
| Understand authority and module ownership | [Architecture overview](ARCHITECTURE_OVERVIEW.md) | `simulation/SimulationManager.gd`, `world/active_world_adapter.gd` |
| Add a new item, animal, enemy, resource, storage, action, situation, or dialogue intent | [Authored content and definitions](FEATURE_CONTENT_AND_AUTHORED_DATA.md) | `simulation/definitions/`, `simulation/animals/definitions/`, `world/presentation/catalogs/` |
| Add or change a player/NPC interaction | [Commands, targets, and economy](FEATURE_COMMANDS_TARGETS_AND_ECONOMY.md) | `simulation/commands/`, `world/targets/`, `ActiveWorldAdapter`, `SimulationManager` |
| Make a world event influence memory, relationships, a situation, or dialogue | [Events through dialogue](FEATURE_EVENTS_KNOWLEDGE_SITUATIONS_DIALOGUE.md) | `simulation/events/`, `simulation/knowledge/`, `simulation/situations/`, `simulation/dialogue/` |
| Add caravans, routes, scheduled work, or regional saves | [Regional simulation and persistence](FEATURE_REGIONAL_SIMULATION_AND_PERSISTENCE.md) | `simulation/regional/`, `simulation/state/*StateRecord.gd` |
| Add an enemy or animal using an existing behavior | [Entity families](FEATURE_ENTITIES_COMBAT_ANIMALS.md) | `CombatantFactory.gd`, `AnimalFactory.gd`, shared visual roots |
| Change rendering or support weaker PCs | [Presentation and performance](FEATURE_PRESENTATION_PERFORMANCE.md) | `world/jajce/jajce_world.gd`, `world/presentation/` |
| Change a saved field or migration | [Save schema and migrations](FEATURE_SAVE_SCHEMA_AND_MIGRATIONS.md) | `SimulationStateRecord.gd`, nested state records, `SaveSlotStore.gd` |
| Add a regression, benchmark, or run the gate | [Testing and benchmarks](FEATURE_TESTING_AND_BENCHMARKS.md) | `tests/`, `tools/`, `docs/benchmarks/` |
## Current implementation at a glance
| Area | Current implementation | Boundary to keep visible |
| --- | --- | --- |
| Definitions | Transactionally validated `.tres` packs and a sorted `ContentCatalog` | New mechanics still require one explicit reusable handler; data-only combinations are the supported extension surface |
| Actions | Definition-backed actions, typed effects, copy-only commands/offers/results, shared activity command service | Do not let UI or a scene mutate authority directly |
| Targets | Context-scoped `WorldTargetRegistry` with stable IDs, capabilities, generation handles, and provider lookup | Registry handles are transient; target identity in saves remains primitive and contextual |
| Economy | Exact resource, inventory, storage, player-citizen, and animal-care transactions | Village pantry/woodpile are still the production economy; regional markets are not wired |
| World facts | `WorldEventRecord`/`WorldEventStore` plus compatibility `EconomicEventRecord`/`SimulationEventLog` | Facts are appended after successful mutation and are never quest-only substitutes |
| Memory/social | Bounded knowledge with provenance, one-hop communication, seven relationship dimensions, causal event IDs | Objective history and broader rumours still need retention/rollup policy at regional scale |
| Situations/quests | Definition-driven bounded situations, derived progress, journal metadata, commitments and idempotent outcomes | The initial Jajce slice is bounded; it is not a universal objective scripting language |
| Dialogue | Deterministic semantic `ConversationService`, authored intent/template resources, Dialogue Manager v3 presenter | Dialogue Manager renders planned turns; it is not an authority or save format |
| Entities | Shared definition → instance/state → presentation contracts for resources, animals, enemies, items, storage | New behavior profiles and genuinely new mechanics still need typed code and tests |
| Regional kernel | Primitive records, due-job heap, keyed random, analytical updates, caravan proof, chunk/file store | Regional service and 20-caravan trade are currently integration fixtures, not the live Jajce loop |
| Presentation | Active-world adapters, context registries, relevance cap of 40, navigation budget of 2/frame, High/Balanced/Low profiles | Hardware frame-time/VRAM evidence is still pending; structural benchmarks are not rendered claims |
| Persistence | Local save schema v16 with migrations and scope preflight; regional chunk generations with recovery | No save menu/profile system; regional chunk store is not yet the local `SaveSlotStore` backend |
## The authority pipeline
```text
player input or NPC decision
|
v
ActionCommand / domain service
|
v
validate actor, target, capability, range, revision, cost, permission
|
v
mutate authoritative records atomically
|
+--> append WorldEventRecord / EconomicEventRecord
| |
| +--> knowledge and relationship consequences
| +--> SituationSystem and derived journal progress
| +--> SocialCommitmentSystem outcomes
| +--> semantic ConversationService context
|
v
signals and copied results to presentation
```
The important ordering rule is **mutation first, fact second, projection last**.
A situation, journal entry, conversation line, HUD card, or visual cue may
explain an event, but it may not manufacture the inventory, damage, enemy,
relationship, or reward that the event claims happened.
## Repository map
### Authoritative simulation
- `simulation/SimulationManager.gd` — scene-tree facade and tick boundary;
coordinates the current Jajce simulation and exposes player/NPC-facing APIs.
- `simulation/SimNPC.gd`, `simulation/SimVillage.gd` — local person and
settlement state used by the playable slice.
- `simulation/SimulationClock.gd` — fixed-step simulation time, speed, and
capped catch-up.
- `simulation/actions/` — utility selection, progress, and target resolution.
- `simulation/commands/` — command values, typed effects, activity handler,
catalog interaction offers, and transactional effect planning.
- `simulation/economy/` — exact inventory/storage transfers and routing.
- `simulation/animals/` and `simulation/conflict/` — reusable animal and enemy
domain systems.
- `simulation/events/`, `knowledge/`, `relationships/`, `situations/`, and
`dialogue/` — causal history and emergent narrative layers.
- `simulation/state/` — versioned primitive records and validation.
- `simulation/persistence/` — local quicksave safety.
- `simulation/regional/` — regional records, scheduler, caravan proof, chunk
codec, and file generations.
### Loaded world and presentation
- `world/active_world_adapter.gd` — context identity, target registry, loaded
geometry, and spatial query boundary.
- `world/targets/` — stable target descriptors, capabilities, and transient
generation-checked handles.
- `world/resource_nodes/`, `world/storage/`, `world/activity/`, and
`world/animals/` — reusable interaction providers bound to simulation state.
- `world/creatures/creature_visual.gd` — shared navigation/death visual root;
`world/combat/HostileCombatant.gd` specializes it for enemies.
- `world/dialogue/` — player dialogue mode and the Dialogue Manager adapter.
- `world/presentation/` — cue catalog, relevance selection, and path-request
budget.
- `world/jajce/` — authored Terrain3D valley, environment, grass, clusters,
day/night, and quality profiles.
- `world/ui/`, `world/world_view_manager.gd`, and `player/` — presentation and
input only; they query copied results/signals from authority.
### Definitions and authored resources
`simulation/definitions/` contains action, item, resource, storage, profession,
enemy, event, social-consequence, situation, dialogue, and pack resources.
`world/presentation/catalogs/` contains visual cue mappings. A `.tres` file
must never be treated as a mutable save record; current-build definition values
are used when an existing save is loaded.
## Extension workflow
1. Decide whether the request is a new combination of existing capabilities
and effects or a genuinely new mechanic.
2. For a data-only combination, create a typed definition resource, add it to a
pack, validate the catalog, and add a focused headless test.
3. Give placed instances contextual stable IDs and register them with the
active adapter. Do not add a process-global registry or a scene-path lookup.
4. For a new mechanic, add one bounded typed handler/strategy, its validation,
reason trace, save semantics, and a real consumer test. Do not add a script
per item, quest, animal, or enemy.
5. If the action changes meaningful state, append an exact world fact after the
mutation and connect any knowledge, relationship, situation, or commitment
projection to that fact.
6. If state changes, update `SimulationStateRecord` and its nested migration,
checksum, tamper, and continuation tests before wiring presentation.
7. If loaded geometry is involved, prove loaded and abstract execution use the
same authority and that unload/reload does not alter the checksum.
8. Update the narrow feature guide and this index, then run the changed gate
and full gate before committing.
## Current next work
The following are deliberately **not** claimed as complete:
- wire `RegionalCaravanService` into the production regional facade and local
save manifest;
- run 20 real caravan trades across five settlement economies with atomic
cargo/stock ledgers;
- replace local unloaded travel freezes with authoritative route-time work;
- integrate event retention/rollups/chunked history into one long-horizon save;
- add settlement projects, cohort promotion/demotion, offices, diplomacy,
armies, campaigns, and aggregate conflict;
- add visual relevance streaming to the live world view rather than only the
pure policy/benchmark contract;
- record weak-PC High/Low gameplay-camera CPU/GPU frame-time, draw-call,
memory, and VRAM captures.
See [the regional contract](REGIONAL_SIMULATION.md), [the roadmap](LEARNING_ROADMAP.md),
and [the performance guide](FEATURE_PRESENTATION_PERFORMANCE.md) before
starting one of these slices.
## Documentation authority
1. Code and focused tests define current behavior.
2. [ADR 0001](decisions/0001-simulation-authority-boundary.md) and
[ADR 0002](decisions/0002-emergent-world-content-and-narrative.md) define
durable architectural decisions.
3. [Architecture overview](ARCHITECTURE_OVERVIEW.md) defines ownership and
dependency boundaries.
4. The focused contracts define their module: [definitions](SIMULATION_DEFINITIONS.md),
[state](SIMULATION_STATE_SCHEMA.md), [actions](ACTION_SYSTEM_ARCHITECTURE.md),
and [regional simulation](REGIONAL_SIMULATION.md).
5. The [learning roadmap](LEARNING_ROADMAP.md) and [build-in-public plan](BUILD_IN_PUBLIC_PLAN.md)
describe sequencing and presentation goals; they do not override current
code behavior.
## Verification commands
```bash
./tools/quality.sh --changed
./tools/quality.sh
git diff --check
```
For a focused test, use the Godot binary configured by the local quality
scripts and pass the test script through the repository's isolated profile.
The complete test and benchmark map is in
[Testing and benchmarks](FEATURE_TESTING_AND_BENCHMARKS.md).
+20 -9
View File
@@ -10,6 +10,10 @@ resource_extracted
storage_deposited
storage_withdrawn
item_consumed
npc_slept
npc_died
task_started
resource_depleted
animal_fed
villager_weak
home_damaged
@@ -19,6 +23,13 @@ raid_started
war_resolved
war_aborted
wolf_hunt
activity_completed
task_blocked
commitment_accepted
commitment_fulfilled
commitment_broken
commitment_released
commitment_superseded
```
Each record contains a monotonically increasing event ID, event type,
@@ -75,17 +86,17 @@ transfer occurred.
## Persistence and determinism
`SimulationStateRecord` schema v11 stores the ordered event stream,
`SimulationStateRecord` schema v16 stores the ordered event stream,
`next_event_id`, directed relationships that may reference an exact event, and
per-NPC known-event references with first-acquisition provenance, plus
opportunity records that reference exact trigger/resolution events. Schema v1
and v2 saves migrate to an empty stream beginning at ID zero; world schemas
v1v7 migrate to an empty opportunity list, while world schemas v8v10
preserve and normalize opportunity history. World v9 adds an empty animal list
during migration; world v10 preserves and upgrades its animal records. Parsing
rejects duplicate event IDs, invalid or duplicate knowledge/opportunity
references, impossible communicator sources, relationship causes the observer
does not know, and next IDs that could collide with restored history.
opportunity, situation, journal, commitment, and conversation records that
reference exact trigger/resolution/outcome events. Historical world schemas
migrate explicitly through nested record versions; current v16 adds non-empty
world/location scope and rejects a save whose scope differs from the active
adapter before mutating the simulation. Parsing rejects duplicate event IDs,
invalid or duplicate knowledge/situation/commitment references, impossible
communicator sources, relationship causes the observer does not know, forged
activity facts, and next IDs that could collide with restored history.
Animal-feed facts receive an additional cross-record check: actor is either an
existing NPC or the player sentinel, destination is an existing animal, item
@@ -0,0 +1,149 @@
# Feature slice: commands, targets, and economy
This slice makes player and NPC interactions converge on one authority path.
The main pieces are `ActionCommand`, `ActionOffer`, `ActionResult`,
`ActionCommandService`, `CatalogInteractionService`,
`ActivityActionCommandService`, `WorldTargetRegistry`, `ActiveWorldAdapter`,
`StorageRoutingPolicy`, and `VillageEconomy`.
## Offer versus command
An offer is a read-only, copied description of what an actor could attempt.
It may contain stable action/target IDs, capability data, copy text, a reason
trace, expected state revision, target generation, context identity, and
registry-instance identity. It must not contain a `Node`, provider reference,
callable, or authoritative result.
The actor submits an `ActionCommand` with stable actor/action/target IDs,
optional item and amount, and an expected state revision. The service rechecks
everything at execution time. A valid offer is not permission to execute after
the world changes.
```text
query -> copied ActionOffer
|
v
player/NPC command
|
v
revalidate revision + registry instance + generation + provider
|
v
revalidate actor life + capability + range + target + reservation + cost
|
v
atomic mutation -> exact world fact -> copied ActionResult
```
## Context-scoped targets
`ActiveWorldAdapter` has explicit `world_id`, `location_id`, and `context_id`
identity. It owns one `WorldTargetRegistry` for that context. Resource,
storage, activity, and animal providers register a `WorldTargetDescriptor`
containing a canonical target ID, target kind, capability descriptors, and a
transient provider binding.
The registry provides:
- O(1) canonical ID lookup;
- cross-kind duplicate rejection within a context;
- deterministic sorted IDs and capability filtering;
- generation-checked transient `WorldTargetHandle` values;
- provider registration/unregistration and stale-provider pruning.
The target ID and contextual scope are the authority. A handle is only a
short-lived geometry/provider reference. Rebuilding a context restarts local
generations, so player results also carry a registry-instance token to prevent
ABA execution against a replacement registry.
`LoadedResourceSpatialIndex` accelerates finite resource queries by action and
horizontal cell. It is disposable presentation state; amounts, reservations,
yield, risk, and discovery priority remain in simulation records.
## The current shared handler
`ActivityActionCommandService` is the first production shared command handler.
Patrol and study are authored `activity_metric_delta` actions. The handler:
1. validates the actor and expected revision;
2. validates the action's typed handler/effect contract;
3. validates activity target kind, live provider, registry scope/instance,
capability, range, capacity, and task authorization;
4. preflights and withdraws any authored completion cost through
`VillageEconomy` and `StorageRoutingPolicy`;
5. applies the bounded safety or knowledge metric delta;
6. appends one scoped `activity_completed` fact;
7. returns exact event IDs, final revision, and reason trace.
If the cost is unavailable, no effect is applied and one structured
`task_blocked` fact records the action, source storage, item, and required
amount. A capped metric does not consume a cost or emit a false delta. The
primary event keeps the command revision; synchronous situation/commitment
consequences may append a causal suffix, and the result reports the final
revision.
NPCs must use their actual current action, target, task state, completion flag,
alive state, and context scope. An idle, dead, downed, wrong-target, or
wrong-world direct command fails without mutation. Players use the dedicated
player boundary, which requires all stale-offer tokens rather than accepting
sentinel defaults. Both paths share definition validation, economy, event, and
state checks.
## Typed effects and transactional planning
`ActionEffectPlanner` turns authored `ActionEffect` resources into an explicit
`ActionEffectPlan` with stable operation IDs, authored order, parameters, and a
reason trace. It supports the bounded typed effects currently registered:
metric/need changes, exact inventory/storage/cargo transfers, damage/healing,
relationship changes, scheduling ordinary work, and site-condition changes.
`TransactionalActionEffectCommitter` is the extension seam for a mechanic that
needs more than the built-in activity handler. A committer must apply one whole
plan atomically, return unique event IDs on success, or return a rollback-safe
failure with no event IDs. Plans reject unknown kinds, duplicate/reordered
operations, non-primitive values, non-finite numbers, same-role transfers, and
empty effect sets.
## Economy and routing
`VillageEconomy` owns exact storage/NPC/player inventory mutations. The
pantry, woodpile, apothecary, resource yields, animal feeding, player carry
loop, and action costs use the same transaction boundary. `village.food` and
`village.wood` are synchronized aggregate views, not second mutation paths.
`StorageRoutingPolicy` selects compatible storage by accepted item ID/tag,
priority, capacity, settlement scope, and stable target ID. The herb →
apothecary route proves that routing is no longer a food/wood switch. The
production Jajce economy still has one village settlement; regional stockpiles
and markets are future work.
## Adding a new interaction
1. Add or reuse an `ActionDefinition` with target/capability/handler/effect
fields.
2. Add target capability data to the provider definition or scene provider.
3. Register the provider through `ActiveWorldAdapter`.
4. Add an offer query and a command service/handler only if the existing typed
handler cannot express the mechanic.
5. Revalidate all authority at command time.
6. Mutate records, append the exact event, and test both player and NPC paths.
7. Add loaded/abstract and stale-offer regressions where applicable.
Do not put persistent amounts on `StorageNode`, use abstract Vector3 fallback
targets, or let the HUD call `SimulationManager` mutation methods directly.
## Reference tests and contracts
- `tests/world_target_registry_test.gd`
- `tests/context_scoped_world_targets_test.gd`
- `tests/unit/test_catalog_interaction_service.gd`
- `tests/unit/test_activity_action_command_service.gd`
- `tests/unit/test_activity_content_catalog_contract.gd`
- `tests/unit/test_action_effect_planning.gd`
- `tests/resource_node_player_parity_test.gd`
- `tests/food_storage_loop_test.gd`
- `tests/animal_food_delivery_test.gd`
For the complete action lifecycle and active-position rules, read
[Action System Architecture](ACTION_SYSTEM_ARCHITECTURE.md). For definition
fields, read [Simulation Definitions](SIMULATION_DEFINITIONS.md).
+139
View File
@@ -0,0 +1,139 @@
# Feature slice: authored content and presentation data
This slice is the no-code extension boundary for content that reuses existing
behavior. It is implemented by `SimulationContentPack`, `ContentCatalog`, the
typed definition resources under `simulation/definitions/`, and the separate
`PresentationCatalog` under `world/presentation/`.
## The four layers
| Layer | Lives in | Contains | Never contains |
| --- | --- | --- | --- |
| Definition | `.tres` resources and typed `Resource` classes | immutable IDs, balance, capabilities, effects, handlers, cue IDs | mutable amount, owner, reservation, node references |
| Instance descriptor | placement/provider nodes and regional records | stable runtime ID, context/location, authored overrides | script/callable/`NodePath` authority |
| State record | `simulation/state/` | primitive mutable values, IDs, history/event references | scene assets or resource paths |
| Presentation binding | `world/presentation/` and scene providers | cue-to-scene/material/icon/animation metadata | simulation quantities or quest authority |
Definitions are current-build balance. A save stores the definition ID and
mutable instance state; it does not freeze the `.tres` path or copy the full
resource. Renaming an ID requires an alias/migration, not a silent replacement.
## Content packs and catalog publication
`SimulationContentPack` groups typed arrays for:
- actions, professions, capability tags;
- items, resources, storages;
- enemies and animals;
- situations, dialogue intents, and template catalogs;
- event types and social consequences;
- required pack IDs and required handler IDs.
`ContentCatalog.create_core()` loads `simulation/definitions/packs/core.tres`.
`ContentCatalog.rebuild()` sorts packs and every definition by stable ID,
collects references into temporary dictionaries, validates the complete graph,
and publishes only if there are no errors. A failed rebuild clears the
published catalog; it never exposes a partially valid pack.
Validation currently rejects duplicate IDs, unknown references, missing
handlers, unsupported situation predicates, missing presentation cues, invalid
categories, invalid event/consequence references, duplicate template keys, and
malformed typed action contracts. `get_authoring_report()` is the deterministic
machine-readable inventory used by tests and tooling.
The first built-in handler contract is `activity_metric_delta`. It requires an
activity target, exactly one positive safety/knowledge metric effect, typed
optional `player_multiplier`/`npc_productivity` values, a known completion
event, and a routable completion-cost item. This closes the gap where an
authored action could publish but produce a fact that the validator could not
restore.
## Adding data-only content
### New item and route
1. Add an `ItemDefinition` with a category and stable `item_id`.
2. Add a `StorageDefinition` that accepts the item ID or one of its tags, or
update an existing typed storage route.
3. Add both to a pack and make sure the `StorageRoutingPolicy` can rank a
compatible storage.
4. Add a `PresentationCueDefinition` only if the item needs a visual.
5. Add a conservation/routing test. The herb/apothecary route is the reference
example; food/pantry and wood/woodpile preserve legacy coverage.
### New resource
1. Add a `ResourceDefinition` with yielded item, gather action, capacity/yield,
regrowth, access policy, capability tags, and a cue ID.
2. Place a `ResourceNode` with a unique contextual ID and meaningful
`safety_risk`, `comfort_distance`, `discovery_priority`, and interaction
point.
3. Register it through the nearest `ActiveWorldAdapter`; do not add a global
array or manager branch.
4. Test player/NPC selection, depletion, reservation, unload/rebind, and
save/restore authority.
### New animal
1. Add an `AnimalDefinition` with behavior/routine/feed capability IDs and a
stable presentation cue.
2. Add it to `core_animals.tres` or an explicit content pack.
3. Spawn through `AnimalFactory` and persist its `animal_definition_id` in
`AnimalStateRecord`.
4. Reuse `AnimalNode`, `AnimalRoutineSite`, and the grazer visual where the
existing contract fits. The goat/sheep pair proves this path.
### New enemy
1. Add an `EnemyDefinition` with `behavior_profile_id`, faction, item/weapon
references, hostile flag, and cue ID.
2. Register it in a pack. `CombatantFactory` owns deterministic per-prefix ID
allocation and restore collision checks.
3. Spawn through `ConflictSystem.spawn_enemy()`; keep `spawn_wolf()` only as a
compatibility wrapper.
4. Reuse a behavior profile when possible. `enemy_boar` deliberately reuses
`wolf_hunt`; no combat switch was added.
### New situation or dialogue content
See [Events through dialogue](FEATURE_EVENTS_KNOWLEDGE_SITUATIONS_DIALOGUE.md).
The authored resources must reference existing predicate IDs, event types,
intent IDs, template catalogs, and action/item definitions. A definition may
describe an unresolved condition, but it cannot create substitute world facts.
## Presentation catalogs
`PresentationCatalog` accepts `PresentationCatalogResource` entries whose
`PresentationCueDefinition` contains stable cue metadata and optional asset
paths. It validates metadata headlessly and loads scenes only at the world
boundary. `AnimalDefinition`, enemy definitions, resource definitions, and
other simulation content carry only a cue ID. This keeps headless tests and
saves independent of imported meshes/materials.
Presentation catalogs may be absent in a headless `AnimalFactory` or regional
simulation. Missing optional presentation is an explicit no-visual result, not
a reason to fail simulation state loading.
## What requires code
Data-only authoring is guaranteed only when the new combination uses existing
typed predicates, capabilities, effects, handlers, behavior profiles, and cue
contracts. Add one reusable typed handler when a new mechanic needs a new
authority rule. The handler must provide validation, a reason trace, atomic
mutation semantics, world facts, save fields/migration if needed, and a real
consumer test. Do not solve a new mechanic with a reflection DSL or a script
per content asset.
## Reference tests
- `tests/content_catalog_test.gd`
- `tests/simulation_definitions_test.gd`
- `tests/unit/test_authored_situation_dialogue_content.gd`
- `tests/unit/test_activity_content_catalog_contract.gd`
- `tests/unit/test_content_routing_foundations.gd`
- `tests/animal_definition_contract_test.gd`
- `tests/unit/test_combatant_factory.gd`
- `tests/unit/test_presentation_catalog.gd`
The [simulation definitions contract](SIMULATION_DEFINITIONS.md) is the
authoritative field-level reference; this guide explains the extension path.
+84
View File
@@ -0,0 +1,84 @@
# Feature slice: reusable entity families
Resources, animals, enemies, items, and storage follow a common extension
pattern. A definition describes behavior; a state record owns mutable facts; a
factory/system creates or advances the instance; a presentation binding uses a
stable cue ID.
## Resources and storage
`ResourceDefinition` describes yielded item, gather action, capacity/yield,
regrowth, access policy, capability tags, and presentation cue. A placed
`ResourceNode` binds its unique ID to `ResourceStateRecord`; amount,
reservation, enabled state, risk, comfort, priority, and regrowth survive scene
unload. `LoadedResourceSpatialIndex` only accelerates loaded anchor queries.
`StorageDefinition` describes accepted items/tags, capacity, policy priority,
settlement scope, actions, and cue. `StorageNode` supplies interaction geometry;
`StorageStateRecord` owns quantities. The current authored storages are pantry,
woodpile, and apothecary.
## Animals
`AnimalDefinition` owns stable definition ID, species, feed capability,
behavior/routine profile, and presentation cue. `AnimalStateRecord` v3 stores
the mutable animal ID, definition ID, position, hunger, routine/travel target,
reservation, and feed history. `AnimalFactory` creates/restores by definition;
`AnimalCareSystem` advances needs, routines, claims, and exact feeding.
`AnimalNode` binds a loaded provider to the saved state and follows an
authoritative destination via navigation. `AnimalRoutineSite` handles private
resident shelters versus shared species context. The goat and sheep resources
prove two definitions can reuse the same grazer root without Dunja-specific
simulation branches.
## Enemies and combatants
`EnemyDefinition` carries enemy ID, behavior profile, faction, item/weapon
references, hostile flag, and cue. `CombatantFactory` owns per-prefix stable ID
allocation and rebuilds occupied IDs after restore. `CombatantStateRecord` v2
persists `enemy_definition_id`; legacy hostile kinds migrate to current raider
or wolf definitions.
`ConflictSystem` owns authoritative health, weapon readiness, factions,
hostility, raids, war motivation, damage, death, and conflict facts. It creates
combatants through the factory and exposes generic `spawn_enemy()` while keeping
compatibility wrappers for old wolf/raid callers. `HostileCombatant` is a
presentation specialization of `CreatureVisual`; it must not become the owner
of health or death authority.
The boar definition deliberately reuses `wolf_hunt` behavior. A new enemy that
needs a new decision pattern adds one bounded behavior strategy plus tests; it
does not add a switch in the manager or a duplicate combat system.
## Shared visual contract
`CreatureVisual` follows a simulation-owned target position, retargets only
when needed, rate-limits failed navigation retries, reports position/arrival,
and plays death presentation. NPC, animal, and hostile visuals use this
boundary. A visual may report local blockage, but it may not decide that
simulation travel completed or mutate combat/resource state.
## Adding an entity safely
1. Identify whether an existing definition/profile/root already expresses the
behavior.
2. Add the definition and presentation cue, then register it in a pack/catalog.
3. Add the mutable definition ID to the nested state record if it is not
already present, including a legacy migration.
4. Route creation/restoration through the factory/system and stable allocator.
5. Bind the presentation at the world boundary and test unload/reload.
6. Prove a third data-only consumer before generalizing a bespoke handler.
## Reference tests
- `tests/animal_definition_contract_test.gd`
- `tests/animal_feeding_vertical_slice_test.gd`
- `tests/animal_routine_vertical_slice_test.gd`
- `tests/animal_pair_vertical_slice_test.gd`
- `tests/unit/test_combatant_factory.gd`
- `tests/combat_patterns_test.gd`
- `tests/conflict_war_system_test.gd`
- `tests/creature_visual_path_retry_test.gd`
- `tests/resource_regrowth_test.gd`
- `tests/jajce_resource_presentation_state_test.gd`
@@ -0,0 +1,173 @@
# Feature slice: events, memory, situations, quests, and dialogue
This is the emergent narrative pipeline. It turns real world mutations into
evidence, social meaning, unresolved situations, commitments, and semantic
conversation options without creating a parallel scripted world.
## One causal chain
```text
authoritative mutation
-> WorldEventRecord / EconomicEventRecord
-> indexed WorldEventStore + compatibility SimulationEventLog
-> performed/witnessed knowledge with provenance
-> relationship dimensions with exact causal event IDs
-> SituationSystem evaluates unresolved predicates
-> QuestJournalSystem exposes discovery/tracking metadata
-> SocialCommitmentSystem records promises and outcomes
-> ConversationService plans semantic intents/topics/options
-> Dialogue Manager presenter renders one derived turn
```
The event is the shared join key. A quest, journal entry, relationship, or
dialogue line may reference it, but none of those layers may invent an item,
resource, damage result, enemy, or reward.
## World facts
`WorldEntityRef` identifies an actor/participant by typed stable ID.
`SpatialAddress` identifies `(world_id, location_id, local_position)` and keeps
coincident local coordinates from crossing contexts. `WorldEventRecord` stores
event ID/type/tick, participants, optional location, and JSON-safe payload.
`WorldEventStore` provides O(1) ID lookup plus deterministic indexes by type,
participant, location, exact tick, and tick range.
`SimulationEventLog` remains the local Jajce compatibility façade. It records
economic and activity facts, enforces the configured world/location scope, and
refuses to reinterpret retained events under a different active context.
`ActivityEventValidator` validates definition-backed success and blocked
facts, including legacy markers. Older unstructured blocked facts remain
loadable only with explicit migration provenance and cannot become new
knowable evidence.
## Knowledge and relationships
`EventKnowledgeSystem` stores a bounded `(knower, event)` record, not copied
prose. Each memory carries acquisition method, source actor, hop count,
confidence, salience, pinning, and acquisition tick. Direct actor/witness
observation happens when the event is recorded; restore never recomputes old
witnesses from current positions. Communication is one hop from a performed or
witnessed fact, and the player is a valid knower/source sentinel.
Ordinary recent memories are bounded to three per knower; pinned causal facts
and facts referenced by situations/commitments are protected. The retention
system changes memory references only; it never deletes objective event facts.
`RelationshipSystem` stores sparse directed edges. The seven dimensions are
familiarity, trust, affection, respect, fear, obligation, and hostility. Each
non-neutral dimension can retain its exact causal event ID. Consequences are
bounded and replay-safe: for example, food aid can increase a hungry familiar
villager's trust toward the actor, while commitment lifecycle consequences
adjust obligation/trust/hostility by commitment and outcome identity.
## Situations and journal entries
`SituationDefinition` declares triggering predicates, interested-party and
participant context, dedupe fields, severity/priority, alternatives, valid
resolution patterns, expiry/invalidation rules, topics, and commitment terms.
`SituationSystem` evaluates definitions in deterministic order, caps the
initial Jajce slice at three active situations, deduplicates by configured
context, and stores only a `SituationStateRecord` with exact trigger and
resolution event IDs. Progress is derived live from current state and ordinary
events.
`QuestJournalSystem` stores `QuestJournalEntryStateRecord`: situation ID,
discovery tick, selected alternative, tracking preference, and close/archive
state. It never copies objectives or progress. The journal remains empty until
the player acquires knowledge of the situation through an ordinary path.
The current authored pantry shortage is in
`simulation/situations/resources/pantry_shortage.tres`. Four legacy local
opportunity types are retained as a compatibility family in
`VillageOpportunitySystem`; the generic situation system is the direction for
new content. Roof repair remains disabled until authoritative home-condition
state exists.
## Commitments
`SocialCommitmentSystem` creates a `CommitmentStateRecord` when a social promise
is accepted. It stores debtor/creditor refs, situation, terms, deadline, status,
and exact acceptance/outcome event IDs. Acceptance does not reserve stock or
prevent another actor from resolving the situation.
`CommitmentLifecycleService` records accepted, fulfilled, broken, released, and
superseded facts and applies relationship consequences idempotently. A matching
outcome performed by the debtor fulfills; an outcome performed by another actor
supersedes without penalty. A still-achievable deadline reached at or after its
due tick breaks the promise. Release and external impossibility are explicit
outcomes. Untracking a journal entry is not cancellation.
## Deterministic conversations
`ConversationService` owns transient conversation records and semantic turns:
- `begin(speaker, listener, context)` validates typed participants and context;
- `get_turn(conversation_id)` returns a copied current turn;
- `select_option(conversation_id, option_id, expected_revision)` revalidates
the revision and recomputes the next turn/action;
- `end()` or `invalidate()` closes the transient session.
The planner reads current relationship dimensions/causes, knowledge, performed
and witnessed history, task/need/profession/role, known situations,
commitments, and retained conversation acts. Intent definitions provide
preconditions, deterministic scoring, response intents, cooldowns, and optional
authoritative actions. Current Jajce intents include greeting, work/wellbeing,
what happened, sharing a fact, describing a need, offering help, commitment
accept/decline/renegotiation, progress, thanks/reproach, supersession, and
goodbye.
Only `ConversationActStateRecord` is persisted: typed speaker/listener refs,
intent ID, topic IDs, tick, and a bounded history. Conversation IDs, revision,
options, rendered text, and balloon state are transient.
## Dialogue Manager boundary
The project vendors Dialogue Manager v3.10.4 under `addons/dialogue_manager`
with provenance/checksum and MIT license in `addons/dialogue_manager/UPSTREAM.md`.
`DialogueManagerConversationPresenter` receives one planned `ConversationTurn`,
selects a stable template variant, escapes dynamic names/topics, and creates an
ephemeral resource. It does not execute game mutations, conditions, or
authority shortcuts. Controlled option tags map back to stable option IDs and
the presented revision.
`DialogueModeController` owns the loaded player mode: nearest-NPC Talk has
priority, movement/combat/ordinary interaction lock while open, simulation
speed is normalized to 1×, stale option selections refresh safely, and the
mode closes if the speaker dies, unloads, or leaves range. The fake presenter
tests keep domain tests renderer-independent; parser and mapping tests cover
the vendored plugin.
## Vertical slice and extension recipe
The complete Jajce proof is in `tests/emergent_jajce_slice_test.gd` and
`tests/conversation_domain_test.gd`:
1. exact pantry shortage evidence opens a situation;
2. an NPC knows it while the player does not;
3. speaking transfers knowledge and discovers the journal entry;
4. generated options reflect hunger, relationship, evidence, and need;
5. accepting creates a commitment without reserving stock;
6. ordinary food gather/deposit resolves the real shortage;
7. the commitment fulfills or, in variants, breaks/supersedes;
8. later conversation references the actual outcome;
9. save/reload preserves semantic history and deterministic options.
To add a new emergent story, author an event type and situation definition,
ensure the authoritative action emits the event, add any social consequence or
dialogue intent/template, and test the complete causal chain. Do not begin with
a quest-only enemy/item or a dialogue graph that mutates state behind the
simulation.
## Reference tests and deeper contracts
- `tests/unit/test_world_event_store.gd`
- `tests/unit/test_social_foundations.gd`
- `tests/unit/test_situation_system.gd`
- `tests/unit/test_commitment_lifecycle_service.gd`
- `tests/conversation_domain_test.gd`
- `tests/dialogue_manager_conversation_presenter_test.gd`
- `tests/dialogue_mode_controller_test.gd`
- `tests/emergent_jajce_slice_test.gd`
- [Economic event stream](ECONOMIC_EVENTS.md)
- [Simulation state schema](SIMULATION_STATE_SCHEMA.md)
- [ADR 0002](decisions/0002-emergent-world-content-and-narrative.md)
+101
View File
@@ -0,0 +1,101 @@
# Feature slice: presentation, relevance, and weak-PC performance
Presentation is deliberately replaceable and bounded. The simulation can run
headless; loaded scenes provide geometry, navigation, animation, cues, and
input. This guide records the current performance controls and the evidence we
do and do not have.
## Loaded-world boundary
`ActiveWorldAdapter` owns a context identity `(world_id, location_id,
context_id)`, a `WorldTargetRegistry`, loaded resource spatial index, and
provider handles for resource/storage/activity/animal nodes. It is the only
place where a simulation target is paired with loaded geometry. Multiple
contexts must use separate ancestor containers; duplicate stable IDs are valid
only when they belong to different explicit contexts.
`WorldViewManager` owns active NPC visual instances and forwards target/arrival/
navigation callbacks. `NpcVisual`, `CreatureVisual`, and `AnimalNode` follow
authoritative positions. They do not own elapsed travel or saved quantities.
## Relevance and navigation budgets
`PresentationRelevancePolicy.select_detailed_visuals()` rejects foreign world,
context, or location candidates before ranking and returns at most 40 detailed
visuals. Explicit pins include named/important people, player-affecting
threats, situation/commitment participants, leaders, and unique owners. If
mandatory pins exceed the cap, the policy fails closed instead of silently
dropping identities. Decisions include score, distance, pin reasons, and
rejection traces.
`PresentationNavRequestBudget` admits at most two new navigation requests per
frame. It exposes pending/in-flight IDs and deterministic priority order. The
budget is a pure policy contract today; wiring it into every production visual
spawn is a next integration.
## Jajce quality profiles
`JajceWorld` owns reversible High, Balanced, and Low presentation profiles with
instance-local mutable environment/grass resources and viewport-scale ownership.
| Profile | 3D scale | Shadow distance | Volumetric/glow | Grass |
| --- | ---: | ---: | --- | --- |
| High | authored | authored (currently 180 m) | authored when renderer supports it | authored density/interactors |
| Balanced (default) | 0.85 cap | 120 m cap | authored ordinary lookdev; unsupported volumetric disabled | reduced spacing/interactors/update rate |
| Low | 0.70 cap | 80 m, orthogonal | volumetric/glow/adjustment disabled; ordinary fog retained | hidden/stopped, emitters off, shader interactors zero |
UI remains at native resolution. High/Low switches restore captured authored
values; viewport scale restoration is guarded by the current quality owner so a
second world cannot clobber the active owner. Compatibility/mobile feature
selection never forces unsupported volumetric fog on.
## Runtime hot paths already bounded
- Player interaction and villager inspection probes are throttled/event-driven;
they no longer rebuild all candidate data every rendered frame.
- `LoadedResourceSpatialIndex` bounds finite-resource discovery.
- `SimulationPopulationView` gives stable-ID all/living/starving indexes once
per tick.
- `WorldTargetRegistry` gives O(1) target lookup and deterministic iteration.
- Creature path failures retain the failed target and use bounded backoff.
- Hostile hit flashes are health-change driven, not position-change driven.
- Day/night, status, time dial, storage/debug labels, and grass updates run at
human-readable rates or only on state changes.
- Regional scheduled work uses a due-job heap rather than a permanent scan.
## What the benchmarks prove
- Simulation baseline 03: 600 full-fidelity data-only NPC/combatant records at
about 90.6 ticks/s on the reviewed Apple M1 Max capture, with matching
checksums across samples.
- Loaded Resource Discovery 01: indexed 1,800-anchor queries preserve exact
selection checksums while reducing resolution work dramatically.
- Regional scale baseline 01: five settlements, 20 caravans, 2,000 named
people, 50,000 aggregate residents, 2,025 jobs, budget/checksum parity.
These are headless/structural measurements. They do **not** prove 600 rendered
visuals, a 30-day regional economy, or weak-PC GPU frame time.
## Remaining evidence gap
The project still needs a recorded reference weak-PC active-world workload at a
fixed gameplay camera, including High and Low p50/p95/p99 CPU/GPU frame time,
draw calls/primitives, memory/VRAM, active detailed visuals, and navigation
queue depth. The CI gate should keep structural profile assertions and the
two-request policy; hardware measurements belong in a reviewed benchmark
ledger, not brittle cross-machine CI thresholds.
## Performance extension rules
1. Measure a representative workload before optimizing.
2. Keep simulation and presentation results checksum-identical across loaded,
unloaded, and load/unload runs.
3. Add indexes, dirty sets, analytical updates, or scheduled jobs before adding
more per-frame scans.
4. Keep allocations out of render-frame probes and avoid sorting when a stable
bounded comparison is sufficient.
5. Any new visual must declare relevance/pin behavior and obey the navigation
request budget.
See [the benchmark ledger](benchmarks/README.md), [regional contract](REGIONAL_SIMULATION.md),
and [the build-in-public visual plan](BUILD_IN_PUBLIC_PLAN.md).
@@ -0,0 +1,117 @@
# Feature slice: regional simulation and persistence
The regional package is the scale foundation for one authority spanning
settlements, routes, caravans, named people, aggregate cohorts, and polities.
It is intentionally additive: the current Jajce `SimulationManager` remains
the playable local facade while the regional contracts prove deterministic
unloaded work and conservation in isolation.
## Regional record model
`RegionalWorldState` is a primitive-only, versioned aggregate with deterministic
indexes for:
- `LocationStateRecord` — world/location identity, type, address, and history;
- `SettlementStateRecord` — location/polity, tier, founding group, stockpile;
- `RouteStateRecord` — endpoints, travel ticks, direction, history;
- `MobileGroupStateRecord` — caravan/army identity, membership, route state,
cargo ledger, capacity, arrival/departure ticks, and cargo event cursor;
- `PersonStateRecord` — globally unique named people and memberships;
- `PopulationCohortRecord` — conserved aggregate residents;
- `PolityStateRecord` and `DiplomaticRelationRecord` — sparse regional politics.
Membership is explicit and validated: a person is at one location or in one
mobile group, routes connect existing locations, groups reference existing
routes/members, and IDs are globally unique and append-only. Records contain
stable IDs and primitive values only; no scene, `NodePath`, resource, or
callable can enter the regional payload.
## Deterministic scheduled work
`ScheduledJobRecord` and `RegionalJobScheduler` order due work by:
```text
(due_tick, phase, entity_id, stable_sequence)
```
The scheduler supports O(log n) insertion/cancellation, deterministic due
draining, repeats, dedupe keys, a serialized cursor, and strict restore
validation. A frame or service budget may process one, eight, or unlimited jobs
per call, but all budgets must produce the same order, checksum, and final
backlog. Dynamic jobs inserted while draining are ordered into the same queue,
not handled by an ad-hoc frame loop.
`KeyedRandom` derives values from `(world_seed, system_id, entity_id,
occurrence)`, so update order and save/load do not consume another entity's
random stream. `AnalyticalRegionalUpdates` advances elapsed needs, growth, and
periodic work from last-updated ticks instead of scanning every entity each
tick.
## The caravan proof
`RegionalCaravanService` owns an envelope containing `RegionalWorldState`,
`RegionalJobScheduler`, and `WorldEventStore`. `depart()` validates origin,
destination, route, cargo capacity, group status, and exact state; it withdraws
the cargo and schedules a typed arrival. The arrival handler updates the group,
deposits the exact cargo into the destination settlement, and appends the causal
load/depart/arrive/deposit chain. Presentation mode is an input label for the
test, never an authority branch.
The service is transactional: invalid jobs, forged event pairs, stale event
cursors, duplicate departure pairs, and malformed restore envelopes fail with
no partial mutation. `to_dictionary()`, `from_dictionary()`, and `checksum()`
provide deterministic save/restore. The test runs always-loaded, never-loaded,
and load/unload presentation modes through the same authoritative service.
## Chunked persistence
`RegionalChunkedPersistence` captures the service into a manifest plus fixed
primitive chunks:
- one global index;
- one location chunk per location;
- one mobile-group chunk per group;
- one scheduler chunk;
- fixed-size event segments.
Each descriptor contains record count, byte size, and SHA-256 checksum. Restore
rejects missing, duplicated, unknown, reordered, tampered, out-of-scope, or
non-primitive chunks. `load_active_location_metadata()` reads the global index
and one location while retaining authoritative references to unloaded chunks.
`RegionalChunkedFileStore` writes immutable fixed-width generation directories,
validates a temporary generation before install, updates a small current
pointer, recovers the newest valid generation when the pointer is stale, and
keeps at least two valid generations when pruning. It uses Godot's available
flush/close/rename primitives; a platform fsync is not exposed by this layer.
The store has explicit path, file-count, chunk-size, generation-size, and
primitive-tree bounds.
## Current integration boundary
The regional package is not yet the production authority for `main.tscn`:
- Jajce local saves still use `SaveSlotStore` and `SimulationStateRecord` v16;
- `RegionalCaravanService` is a complete isolated transfer proof, not the live
economy's 20-trade scheduler;
- event retention/rollups and regional chunk files are not yet one integrated
long-horizon save transaction;
- loaded/unloaded local NPC travel still has the old active-visual boundary;
- cohort promotion, settlement growth, offices, diplomacy, armies, and
aggregate conflict remain planned.
Do not create one `SimulationManager` per settlement or caravan. The next
production step is an explicit regional facade that delegates to these records
and services, while the active adapter presents only a bounded relevant context.
## Scale evidence
`RegionalScaleBenchmark` builds five settlements, 20 caravans × 12 named
members, 2,000 named people, 50,000 aggregate residents, and 2,025 jobs. It
proves conservation, canonical roundtrip, budget parity, and repeatable
checksums. It does **not** claim rendered frame time, a complete market, a
30-day economy, or weak-PC GPU performance.
See [Regional simulation contract](REGIONAL_SIMULATION.md),
[regional baseline 01](benchmarks/REGIONAL_SCALE_BASELINE_01.md), and
[Testing and benchmarks](FEATURE_TESTING_AND_BENCHMARKS.md).
@@ -0,0 +1,98 @@
# Feature slice: saves, schema, migrations, and continuation
The local playable world and the regional proof use different persistence
boundaries. Both are primitive-only, versioned, deterministic, and validated
before mutation.
## Local save boundary
`SimulationStateRecord` is currently schema **v16**. It contains the local
simulation seed/clock scope, village/NPC/animal/resource/storage/player/
combatant/faction records, ordered economic events, relationships, knowledge,
legacy opportunities, situations, journal entries, commitments, and bounded
semantic conversation acts. `SimulationManager.create_state_record()` creates
it; `serialize_state()` emits canonical JSON; `restore_state()` preflights and
then atomically restores dependent systems.
Top-level v16 adds non-empty `world_id` and `location_id`. Activity success and
blocked facts carry the same scope. A save recorded under another active adapter
is rejected before clock, economy, NPC, or event state is mutated. This closes
the context-reinterpretation hole where equal local coordinates could be
treated as the same world.
The record stores IDs, primitive values, event IDs, and semantic topic IDs. It
never stores scripts, nodes, `NodePath`s, callables, imported assets, dialogue
prose, or transient HUD/balloon state.
## Migration policy
Nested records migrate at their own version; the top-level schema accepts an
explicit historical range and normalizes old fields before cross-record
validation. Current supported migrations include:
- legacy village/storage and event streams;
- directed relationships and causal knowledge provenance;
- opportunity history and its v2 lifecycle;
- animal routine/definition IDs;
- player citizen needs/health;
- combatants, factions, and enemy definition IDs;
- situations, journal entries, commitments, and conversation acts;
- world/location scope v16;
- explicitly marked legacy unstructured `task_blocked` facts.
Old definition values are not snapshotted. The current build supplies balance
and handler definitions while mutable saved state is preserved. Unknown future
schema versions fail closed until a migration exists.
## Validation invariants
Restore checks include:
- unique IDs and next-ID cursors above restored history;
- valid definition/action/item/storage references;
- actor/target/cost/effect contracts for activity facts;
- animal reservations and feed event agreement;
- relationship causes pointing at valid known events;
- knowledge provenance and one-hop communication rules;
- situation/journal/commitment references and statuses;
- scope consistency for the event log and activity facts;
- no future event tick or forged actor/source/destination fields.
The test suite deliberately removes derived knowledge/opportunity state and
tamper-tests facts, so validation does not rely on a secondary projection to
catch corruption.
## Local file safety
`SaveSlotStore` validates the complete JSON before replacement, restricts slot
names and size, preserves a previous file, and can recover a backup after an
interrupted replacement. `F5`/`F9` use the current quicksave path in `main.tscn`.
The player transform and presentation-only scene state are intentionally not
saved.
## Regional persistence boundary
`RegionalChunkedPersistence` and `RegionalChunkedFileStore` are the regional
counterpart. They serialize a `RegionalCaravanService` envelope into global,
location, mobile-group, scheduler, and event-segment chunks, with manifest and
per-file checksums. The file store uses immutable generations, a current
pointer, recovery scanning, and conservative pruning. Partial active-location
loads are metadata-only and cannot silently resurrect a removed location.
This regional codec is not yet the backend for local `SaveSlotStore`. Integrate
it only after the production regional facade, event retention, and lazy context
loading have one atomic manifest contract.
## How to change state safely
1. Add the field to the narrow nested record first.
2. Define `SCHEMA_VERSION`, validation, canonical `to_dictionary()`, and an
explicit migration from the prior version.
3. Update top-level construction/restore/checksum only after the nested record
round-trips.
4. Add malformed/tamper, old-save migration, save/load, and deterministic
continuation tests.
5. Update [SIMULATION_STATE_SCHEMA.md](SIMULATION_STATE_SCHEMA.md) and this
guide with the actual version and compatibility range.
The full field-by-field history remains in [Simulation State Schema](SIMULATION_STATE_SCHEMA.md).
+101
View File
@@ -0,0 +1,101 @@
# Feature slice: tests, quality, and performance evidence
The project uses deterministic headless scenarios, GUT unit tests, formatting/
lint checks, and reviewed benchmark ledgers. Tests are part of the architecture:
they prove authority boundaries, conservation, migration, parity, and
deterministic continuation.
## Quality gate
Run from the repository root:
```bash
./tools/quality.sh --changed # fast iteration
./tools/quality.sh # full gate before commit
git diff --check
```
The gate currently runs Godot 4.7 platform/import checks, `gdformat`,
`gdlint`, headless project checks, standalone scenarios, GUT, and a dedicated
Compatibility presentation smoke. It uses an isolated Godot profile under
`logs/quality/godot_profile`. On macOS, Godot's certificate-store diagnostic,
Terrain3D interpolation deprecation, and the exact known dummy-shader exit
diagnostic are classified by the gate; new error classes must not be hidden.
## Scenario families
| Concern | Representative tests |
| --- | --- |
| Definitions and packs | `content_catalog_test`, `simulation_definitions_test`, `test_activity_content_catalog_contract` |
| Targets and interaction parity | `world_target_registry_test`, `context_scoped_world_targets_test`, `resource_node_player_parity_test`, `test_activity_action_command_service` |
| Economy and resources | `food_storage_loop_test`, `resource_regrowth_test`, `animal_food_delivery_test` |
| Animals and enemies | `animal_definition_contract_test`, `animal_pair_vertical_slice_test`, `test_combatant_factory`, `conflict_war_system_test` |
| Events and social facts | `test_world_event_store`, `test_social_foundations`, `witnessed_knowledge_consequence_test`, `relationship_consequence_test` |
| Situations and dialogue | `test_situation_system`, `emergent_jajce_slice_test`, `conversation_domain_test`, `dialogue_manager_conversation_presenter_test`, `dialogue_mode_controller_test` |
| Save/migration | `test_emergent_state_validation`, `simulation_state_serialization_test`, `save_slot_persistence_test`, `test_commitment_lifecycle_service` |
| Regional records | `test_regional_world_state`, `test_regional_scheduler`, `test_regional_caravan_service`, `test_regional_chunked_persistence`, `test_regional_chunked_file_store` |
| Presentation | `jajce_world_scaffold_test`, `jajce_presentation_quality_test`, `jajce_runtime_integration_test`, `test_presentation_relevance_policy`, `creature_visual_path_retry_test` |
When a change affects state, target selection, saves, navigation reachability,
player/NPC parity, deterministic continuation, or a fact-representing UI,
extend a scenario rather than relying on a parser/lint pass.
## Benchmark runners
```bash
# local simulation population/history fixture
godot --headless --path "$PWD" \
--script res://tools/benchmark_simulation_scaling.gd
# loaded finite-resource spatial discovery
godot --headless --path "$PWD" \
--script res://tools/benchmark_loaded_resource_discovery.gd
# regional records and scheduler (20 caravans / 50k cohorts)
godot --headless --path "$PWD" \
--script res://tools/benchmark_regional_scale.gd
```
Reviewed reports live in `docs/benchmarks/` beside machine-readable JSON. A
benchmark must record seed, Godot version, host label, workload/schema ID,
sample count, warmup, simulated ticks, checksums, and exclusions. Structural
headless measurements cannot be presented as rendered frame-time claims.
## Determinism and conservation checklist
For a new system, add tests for:
- same seed/run produces the same canonical checksum;
- save/load at the important boundary continues identically;
- execution budgets `{1, 8, unlimited}` preserve order and result where a
scheduler is involved;
- loaded, unloaded, and load/unload presentation produce identical authority;
- people, cargo, inventory, storage, unique items, and event IDs conserve;
- stale offers, duplicate IDs, unknown definitions, forged facts, and scope
mismatches fail without partial mutation;
- important identities remain pinned through relevance/aggregation decisions.
## Current evidence and gaps
The current full suite passes 154/154 tests with 2,047 assertions. Baseline 03
measures 600 full-fidelity data-only NPC/combatant records at about 90.6 ticks/s
on the reviewed development host. The regional baseline proves the target
record counts and scheduler parity. Neither measures a complete regional
economy or weak-PC rendering.
The next performance evidence should capture High and Low at a fixed gameplay
camera on a named weak-PC reference: p50/p95/p99 CPU/GPU frame time, draw calls,
primitives, memory/VRAM, active detailed visuals, and navigation queue depth.
Keep those measurements in a reviewed ledger instead of adding brittle hardware
limits to CI.
## Test authoring conventions
- Use stable IDs and fixed seeds.
- Prefer exact assertions over prose or frame timing.
- Make test fixtures isolate duplicate stable-ID registries and world scopes.
- Use fake presenters for semantic dialogue; reserve plugin/parser tests for the
adapter boundary.
- Keep benchmark fixtures independent of production managers unless the test is
explicitly an integration proof.
- Run focused tests while editing, then the full gate before committing.
+7 -4
View File
@@ -72,11 +72,14 @@ the unit never consults pantry stock again before delivery.
## Deliberate limits
The current bounded economy has one authored pantry and one woodpile with
simple food/wood stacks. It does not yet add:
The current playable economy still has one authored village pantry and one
woodpile, but it now also has typed item definitions, an apothecary route,
persisted player inventory, and definition-backed activity costs. It does not
yet add:
- player inventory;
- storage reservations or access capacity;
- regional storage/market ledgers;
- storage reservations or ownership/access policy beyond authored capability
routing;
- multiple households or ownership;
- spoilage, item quality, weight, or stack definitions.
+8
View File
@@ -1,5 +1,13 @@
# The Steward — Learning and Reusable-Systems Roadmap
> **Current implementation checkpoint (2026-08-13):** The roadmap below is a
> chronological learning record. The current code has completed the authored
> content/catalog, shared target/command, event/social, situation/dialogue,
> entity-family, context-scoping, and first regional record/caravan/chunk
> slices. For a source-based status matrix and the remaining integration work,
> start with [`DEVELOPER_INDEX.md`](DEVELOPER_INDEX.md). Do not infer current
> behavior from an older milestone paragraph when the index or code disagrees.
## Purpose
This project is both a playable prototype and an advanced Godot curriculum.
+24 -18
View File
@@ -2,8 +2,10 @@
> Agent-facing context for understanding the project quickly.
>
> Snapshot basis: repository state on July 16, 2026. Treat the code as the
> source of truth when this document and the implementation differ.
> Snapshot basis: repository state on August 13, 2026. This document preserves
> the product context and learning history; the current implementation matrix
> is maintained in [`DEVELOPER_INDEX.md`](DEVELOPER_INDEX.md). Treat code and
> tests as the source of truth when a historical paragraph differs.
See the [documentation map](README.md) for the authority and scope of each plan.
@@ -663,7 +665,8 @@ These are expected prototype constraints, not necessarily isolated bugs:
the typed pantry `StorageNode`, and patrol/study/rest use `ActivitySite`.
- Current NPC, village, resource, storage, animal, event, knowledge,
relationship, opportunity, clock, RNG, player-citizen, combatant, and
faction state serialize through world schema v14. F5/F9 provide one validated
faction, situation, journal, commitment, and semantic conversation state
serialize through world schema v16. F5/F9 provide one validated
local quicksave; a save menu, metadata, and player-transform persistence
remain deferred.
- Combatants and factions are deterministic simulation records. NPC defenders,
@@ -702,20 +705,20 @@ These are expected prototype constraints, not necessarily isolated bugs:
other knowledge is capped and reviewed after one simulated day. NPCs do not
yet have wider social dimensions, goals, line-of-sight/hearing evidence,
personalized reinforcement/decay, false beliefs, or multi-hop rumours.
- The bounded opportunity family supports one open need from exact known
evidence: `restock_empty_pantry`, `supply_missing_wood`,
`feed_weak_villager`, and `repair_home_roof`. The pantry and weak-villager
needs resolve through real food supply (the weak need only when the
interested villager actually eats), the wood needs through the real
wood-deposit path, and all close deterministically on interested-party death
or one-day staleness. One read-only query derives and explains a capable
helper, and the matching idle NPC can consume it through ordinary action
selection after urgent self-care and schedule precedence. There is still no
helper assignment, acceptance, rewards, free-form dialogue, or quest log.
- The legacy bounded opportunity family supports one open need from exact
known evidence: `restock_empty_pantry`, `supply_missing_wood`,
`feed_weak_villager`, and `repair_home_roof`. The newer `SituationSystem`
supports up to three authored active situations in the initial Jajce slice,
a player journal projection, social commitments, deterministic semantic
dialogue, and idempotent fulfilled/broken/released/superseded consequences.
Progress remains derived from real state and ordinary events; no layer
manufactures quest-only stock or rewards.
- The reason inspector exposes current decisions, utility rejections, one exact
relationship cause, and a compact person-history view that distinguishes
importance-ranked retained memories from objective personal actions.
- Active navigation is used as if all agents are local; no simulation LOD exists.
- Active navigation is still used as if all detailed agents are local; the
regional relevance policy and 40-visual/navigation-budget contracts exist,
but production simulation LOD/streaming is not yet wired into the live view.
- Unloaded traveling NPCs preserve their state but do not yet advance through
abstract travel time.
- Loaded finite-resource anchors have one measured active-world spatial index;
@@ -732,8 +735,11 @@ These are expected prototype constraints, not necessarily isolated bugs:
- Terrain3D and the bounded Jajce beauty baseline now run in the main game
scene; `Jajce Lookdev 01` and the runtime `Simulation Garden 01` are captured
as reproducible presentation baselines.
- Combat, companions, factions, politics, trade, generalized/multi-hop rumours,
a general quest framework, and regional travel do not yet exist.
- Companions, a complete market/trade economy, settlement growth, offices,
politics, generalized/multi-hop rumours, production regional travel
integration, and kingdom-scale conflict do not yet exist. Regional records,
scheduled work, one exact caravan transfer, situations, commitments, and
generated semantic dialogue do exist as tested foundations.
- Stylized player/NPC silhouettes, water, foliage, and VFX support the current
build-in-public baseline, while blockout buildings and several work/resource
props remain visibly prototype-grade.
@@ -1112,7 +1118,7 @@ parallel.
## Out of scope until the simulation garden works
- full regional map;
- production-wired full regional map and travel facade;
- large city population;
- multiplayer;
- procedural world generation;
@@ -1121,7 +1127,7 @@ parallel.
- kingdom-scale diplomacy;
- large battles;
- broad crafting catalog;
- fully generated dialogue;
- free-form generated prose (semantic deterministic dialogue is implemented);
- multiple large production chains;
- production-quality character customization.
+23 -8
View File
@@ -1,35 +1,50 @@
# Documentation map
Start with [`DEVELOPER_INDEX.md`](DEVELOPER_INDEX.md). It is the developer
onboarding index: a current feature matrix, repository map, extension workflow,
verification commands, and links into the detailed contracts below.
The project uses a small hierarchy so overlapping plans do not become competing
sources of truth.
This directory is intentionally covered by `.gdignore`: plans and baseline
captures are review artifacts, not runtime Godot resources.
1. [`PROJECT_CONTEXT.md`](PROJECT_CONTEXT.md) is the canonical description of
1. [`DEVELOPER_INDEX.md`](DEVELOPER_INDEX.md) is the current implementation
index and the fastest route to a feature slice.
2. [`PROJECT_CONTEXT.md`](PROJECT_CONTEXT.md) is the canonical description of
the vision, current implementation, target architecture, and active
constraints.
2. [`ARCHITECTURE_OVERVIEW.md`](ARCHITECTURE_OVERVIEW.md) is the concise map of
3. [`ARCHITECTURE_OVERVIEW.md`](ARCHITECTURE_OVERVIEW.md) is the concise map of
runtime ownership, folder responsibilities, and dependency rules.
3. [`LEARNING_ROADMAP.md`](LEARNING_ROADMAP.md) owns milestone order,
4. [`LEARNING_ROADMAP.md`](LEARNING_ROADMAP.md) owns milestone order,
architecture gates, reusable-system exit tests, and intentionally deferred
work.
4. [`BUILD_IN_PUBLIC_PLAN.md`](BUILD_IN_PUBLIC_PLAN.md) owns the scoped Jajce
5. [`BUILD_IN_PUBLIC_PLAN.md`](BUILD_IN_PUBLIC_PLAN.md) owns the scoped Jajce
visual slice. It must respect the architecture gates in the learning
roadmap.
5. [`RESOURCE_NODE_MIGRATION.md`](RESOURCE_NODE_MIGRATION.md) is a focused
6. [`RESOURCE_NODE_MIGRATION.md`](RESOURCE_NODE_MIGRATION.md) is a focused
migration plan. Phases 16 are complete; follow-up work should expand
resource discovery without reintroducing abstract resource zones.
6. [`ACTION_SYSTEM_ARCHITECTURE.md`](ACTION_SYSTEM_ARCHITECTURE.md),
7. [`ACTION_SYSTEM_ARCHITECTURE.md`](ACTION_SYSTEM_ARCHITECTURE.md),
[`ECONOMIC_EVENTS.md`](ECONOMIC_EVENTS.md),
[`FOOD_STORAGE_ARCHITECTURE.md`](FOOD_STORAGE_ARCHITECTURE.md),
[`REGIONAL_SIMULATION.md`](REGIONAL_SIMULATION.md),
[`SIMULATION_DEFINITIONS.md`](SIMULATION_DEFINITIONS.md), and
[`SIMULATION_STATE_SCHEMA.md`](SIMULATION_STATE_SCHEMA.md) document the
current data contracts.
7. [`decisions/`](decisions/) contains durable architectural decisions,
8. The implementation guides explain the larger slices without replacing the
narrow contracts: [authored content](FEATURE_CONTENT_AND_AUTHORED_DATA.md),
[commands and economy](FEATURE_COMMANDS_TARGETS_AND_ECONOMY.md),
[events through dialogue](FEATURE_EVENTS_KNOWLEDGE_SITUATIONS_DIALOGUE.md),
[regional simulation](FEATURE_REGIONAL_SIMULATION_AND_PERSISTENCE.md),
[entity families](FEATURE_ENTITIES_COMBAT_ANIMALS.md),
[presentation/performance](FEATURE_PRESENTATION_PERFORMANCE.md),
[saves/migrations](FEATURE_SAVE_SCHEMA_AND_MIGRATIONS.md), and
[testing/benchmarks](FEATURE_TESTING_AND_BENCHMARKS.md).
9. [`decisions/`](decisions/) contains durable architectural decisions,
including consequences and revisit conditions.
8. [`benchmarks/`](benchmarks/) contains reviewed, workload-specific
10. [`benchmarks/`](benchmarks/) contains reviewed, workload-specific
performance ledgers and their machine-readable samples. Measurements are
local evidence, not portable CI limits.
+6
View File
@@ -1,5 +1,11 @@
# Regional simulation contract
> **Implementation status (2026-08-13):** The record, scheduler, caravan,
> chunk, file-generation, relevance, and navigation-budget contracts are
> implemented and tested. They are still an additive regional package; the
> playable Jajce `SimulationManager` and `SaveSlotStore` remain the local
> production authority until the integrations listed at the end land.
The regional layer extends the Jajce simulation without multiplying scene-tree
managers. One authority owns every loaded and unloaded location, settlement,
route, mobile group, named person, population cohort, polity, scheduled job,
+5 -5
View File
@@ -2,7 +2,7 @@
## Current contract
`SimulationStateRecord` is the versioned JSON boundary for the current
`SimulationStateRecord` is the versioned JSON boundary for the current local
simulation. The current world schema is v16 and captures:
- simulation seed, stable regional world/location scope, tick interval, tick
@@ -445,7 +445,7 @@ replayed after restore.
## Local quicksave boundary
`SaveSlotStore` writes the existing versioned JSON record to
`SaveSlotStore` writes the existing versioned local JSON record to
`user://saves/quicksave.json`. It validates the serialized record before
replacement, limits file size, restricts slot names, preserves the previous
file during replacement, and can recover that backup if replacement is
@@ -461,7 +461,7 @@ This phase does not yet provide:
- a save-slot menu, metadata, thumbnails, autosaves, or multiple profiles;
- migrations from any historical world schema other than the explicitly
supported v1-v14 layouts;
supported v1-v16 layouts;
- line-of-sight/hearing evidence, personalized confidence decay,
reinforcement, false beliefs, or multi-hop rumours;
- the full regional situation predicate library, regional quest bounds,
@@ -470,8 +470,8 @@ This phase does not yet provide:
- persistence of a currently open dialogue UI/turn. Semantic conversation
history and commitment authority persist, while a balloon is regenerated
after restore;
- chunked regional saves, lazy location loading, or aggregate population
persistence;
- production integration of the regional chunked save manifest, lazy location
loading, or aggregate population promotion/persistence;
- persistence for the player transform or presentation-only scene state.
Those features should build on this boundary rather than inventing parallel
+4
View File
@@ -1,5 +1,9 @@
# Simulation benchmark ledger
The [testing and benchmarks guide](../FEATURE_TESTING_AND_BENCHMARKS.md)
explains how to add a workload, what each current result proves, and which
rendered-performance claims are intentionally still unmeasured.
This directory stores reviewed benchmark snapshots. Each snapshot must name the
workload, seed, Godot version, hardware context, sample count, simulated tick
count, and important exclusions so later comparisons remain honest.
+11
View File
@@ -10,3 +10,14 @@ plans. Use the next sequential number and include:
- conditions for revisiting the decision.
Do not create records for routine edits or choices that are still exploratory.
## Accepted records
| ADR | Scope | Current consequence |
| --- | --- | --- |
| [0001 — simulation authority boundary](0001-simulation-authority-boundary.md) | Fixed-step simulation, scene/presentation boundary, stable IDs, save authority | Loaded nodes may present and report geometry, but they do not own persistent facts or completion |
| [0002 — emergent world content and narrative](0002-emergent-world-content-and-narrative.md) | Typed packs, shared commands, causal facts, situations, dialogue, and one regional authority | New reusable content is data-first; new mechanics add bounded handlers; quests/dialogue project real world state |
The [developer index](../DEVELOPER_INDEX.md) summarizes how these decisions
appear in the current modules. Add a new ADR only when a choice should survive
multiple feature plans and has explicit revisit conditions.