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:
485
Implementation_plan.md
Normal file
485
Implementation_plan.md
Normal 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.
|
||||
Reference in New Issue
Block a user