# thoughts.md ## Current Status - Scaffold complete: `charactergarden/` folder structure created per spec section 9 - Core contracts defined in `app/src/types.ts`: Entity, Action, Verb, ValidationResult, StateChange, GameEvent, Turn, Belief, Fact, Affordance, Summary - `docker-compose.yml` created; ollama service gated behind `--profile llm` (not required for MVP) - `.env` / `.env.example` / `.gitignore` in place - Container-first runtime files added: app/frontend Dockerfiles and `.dockerignore`s - **Truth Engine implemented** in `app/src/truthEngine.ts` — pure function, no I/O, no LLM - `validate(actions, worldState)` → ValidationResult - `applyChanges(worldState, changes)` → new WorldState (immutable) - All 8 verbs handled with explicit rejection reasons - `move` now supports a built-in offscene room convention via `createOffsceneRoom()` - `latentEntities.ts` can promote plausible personal items from belief to fact when the actor has carrying context - `db.ts` added with SQLite schema + persistence helpers for `entities`, `events`, `turns`, `beliefs`, and `summaries` - Minimal Fastify server + app pipeline added with seeded world state and fallback parser - Minimal Vite React inspector added for visual boot testing and state inspection ## Current Architecture Decisions - App: Node.js + Fastify + TypeScript - Frontend: React + Vite + TypeScript - Database: better-sqlite3 (synchronous, no ORM) - Ollama is optional; system must work without it (per section 14) - `Event` type renamed `GameEvent` in code to avoid collision with the DOM `Event` global - Latent personal items are gated by facts-derived affordances, not accepted directly from beliefs - The offscene room is represented as a normal room entity with id `offscene` - App and frontend should be run and validated through Docker Compose rather than host-installed Node ## MVP Readiness (2026-04-23) - Estimated completion: ~90% - Completed against spec: - Turn pipeline implemented end-to-end (input -> parse -> validate -> apply -> events -> narration) - Truth Engine is authoritative for state mutation - SQLite persistence for entities/events/turns/beliefs/summaries - Dockerized app + frontend stack, with optional Ollama profile - Minimal inspector UI for state/events/turns - MVP world bounded to 2 rooms and <=3 characters - Remaining to call MVP done: - Run one clean boot smoke pass from current branch and record expected outputs in this file - Add a short "MVP acceptance checklist" section and mark each contract as pass/fail - Tighten fallback parser behavior for known starter prompts and ensure no regression on latent-item turns ## MVP Acceptance Checklist - PASS: Core contracts implemented (Entity, Action, ValidationResult, StateChange, GameEvent) - PASS: Truth Engine is sole authority for state mutation - PASS: Invalid actions produce explicit rejection reasons - PASS: Turn flow wired end-to-end in app service - PASS: SQLite tables for entities/events/turns/beliefs/summaries - PASS: Dockerized app + frontend, no host Node dependency required - PASS: MVP scope bounds respected (2 rooms, <=3 characters, limited verb set) - PASS: Frontend inspector can submit turns and inspect state/events - PASS: Latent personal-item flow works in runtime (`pull out my phone` => promoted + accepted) - PASS: Parser now fails gracefully with explicit rephrase guidance for unknown/underspecified input ## Next Steps 1. Execute and capture a final Docker boot smoke check (`/health`, `/api/state`, `/api/turn` happy path + latent-item turn) 2. Add explicit MVP acceptance checklist results here (contracts, scope limits, runtime constraints) 3. Do a small parser-hardening pass for prompt coverage, then freeze MVP scope 4. Defer non-MVP work (container-entity plausibility, richer narration, full LLM adapter) ## Open Questions - None currently blocking MVP implementation. ## Resolved Decisions (2026-04-23) - Room/location model: keep `location` as an entity attribute that points to another entity id. Rooms remain normal entities (`type: room`) and inventory uses `inventory:`. - MVP initial world: lock to 2 rooms (`garden`, `shed`) and 2 characters (`player`, `groundskeeper`), plus static scene objects (`gate`, `bench`) and built-in `offscene` room. - Latent personal-item plausibility: use actor attributes as the MVP source of truth (`clothed`, `pocket_count`, `has_bag`, `searched_empty`). - Container/worn-item entities: defer to post-MVP. Add this as a planned extension, not a current gate for plausibility. ## Follow-up Implications 1. Keep truth rules keyed to entity `attributes.location` and avoid introducing a separate room-graph subsystem yet. 2. Keep fallback parser and truth engine behavior tuned to the locked MVP world and verb set. 3. Add a backlog item for container-aware plausibility (`worn`, `container`, nested inventory checks) after MVP boot/test milestone. ## Turn Contract (Higher Stack) - End-user turn response should always include: - `narration` (what happened) - `parser_feedback` when intent is unclear or underspecified - accepted/rejected action detail for debugger/inspector views - UX rule: when `parser_feedback` is present, UI should explicitly surface it and encourage rephrasing. ## LLM Prompting Contract (In-Environment) - Added prompt builder in `app/src/llmAdapter.ts` (`buildActionExtractionPrompt`) to enforce world-grounded extraction. - Prompt constraints: - only allowed verbs - only known entity ids from current world snapshot - strict JSON output shape (`actions`, optional `parser_feedback`) - no freeform world mutation outside truth-engine validation ## Session Notes - 2026-04-23: Project started. Scaffold, type contracts, .gitignore, and .env.example created. - 2026-04-23: Truth Engine implemented. Pure validation with per-verb handlers and immutable applyChanges helper. - 2026-04-23: Added facts/affordances + latent entity resolver for improv-style personal items, plus offscene room support. - 2026-04-23: Added SQLite schema module. Host `npm install` is blocked by `better-sqlite3` on Windows Node 25, so runtime validation should happen inside Docker on an LTS Node image instead. - 2026-04-23: Added minimal backend/frontend boot slice so the project can be tested visually through Docker. - 2026-04-23: Runtime smoke check passed with live services (`/health` ok, state returned, `look around` accepted, `pull out my phone` accepted with zero rejections). - 2026-04-23: Fallback parser hardened to return human-readable guidance on unclear input (e.g., unknown intent, missing take target, missing move destination) and suggest concrete rephrasing examples.