# CHUNK #4 โ€” Characters & World-Building **Duration:** ~2 days ยท **Prereqs:** CHUNK #2 (entity storage), CHUNK #3 (editor for mentions) ยท **Follows:** CHUNK #3 ยท **Unblocks:** CHUNK #5 (plot links entities), #9 (AI context pulls from entities) **Purpose:** Make the story's moving parts first-class: character sheets with typed relationships, flexible world items, fast fuzzy search, backlinks, and `@mention` links from prose to entities. After this chunk, the "tools to facilitate and integrate characters, world building" requirement is fully delivered. **Status (updated 2026-10-25):** ๐ŸŸก In progress. Data layer and entity UI layer complete and CI-green (`pnpm typecheck`, `pnpm test`, `pnpm lint`, `pnpm build` all pass; 65 tests green across the workspace). See "Remaining" below. --- ## Steps ### 1. Entity engine (`apps/web/src/features/entities/`) 1. **Generic field renderer** (`EntityFields.tsx`) โ€” driven entirely by `ENTITY_FIELDS` from `packages/core/src/schema.ts` (Chunk 2): renders `text`, `textarea`, `select`, `list` (add/remove/reorder chips), `link` (entity reference) fields. Adding a field to the registry = it appears in the UI. No per-entity UI code. 2. **Entity panel** (`EntitiesView.tsx`) โ€” tabbed: **Characters ยท World ยท Notes**. Each tab: search box (fuzzy, <50 ms at 500 entities), grouped list, create button, card grid. 3. **Entity editor** (`EntityEditor.tsx`) โ€” right-rail drawer: fields from the registry, plus: - **Relationships editor** (characters): add `{ target: character, kind: 'ally'|'rival'|'family'|'mentor'|'romantic'|'custom', note }`; render as a list with the linked character's chip. - **Backlinks** tab: "Appears in" โ€” chapters containing a mention of this entity, plus entities that reference it. - Duplicate, delete (with confirm + backlink warning), "pin to context rail". 4. **World kinds** โ€” `location`, `faction`, `item`, `glossary`, `system` โ€” each with its field set from the registry (e.g. location: region, climate, key-people list; faction: goals, rivals list; item: origin, powers, holder). 5. **Relationship map** (`RelationshipMap.tsx`) โ€” a small force/graph view (reuse React Flow with a static layout) of the selected character's web; click a node โ†’ open that character. ### 2. Mentions & linking 1. **Autocomplete** (`packages/editor/src/mention.ts`): typing `@` opens an entity picker (fuzzy over characters + world items); Enter inserts a `CharacterMention` atom (node from Chunk 3) carrying `entityId`. 2. **Mention chip** โ€” styled, hover โ†’ mini popover (name + one-line bio + "Open sheet"); click โ†’ opens the entity editor. 3. **Backlink computation** (`packages/core/src/backlinks.ts`): walk docs' JSON for mention nodes + entity-name matches; cache per project, invalidate on doc save. Power the "Appears in" tab and the context rail. 4. **Consistency check** (small win, big value): `findInconsistencies(project)` โ€” e.g. a character marked `status: 'dead'` who is mentioned *after* their death chapter (by chapter order). Surface as a warnings list in the Characters tab. ### 3. Search & navigation 1. Global search (`Ctrl/Cmd+K` palette, from Chunk 1) now indexes: chapters, entities, notes, boards. Grouped results, jump-to with context snippet. 2. Context rail (right side of WritingView) shows: this chapter's mentioned characters (auto), pinned entities, and related notes. ### 4. Data & seed 1. Extend `server/src/seed.ts`: the demo project's 4 characters get full sheets (bio, voice, 2 relationships each); 5 world items (a city, a faction, an artifact, 2 glossary terms). 2. `docs/DATA_MODEL.md` โ€” the entity registry table (kind โ†’ fields) as the human reference. ### 5. Tests 1. Unit: field renderer per type; fuzzy search (500 entities < 50 ms); backlink computation (property test: insert mention โ†’ backlink appears; remove โ†’ disappears). 2. Contract: entity CRUD + `?q=` search through both adapters (add to Chunk 2's suite). 3. E2E: create character โ†’ `@mention` it in a chapter โ†’ backlinks tab lists the chapter โ†’ open sheet from the chip. --- ## Acceptance criteria (all must pass) - [~] Create a character with all field types (incl. list + link) via the generic renderer โ€” no custom code added. Generic renderer done for `text`/`textarea`/`checkbox`/`tags` (list); `link` field type pending. - [x] Fuzzy search over 500 seeded entities returns in < 50 ms (measured in test). - [~] `@` autocomplete โ†’ chip โ†’ popover โ†’ open sheet, all working; works while typing fast (no stale menus). Autocomplete + chip + open sheet done; hover popover pending. - [x] Backlinks correct and live-updating (save doc โ†’ "Appears in" updates within one event round-trip). - [ ] Consistency check flags the seeded dead-character-after-death case. Logic in `packages/core/src/consistency.ts` done; UI wiring + seeded case pending. - [x] Relationship map renders and navigates. - [ ] Global palette finds a chapter, a character, and a note from one box. - [x] Contract suite still green on both adapters. ## Remaining work - Wire `findInconsistencies()` into the Characters tab as a warnings list (Chunk 3.2). - Index entities in the global `Ctrl/Cmd+K` palette (Chunk 3.1). - Context rail (right side of WritingView): this chapter's mentioned characters, pinned entities, related notes (Chunk 3.2). - Extend `server/src/seed.ts`: full character sheets (bio, voice, 2 relationships each) + 5 world items (Chunk 4.1). - `docs/DATA_MODEL.md`: the entity registry table (kind โ†’ fields) as the human reference (Chunk 4.2). - E2E: create character โ†’ `@mention` it in a chapter โ†’ backlinks tab lists the chapter โ†’ open sheet from the chip (Chunk 5.3). ## Definition of done The author's canon โ€” who, what, where โ€” is structured, searchable, linked to the prose, and checkable. This is the data the AI roles will reason from in Chunk 9. ## Risks & fallbacks - **Fuzzy search quality** โ†’ use a small, well-tested scorer (e.g. `fuse.js` or a hand-rolled subsequence scorer in `packages/core`); keep it dependency-light and unit-tested. - **Backlink cost on huge docs** โ†’ compute incrementally on save (diff the doc), not full re-scan; cache in the DB (`mentions(entity_id, chapter_id)` table). - **Mention vs. plain text ambiguity** โ†’ only explicit `@`-inserted atoms are links; name-matching is *suggested*, never auto-applied.