Phase 2: multi-entity scene support (you + host + guest) #2
Reference in New Issue
Block a user
Delete Branch "phase-2"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Phase 2 adds 3-entity scene support to the roleplay engine: a guest bot can be added to a host's chat, with up to 3 entities present (you + host + guest). Built on top of Phase 1's event-sourced architecture — most schema changes are additive.
Note: this PR currently includes all of Phase 1 (PR #1). Once #1 merges, this PR auto-narrows to just Phase 2 deltas (~4.2k lines, 13 commits).
What shipped (13 tasks, 6 waves)
group_nodetable + projector handlers;guest_added/guest_removedevents; relationship-seed classifier ("have they met?")assemble_narrative_promptacceptsguest_id(auto-detected fromchat.guest_bot_id), adds guest activity + group dynamic blocks; per-POV scene close summaries for each present witness; group_node updated on closepost_turnrewrite — addressee detection (substring match), narrative for addressee, multi-witness memory writes, 6-pair state updates, conditional interjection beat from silent witness;regenerate.pymirrors changes (interjection regenerate deferred to 2.5)bot_resetcascades to clearchats.guest_bot_idreferences in other chats, Phase 2 status + 2.5 backlog inCLAUDE.mdArchitecture notes
append_and_applyfor the new event kinds. No projector replay regressions.chat.guest_bot_id is None, all flows reduce exactly to Phase 1 behavior. All 168 Phase 1 tests pass unmodified.asyncio.gather).Test plan
scene_closedfiresguest_bot_idreference clearsPhase 2.5 backlog (tracked in
CLAUDE.md)post_turn(T44 added; T47 fixes root cause — degrade can be removed)ACTIVITIES:block consolidation in promptchat/services/prompt.py:436hardcodeswitness_role="host"regardless of speakerPlan
docs/plans/2026-04-26-v2-phase2-implementation.md(committed inb833589).Idempotent seeder for three sample bots (Maya — coworker slow-burn, Eli — live-in partner, Sam — bartender / new connection). Each is a distinct relational archetype to exercise the system from different angles. Run from repo root: .venv/bin/python scripts/seed_sample_bots.py Re-running skips ids that already exist. After seeding, walk each bot through kickoff parse-and-confirm at /bots/<id>/kickoff.The kickoff parse-and-confirm route was 500-ing intermittently because Hermes-3 + Featherless's response_format={"type":"json_object"} only guarantees JSON output, NOT a particular schema. The model was inventing its own field names (sceneTime, entities, settingDetails) instead of the KickoffParse fields, causing Pydantic validation to fail on both classify() retries. Three changes: 1. Include the Pydantic JSON schema in the system prompt so the model knows exactly which keys to produce. Affects every classify() call (kickoff parse, turn parse, scene-close detect, significance, state-update, scene summarize). Strip ```json fences if the model wraps its output. Bump retries 2 → 3 (model is stochastic; one extra attempt closes most of the remaining gap). 2. parse_kickoff() now passes a default empty KickoffParse so the route degrades to a fillable form instead of 500 when the classifier ultimately fails. The confirm form is the human-in-the-loop; an empty form is strictly better UX than a stack trace. 3. Tests updated: bumped canned-failure arrays from 2 → 3 entries to match the new attempt count; renamed kickoff test from "raises_when_classifier_fails_twice" to "falls_back_to_empty_when_classifier_fails" reflecting the new degraded-but-usable behavior. Verified live with all 3 sample bots (maya/eli/sam) — kickoff route returns 200 across multiple attempts. Full suite: 168 passed.13 tasks across 6 waves (1, 2, 3, 4a, 4b, 5). Designed for parallel subagent execution where file-disjointness allows. Waves 1, 2, 4a, and 5 each contain 2-3 tasks that touch disjoint files and can be dispatched concurrently via the Agent tool with isolation: "worktree". Waves 3 (drawer guest support) and 4b (multi- entity turn flow) are single-task because they touch hot files (_drawer.html, turns.py) that cannot be safely co-modified. Plan covers: - T36: group_node schema + handlers (new migration 0008) - T37: guest_added / guest_removed event handlers (modifies world.py) - T38: relationship-seed service ("have they met?") - T39: interjection classifier service - T40: multi-entity state-update coordinator (6 directed pairs) - T41: multi-witness memory write helper - T42: drawer guest add/remove UI + render - T43: multi-entity prompt assembly (extends T18) - T44: multi-entity turn flow (rewrites post_turn) - T45: multi-entity per-POV summaries on scene close - T46: witness filter cross-coverage tests - T47: bot_reset cascades to guest references - T48: Phase 2 documentation update Plan also documents: - Worktree-per-subagent dispatch pattern using Agent isolation flag - Merge ordering per wave (file-disjointness = conflict-free merges) - Failure recovery (cancel failed parallel task, re-dispatch as solo) - Conflict prevention checklist (verify Files sections disjoint per wave) Tasks file (.tasks.json) carries dependency DAG with `blockedBy` and `parallelGroup` so a future executing-plans run can dispatch correctly. NOT EXECUTING. Plan only.