# CharacterGarden MVP โ€” Strict Architecture ## ๐Ÿง  Core Principle > The system is a **deterministic simulation engine**. > The LLM is **not a source of truth**. It is an **input/output translator only**. --- # ๐Ÿงฉ System Architecture User / LLM Input (Prose) โ†“ [Parser Layer] โ†“ [Normalization Layer] โ†“ [Truth Engine (Validation)] โ†“ [State Mutation Engine] โ†“ [Persistence Layer] โ†“ [LLM Output Generation] --- # ๐Ÿ”’ Non-Negotiable Rules 1. Truth Engine MUST NEVER parse natural language 2. Only structured Actions may mutate world state 3. All mutations must be validated before execution 4. World state is modified ONLY through engine functions 5. LLM output is never trusted without validation 6. Every turn must be fully traceable --- # ๐Ÿ“ฆ Core Data Contracts ## Action (STRICT UNION TYPE) ```ts export type Action = | { type: "move"; actorId: string; targetId: string } | { type: "take"; actorId: string; targetId: string } | { type: "open"; actorId: string; targetId: string } | { type: "close"; actorId: string; targetId: string } | { type: "use"; actorId: string; targetId: string; toolId?: string } | { type: "speak"; actorId: string; content: string } | { type: "introduce"; actorId: string; targetId?: string; metadata?: Record }; ``` โš ๏ธ DO NOT use generic `type: string` actions. --- ## NormalizedAction ```ts export type NormalizedAction = Action; ``` If invalid โ†’ reject BEFORE validation. --- ## ValidationResult ```ts export type ValidationResult = { success: boolean; reasonCode: | "OK" | "NOT_FOUND" | "NOT_PRESENT" | "LOCKED" | "INVALID_TARGET" | "MISSING_REQUIREMENT" | "OUT_OF_TURN" | "UNKNOWN"; message: string; }; ``` --- ## Turn (Traceable Execution Unit) ```ts export type Turn = { id: string; rawText: string; parsedActions: unknown[]; normalizedActions: NormalizedAction[]; validationResults: ValidationResult[]; appliedActions: NormalizedAction[]; timestamp: number; }; ``` --- # ๐ŸŒ World State (STRICT SCHEMA) ```ts export type Entity = { id: string; name: string; locationId?: string; attributes?: Record; }; export type Location = { id: string; name: string; connectedTo: string[]; }; export type WorldState = { entities: Record; locations: Record; inventory: Record; // actorId โ†’ itemIds flags: Record; }; ``` --- # ๐Ÿง  System Layers --- ## 1. Parser Layer (`parser/`) **Responsibility:** - Convert prose โ†’ rough actions - May use LLM **Output:** ```ts unknown[] ``` โš ๏ธ Parser output is NOT trusted. --- ## 2. Normalization Layer (`parser/normalizeActions.ts`) **Responsibility:** - Enforce schema - Resolve references (`he` โ†’ `john`) - Fill missing fields - Reject invalid structures **Input:** ```ts unknown[] ``` **Output:** ```ts NormalizedAction[] ``` --- ## 3. Truth Engine (`engine/validate.ts`) **Responsibility:** - Determine if action is valid - MUST be deterministic - MUST NOT mutate state **Input:** ```ts NormalizedAction + WorldState ``` **Output:** ```ts ValidationResult ``` --- ## 4. Mutation Engine (`engine/apply.ts`) **Responsibility:** - Apply ONLY successful actions - Mutate world state **Input:** ```ts NormalizedAction + WorldState ``` **Output:** ```ts WorldState ``` --- ## 5. Persistence Layer (`storage/`) **Responsibility:** - Store: - world state - turns - history --- # ๐Ÿ”„ Turn Execution Pipeline ```ts function processTurn(rawText: string): Turn { const parsed = parse(rawText); const normalized = normalize(parsed); const validationResults = normalized.map(action => validate(action, worldState) ); const successfulActions = normalized.filter((_, i) => validationResults[i].success ); const newState = apply(successfulActions, worldState); const turn: Turn = { id: generateId(), rawText, parsedActions: parsed, normalizedActions: normalized, validationResults, appliedActions: successfulActions, timestamp: Date.now() }; persist(turn, newState); return turn; } ``` --- # ๐Ÿง  Reference Resolution Rules Handled in normalization layer. Examples: | Input | Output | |-------------|--------------| | "he" | actorId | | "the door" | door_1 | | "my key" | key_owned_by_actor | Must be: - deterministic - context-aware - testable --- # ๐Ÿงช Debug & Traceability (REQUIRED) Every turn MUST store: - raw input - parsed output - normalized actions - validation results - applied actions This enables: - replay - debugging - AI correction --- # โš™๏ธ Initial Action Rules (MVP) ## move - actor must exist - target must be connected location ## take - item must be in same location - item not already owned ## open - target must exist - if locked โ†’ fail unless key present ## introduce - creates entity OR brings into scene --- # ๐Ÿšซ Anti-Patterns (DO NOT DO) โŒ Let LLM mutate state โŒ Store unvalidated actions โŒ Use dynamic/untyped actions โŒ Skip normalization โŒ Combine validation + mutation โŒ Allow hidden side effects --- # ๐Ÿš€ Future Extensions (Planned) - Memory system (vector + summaries) - Belief vs truth separation - Multi-agent turns - Time-based simulation - Rule plugins per scenario - UI action inspector --- # ๐Ÿง  Mental Model This system is: > A deterministic simulation engine with LLM-based I/O NOT: > A chatbot with memory --- # โœ… Definition of Done (MVP) - [ ] Actions fully typed - [ ] Normalization layer implemented - [ ] Validation fully deterministic - [ ] State mutation isolated - [ ] Turn trace persisted - [ ] Simple scenario (door + key) works --- # ๐Ÿ’ฌ Guidance for Copilot When generating code: - Prefer explicit types over generic objects - Avoid dynamic structures - Keep functions pure where possible - Do not introduce hidden state - Follow pipeline strictly