Skip to content

Commit 8713500

Browse files
dennys246claude
andcommitted
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>
1 parent a62077e commit 8713500

7 files changed

Lines changed: 732 additions & 30 deletions

File tree

docs/plans/biosystem_wiring_hardening.md

Lines changed: 213 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,209 @@ def update_with_entity(self, entity_name: str, entity_type: str) -> float:
241241

242242
---
243243

244+
## Phase 1.5: Cascade Result Surfacing (~120 LOC)
245+
246+
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
257+
258+
Meanwhile, silently:
259+
→ Entity.vital_metrics updated
260+
→ EmbodimentPerceptSource polls at 1Hz → evaluates failures → publishes pain
261+
→ 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)
282+
if not result.success:
283+
return ToolOutput(success=False, error=result.error)
284+
285+
# Read back entity state after cascade resolution
286+
entity_state = {}
287+
for sensor_name, sensor in self._entity.sensors.items():
288+
try:
289+
reading = sensor.read()
290+
if isinstance(reading.value, (int, float)):
291+
entity_state[sensor_name] = reading.value
292+
except Exception:
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 in self._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)
325+
if self._embodiment is not None:
326+
failure_events = self._embodiment.evaluate_failures()
327+
# 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)
335+
336+
**Files:** `agents/memory_agent.py:build_context()` + `agents/prompt_builder.py` (~30 LOC)
337+
338+
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)
345+
346+
# In memory_agent.py build_context():
347+
if self._memory_hub and hasattr(self._memory_hub, '_embodiment'):
348+
embodiment = self._memory_hub._embodiment
349+
if embodiment is not None:
350+
sync_fields["body_state"] = embodiment.format_body_state_for_prompt()
351+
352+
# In prompt_builder.py, add section (CRITICAL priority — always shown):
353+
if context.body_state:
354+
budgeter.add("body_state", context.body_state, SectionPriority.CRITICAL)
355+
```
356+
357+
Making body state `CRITICAL` priority means it's never dropped under token pressure. The LLM always sees:
358+
```
359+
=== Body State ===
360+
- derek.body.hp: 22 points
361+
- derek.body.stamina: 0.65 ratio
362+
- derek.inventory.longsword.durability: 0.85 ratio
363+
- derek.combat.threat_level: 0.45 ratio (WARN: overextension at 0.9)
364+
```
365+
366+
**LOC:** ~30
367+
368+
### 1.5d Embodiment reference on MemoryHub
369+
370+
**File:** `integration/memory_hub.py` + `simulation/orchestrator.py` (~10 LOC)
371+
372+
MemoryHub needs an optional `embodiment` reference so `build_context()` can access body state and Cerebellum can be wired to it.
373+
374+
```python
375+
# In memory_hub.py (already has cerebellum field):
376+
embodiment: Any = None # Embodiment runtime for body state access
377+
378+
# In orchestrator.py, after Embodiment init (if/when it exists):
379+
if aut_memory_hub is not None and aut_embodiment is not None:
380+
aut_memory_hub.embodiment = aut_embodiment
381+
```
382+
383+
**LOC:** ~10
384+
385+
### 1.5e Cerebellum observes cascade outcomes
386+
387+
**File:** `embodiment/tool_bridge.py` (~15 LOC)
388+
389+
After ModulatorAffordanceTool executes and failure evaluation runs, feed the actual sensor readings to Cerebellum so it can train forward models.
390+
391+
```python
392+
# In ModulatorAffordanceTool.execute(), after evaluate_failures():
393+
if self._cerebellum is not None:
394+
try:
395+
self._cerebellum.observe_from_action(
396+
entity_path=self._entity.full_path,
397+
modulator=self._modulator.name,
398+
affordance=self._affordance_name,
399+
params=kwargs,
400+
actual_sensors=entity_state,
401+
sensor_ranges={s: self._entity.sensors[s].reading_schema.get("range", [0, 1])
402+
for s in entity_state},
403+
)
404+
except Exception:
405+
pass
406+
```
407+
408+
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)
437+
438+
=== Available Motor Programs ===
439+
- slash_combo (confidence=0.45, 3 runs, success=67%)
440+
Steps: derek.combat.attack({target: "guard", weapon: "longsword"}) → ...
441+
```
442+
443+
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+
244447
## Phase 2: Pipeline Correctness (~150 LOC)
245448

246449
Systems that are connected but produce wrong outputs.
@@ -885,23 +1088,25 @@ expectations:
8851088
| Phase | What | LOC | Days |
8861089
|---|---|---|---|
8871090
| **Phase 1** | Critical wiring (9 items) | ~200 | ~1.5 |
1091+
| **Phase 1** | **SHIPPED** (6c262c5) | 189 | done |
1092+
| **Phase 1.5** | Cascade result surfacing (5 items) | ~120 | ~1 |
8881093
| **Phase 2** | Pipeline correctness (10 items) | ~150 | ~1 |
8891094
| **Phase 3** | Percept abstraction + entity-modulated perception | ~180 | ~1 |
8901095
| **Phase 4** | Design gap fixes (6 items) | ~80 | ~0.5 |
8911096
| **Phase 5** | Pipeline audit script | ~250 | ~0.5 |
8921097
| **Phase 6** | Sensory ablation campaign YAML | ~150 | ~0.5 |
893-
| **Total** | | **~1,010** | **~5** |
1098+
| **Total** | | **~1,130** | **~6** |
8941099

8951100
### Implementation Order
8961101

897-
1. **Phase 1.1-1.2** first (init systems + connect) — unlocks everything else
898-
2. **Phase 1.3** (NAc observe) — most impactful single fix
1102+
1. ~~**Phase 1.1-1.9** (critical wiring) — **SHIPPED** (commit 6c262c5)~~
1103+
2. **Phase 1.5** (cascade surfacing) — rich tool results, immediate failure eval, body state in prompt, Cerebellum observation
8991104
3. **Phase 2.1** (forming boost) — most impactful correctness fix
900-
4. **Remaining Phase 1** (PainBus→NAc, SCN capture, Cerebellum, motor programs, novelty)
901-
5. **Remaining Phase 2** (context_match, decay, concept gate, pain cooldown, priority, truncation, thread safety)
902-
6. **Phase 3** (percept abstraction) — enables rich DM percepts
903-
7. **Phase 4** (design gaps)
904-
8. **Phase 5** (audit script) — validates everything
1105+
4. **Remaining Phase 2** (context_match, decay, concept gate, pain cooldown, priority, truncation, thread safety)
1106+
5. **Phase 3** (percept abstraction) — enables rich DM percepts with entity-modulated SensoryGate
1107+
6. **Phase 4** (design gaps)
1108+
7. **Phase 5** (audit script) — validates everything
1109+
8. **Phase 6** (sensory ablation campaign)
9051110

9061111
### Verification
9071112

0 commit comments

Comments
 (0)