Files
initiative/specs/029-on-demand-bestiary/plan.md

5.0 KiB

Implementation Plan: On-Demand Bestiary with Pre-Indexed Search

Branch: 029-on-demand-bestiary | Date: 2026-03-10 | Spec: spec.md Input: Feature specification from /specs/029-on-demand-bestiary/spec.md

Summary

Replace the bundled data/bestiary/xmm.json (503 creatures, ~1.3 MB, one source) with a two-tier architecture: a pre-shipped lightweight search index (data/bestiary/index.json, 3,312 creatures, 102 sources, ~52 KB gzipped) for instant multi-source search and combatant creation, plus on-demand full stat block data fetched from user-provided URLs and cached in IndexedDB per source. This removes copyrighted prose from the distributed app, expands coverage from 1 source to 102, and preserves the existing normalization pipeline.

Technical Context

Language/Version: TypeScript 5.8 (strict mode, verbatimModuleSyntax) Primary Dependencies: React 19, Vite 6, Tailwind CSS v4, Lucide React (icons), idb (IndexedDB wrapper) Storage: IndexedDB for cached source data (new); localStorage for encounter persistence (existing, unchanged) Testing: Vitest (existing) Target Platform: Modern browsers (Chrome, Firefox, Safari, Edge) Project Type: Web application (monorepo: domain, application, web adapter) Performance Goals: Search results <100ms, stat block display <200ms after cache, source fetch <5s on broadband Constraints: Zero copyrighted prose in shipped bundle; offline-capable for cached data; single-user local-first Scale/Scope: 3,312 creatures across 102 sources; index ~320 KB raw / ~52 KB gzipped

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

Principle Status Notes
I. Deterministic Domain Core PASS Index data is static. No I/O, randomness, or clocks in domain. Fetch/cache operations are purely adapter-layer.
II. Layered Architecture PASS New BestiaryIndex type in domain (pure data). IndexedDB cache and fetch logic in adapter layer. Application layer orchestrates via port interfaces.
III. Agent Boundary N/A No agent layer changes.
IV. Clarification-First PASS Spec is comprehensive with zero NEEDS CLARIFICATION markers. All decisions documented in assumptions.
V. Escalation Gates PASS Implementation follows spec → plan → tasks pipeline.
VI. MVP Baseline Language PASS Cache management UI (P4) is included but scoped as MVP. No permanent bans.
VII. No Gameplay Rules PASS No gameplay mechanics in plan — only data loading and caching architecture.
Merge Gate PASS All changes must pass pnpm check before commit.

Project Structure

Documentation (this feature)

specs/029-on-demand-bestiary/
├── spec.md
├── plan.md              # This file
├── research.md          # Phase 0 output
├── data-model.md        # Phase 1 output
├── quickstart.md        # Phase 1 output
├── contracts/           # Phase 1 output
│   └── bestiary-port.md
├── checklists/
│   └── requirements.md
└── tasks.md             # Phase 2 output (via /speckit.tasks)

Source Code (repository root)

packages/domain/src/
├── creature-types.ts         # Extended: BestiaryIndexEntry, BestiaryIndex types
└── index.ts                  # Updated exports

packages/application/src/
├── ports.ts                  # Extended: BestiarySourceCache port interface
├── index.ts                  # Updated exports
└── (no new use cases — orchestration stays in adapter hooks)

apps/web/src/
├── adapters/
│   ├── bestiary-adapter.ts   # Unchanged (processes fetched data as before)
│   ├── strip-tags.ts         # Unchanged
│   ├── bestiary-index-adapter.ts  # NEW: loads/parses index.json, converts to domain types
│   └── bestiary-cache.ts     # NEW: IndexedDB cache adapter implementing BestiarySourceCache
├── hooks/
│   ├── use-bestiary.ts       # REWRITTEN: search from index, getCreature from cache
│   └── use-encounter.ts      # MODIFIED: addFromBestiary uses index entry instead of full Creature
├── components/
│   ├── bestiary-search.tsx    # MODIFIED: show source display name in results
│   ├── stat-block.tsx         # Unchanged
│   ├── stat-block-panel.tsx   # MODIFIED: trigger source fetch prompt when creature not cached
│   ├── source-fetch-prompt.tsx    # NEW: fetch/upload prompt dialog
│   └── source-manager.tsx     # NEW: cached source management UI
└── persistence/
    └── encounter-storage.ts   # Unchanged

data/bestiary/
├── index.json                # Existing (shipped with app)
└── xmm.json                  # REMOVED from repo

Structure Decision: Follows existing monorepo layering. New adapter files for index loading and IndexedDB caching. New components for source fetch prompt and cache management. Domain extended with lightweight index types only — no I/O.

Complexity Tracking

No constitution violations to justify.