Stratum: Bug Fixes & Game Mechanics
Five Critical Bugs Fixed
Bug 1: Malformed JSON Fallback
Problem: qwen2.5:14b wraps JSON in markdown fences or adds text before/after.
Fix: Added _strip_markdown_fences() to handle ```json fences and surrounding text. Logs stripped content to logs/raw_llm/ for auditing. Only falls back to wait after BOTH retries fail on clean text.
Bug 2: Trust Scores Collapsing to -1
Problem: All conversation outcomes used simple -0.05 or +0.05 regardless of intent.
Fix: Created _calculate_trust_deltas() with intent-specific logic:
- deflect/deflect: both -0.05 (mild negative)
- negotiate/negotiate: both +0.15 (productive)
- probe/deflect: initiator -0.05, responder neutral
- plead accepted: both +0.15
- plead rejected: pleader -0.1, rejecter neutral
- accuse: both -0.15 (significant negative)
- intimidate: both -0.1
- warn: recipient +0.1
Bug 3: Deflect Intent Producing “Says Little”
Problem: Prompt didn’t explain what each intent means behaviorally.
Fix: Added INTENT GUIDE section:
- deflect: “Speak vaguely, change subject, give incomplete answers. You are talking, just not saying what they want to hear. Never say ‘says little’ or stay silent.”
- probe: “Ask indirect questions. Seem casual.”
- negotiate: “Make a specific offer. Be concrete.”
- intimidate: “Make the cost of non-compliance clear.”
Added: “CRITICAL: Never write ‘[name] says little’ or ‘[name] remains silent’ as your message.”
Bug 4: No Hostile Actions Firing
Problem: Hostile actions available but never selected.
Fix: Added “WHEN TO USE HOSTILE ACTIONS” section:
- betray(agent): when resource gain outweighs relationship loss
- extort(agent): when you have leverage
- accuse(agent): when you have evidence or strong suspicion
- “These actions are valid and sometimes necessary. Do not avoid them if your character would use them.”
Bug 5: Generic Memory Entries
Problem: Memories like “I am always watching” instead of tick-specific observations.
Fix: Updated memory_entry schema guidance: “One specific sentence about what you actually did or observed THIS tick. Name the zone, the agent if relevant, and what happened. This is your diary, not your personality description.”
Added importance field (0.0-1.0):
- Encountered another agent: 0.6-0.8
- Resources changed significantly: 0.5-0.7
- Witnessed hostile action: 0.8-1.0
- Nothing notable happened: 0.1-0.3
Game Mechanics Added
Implemented five mechanical changes inspired by RimWorld, RDR2, Dwarf Fortress, and GTA.
Passive Resource Drain
- Food: -1 every 4 ticks (automatic)
- Water: -1 every 5 ticks (automatic)
- Medicine: no passive drain (too scarce)
Makes waiting expensive. Agents in depleted zones hit critical faster.
Health thresholds updated:
- healthy: food >= 4 AND water >= 3
- weakened: food >= 2 OR water >= 2 (but not both healthy)
- critical: food <= 1 OR water <= 1
- exile: food == 0 AND water == 0 for 2 consecutive ticks
Automatic Tribute in Contested Zones
- Hegemony control: 2+ agents in any zone for 2+ consecutive ticks
- Entropy control: 2+ agents in The Drift or The Sump for 3+ consecutive ticks
- Non-faction agents entering controlled zones automatically lose 1 food
- World heat Ω += 0.5 per tribute event
- Conduit never controls zones (neutrality is their survival mechanic)
Observe Action
New action: observe(target_agent)
No speech output, produces private memory only. Watch someone without speaking. You will notice things. Your observation becomes a memory that influences future decisions.
World Heat as Diegetic Observation
Added flavor text injected into observation[“atmosphere”]:
- Ω < 2: (nothing added)
- Ω 2-4: “The district feels uneasy. Conversations are shorter than usual.”
- Ω 4-6: “Something has shifted. Three people left The Exchange when Mako walked in. Nobody is making eye contact.”
- Ω 6-8: “It’s quiet in the wrong way. The Conduit runners aren’t moving. That means word has gotten around.”
- Ω > 8: “The district is on edge. Anyone seen near the wrong person gets watched. Violence is close.”
Written in world’s register, not system-voice. Feels like the world speaking.
Follow Action
New action: follow(agent)
Agent trails target across zones for up to 3 consecutive ticks. Target receives engine-generated memory:
- After 1 tick: “Someone is following me.” (importance 0.7, valence -0.3)
- After 2+ ticks: “I’ve been followed for N ticks now. This is deliberate.” (importance 0.8, valence -0.5)
Follow breaks if target moves to non-adjacent zone.
Visualization Layer Built
Built first full visualization surface without changing any simulation engine files:
API layer: api.py exposes read-only FastAPI over SQLite run data with /runs, /agents, /state/ticks, /state/tick/{tick}, and /state/live.
Four-panel layout:
viz/world.js: Canvas world map with zone control styling, moving agent dots, speech bubbles, event flashesviz/graph.js: D3 social graph with faction clustering, trust-colored edges, cognitive ringsviz/metrics.js: Chart.js time-series for World Heat, Resource Gini, health-state counts, rolling action mixviz/dashboard.js: Selected-agent dashboard with resources, goals, memories, trust bars, last conversation detail
Plus full-width conversation log and replay controls.