feat: unify authored activity commands

This commit is contained in:
Rijad Zuzo
2026-08-13 00:08:30 +02:00
parent e5aecee0de
commit b3b98c6456
36 changed files with 3013 additions and 96 deletions
+26 -1
View File
@@ -9,7 +9,8 @@ state, systems, world adapters, scenes, tests, and save migrations.
IDs. `SimulationContentPack` is the no-code authoring manifest and
`ContentCatalog` transactionally validates and publishes typed definitions in
stable-ID order. The current core pack contains actions, professions,
capability tags, items, resources, storage policies, and enemies. A failed pack
capability tags, items, resources, storage policies, enemies, animals,
situations, dialogue, event types, and social consequences. A failed pack
publishes nothing: duplicate IDs, unknown references, unsupported handlers, or
missing presentation cues are authoring errors rather than silent overrides.
@@ -57,6 +58,30 @@ are copy-only values. `ActionCommandService` is the authority boundary and must
revalidate range, capabilities, reservation, permission, cost, and target state
at execution time for either a player or NPC caller.
`activity_metric_delta` is the first concrete shared command handler. Patrol
and study targets publish capabilities through the context-owned
`WorldTargetRegistry`; player offers carry the current state revision, target
generation, context ID, and registry-instance identity. The command revalidates
all four, the live provider, actor availability, target kind, capability, and
interaction range, then atomically consumes the authored cost, applies the
bounded real metric delta, and records one `activity_completed` fact. Registry
identity prevents an old offer from matching a replacement target whose local
generation restarted. NPC completion submits the same command and must match
the NPC's authoritative current action, target, and work state. Unloaded NPC
work may use an explicitly registered simulation-owned activity target, but it
never invents a missing target or treats presentation position as travel
authority. A failed cost check records and returns the exact ordinary
`task_blocked` fact; a capped metric consumes nothing and records no false
completion. The completion fact keeps the first event ID while synchronous
situation or commitment consequences may append a deterministic causal suffix;
the command result reports the final revision rather than misclassifying that
valid event chain as failure.
The catalog validates this handler as a closed typed contract: an activity
target, exactly one positive `metric_delta` effect for safety or knowledge,
typed optional player/NPC modifiers, a known completion event, and a cost item
with an authored storage route. A malformed additive pack publishes nothing.
The current actions are gather food, gather wood, feed animal, deposit food,
deposit wood, withdraw food, patrol, study, eat, rest, sleep, wander, and
defend. Idle and dead are state sentinels, not executable definitions. The
+26 -4
View File
@@ -3,10 +3,10 @@
## Current contract
`SimulationStateRecord` is the versioned JSON boundary for the current
simulation. The current world schema is v15 and captures:
simulation. The current world schema is v16 and captures:
- simulation seed, tick interval, tick count, clock remainder, and elapsed
clock ticks;
- simulation seed, stable regional world/location scope, tick interval, tick
count, clock remainder, and elapsed clock ticks;
- every NPC's identity, needs, attributes, task lifecycle, target, position,
starvation state, and decision RNG stream;
- village resource counters;
@@ -45,7 +45,7 @@ The top-level identity is:
```json
{
"schema": "the_steward.simulation",
"schema_version": 15
"schema_version": 16
}
```
@@ -213,6 +213,15 @@ nested v1/v2 events receive empty/zero defaults, while a new `task_blocked`
event requires its stable action, source storage, resource, and positive
requirement contract.
The v3 event envelope also carries validated extension fields for authored
activity facts without adding a parallel quest record. An
`activity_completed` fact stores the target/action IDs, world and location
scope, metric ID, actual bounded delta, and exact item cost. Loading derives
the expected handler, effect, event type, metric, maximum delta, and cost from
the current action definition and rejects forged actor, discriminator, scope,
or amount combinations even when no knowledge record references the event.
Player and NPC performed knowledge points to that same immutable fact.
SimulationStateRecord v10 adds the top-level `animals` array and the first
`AnimalStateRecord` schema. Each animal record stores its stable animal and
species IDs, display name, authoritative world position, hunger,
@@ -294,6 +303,19 @@ templates, balloon state, and prose are transient. Reopening dialogue plans from
current facts plus the retained semantic acts, so wording can evolve without
changing authoritative history.
SimulationStateRecord v16 adds non-empty `world_id` and `location_id` fields to
the top-level simulation record. Activity completion and blocked-action facts
retain that same scope in their immutable payload, so event-store indexes,
knowledge validation, and save restoration cannot reinterpret a local position
as belonging to another loaded context. World v15 migrates explicitly to the
existing Bosnia/Jajce IDs. An event log that already contains facts refuses a
different configured scope instead of silently reindexing history. Restore
preflights this scope against the active adapter before changing clock,
economy, NPC, or event state, so both empty- and populated-history mismatches
fail atomically. Older unstructured `task_blocked` facts are retained with
explicit legacy provenance for save continuity, but they are not promoted into
knowable evidence or new situation triggers.
Nested `KnownEventStateRecord` v4 adds the player as a valid knower/source and
records hop count, confidence, salience, and explicit pinning. Communicated
facts remain exactly one hop from a performed or witnessed source. Nested