Просмотр исходного кода

docs: design interactive fiction v1

Ma 7 месяцев назад
Родитель
Сommit
181caa7e91

+ 356 - 0
docs/plans/2026-03-30-interactive-fiction-v1-design.md

@@ -0,0 +1,356 @@
+# 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.

+ 505 - 0
docs/plans/2026-03-30-interactive-fiction-v1-implementation-plan.md

@@ -0,0 +1,505 @@
+# 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"
+```