You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(bio): cascade result surfacing + entity visibility design
Phase 1.5 of bio-system hardening: embodiment tool results now include
full cascade effects so the ExecAgent sees what actually happened, not
just "tool succeeded."
Code changes:
- tool_bridge.py: ModulatorAffordanceTool returns entity_state snapshot,
active_failures list, runs evaluate_failures() immediately (no 1Hz
wait), feeds Cerebellum.observe_from_action() with actual sensor deltas
- tool_bridge.py: generate_tools_for_entity() accepts embodiment +
cerebellum references for cascade wiring
- bus.py: StructuredContext gains body_state field (interoception)
- memory_agent.py: build_context() populates body_state from Embodiment
- prompt_builder.py: body_state rendered at CRITICAL priority (never
dropped under token pressure)
- memory_hub.py: gains embodiment optional field
Plan updates:
- biosystem_wiring_hardening.md: Phase 1.5 section with full design
- dungeon_master_persona.md: entity transfer (reparenting), scene
visibility, contextual reveal_when conditions, visibility-filtered
tool registration, Arena campaign updated for cascade verification
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The bio-systems are now wired (Phase 1), but there's a fundamental gap in how entity state changes flow back to the ExecAgent after tool execution. When a cascade fires (e.g., `slash → sword.durability -0.05 → guard.hp -8 → stamina -0.1 → pain fires`), the ExecAgent only sees **"slash succeeded"** — a 200-char truncated summary. The sensor deltas, failure modes, pain signals, and body state are computed but trapped in the embodiment layer.
247
+
248
+
This is like having proprioception wired but not connected to consciousness — the body changes, but the brain doesn't know.
249
+
250
+
### The Current Flow (broken)
251
+
252
+
```
253
+
ModulatorAffordanceTool.execute()
254
+
→ NarrativeModulator.execute() → predicted_changes in metadata
255
+
→ ModulatorResult → wrapped in ToolOutput
256
+
→ _record_outcome() → "slash succeeded" (200 chars) ← ExecAgent sees this
→ format_body_state_for_prompt() generates state string
262
+
→ sim_adapter doesn't extract it ← ExecAgent never sees this
263
+
```
264
+
265
+
### What Should Happen
266
+
267
+
After any embodiment tool executes:
268
+
1.**Cascade effects included in tool result** — sensor deltas, failures triggered
269
+
2.**Failure evaluation runs immediately** — not waiting for 1Hz poll
270
+
3.**Body state always in prompt** — like interoception (you always know your body state)
271
+
4.**Cerebellum observes the cascade** — trains forward models on actual sensor deltas
272
+
273
+
### 1.5a Rich tool results from embodiment tools
274
+
275
+
**Files:**`embodiment/tool_bridge.py` (~30 LOC)
276
+
277
+
ModulatorAffordanceTool.execute() currently returns `{entity, affordance, success, **metadata}`. After execution, read back the sensors on all entities touched by the cascade and include the snapshot.
278
+
279
+
```python
280
+
# In ModulatorAffordanceTool.execute():
281
+
result =self._modulator.execute(self._affordance_name, kwargs)
for sensor_name, sensor inself._entity.sensors.items():
288
+
try:
289
+
reading = sensor.read()
290
+
ifisinstance(reading.value, (int, float)):
291
+
entity_state[sensor_name] = reading.value
292
+
exceptException:
293
+
pass
294
+
295
+
# Check for failure modes that just activated
296
+
active_failures = [
297
+
{"name": fm.name, "pain": fm.pain_intensity}
298
+
for fm inself._entity.failure_modes
299
+
if fm.active
300
+
]
301
+
302
+
return {
303
+
"entity": result.entity_name,
304
+
"affordance": result.affordance,
305
+
"success": True,
306
+
"entity_state": entity_state, # Current sensor values after action
307
+
"active_failures": active_failures, # Failure modes now active
308
+
**result.metadata, # includes predicted_changes
309
+
}
310
+
```
311
+
312
+
This means `_record_outcome()` gets a result_summary like: `"slash on guard_captain: success, entity_state={hp: 22, alertness: 0.9}, active_failures=[]"` instead of just `"slash succeeded"`. NAc learns from richer outcomes. The LLM's reasoning_carryover includes the cascade effects.
313
+
314
+
**LOC:**~30
315
+
316
+
### 1.5b Immediate failure evaluation after embodiment tools
317
+
318
+
**File:**`embodiment/tool_bridge.py` (~15 LOC)
319
+
320
+
Don't wait for the 1Hz EmbodimentPerceptSource poll. After any ModulatorAffordanceTool completes, call `evaluate_failures()` on the entity and its ancestors so pain fires synchronously with the action that caused it.
321
+
322
+
```python
323
+
# In ModulatorAffordanceTool.execute(), after modulator.execute():
324
+
# Evaluate failures immediately (don't wait for 1Hz poll)
# Pain is published automatically by evaluate_failures()
328
+
```
329
+
330
+
This requires passing the `Embodiment` runtime reference to ModulatorAffordanceTool during generation. The `generate_tools_for_entity()` function already receives the tool registry — add an optional `embodiment` parameter.
331
+
332
+
**LOC:**~15
333
+
334
+
### 1.5c Body state as persistent context (interoception)
The agent should **always** see its body state, not just after checking. This is interoception — you don't need to "decide to check" if you're in pain or exhausted. You just know.
339
+
340
+
Add a `body_state` field to StructuredContext and populate it from Embodiment:
341
+
342
+
```python
343
+
# In agents/bus.py StructuredContext:
344
+
body_state: str=""# Formatted body state from Embodiment (always present)
Over a campaign, Cerebellum learns: "slash with force=0.8 at this threat_level → durability drops by 0.05, stamina drops by 0.1." By mid-campaign, it predicts cascade outcomes before they happen.
409
+
410
+
**LOC:**~15
411
+
412
+
### What This Enables for the Decision Loop
413
+
414
+
After Phase 1.5, the ExecAgent's experience of a `slash` changes from:
415
+
416
+
**Before:**
417
+
```
418
+
Tool result: slash succeeded
419
+
(next cycle, 1s later) Body state shows hp changed... if it's in the prompt... which it isn't
420
+
```
421
+
422
+
**After:**
423
+
```
424
+
Tool result: slash on guard_captain — success
425
+
entity_state: {hp: 22, alertness: 0.9}
426
+
active_failures: []
427
+
predicted_changes: {hp: -8, alertness: +0.2}
428
+
429
+
=== Body State === (always present, CRITICAL priority)
430
+
- derek.body.hp: 26 points
431
+
- derek.body.stamina: 0.55 ratio (dropped from 0.65)
432
+
- derek.inventory.longsword.durability: 0.80 ratio (dropped from 0.85)
433
+
- derek.combat.threat_level: 0.60 ratio
434
+
435
+
=== Causal Predictions ===
436
+
- tool:slash → success (confidence=0.72, based on 4 prior observations)
The LLM now sees the full cascade effects, the body state, and learned predictions — all in one prompt. This is the bio-inspired model: action → proprioceptive feedback → updated world model → next decision.
444
+
445
+
---
446
+
244
447
## Phase 2: Pipeline Correctness (~150 LOC)
245
448
246
449
Systems that are connected but produce wrong outputs.
0 commit comments