feat: implement processTurn function to handle turn processing and world state updates

refactor: remove legacy types.ts file and update frontend to use new contracts

feat: add applyActions function to manage action application and world state mutation

chore: remove empty .gitkeep file from sqlite data directory

refactor: update frontend App component to align with new API contracts and improve UX

docs: revise project.md to reflect updated architecture and system requirements

docs: update thoughts.md with current status, architecture decisions, and remaining checks
This commit is contained in:
2026-04-24 01:04:17 -04:00
parent 2f6af46c79
commit 998635f542
21 changed files with 1472 additions and 1740 deletions

485
Implementation_plan.md Normal file
View File

@@ -0,0 +1,485 @@
# CharacterGarden — Iterative Implementation Plan
## Copilot Operating Rules
Work in small, reviewable steps.
After every completed step:
1. Update `thoughts.md`
2. Record files changed
3. Record assumptions made
4. Record next step
5. Do not skip ahead unless the current step is complete
Do not redesign the project without updating this plan.
---
# Phase 1 — Contracts First
## Step 1.1 — Create contracts folder
Create:
```txt
app/src/contracts/
```
Add:
```txt
app/src/contracts/action.ts
app/src/contracts/turn.ts
app/src/contracts/validation.ts
app/src/contracts/world.ts
app/src/contracts/entity.ts
```
Goal: all shared types live here.
---
## Step 1.2 — Define Action contract
In `action.ts`:
```ts
export type Action = {
actorId: string;
type: string;
targetId?: string;
locationId?: string;
metadata?: Record<string, unknown>;
};
```
No other action shape should be used.
---
## Step 1.3 — Define ValidationResult contract
In `validation.ts`:
```ts
export type ValidationResult = {
actionIndex: number;
success: boolean;
reason?: string;
message?: string;
};
```
---
## Step 1.4 — Define Turn contract
In `turn.ts`:
```ts
import type { Action } from "./action";
import type { ValidationResult } from "./validation";
export type Turn = {
id: string;
rawText: string;
actions: Action[];
validation: ValidationResult[];
createdAt: number;
};
```
---
## Step 1.5 — Define Entity contract
In `entity.ts`:
```ts
export type Entity = {
id: string;
name: string;
type: string;
attributes: Record<string, unknown>;
};
```
---
## Step 1.6 — Define WorldState contract
In `world.ts`:
```ts
import type { Entity } from "./entity";
export type WorldState = {
id: string;
entities: Record<string, Entity>;
metadata: Record<string, unknown>;
createdAt: number;
};
```
---
# Phase 2 — Enforce Layer Boundaries
## Step 2.1 — Refactor truth engine imports
Update `truthEngine.ts` so it imports:
```ts
import type { Action } from "./contracts/action";
import type { ValidationResult } from "./contracts/validation";
import type { WorldState } from "./contracts/world";
```
Truth engine must only receive structured actions.
---
## Step 2.2 — Remove text parsing from truth engine
Search `truthEngine.ts` for:
* string parsing
* natural language interpretation
* prompt logic
* LLM calls
Move any such logic out.
Truth engine should expose:
```ts
export function validateActions(
actions: Action[],
worldState: WorldState
): ValidationResult[] {
// deterministic validation only
}
```
---
## Step 2.3 — Create parser layer
Create:
```txt
app/src/parser/
app/src/parser/parseTextToActions.ts
```
Function:
```ts
import type { Action } from "../contracts/action";
export function parseTextToActions(text: string): Action[] {
// temporary simple parser
return [];
}
```
For now, returning `[]` is acceptable.
---
## Step 2.4 — Create world state engine
Create:
```txt
app/src/world/
app/src/world/applyActions.ts
```
Function:
```ts
import type { Action } from "../contracts/action";
import type { ValidationResult } from "../contracts/validation";
import type { WorldState } from "../contracts/world";
export function applyActions(
actions: Action[],
results: ValidationResult[],
worldState: WorldState
): WorldState {
// apply only successful actions
return worldState;
}
```
---
# Phase 3 — Build First Deterministic Test Domain
Use a simple door/key room before anything complex.
## Step 3.1 — Seed initial world
Create initial world state:
* actor: `player`
* room: `room_start`
* door: `door_1`
* key: `key_1`
Door starts locked.
Key starts in room.
Player starts in room.
---
## Step 3.2 — Support action types
Truth engine should recognize:
```txt
inspect
take
open
move
```
Unknown action types fail with:
```txt
reason: "unknown_action"
```
---
## Step 3.3 — Validate take action
Rules:
* Actor must exist
* Target must exist
* Target must be in same location
* Target must be takeable
Failure reasons:
* `actor_not_found`
* `target_not_found`
* `not_in_same_location`
* `not_takeable`
---
## Step 3.4 — Validate open action
Rules:
* Actor must exist
* Target must exist
* Target must be openable
* If locked, actor must have matching key
Failure reasons:
* `actor_not_found`
* `target_not_found`
* `not_openable`
* `locked_requires_key`
---
## Step 3.5 — Apply successful take
If `take` succeeds:
* move item into actor inventory
---
## Step 3.6 — Apply successful open
If `open` succeeds:
* set door attribute `open: true`
---
# Phase 4 — Wire Full Turn Processing
## Step 4.1 — Create turn processor
Create:
```txt
app/src/turns/processTurn.ts
```
Function:
```ts
export async function processTurn(rawText: string): Promise<Turn> {
// parse
// validate
// apply
// persist
// return turn
}
```
---
## Step 4.2 — Enforce pipeline order
The turn processor must call:
```txt
parseTextToActions
validateActions
applyActions
persistTurn
```
In that order.
No layer may skip ahead.
---
## Step 4.3 — Add debug response
API should return:
```ts
{
rawText,
actions,
validation,
worldState
}
```
This is for MVP debugging.
---
# Phase 5 — Persistence
## Step 5.1 — Add database tables
Minimum SQLite tables:
```sql
turns
actions
validation_results
entities
world_states
```
---
## Step 5.2 — Persist every turn
Each call to `processTurn` must save:
* raw text
* parsed actions
* validation results
* resulting world state snapshot
---
## Step 5.3 — Add reset endpoint
Add an endpoint to reset world state to seed state.
This is needed for testing.
---
# Phase 6 — LLM Adapter Reintroduction
Only after deterministic flow works.
## Step 6.1 — LLM parser adapter
Add optional LLM parser:
```ts
parseTextToActionsWithLLM(text: string, worldState: WorldState): Promise<Action[]>
```
It must output only valid `Action[]`.
---
## Step 6.2 — LLM narrative adapter
Add:
```ts
generateNarrative(turn: Turn, worldState: WorldState): Promise<string>
```
The narrative adapter may describe results but must not alter them.
---
# Phase 7 — Frontend Debug UI
## Step 7.1 — Show raw text input
User can submit a turn.
---
## Step 7.2 — Show parsed actions
Display action JSON.
---
## Step 7.3 — Show validation results
Display success/failure reasons.
---
## Step 7.4 — Show world state
Display current world state JSON.
---
# MVP Completion Criteria
MVP is complete when this works:
1. User enters: `take key`
2. Parser returns a `take` action
3. Truth engine validates it
4. World state moves key to inventory
5. User enters: `open door`
6. Truth engine verifies key ownership
7. Door becomes open
8. All steps are visible in debug UI
9. All turns are persisted
---
# Do Not Do Yet
Do not implement:
* autonomous agents
* complex memory retrieval
* embeddings
* relationship simulation
* long-term summaries
* branching timelines
Until the deterministic MVP is working.