mirror of
https://github.com/Narcooo/inkos
synced 2026-04-21 14:37:16 +00:00
chore: untrack docs/ and add to .gitignore
This commit is contained in:
parent
b23f5bf46c
commit
44b5e52e50
6 changed files with 1 additions and 1224 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -17,3 +17,4 @@ docs/
|
|||
novel-to-comic-research.docx
|
||||
.pnpm-store/
|
||||
tmp/
|
||||
docs/
|
||||
|
|
|
|||
|
|
@ -1,171 +0,0 @@
|
|||
# v0.6.3 Hardening Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Close the remaining 0.6.3 safety gaps around agent tool misuse, final-content paragraph checks, duplicate chapter titles, migration hints, and rewrite regression coverage.
|
||||
|
||||
**Architecture:** Keep the current v2 pipeline, but tighten the remaining soft spots at the edges. Add deterministic guards in the agent dispatch layer, run final-content validation at the last safe point before persist, surface migration status in user-facing CLI flows, and add targeted regressions for the exact user-visible failures.
|
||||
|
||||
**Tech Stack:** TypeScript, Vitest, Commander CLI
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Guard the remaining agent path
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/core/src/__tests__/pipeline-agent.test.ts`
|
||||
- Modify: `packages/core/src/pipeline/agent.ts`
|
||||
|
||||
**Step 1: Write the failing tests**
|
||||
|
||||
Add tests that prove:
|
||||
- `write_full_pipeline` is rejected when the chapter sequence is inconsistent
|
||||
- `write_truth_file` cannot use `current_state.md` to mutate progress
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm vitest run src/__tests__/pipeline-agent.test.ts
|
||||
```
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Reuse the same guard logic currently applied to `write_draft` and block `write_full_pipeline` before it calls `writeNextChapter()`.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm vitest run src/__tests__/pipeline-agent.test.ts
|
||||
```
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/core/src/pipeline/agent.ts packages/core/src/__tests__/pipeline-agent.test.ts
|
||||
git commit -m "fix: guard write_full_pipeline in agent dispatch"
|
||||
```
|
||||
|
||||
### Task 2: Validate final persisted chapter shape
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/core/src/__tests__/post-write-validator.test.ts`
|
||||
- Modify: `packages/core/src/__tests__/pipeline-runner.test.ts`
|
||||
- Modify: `packages/core/src/agents/post-write-validator.ts`
|
||||
- Modify: `packages/core/src/pipeline/runner.ts`
|
||||
|
||||
**Step 1: Write the failing tests**
|
||||
|
||||
Add tests that prove:
|
||||
- final persisted content can trigger `段落过碎` / `连续短段`
|
||||
- duplicate titles are handled as a real final-stage action, not a passive warning
|
||||
|
||||
**Step 2: Run tests to verify they fail**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm vitest run src/__tests__/post-write-validator.test.ts src/__tests__/pipeline-runner.test.ts
|
||||
```
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Export a reusable final paragraph-shape detector and run it on `finalContent` before persist. Upgrade duplicate-title handling from a passive warning to an explicit final-stage resolution path.
|
||||
|
||||
**Step 4: Run tests to verify they pass**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm vitest run src/__tests__/post-write-validator.test.ts src/__tests__/pipeline-runner.test.ts
|
||||
```
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/core/src/agents/post-write-validator.ts packages/core/src/pipeline/runner.ts packages/core/src/__tests__/post-write-validator.test.ts packages/core/src/__tests__/pipeline-runner.test.ts
|
||||
git commit -m "fix: harden final content validation and title handling"
|
||||
```
|
||||
|
||||
### Task 3: Surface migration status in user-facing CLI flows
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/cli/src/__tests__/cli-integration.test.ts`
|
||||
- Modify: `packages/cli/src/commands/status.ts`
|
||||
- Modify: `packages/cli/src/commands/write.ts`
|
||||
- Modify: `packages/cli/src/utils.ts`
|
||||
|
||||
**Step 1: Write the failing tests**
|
||||
|
||||
Add tests that prove:
|
||||
- `inkos status` shows a migration hint for legacy books
|
||||
- `inkos write next` warns before auto-migrating a legacy book
|
||||
|
||||
**Step 2: Run tests to verify they fail**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm vitest run src/__tests__/cli-integration.test.ts
|
||||
```
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Add a shared CLI helper that detects legacy pre-v0.6 books and emits a clear hint in `status` and `write next`.
|
||||
|
||||
**Step 4: Run tests to verify they pass**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm vitest run src/__tests__/cli-integration.test.ts
|
||||
```
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/cli/src/utils.ts packages/cli/src/commands/status.ts packages/cli/src/commands/write.ts packages/cli/src/__tests__/cli-integration.test.ts
|
||||
git commit -m "feat: show migration hints in status and write flows"
|
||||
```
|
||||
|
||||
### Task 4: Verify rewrite from user path
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/cli/src/__tests__/cli-integration.test.ts`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add a CLI-level regression covering:
|
||||
- create 3 chapters
|
||||
- rewrite chapter 2
|
||||
- assert next chapter number is 2 again
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm vitest run src/__tests__/cli-integration.test.ts
|
||||
```
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Prefer no production change unless the new CLI regression exposes a real gap.
|
||||
|
||||
**Step 4: Run tests to verify it passes**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm vitest run src/__tests__/cli-integration.test.ts
|
||||
```
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/cli/src/__tests__/cli-integration.test.ts
|
||||
git commit -m "test: cover rewrite flow from CLI path"
|
||||
```
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
# Book Create Atomic Init Design
|
||||
|
||||
**Goal:** Eliminate half-initialized books by making `inkos book create` fail-closed: foundation generation must either produce a complete book or leave no book directory behind.
|
||||
|
||||
## Problem
|
||||
|
||||
Current `book create` is not atomic:
|
||||
|
||||
- `PipelineRunner.initBook()` writes `book.json` before foundation generation.
|
||||
- `ArchitectAgent.parseSections()` silently substitutes placeholder text when a required section is missing.
|
||||
- CLI treats an existing but incomplete directory as resumable state.
|
||||
|
||||
This creates dirty `books/<bookId>/` directories with partial files. Re-running create on the same title can then behave unpredictably.
|
||||
|
||||
## Design
|
||||
|
||||
### 1. Stage to a temporary book directory
|
||||
|
||||
`initBook()` should build the full book in a temporary directory under `books/`:
|
||||
|
||||
- write `book.json`
|
||||
- generate foundation
|
||||
- write foundation files
|
||||
- initialize control docs
|
||||
- create chapter index
|
||||
- snapshot chapter 0
|
||||
|
||||
Only after all of that succeeds should the temp directory be renamed to the final `books/<bookId>/`.
|
||||
|
||||
If any step fails, the temp directory is deleted.
|
||||
|
||||
### 2. Treat missing Architect sections as hard failure
|
||||
|
||||
`ArchitectAgent.parseSections()` should no longer emit placeholder text like:
|
||||
|
||||
- `[book_rules 生成失败,需要重新生成]`
|
||||
|
||||
Missing required sections should throw an error immediately. This prevents malformed foundations from being persisted as if they were valid content.
|
||||
|
||||
### 3. Remove resumable incomplete-init semantics
|
||||
|
||||
CLI should no longer “resume incomplete book creation”.
|
||||
|
||||
If `books/<bookId>/` exists:
|
||||
|
||||
- if it looks complete, `book create` should fail with “already exists”
|
||||
- if it looks incomplete, it should be treated as stale partial state and removed before a fresh atomic create starts
|
||||
|
||||
This keeps the behavior simple:
|
||||
|
||||
- success => complete book exists
|
||||
- failure => no book directory remains
|
||||
|
||||
## Scope
|
||||
|
||||
In scope:
|
||||
|
||||
- `book create`
|
||||
- `PipelineRunner.initBook()`
|
||||
- Architect foundation parsing
|
||||
- stale incomplete directory cleanup
|
||||
- regression tests
|
||||
|
||||
Out of scope:
|
||||
|
||||
- `initFanficBook()` atomic staging
|
||||
- state-validator robustness
|
||||
- interactive-fiction work
|
||||
|
||||
## Validation
|
||||
|
||||
Add regressions for:
|
||||
|
||||
1. `ArchitectAgent` throws when a required section is missing.
|
||||
2. `PipelineRunner.initBook()` leaves no final book directory when foundation generation fails.
|
||||
3. CLI `book create` removes stale incomplete directories and does not keep a dirty directory when create fails downstream.
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
# Book Create Atomic Init Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Make `book create` atomic so malformed Architect output or failed initialization never leaves a half-built book directory behind.
|
||||
|
||||
**Architecture:** Stage new books in a temporary directory, require Architect to return all mandatory sections, and delete stale incomplete directories instead of resuming them. Promote the staged directory into place only after all initialization steps succeed.
|
||||
|
||||
**Tech Stack:** TypeScript, Vitest, Commander CLI, Node `fs/promises`
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Lock down Architect missing-section failure
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/core/src/__tests__/architect.test.ts`
|
||||
- Modify: `packages/core/src/agents/architect.ts`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add a test showing `generateFoundation()` rejects when `book_rules` is missing from the LLM response.
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `pnpm vitest run src/__tests__/architect.test.ts -t "throws when a required foundation section is missing"`
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Change `parseSections()` to throw on missing required sections instead of returning placeholder content.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run the same Vitest command and confirm it passes.
|
||||
|
||||
### Task 2: Lock down atomic init rollback
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/core/src/__tests__/pipeline-runner.test.ts`
|
||||
- Modify: `packages/core/src/state/manager.ts`
|
||||
- Modify: `packages/core/src/pipeline/runner.ts`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add a regression showing `initBook()` leaves no final `books/<bookId>/` when foundation generation fails.
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `pnpm vitest run src/__tests__/pipeline-runner.test.ts -t "cleans staged files when initBook fails before foundation is complete"`
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Implement staged initialization in a temporary directory, then rename into place only after success. Add helper methods in `StateManager` as needed for path-based control-doc/index/snapshot writes.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run the same Vitest command and confirm it passes.
|
||||
|
||||
### Task 3: Lock down stale incomplete-directory cleanup at CLI level
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/cli/src/__tests__/cli-integration.test.ts`
|
||||
- Modify: `packages/cli/src/commands/book.ts`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add a CLI regression where a stale incomplete `books/<bookId>/` exists before `book create`, and verify the command does not preserve that stale directory when create later fails.
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `pnpm vitest run src/__tests__/cli-integration.test.ts -t "removes stale incomplete book directories before retrying create"`
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Replace “resume incomplete book creation” behavior with stale-directory cleanup plus fresh atomic create.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run the same Vitest command and confirm it passes.
|
||||
|
||||
### Task 4: Run focused verification
|
||||
|
||||
**Files:**
|
||||
- No production changes expected
|
||||
|
||||
**Step 1: Run targeted suite**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm vitest run src/__tests__/architect.test.ts src/__tests__/pipeline-runner.test.ts
|
||||
```
|
||||
|
||||
in `packages/core`, and:
|
||||
|
||||
```bash
|
||||
pnpm vitest run src/__tests__/cli-integration.test.ts
|
||||
```
|
||||
|
||||
in `packages/cli`.
|
||||
|
||||
**Step 2: Run typecheck**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm typecheck
|
||||
```
|
||||
|
||||
in `packages/core` and `packages/cli`.
|
||||
|
||||
**Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add docs/plans/2026-03-29-book-create-atomic-init-design.md docs/plans/2026-03-29-book-create-atomic-init-implementation-plan.md packages/core/src/agents/architect.ts packages/core/src/pipeline/runner.ts packages/core/src/state/manager.ts packages/core/src/__tests__/architect.test.ts packages/core/src/__tests__/pipeline-runner.test.ts packages/cli/src/commands/book.ts packages/cli/src/__tests__/cli-integration.test.ts
|
||||
git commit -m "fix: make book creation fail closed"
|
||||
```
|
||||
|
|
@ -1,356 +0,0 @@
|
|||
# Interactive Fiction V1 Design
|
||||
|
||||
**Goal:** Add a first-class interactive-fiction mode to InkOS by introducing chapter-end branching while preserving the existing chapter-based writing, audit, revise, truth-file, and runtime-state pipeline.
|
||||
|
||||
## Summary
|
||||
|
||||
Interactive fiction v1 should not replace InkOS's chapter-writing core. It should add a branch system around that core.
|
||||
|
||||
The recommended v1 shape is:
|
||||
|
||||
- chapters remain the unit of generation
|
||||
- each completed chapter can produce 2-4 grounded reader choices
|
||||
- each choice creates a persistent branch node
|
||||
- only the selected branch continues generating new chapters
|
||||
- unselected branches remain dormant but recoverable later
|
||||
|
||||
This gives InkOS real branching narrative structure without forcing a rewrite into a node-by-node text-adventure engine.
|
||||
|
||||
## Why This Shape
|
||||
|
||||
### Do not rewrite the chapter engine first
|
||||
|
||||
InkOS already has valuable infrastructure:
|
||||
|
||||
- chapter writing
|
||||
- revise / audit
|
||||
- truth-file rebuild
|
||||
- runtime state
|
||||
- snapshots / rewrite / rollback
|
||||
- export / review / eval
|
||||
|
||||
If v1 changes the smallest unit from "chapter" to "scene node", all of those systems become unstable at once.
|
||||
|
||||
So the right heavy refactor is:
|
||||
|
||||
- rebuild branching
|
||||
- rebuild branch state management
|
||||
- keep the chapter-writing engine intact
|
||||
|
||||
### Why not free-form player input first
|
||||
|
||||
Free-form actions look attractive, but they explode ambiguity:
|
||||
|
||||
- action parsing
|
||||
- branch normalization
|
||||
- invalid-action handling
|
||||
- state mutation safety
|
||||
- prompt drift
|
||||
|
||||
Reader-choice branching gives InkOS a constrained interaction surface first. Later, free-form player input can be added as a higher-risk edge type on top of the same branch tree.
|
||||
|
||||
## Narrative Mode
|
||||
|
||||
Add a new book-level mode in `book.json`:
|
||||
|
||||
- `narrativeMode: "linear" | "interactive-tree"`
|
||||
|
||||
Default remains `linear`.
|
||||
|
||||
Interactive branching behavior only activates when `narrativeMode === "interactive-tree"`.
|
||||
|
||||
## Core Model
|
||||
|
||||
### 1. Branch Tree
|
||||
|
||||
Create a new persistent artifact:
|
||||
|
||||
- `story/interactive/branch-tree.json`
|
||||
|
||||
This is the source of truth for interactive structure.
|
||||
|
||||
It contains:
|
||||
|
||||
- book-level metadata
|
||||
- active branch pointer
|
||||
- branch nodes
|
||||
- branch edges / choices
|
||||
|
||||
### 2. Branch Nodes
|
||||
|
||||
A branch node represents a recoverable narrative state, not necessarily a fully written branch.
|
||||
|
||||
Recommended fields:
|
||||
|
||||
- `nodeId`
|
||||
- `parentNodeId`
|
||||
- `sourceChapterId`
|
||||
- `sourceChapterNumber`
|
||||
- `branchDepth`
|
||||
- `branchLabel`
|
||||
- `status: active | awaiting-choice | dormant | completed`
|
||||
- `snapshotRef`
|
||||
- `selectedChoiceId`
|
||||
- `chapterIds: string[]`
|
||||
- `displayPath`
|
||||
|
||||
Important meaning:
|
||||
|
||||
- a node is the point from which writing can continue
|
||||
- multiple child nodes may initially share the same chapter-end snapshot
|
||||
- only the active node is projected into live truth files
|
||||
|
||||
### 3. Branch Choices
|
||||
|
||||
Choices are explicit edges between nodes.
|
||||
|
||||
Recommended fields:
|
||||
|
||||
- `choiceId`
|
||||
- `fromNodeId`
|
||||
- `toNodeId`
|
||||
- `label`
|
||||
- `intent`
|
||||
- `immediateGoal`
|
||||
- `expectedCost`
|
||||
- `expectedRisk`
|
||||
- `hookPressure`
|
||||
- `characterPressure`
|
||||
- `tone`
|
||||
- `selected: boolean`
|
||||
|
||||
Choices are not cosmetic strings. They are structured next-chapter directions.
|
||||
|
||||
## Chapter Identity
|
||||
|
||||
### Internal IDs
|
||||
|
||||
Use globally unique internal chapter IDs for storage.
|
||||
|
||||
Do not try to make each branch own an independent global chapter-number namespace.
|
||||
|
||||
Recommended split:
|
||||
|
||||
- internal: globally unique chapter id / monotonic storage number
|
||||
- display: branch-local sequence index for UX
|
||||
|
||||
Why:
|
||||
|
||||
- preserves existing storage, export, review, and audit assumptions
|
||||
- avoids collisions across branches
|
||||
- lets linear tools keep working with minimal change
|
||||
|
||||
## Snapshot Strategy
|
||||
|
||||
Reuse the existing snapshot system.
|
||||
|
||||
After chapter `N` finishes:
|
||||
|
||||
- current state is snapshotted exactly once
|
||||
- all generated child branch nodes reference that same snapshot
|
||||
- no child branch pre-generates chapter `N+1`
|
||||
|
||||
So for a chapter with choices A/B/C:
|
||||
|
||||
- chapter 12 ends
|
||||
- snapshot 12 exists
|
||||
- child nodes A/B/C all point to snapshot 12
|
||||
- user selects B
|
||||
- only then does branch B generate its next chapter
|
||||
|
||||
This keeps branching persistent without duplicating a full book state tree at choice generation time.
|
||||
|
||||
## Choice Generation
|
||||
|
||||
Choice generation should be its own step, not something the writer casually appends.
|
||||
|
||||
### ChoiceGenerator
|
||||
|
||||
Inputs:
|
||||
|
||||
- current chapter content
|
||||
- active branch runtime state
|
||||
- current focus
|
||||
- planner/composer intent
|
||||
- active hooks / dormant pressure
|
||||
- character pressure
|
||||
|
||||
Outputs:
|
||||
|
||||
- 2-4 structured choices
|
||||
|
||||
### ChoiceAuditor
|
||||
|
||||
Audits choice quality:
|
||||
|
||||
- choices must be meaningfully different
|
||||
- no obviously fake branch set
|
||||
- no nonsense or "bad joke" option
|
||||
- choices must be grounded in chapter-end state
|
||||
|
||||
### Recommended choice policy
|
||||
|
||||
Prefer:
|
||||
|
||||
- 1 lower-risk / stabilizing option
|
||||
- 1 higher-risk / high-payoff option
|
||||
- 1 relationship / emotional option
|
||||
- optional 4th only if naturally justified
|
||||
|
||||
This avoids both "three paraphrases of the same branch" and gimmicky fake interactivity.
|
||||
|
||||
## Truth Files And Runtime State
|
||||
|
||||
The live truth files for an interactive book represent the active branch only.
|
||||
|
||||
That means files like:
|
||||
|
||||
- `current_state.md`
|
||||
- `pending_hooks.md`
|
||||
- `chapter_summaries.md`
|
||||
|
||||
are branch-local projections, not universal whole-book truth.
|
||||
|
||||
Persistent cross-branch truth is stored in:
|
||||
|
||||
- `branch-tree.json`
|
||||
- snapshots
|
||||
- chapter lineage attached to branch nodes
|
||||
|
||||
### Branch switch behavior
|
||||
|
||||
Switching branch should perform:
|
||||
|
||||
1. restore the referenced snapshot
|
||||
2. restore branch-local projected state
|
||||
3. update active branch pointer
|
||||
4. continue writing from that branch
|
||||
|
||||
The branch tree is canonical. The live markdown truth files are the active projection.
|
||||
|
||||
## CLI Flow
|
||||
|
||||
Keep `write next`, but add branch control commands.
|
||||
|
||||
Recommended commands:
|
||||
|
||||
- `inkos branch choices`
|
||||
- `inkos branch choose <choice-id>`
|
||||
- `inkos branch tree`
|
||||
- `inkos branch switch <node-id>`
|
||||
|
||||
### Main loop
|
||||
|
||||
1. `write next`
|
||||
2. chapter completes
|
||||
3. choice generation runs
|
||||
4. current node becomes `awaiting-choice`
|
||||
5. user inspects choices
|
||||
6. user chooses one
|
||||
7. selected child node becomes active
|
||||
8. next `write next` continues that branch
|
||||
|
||||
### Important gating rule
|
||||
|
||||
If the active node is `awaiting-choice`, `write next` must refuse to continue until a branch is selected.
|
||||
|
||||
This prevents accidental silent continuation down an undefined path.
|
||||
|
||||
## Compatibility Rules
|
||||
|
||||
### Linear books
|
||||
|
||||
Linear books should behave exactly as today.
|
||||
|
||||
### Interactive books
|
||||
|
||||
Interactive books reuse:
|
||||
|
||||
- writer
|
||||
- reviser
|
||||
- continuity auditor
|
||||
- state validator
|
||||
- snapshots
|
||||
- rewrite
|
||||
|
||||
But commands operate on the active branch by default.
|
||||
|
||||
Later versions can add `--branch`, but v1 should keep default behavior simple.
|
||||
|
||||
## Export And Review
|
||||
|
||||
Do not solve full branch-aware export in v1.
|
||||
|
||||
V1 export and review policy should be:
|
||||
|
||||
- default export = active branch only
|
||||
- default review = active branch only
|
||||
- tree visualization = separate command / future enhancement
|
||||
|
||||
This keeps v1 bounded.
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Branch tree missing or corrupt
|
||||
|
||||
- interactive commands fail closed
|
||||
- linear commands remain unaffected
|
||||
|
||||
### Snapshot missing for target node
|
||||
|
||||
- branch switch fails
|
||||
- active branch remains unchanged
|
||||
|
||||
### Choice generation fails
|
||||
|
||||
Recommended v1 behavior:
|
||||
|
||||
- chapter is still saved
|
||||
- node is marked `awaiting-choice-generation`
|
||||
- user can retry choice generation explicitly
|
||||
|
||||
Do not lose the chapter because choice generation failed.
|
||||
|
||||
### Invalid choose
|
||||
|
||||
- reject with a clear error
|
||||
- do not mutate branch state
|
||||
|
||||
## Non-Goals For V1
|
||||
|
||||
Out of scope:
|
||||
|
||||
- free-form player action parsing
|
||||
- scene/node-level engine
|
||||
- branch-aware EPUB for all paths
|
||||
- auto-generating all child branches in parallel
|
||||
- full interactive UI
|
||||
- branch-local manga export
|
||||
|
||||
These belong to v2+.
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
Minimum required regressions:
|
||||
|
||||
1. interactive book writes one chapter and persists choice nodes
|
||||
2. unselected choices persist as dormant branch nodes with shared snapshot refs
|
||||
3. `write next` refuses when active node awaits choice
|
||||
4. `branch choose` activates the selected child branch
|
||||
5. `branch switch` restores the correct snapshot
|
||||
6. linear books remain unaffected
|
||||
|
||||
## Recommendation
|
||||
|
||||
Build interactive fiction v1 as a chapter-tree system:
|
||||
|
||||
- branch-heavy
|
||||
- stateful
|
||||
- recoverable
|
||||
- chapter-based
|
||||
|
||||
Do not build a free-form adventure engine first.
|
||||
|
||||
Do not rebuild InkOS around scene nodes in v1.
|
||||
|
||||
The right first step is a formal branch tree wrapped around the existing chapter pipeline.
|
||||
|
|
@ -1,505 +0,0 @@
|
|||
# Interactive Fiction V1 Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Add an `interactive-tree` narrative mode where chapter-end reader choices create persistent branch nodes, while keeping InkOS's chapter-writing pipeline intact.
|
||||
|
||||
**Architecture:** Reuse the existing chapter generation pipeline and snapshots. Add a branch-tree persistence layer, branch-aware state activation, chapter-end choice generation, and CLI branch commands. Keep live truth files as the active-branch projection only.
|
||||
|
||||
**Tech Stack:** TypeScript, Commander CLI, Vitest, existing InkOS pipeline/state/snapshot system
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Add the narrative mode model
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/core/src/models/book.ts`
|
||||
- Test: `packages/core/src/__tests__/fanfic-models.test.ts` or a new book-model test
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add a test that parses:
|
||||
|
||||
- `narrativeMode: "linear"`
|
||||
- `narrativeMode: "interactive-tree"`
|
||||
|
||||
and rejects unknown values.
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `pnpm vitest run src/__tests__/...`
|
||||
|
||||
Expected: FAIL because `interactive-tree` is not yet allowed.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Add:
|
||||
|
||||
- `NarrativeModeSchema`
|
||||
- `narrativeMode` on `BookConfigSchema`
|
||||
- default to `linear`
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run the targeted Vitest file again.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/core/src/models/book.ts packages/core/src/__tests__/...
|
||||
git commit -m "feat: add interactive narrative mode"
|
||||
```
|
||||
|
||||
### Task 2: Define branch-tree persistence schema
|
||||
|
||||
**Files:**
|
||||
- Create: `packages/core/src/models/interactive-fiction.ts`
|
||||
- Test: `packages/core/src/__tests__/interactive-fiction-models.test.ts`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add schema tests for:
|
||||
|
||||
- branch tree metadata
|
||||
- branch nodes
|
||||
- branch choices
|
||||
- `activeNodeId`
|
||||
- shared `snapshotRef`
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run the new test file.
|
||||
|
||||
Expected: FAIL because schema file does not exist.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Create schema/types for:
|
||||
|
||||
- tree metadata
|
||||
- node status
|
||||
- choice record
|
||||
- branch node
|
||||
- branch tree
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run the targeted schema test.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/core/src/models/interactive-fiction.ts packages/core/src/__tests__/interactive-fiction-models.test.ts
|
||||
git commit -m "feat: define branch tree schemas"
|
||||
```
|
||||
|
||||
### Task 3: Add state-manager helpers for interactive storage
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/core/src/state/manager.ts`
|
||||
- Test: `packages/core/src/__tests__/state-manager.test.ts`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add tests for helpers that:
|
||||
|
||||
- load/save `story/interactive/branch-tree.json`
|
||||
- initialize an empty root branch
|
||||
- return the active node
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run the targeted tests.
|
||||
|
||||
Expected: FAIL because helper methods do not exist.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Add helpers such as:
|
||||
|
||||
- `interactiveDir(bookId)`
|
||||
- `loadBranchTree(bookId)`
|
||||
- `saveBranchTree(bookId, tree)`
|
||||
- `ensureInteractiveTree(bookId)`
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run the targeted state-manager tests.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/core/src/state/manager.ts packages/core/src/__tests__/state-manager.test.ts
|
||||
git commit -m "feat: add interactive branch tree persistence helpers"
|
||||
```
|
||||
|
||||
### Task 4: Initialize interactive books correctly at create time
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/core/src/pipeline/runner.ts`
|
||||
- Modify: `packages/cli/src/commands/book.ts`
|
||||
- Test: `packages/core/src/__tests__/pipeline-runner.test.ts`
|
||||
- Test: `packages/cli/src/__tests__/cli-integration.test.ts`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add tests that:
|
||||
|
||||
- create an interactive book
|
||||
- verify `branch-tree.json` exists
|
||||
- verify the tree contains a root node and active pointer
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run the targeted test files.
|
||||
|
||||
Expected: FAIL because create flow does not initialize interactive metadata.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
When `narrativeMode === "interactive-tree"`:
|
||||
|
||||
- create interactive storage during book initialization
|
||||
- seed a root branch node
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run the targeted tests again.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/core/src/pipeline/runner.ts packages/cli/src/commands/book.ts packages/core/src/__tests__/pipeline-runner.test.ts packages/cli/src/__tests__/cli-integration.test.ts
|
||||
git commit -m "feat: initialize interactive books with root branch tree"
|
||||
```
|
||||
|
||||
### Task 5: Add chapter-end choice generation models and parser
|
||||
|
||||
**Files:**
|
||||
- Create: `packages/core/src/agents/choice-generator.ts`
|
||||
- Create: `packages/core/src/agents/choice-auditor.ts`
|
||||
- Test: `packages/core/src/__tests__/choice-generator.test.ts`
|
||||
- Test: `packages/core/src/__tests__/choice-auditor.test.ts`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add tests that require generated choices to include:
|
||||
|
||||
- `label`
|
||||
- `intent`
|
||||
- `immediateGoal`
|
||||
- `expectedCost`
|
||||
- `expectedRisk`
|
||||
- `hookPressure`
|
||||
- `characterPressure`
|
||||
- `tone`
|
||||
|
||||
Also add a failure case for near-duplicate choices.
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run both targeted test files.
|
||||
|
||||
Expected: FAIL because these agents do not exist.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Implement:
|
||||
|
||||
- choice generation prompt/output parsing
|
||||
- choice auditing for duplicate/fake branches
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run targeted tests again.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/core/src/agents/choice-generator.ts packages/core/src/agents/choice-auditor.ts packages/core/src/__tests__/choice-generator.test.ts packages/core/src/__tests__/choice-auditor.test.ts
|
||||
git commit -m "feat: add interactive choice generation agents"
|
||||
```
|
||||
|
||||
### Task 6: Persist branch children after chapter completion
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/core/src/pipeline/runner.ts`
|
||||
- Modify: `packages/core/src/state/manager.ts`
|
||||
- Test: `packages/core/src/__tests__/pipeline-runner.test.ts`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add a test for interactive mode where:
|
||||
|
||||
- `writeNextChapter()` saves chapter 1
|
||||
- generates 2-4 choices
|
||||
- creates child nodes
|
||||
- all child nodes reference the same `snapshotRef`
|
||||
- active node becomes `awaiting-choice`
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run the targeted runner test.
|
||||
|
||||
Expected: FAIL because chapters do not yet emit branch nodes.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
In interactive mode, after successful chapter persistence:
|
||||
|
||||
- run choice generation
|
||||
- run choice audit
|
||||
- create child nodes and choices
|
||||
- store them in the branch tree
|
||||
- leave only one active pointer
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run the targeted runner test.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/core/src/pipeline/runner.ts packages/core/src/state/manager.ts packages/core/src/__tests__/pipeline-runner.test.ts
|
||||
git commit -m "feat: persist branch choices after interactive chapters"
|
||||
```
|
||||
|
||||
### Task 7: Block write-next when a choice is pending
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/core/src/pipeline/runner.ts`
|
||||
- Modify: `packages/core/src/pipeline/agent.ts`
|
||||
- Test: `packages/core/src/__tests__/pipeline-runner.test.ts`
|
||||
- Test: `packages/core/src/__tests__/pipeline-agent.test.ts`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add tests that:
|
||||
|
||||
- refuse `writeNextChapter()` on interactive books when active node is `awaiting-choice`
|
||||
- refuse agent `write_draft` / `write_full_pipeline` in the same state
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run the targeted tests.
|
||||
|
||||
Expected: FAIL because write flow still continues linearly.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Add fail-closed guards before chapter generation starts.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run targeted tests again.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/core/src/pipeline/runner.ts packages/core/src/pipeline/agent.ts packages/core/src/__tests__/pipeline-runner.test.ts packages/core/src/__tests__/pipeline-agent.test.ts
|
||||
git commit -m "fix: require branch choice before continuing interactive writing"
|
||||
```
|
||||
|
||||
### Task 8: Add CLI branch commands
|
||||
|
||||
**Files:**
|
||||
- Create: `packages/cli/src/commands/branch.ts`
|
||||
- Modify: `packages/cli/src/index.ts`
|
||||
- Test: `packages/cli/src/__tests__/cli-integration.test.ts`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add CLI integration tests for:
|
||||
|
||||
- `inkos branch tree`
|
||||
- `inkos branch choices`
|
||||
- `inkos branch choose <choice-id>`
|
||||
- `inkos branch switch <node-id>`
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run the targeted CLI test(s).
|
||||
|
||||
Expected: FAIL because `branch` command does not exist.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Add a new CLI command group that:
|
||||
|
||||
- lists current branch tree
|
||||
- shows pending choices
|
||||
- selects a choice
|
||||
- switches active node
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run targeted CLI tests again.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/cli/src/commands/branch.ts packages/cli/src/index.ts packages/cli/src/__tests__/cli-integration.test.ts
|
||||
git commit -m "feat: add interactive branch CLI commands"
|
||||
```
|
||||
|
||||
### Task 9: Restore the correct snapshot on branch switch
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/core/src/state/manager.ts`
|
||||
- Modify: `packages/core/src/pipeline/runner.ts`
|
||||
- Test: `packages/core/src/__tests__/state-manager.test.ts`
|
||||
- Test: `packages/core/src/__tests__/pipeline-runner.test.ts`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add tests that switching to another branch:
|
||||
|
||||
- restores the referenced snapshot
|
||||
- reprojects active branch truth files
|
||||
- leaves other branch nodes unchanged
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run the targeted tests.
|
||||
|
||||
Expected: FAIL because switch only updates metadata or is not implemented.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Implement branch activation as:
|
||||
|
||||
- restore snapshot
|
||||
- mark branch active
|
||||
- persist active branch pointer
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run targeted tests again.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/core/src/state/manager.ts packages/core/src/pipeline/runner.ts packages/core/src/__tests__/state-manager.test.ts packages/core/src/__tests__/pipeline-runner.test.ts
|
||||
git commit -m "feat: restore snapshots when switching interactive branches"
|
||||
```
|
||||
|
||||
### Task 10: Keep linear-mode behavior unchanged
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/core/src/__tests__/pipeline-runner.test.ts`
|
||||
- Modify: `packages/cli/src/__tests__/cli-integration.test.ts`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add regressions proving:
|
||||
|
||||
- linear books still write chapters exactly as before
|
||||
- no branch metadata is required for linear books
|
||||
- branch commands reject linear books cleanly
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run the targeted tests.
|
||||
|
||||
Expected: FAIL because interactive checks leak into linear mode or commands are too permissive.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Tighten mode checks so interactive behavior is gated strictly by `narrativeMode`.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run targeted tests again.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/core/src/__tests__/pipeline-runner.test.ts packages/cli/src/__tests__/cli-integration.test.ts
|
||||
git commit -m "test: lock linear compatibility for interactive mode"
|
||||
```
|
||||
|
||||
### Task 11: Add minimal docs for user flow
|
||||
|
||||
**Files:**
|
||||
- Modify: `README.md`
|
||||
- Modify: `README.en.md`
|
||||
- Modify: `skills/SKILL.md`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
No automated doc test required. Instead, write a short checklist of required user-facing docs:
|
||||
|
||||
- create interactive book
|
||||
- write chapter
|
||||
- inspect choices
|
||||
- choose branch
|
||||
- switch branch
|
||||
|
||||
**Step 2: Run manual verification**
|
||||
|
||||
Check README/skill docs are missing or outdated.
|
||||
|
||||
Expected: missing interactive instructions.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Document:
|
||||
|
||||
- what interactive-tree mode is
|
||||
- the branch CLI flow
|
||||
- what remains out of scope in v1
|
||||
|
||||
**Step 4: Run manual verification**
|
||||
|
||||
Read both READMEs and `skills/SKILL.md` to verify the flow is discoverable.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add README.md README.en.md skills/SKILL.md
|
||||
git commit -m "docs: add interactive fiction branch workflow"
|
||||
```
|
||||
|
||||
### Task 12: End-to-end acceptance and release note prep
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/core/src/__tests__/pipeline-runner.test.ts`
|
||||
- Modify: `packages/cli/src/__tests__/cli-integration.test.ts`
|
||||
- Optional: `docs/plans/...` if acceptance notes are appended
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add one narrow end-to-end acceptance test that:
|
||||
|
||||
- creates an interactive book
|
||||
- writes chapter 1
|
||||
- persists branch choices
|
||||
- chooses a branch
|
||||
- writes chapter 2 on that branch
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run the targeted acceptance tests.
|
||||
|
||||
Expected: FAIL until all pieces are wired together.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Fill any missing wiring only if acceptance still fails.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run:
|
||||
|
||||
- targeted acceptance tests
|
||||
- focused branch CLI tests
|
||||
- focused runner tests
|
||||
- `pnpm typecheck` in `packages/core`
|
||||
- `pnpm typecheck` in `packages/cli`
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/core/src/__tests__/pipeline-runner.test.ts packages/cli/src/__tests__/cli-integration.test.ts
|
||||
git commit -m "test: cover interactive fiction v1 end-to-end flow"
|
||||
```
|
||||
Loading…
Reference in a new issue