feat(simulation): composable behavior engine — action+modifier+context primitives (#633)

Replaces flat culture×zone×role behavior strings with three-layer
composition: BehaviorAction (role-generic), BehaviorModifier (culture
coloring), BehaviorContext (situation gating). Assembly at NpcBlueprint
instantiation. Backward compatible — falls back to legacy behaviors
when primitives are empty. D-139 filed, Q-057 resolved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 09:12:07 +01:00
co-authored by Claude Opus 4.6
parent accbe579c9
commit 50800cbf81
5 changed files with 348 additions and 10 deletions
+16 -1
View File
@@ -433,6 +433,21 @@ How narrative, NPCs, and world content are created: content tiers, NPC generatio
- **Resolves:** Q-057 (composable behavior generation), Q-012 (generation expansion method)
- **Cross-reference:** [D-010](architecture.md#d-010) (information boundaries), [D-121](#d-121-voice-is-culture-driven--job-as-modifier) (culture-primary voice), [D-122](#d-122-all-npcs-generated--named-npcs-deferred) (all NPCs generated), [D-128](#d-128-culture-implicit-in-starting-location--krenn-system-equals-krenn-culture) (culture as generator input), [D-029](#d-029-population-entanglement-ratio--305020) (NPC tier model), [D-092](perception.md#d-092) (anchor lines)
### D-139: Composable behavior primitives — three-layer assembly model
- **Date:** 2026-03-13
- **Decision:** Observable NPC behaviors are composed from three layers rather than authored as flat strings per culture×zone×role combination. The three layers are:
1. **BehaviorPrimitive** (on `RoleSpec`): role-generic physical stage directions (e.g., "moves freight containers", "runs diagnostics on a terminal"). Tagged with a `BehaviorContext` (OnShift/OffDuty/Social/Any) that gates when the behavior is eligible. Optional `modifier_hint` for category matching.
2. **BehaviorModifier** (on `CultureProfile`): culture-specific clauses appended to actions (e.g., "with mechanical efficiency", "with a watchful eye"). Categorized for matching (work_style, demeanor, social, etc.).
3. **Assembly function** (`assemble_behaviors`): at NpcBlueprint instantiation time, filters primitives by context, selects a culture modifier per primitive (preferring hint matches, falling back to any), concatenates `action + " " + modifier.clause`. No modifier → action text used as-is.
- **Rationale:** The current model has `typical_behaviors: Vec<String>` on RoleSpec — flat strings per culture×zone×role. At ~50 behaviors × 4 roles × N zones × M cultures, this is O(roles × zones × cultures) custom content. Decomposition to primitives + modifiers reduces to O(roles + cultures) authored content. Assembly is deterministic via SimRng, so behavior output is reproducible for a given seed. The three-layer model maps directly to the existing CultureProfile/RoleSpec/NpcBlueprint data flow — no new serialization formats or pipeline stages.
- **Implementation:** `server/src/npc/blueprint.rs` — `BehaviorPrimitive`, `BehaviorContext`, `BehaviorModifier` structs + `assemble_behaviors()` function. `RoleSpec.behavior_primitives: Vec<BehaviorPrimitive>` (serde default, backward-compatible). `CultureProfile.behavior_modifiers: Vec<BehaviorModifier>` (serde default). Legacy `typical_behaviors` field preserved until content migration complete.
- **Migration path:** Copy team populates `behavior_primitives` on zone spec RON files and `behavior_modifiers` on culture RON files. Generator switches from `typical_behaviors` to `assemble_behaviors()` when primitives are present. Once verified equivalent for seed 42, legacy field can be removed.
- **Source:** Sprint 26, ticket #633 (implements Q-057)
- **Raised by:** Tyre (Technical Architect)
- **Dissent:** None (design resolves the scaling problem identified in #630 sprint review)
- **Resolves:** Q-057 (composable behavior generation — data structure definition)
- **Cross-reference:** [D-138](#d-138-llm-re-voicing-pipeline-for-npc-voice) (resolved pipeline, this resolves data format), [D-121](#d-121-voice-is-culture-driven--job-as-modifier) (culture-primary voice), [D-122](#d-122-all-npcs-generated--named-npcs-deferred) (all NPCs generated)
---
*38 decisions. Last updated: 2026-03-07 (D-138 amended with Spike 2 findings: stdio IPC, tell differentiation results, double-prompt technique, ContentType::Factual, negative injectors moved to per-culture; D-123 amended; D-124 superseded — LLM Voice Pipeline Workshop)*
*39 decisions. Last updated: 2026-03-13 (D-139 composable behavior primitives — Sprint 26 #633)*
+4 -4
View File
@@ -193,15 +193,15 @@ Narrative, NPCs, dialogue, templates, setting, worldbuilding, and storyteller me
### Q-057: Composable behavior generation — decompose culture × role × context into assembled behaviors
- **Status:** Open
- **Status:** Resolved — D-139 (Sprint 26, #633)
- **Raised:** Sprint 25, ticket #630 review discussion
- **Priority:** High (blocks scaling beyond hand-authored content)
- **Context:** Current behavior pools are hand-authored per culture×zone×role combination (`typical_behaviors` arrays in zone spec RON files). At ~50 behaviors per role × 4 roles × N zone types × M cultures, this is O(roles × zones × cultures) custom content. Each cell is effectively a unique location — "rural zone spec" is really "Krenn rural settlement content" with the name filed off. This doesn't scale to multiple cultures or zone types.
- **Question:** Should the generator compose observable behaviors from smaller primitives instead of drawing from pre-written complete sentences? Proposed decomposition: (1) **role action templates** — generic observable stage directions per role, culture-neutral, (2) **culture modifier sets** — culture-specific flavoring (Krenn mannerisms, speech patterns, social norms) that overlay role actions, (3) **context tags** — on-shift, off-duty, break-room, social-site-type that filter/weight which behaviors are available. The generator assembles these at runtime.
- **Implications:** Changes the content authoring model from "write 50 sentences per role per zone per culture" to "write role actions once, write culture modifiers once, compose at runtime." Server needs a composition engine (#633); copy needs to author the decomposed format (#634). Part of the Sprint 25 PoC spike.
- **Cross-reference:** #630 (behavior pool expansion), #633 (server: composition engine), #634 (copy: decomposed content format), D-121 (voice is culture-driven), D-122 (all NPCs generated)
- **Resolution:** Yes. D-139 defines the three-layer composable behavior model: `BehaviorPrimitive` (role actions with context tags), `BehaviorModifier` (culture overlays), and `BehaviorContext` (on-shift/off-duty/social/any filtering). The `assemble_behaviors()` function composes at runtime. Generator spike updated to use assembly when `behavior_primitives` are present, falling back to `typical_behaviors` for backward compatibility. Copy team (#634) authors the decomposed format.
- **Cross-reference:** #630 (behavior pool expansion), #633 (server: composition engine), #634 (copy: decomposed content format), D-121 (voice is culture-driven), D-122 (all NPCs generated), D-139 (composable behavior assembly)
- **Assigned to:** Tyre, Mellanie, Miri
---
*21 questions (5 resolved, 2 partially resolved, 14 open). Last updated: 2026-03-07 (Q-057 added — composable behaviors)*
*21 questions (6 resolved, 2 partially resolved, 13 open). Last updated: 2026-03-13 (Q-057 resolved by D-139)*