Skip to content

feat: Narrative Layer — prose generation, characters, God Mode, world state#573

Open
anadoris007 wants to merge 25 commits into
666ghj:mainfrom
anadoris007:feat/narrative-layer
Open

feat: Narrative Layer — prose generation, characters, God Mode, world state#573
anadoris007 wants to merge 25 commits into
666ghj:mainfrom
anadoris007:feat/narrative-layer

Conversation

@anadoris007
Copy link
Copy Markdown

Summary

Adds a Narrative Layer on top of the existing OASIS simulation: translates agent actions into story prose, tracks extended character state, adds author-controlled intervention (God Mode), and grounds scenes in a persistent world state. Builds toward making MiroFish a creative-writing platform without modifying the OASIS simulation core.

Two sub-features, shipped as independent commits on one branch:

1. Narrative Layer foundation (commits c5cc599d651cb8)

  • Services: action_mapper (13 OASIS actions → narrative verbs), character_engine (6-dim emotional state + per-action deltas), story_store (file-based persistence), narrative_translator (reads actions.jsonl, generates prose via existing LLM client)
  • API: 5 endpoints under /api/narrative/* — full story, per-round story, translate, characters, init characters
  • Frontend: StoryTimelineView.vue + StoryBeat.vue + CharacterCard.vue on /story/:simId
  • Prompt design: punchy, cinematic, dialogue-driven voice — 2–3 paragraphs, <180 words, emotion shown through body/voice

2. God Mode + World State v1 (commits 7d292e24846897)

  • God Mode handlers: inject_event, modify_emotion, kill_character — all audit-logged to world_state.event_log
  • World State: rules, locations (with cinematic atmosphere field), event log — stored in narrative/world_state.json
  • Translator extension: prompt now includes world rules, recent events, locations + atmosphere, character locations; filters dead characters
  • API: 6 endpoints under /api/narrative/world/* and /api/narrative/godmode/*
  • Frontend: GodModeView.vue (with typed-name kill confirmation + preloaded emotion sliders), WorldBuilderView.vue, shared sim-nav across all three views
  • Key design decision: v1 God Mode affects the prose layer, not the OASIS simulation — ships without any OASIS changes

What's deferred (explicit non-goals)

Mid-sim OASIS prompt injection, timeline branching, resurrection, time skip, factions, resources, EPUB/DOCX input, export studio.

Test plan

  • Backend: cd backend && uv run pytest tests/39/39 passing (action mapper, character engine, story store, translator, god mode, world state, e2e pipeline, event injection integration, kill filter integration)
  • Frontend: cd frontend && npm run build — clean build, no errors
  • Flask routing: all 11 new endpoints smoke-tested via test_client, correct status codes (400/404/200)
  • Full spec + plan committed under docs/superpowers/ for review

Architecture docs

  • Spec: docs/superpowers/specs/2026-03-26-narrative-layer-design.md (532 lines, reviewed + approved)
  • Spec: docs/superpowers/specs/2026-04-20-godmode-worldstate-design.md (372 lines, reviewed + approved)
  • Plan: docs/superpowers/plans/2026-03-26-narrative-layer-foundation.md (12 tasks)
  • Plan: docs/superpowers/plans/2026-04-20-godmode-worldstate.md (10 tasks)

🤖 Generated with Claude Code

anadoris007 and others added 25 commits April 20, 2026 20:41
- docs/superpowers/specs/2026-03-26-narrative-layer-design.md
- docs/superpowers/plans/2026-03-26-narrative-layer-foundation.md

Spec extends MiroFish into a creative storytelling platform via a new
narrative layer over the existing OASIS simulation engine.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New narrative package with action_mapper module that maps the 13 OASIS
action types (CREATE_POST, LIKE_POST, REPOST, etc.) to narrative verbs
and interpretations used when generating story prose.

Tests: 4/4 passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three JSON files per simulation under narrative/ subdir:
- story_beats.json: chronological story passages
- translator_state.json: per-platform file offsets
- characters.json: extended character profiles

Tests: 4/4 passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Character profiles now track a six-dimension emotional state (anger,
fear, joy, sadness, trust, surprise) that evolves based on actions
taken. Initial deltas for all 13 OASIS actions chosen as a balanced
baseline (see module docstring for tuning guidance).

Tests: 5/5 passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reads one round's worth of agent actions from the OASIS log file,
tracking file offset so callers can resume across translation calls.
Handles missing files, malformed JSON lines, and non-target rounds
gracefully.

Tests: 3/3 passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Module-level call_llm() wrapper (patchable by tests) drives
PROSE_PROMPT_TEMPLATE rendering with character summaries, actions,
previous beats, and user-selected tone. Prompt tuned for punchy,
cinematic, dialogue-heavy voice.

Tests: 5/5 passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
translate_round() ties together:
  - reading the round's actions from actions.jsonl
  - generating prose via the LLM
  - updating per-character emotional state
  - persisting the beat, characters, and file offset

Full test suite now at 19/19 passing across action mapper, character
engine, story store, and translator.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Five endpoints under /api/narrative/*:
- GET  /story/<sim_id>                — full story so far
- GET  /story/<sim_id>/round/<num>    — single round
- POST /translate                     — translate a round on demand
- GET  /characters/<sim_id>           — roster with emotional state
- POST /characters/<sim_id>/init      — bootstrap from OASIS profiles

Blueprint registered alongside existing graph/simulation/report
blueprints following the established pattern. Smoke-tested via Flask
test client.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Five named exports covering narrative endpoints, following the
existing pattern in frontend/src/api/simulation.js (service +
requestWithRetry + named exports).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New frontend surface for reading generated stories:
- StoryBeat.vue: renders a round's prose with character attribution
- CharacterCard.vue: compact roster card with animated emotion bars
- StoryTimelineView.vue: reading view with Init Characters, Refresh,
  Translate Next Round, and a tone input
- Route: /story/:simulationId

Recently-active characters are highlighted in the roster. Styling uses
the project's cream/brass palette for consistency with existing views.
Frontend builds cleanly (vite build).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Full-pipeline test: fake simulation dir → actions.jsonl → two
translate_round calls → assertions on beat persistence, character
evolution, and translator offset advancement.

Final test suite: 20/20 passing across action mapper, character
engine, story store, translator (read + prose + orchestration), and
E2E.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Minimal scope: 3 God Mode interventions (inject event, modify emotion,
kill character) + 3 World State primitives (locations, rules, event
log). File-based only — no OASIS simulation changes for v1.

Design trade-off: v1 God Mode affects the prose layer, not the
simulation loop. Agents don't "see" injected events; only the narrator
does. This ships faster and fits the creative-authoring use case.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Addresses 3 critical + 6 major + 7 minor issues from spec review:
- §5.0 new: specifies translator loads world_state, id/name keying
  conventions, brace-escape for user text in prompt
- §4.2/§4.3: god_mode interventions audit-log to event_log
- §4.3: kill auto-appends death event for narrative visibility
- §4.4: documents concurrency limitation
- §6.4/§6.5: navigation on all 3 views, typed-name kill confirmation
- §10: expanded non-goals (no-delete, unbounded log, global enforcement)
- §8: adds explicit prompt-inclusion regression test

Spec review: Approved.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
10-task TDD plan covering:
- WorldStateStore (rules, locations, event log)
- Character status field
- 3 God Mode handlers (inject_event, modify_emotion, kill_character)
- Translator prompt extension + dead filter + brace escape
- E2E integration tests for injection and kill
- 6 API endpoints with validation
- 2 Vue views with shared nav + typed-name kill confirmation

Includes 2 marked user-contribution points:
- EVENT_ENFORCEMENT_STRENGTH (soft/medium/hard)
- Location schema fields (minimal vs atmosphere vs time_of_day)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- PROSE_PROMPT_TEMPLATE extended with world_rules, world_events,
  world_locations, and event_enforcement substitution fields
- EVENT_ENFORCEMENT_STRENGTH set to "hard" — opening line MUST
  reference the most recent world event (aligns with existing
  punchy, cinematic voice)
- Character summaries now resolve location id → name for prompts
- translate_round loads world_state and filters dead characters
  from both the roster and the action list before prose generation
- User-supplied strings (rules, events, locations) brace-escaped
  before str.format() to prevent KeyError on stray { or }

Tests: 36/36 passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When Marcus (the only round-2 actor) was killed, generate_prose
short-circuited on empty actions without calling the LLM, so
mock_llm.call_args_list only had one entry. Added Elena as a
second round-2 actor to exercise the filter with a live participant.
Six new endpoints under /api/narrative/*:
- GET  /world/<sim_id>                       — full world state
- POST /world/<sim_id>/rules                 — replace rules list
- POST /world/<sim_id>/locations             — upsert location
- POST /godmode/<sim_id>/inject-event        — inject world event
- POST /godmode/<sim_id>/modify-emotion      — overwrite emotions
- POST /godmode/<sim_id>/kill                — kill character

Validation:
- 400 on missing required fields (description, character_id, rules)
- 400 on invalid round (must be non-negative int or null)
- 404 on character not found (from ValueError in handlers)

Smoke-tested via Flask test client — all 11 response codes correct.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New Vue views: GodModeView (inject event, modify emotions with
  preloaded current values, kill character with typed-name
  confirmation) and WorldBuilderView (rules editor, locations with
  cinematic atmosphere field, event log viewer)
- Shared sim-nav strip across Story/GodMode/World views
- Location schema extended with optional atmosphere field — a short
  mood phrase that anchors the opening visual of every scene set there
- Translator's _format_world_locations surfaces atmosphere to the LLM
  prompt so the field actually influences generation
- Routes added for /godmode/:simId and /world/:simId

Tests: 39/39 passing (added test_location_atmosphere_surfaces_in_prompt).
Frontend builds clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@dosubot dosubot Bot added size:XXL This PR changes 1000+ lines, ignoring generated files. enhancement New feature or request labels Apr 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant