Files
PubScript/CHUNK#4.md
T
Schwitzz 4efa71515e feat(entity): Chunk #4 data layer + entity UI (characters & world-building)
Data layer:
- Fuzzy scorer (packages/core/src/fuzzy.ts) with index recovery + unit tests
- Backlink computation (packages/core/src/backlinks.ts) incl. entity-level backlinks
- Consistency checker (packages/core/src/consistency.ts) incl. reciprocity/scope + unit tests
- StorageAdapter contract extended with listEntities/searchEntities/saveEntity/deleteEntity;
  implemented in Local (Dexie) and Server adapters; contract tests cover entity CRUD + ?q= search
- WorldItem.fields now supports list values (keyPeople, rivals); expanded RelationshipKind
- @mention autocomplete extension + picker in the editor package

Entity UI (apps/web/src/features/entities/):
- Generic field renderer (EntityFields) driven by ENTITY_FIELDS/FieldDef
- Tabbed directory with fuzzy search + entity cards (EntitiesView)
- Create/edit/delete drawer (EntityEditor) reusing shared fields
- Relationship editor + force-directed SVG relationship map
- Entity engine stylesheet; wired into nav

Store: project-store entity methods (load/create/update/delete) over the StorageAdapter contract.

CI-green: typecheck, lint, 65 tests, and web build all pass.
2026-10-25 22:42:55 -05:00

6.2 KiB

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.
  • 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.
  • 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.
  • Relationship map renders and navigates.
  • Global palette finds a chapter, a character, and a note from one box.
  • 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.