feat(universe-builder): Phase A — add category kind + retire default characters bucket#264
Merged
Conversation
…lt `characters` (Phase A) Schema v3→v4: every category bucket now carries `kind: 'characters'|'settings'|'objects'|'other'` tagging it to one of the 3 canon trunks the Phase C UI will render under. Built-in defaults get a known kind (landscapes/environments/structures→settings, vehicles→objects); custom buckets fall to 'other' until the Phase C Auto-sort action LLM-classifies them. The default `characters` category is retired — canon owns characters now. Any incoming `characters` bucket gets folded into universe.characters[] (via the existing backfillCanonFromCategories logic) and dropped on sanitize. A new one-shot migration (017-categorize-universe-buckets.js) does the same proactively on upgrade for observability + atomicity. Phase A is data-model + migration only; Phase B enriches the expand LLM contract and Phase C delivers the tabbed-trunk layout (see PLAN.md).
…d collision with volume-cover-concepts-stage
Contributor
There was a problem hiding this comment.
Pull request overview
Phase A of the Universe Builder redesign: introduces a kind discriminator on each category bucket (mapping it to one of the canon "trunks") and retires the default characters bucket so canon becomes the sole home for characters. This is a data-model + migration change with no UI rework.
Changes:
- Bump universe schema to v4 with
CATEGORY_KINDS,WORLD_CATEGORY_DEFAULT_KINDS, andRETIRED_CATEGORY_KEYS; sanitizers/route Zod accept and persistkind, retired buckets are dropped (variations folded into canon). - Add one-shot migration
018-categorize-universe-buckets.jsplus a dedicated test file (with vitest config updated to includescripts/migrations/**). - Update affected unit/route tests, drop the retired
characterslabel inUniverseBuilder.jsx, and document the design decision in PLAN.md and the changelog.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| server/services/universeBuilder.js | Schema v4, kind constants, sanitizer/merge changes, retired-key drop |
| server/routes/universeBuilder.js | Zod kind enum on categoryShape |
| server/services/universeBuilder.test.js | New kind tests; swap retired characters for custom outfits in seed |
| server/services/universeBuilderExpand.test.js | Update tests for retired characters default + kind tag |
| server/services/pipeline/arcPlanner.test.js | Update arc-planner test for retired characters bucket |
| server/routes/universeBuilder.test.js | Route tests for kind enum + bucket folding |
| server/vitest.config.js | Add scripts/migrations/** to include glob |
| scripts/migrations/018-categorize-universe-buckets.js | New v3→v4 migration |
| scripts/migrations/018-categorize-universe-buckets.test.js | Migration test suite |
| client/src/pages/UniverseBuilder.jsx | Drop retired characters label |
| PLAN.md | Capture rejected/accepted framings + multi-phase plan |
| .changelog/NEXT.md | User-facing entry |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase A of the Universe Builder redesign (PLAN.md Next Up #1). Data model + migration only — no UI changes yet.
kind: 'characters'|'settings'|'objects'|'other'to every category bucket, tagging it to one of the 3 canon trunks the Phase C UI will render under.characterscategory — canon owns characters now. Variations from any incomingcharactersbucket get folded intouniverse.characters[](via existingbackfillCanonFromCategories) and the bucket is dropped on sanitize.018-categorize-universe-buckets.jsmakes the v3→v4 transition observable + atomic on upgrade (the on-read sanitizer also converges installs that skip it).Design context
This rewrites the original "retire
universe.categoriesentirely" PLAN item, which was a misread — canon (consistency workflow) and categories (visual-exploration workflow) are complementary, not redundant. The accepted framing unifies them under 3 canon trunks: canon entries are first-class entities with rich metadata; sub-buckets (formerly "categories") are organizational + bulk-generation surfaces within each trunk. SeePLAN.md→ "Categories vs canon — decision (2026-05-17)".Multi-phase rollout (each phase is a separate PR):
characters[]/settings[]/objects[]) alongside categories.Changes
server/services/universeBuilder.js— schema v3→v4; newCATEGORY_KINDS,DEFAULT_CATEGORY_KIND,WORLD_CATEGORY_DEFAULT_KINDS,RETIRED_CATEGORY_KEYS,resolveCategoryKind.sanitizeCategoryaccepts + persistskind;sanitizeCategoriesdrops retired buckets;mergeCategoriespropagates kind.server/routes/universeBuilder.js—categoryShapeZod accepts optionalkindenum.client/src/pages/UniverseBuilder.jsx— 1-line cleanup (dropped retired-default label).scripts/migrations/018-categorize-universe-buckets.js— one-shot upgrade with per-universe error context.PLAN.md— full design history captured (rejected framings → accepted unification), Phase A/B/C/D scoped..changelog/NEXT.md— user-facing entry.Test plan
cd server && npm test— 5102 passing, 5 skipped, 234 filescd client && npm run build— clean build/simplify(3 medium findings applied: extractedRETIRED_CATEGORY_KEYS, simplifiedmergeCategoriesfallback, added per-universe error context)characters) and each has akindcharactersbucket variations now appear under canon CharactersKnown deferred
arcPlanner.js#worldCategoriesTextonly renders fromworld.categories. With thecharactersdefault retired, character names that were in that bucket no longer appear in arc-planning prompts (they're inworld.characters[]canon now). Captured in PLAN.md Backlog as Phase B follow-up: addrenderCanonForPrompt(world)+worldCanonTextcontext field.🤖 Generated with Claude Code