95 lines
5.0 KiB
Markdown
95 lines
5.0 KiB
Markdown
# Implementation Plan: On-Demand Bestiary with Pre-Indexed Search
|
|
|
|
**Branch**: `029-on-demand-bestiary` | **Date**: 2026-03-10 | **Spec**: [spec.md](./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)
|
|
|
|
```text
|
|
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)
|
|
|
|
```text
|
|
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.
|