feat(universe-builder): Phase B — enrich expand contract with canon + plumb canon into arc prompts#267
Open
atomantic wants to merge 10 commits into
Open
feat(universe-builder): Phase B — enrich expand contract with canon + plumb canon into arc prompts#267atomantic wants to merge 10 commits into
atomantic wants to merge 10 commits into
Conversation
…rrays + plumb canon into arc prompts Universe Builder "Generate From Idea" now returns rich first-class canon alongside categories: - characters[]: name, physicalDescription, personality, background, prompt, tags - settings[]: name, slugline, description, palette, recurringDetails, ... - objects[]: name, description, significance, prompt, tags The expand LLM is also taught to tag each category with `kind` so the Phase A data model lands them under the right canon trunk in the upcoming UI. Client handleExpand merges canon into draft (existing entries always win on name/ slugline collision — server-side dedupe via sanitizeBibleList). arcPlanner now feeds named canon into LLM prompts via a new worldCanonText context field (sibling to worldCategoriesText). Migration 019 auto-updates the pipeline-arc-resolve and pipeline-volume-verify templates on launch for unmodified installs; customized prompts get a manual-merge warning.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR enriches Universe Builder expansion output with first-class canon entities and makes linked-world canon available to downstream arc/volume prompt contexts.
Changes:
- Expands the server/client “Generate From Idea” contract to include characters, settings, objects, and category
kind. - Adds canon prompt rendering and wires
worldCanonTextinto arc planner context/templates. - Adds migration/setup-data hash handling and tests/documentation for the new contract.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| server/services/universeBuilderExpand.js | Adds canon arrays and category kind guidance to expansion output. |
| server/services/universeBuilderExpand.test.js | Adds tests for canon normalization and prompt contract text. |
| server/lib/universePromptRenderers.js | Adds canon-to-prompt rendering helper. |
| server/services/pipeline/arcPlanner.js | Adds worldCanonText to linked-world context. |
| server/services/pipeline/arcPlanner.test.js | Verifies canon context flows into arc planner calls. |
| server/routes/universeBuilder.js | Updates route docs for expand response shape. |
| client/src/pages/UniverseBuilder.jsx | Merges returned canon into the draft and auto-save payload. |
| data.sample/prompts/stages/pipeline-arc-resolve.md | Adds World canon block to resolve prompt. |
| data.sample/prompts/stages/pipeline-volume-verify.md | Adds World canon block to volume verification prompt. |
| scripts/migrations/019-arc-verify-resolve-canon-context.js | Adds hash-gated prompt template migration. |
| scripts/setup-data.js | Updates shipped prompt hashes for drift detection. |
| PLAN.md | Marks Phase B canon context work as folded in and records follow-ups. |
| .changelog/NEXT.md | Adds user-facing release notes for expansion canon and prompt canon context. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Also forward LLM-returned category `kind` through normalizeCategories so custom buckets land under the right canon trunk; use normalizeSlugline for settings dedupe so dash/punct-variant sluglines collide; rename migration 019 to reflect the files it actually updates (pipeline-arc-resolve.md + pipeline-volume-verify.md, not arc-verify).
… in all arc/volume prompts
Five Copilot follow-ups:
- handleExpand: build mergedCategories with both 'kind' (existing draft wins,
LLM-returned kind as fallback) and 'variations', so the LLM/user-tagged
bucket trunk survives the round-trip through ensureDraftCategories.
- arc-overview.md + arc-verify.md: render {{worldCanonText}} so the canon
context fed by arcPlanner actually reaches the prompt (previously only
arc-resolve + volume-verify referenced it).
- Migration 019 now updates all four arc/volume templates, renamed to
019-arc-volume-prompts-canon-context.js; setup-data.js hash bumps mirror.
- normalizeCanonArray strips LLM-supplied id/createdAt/updatedAt before
sanitize so a hallucinated/example id can't introduce duplicate canon ids.
- mergeCanonByName is kind-aware — settings use normalizeSlugline for both
name and slugline (matches storyBible MERGE_CONFIG.setting.keyFields), so
dash/punct-variant identifiers collide instead of duplicating.
…oast + complete changelog Four Copilot follow-ups: - normalizeCanonArray strips locked/sourceSeriesId/imageRefs/primaryImageRef alongside the previously-stripped id/timestamps. Without this, a hallucinated 'locked: true' from the LLM would silently lock new canon entries (blocking user edits via the lock UI), and stale sourceSeriesId/ imageRefs would falsely attribute provenance + pin visuals. - expandToast now reports NEW canon entries added by this expand (post-merge minus pre-existing), not the draft's running total. Re-expanding on a populated universe no longer claims credit for entries the user authored. - mergeCanonByName is alias-aware for character/object kinds, matching the server's MERGE_CONFIG keyFields. Existing 'Ashley' with alias 'Ash' collides with an LLM-returned 'Ash' instead of duplicating. - Changelog NEXT.md names all four templates updated by migration 019 (arc-overview, arc-verify, arc-resolve, volume-verify), not just the two originally listed.
…reate on expand for unsaved drafts
… boundary; gate Linked World prompt block on hasLinkedWorld; add migration 019 test
…rompt rendering, drift-catch + OLD→NEW migration test via applyMigration helper
… enrich canon renderer with personality/slugline/recurringDetails, fix mergeCanonByName within-batch dedupe, correct PLAN doc
…e on setSaving to prevent double-submit, exclude *.test.js from migration runner, refresh stale comment
…e payload, drop premature cap in characters-bucket fold, capture canon-clobber risk in PLAN
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 B of the Universe Builder redesign (PLAN.md Next Up #1). Builds on the Phase A schema (#264) by enriching the LLM "Generate From Idea" contract to return first-class named canon and plumbing that canon into downstream prompts.
characters[]/settings[]/objects[]with rich metadata (physicalDescription,palette,slugline,recurringDetails, etc.) alongside the existing categories + composite sheets. Each category also gets akindtag so the Phase C UI knows which canon trunk to render it under.handleExpandmerges returned canon into the draft'suniverse.characters/.settings/.objectsarrays. Existing entries always win on name/slugline collision so a re-expand can't clobber hand-authored or series-extracted records.worldCanonTextcontext field viarenderCanonForPrompt(world)— the LLM now sees named cast/places/objects alongside the existingworldCategoriesTextexploratory variations. Folds in the "arcPlanner prompt context — include canon" backlog item.pipeline-arc-resolve.md+pipeline-volume-verify.mdtemplates to include the new{{worldCanonText}}block when unmodified; customized prompts get a manual-merge warning (mirrors the migration-003 pattern).Files changed
server/services/universeBuilderExpand.js—buildExpansionPromptasks for canon +kind;isExpansionShaperecognizes canon arrays;normalizeCanonArray(raw, kind)runs LLM output throughsanitizeBibleListwithBIBLE_SOURCE.UNIVERSE_EXPANDstamped pre-sanitize.server/lib/universePromptRenderers.js— newrenderCanonForPrompt(world), table-driven per-kind formatting.server/services/pipeline/arcPlanner.js— wiresworldCanonTextintoloadWorldContext+EMPTY_WORLD_CONTEXT.client/src/pages/UniverseBuilder.jsx—mergeCanonByNamehelper (dedupe by name + slugline, identity-preserving on empty input);handleExpandmerges canon viapickCanon; toast dedupe viaexpandToasthelper.data.sample/prompts/stages/pipeline-{arc-resolve,volume-verify}.md— new "World canon" block above the categories block.scripts/migrations/019-arc-verify-resolve-canon-context.js— hash-driven one-shot template upgrade.scripts/setup-data.js— bumped shipped-MD5 entries (OLD becomes array of two hashes; NEW gets the post-019 hash).server/routes/universeBuilder.js— JSDoc update to advertise the new expand return shape.Test plan
cd server && npm test— 5108 passing, 5 skipped (was 5102 in Phase A, +6 new)cd client && npm run build— cleanEXPANSION_PROMPT contains characters/settings/objects + physicalDescription + slugline + significance(prompt contract)EXPANSION_PROMPT teacheskindenum on categories(LLM hint matches Zod enum)normalizeCanonArrayx4 (non-array → []; character → bible shape; setting requires name OR slugline; object requires name)worldCanonTextcontainsMira Holt+field detective+The Tongue(canon context flows into prompt); placeholder branch assertsnone/simplifyreview applied: HIGH ×1 (BIBLE_SOURCEconstant); MED ×4 (toast dedupe, table-driven renderer,mergeCanonByNameshort-circuit,pickCanonhelper); 1 MED deferred → PLAN.md (mergeExpandIntoDraftextraction)data/runs/<id>/output.txt) includes the World canon blockMigration safety
Migration 019 is unmodified-only —
data/prompts/stages/pipeline-arc-resolve.mdandpipeline-volume-verify.mdare replaced ONLY when their on-disk hash matches one of the two prior shipped hashes (pre-005 or current). Customized prompts get a warning + manual-merge instructions and stay unchanged. Idempotent: re-runs against the new hash are no-ops.Known deferred
mergeExpandIntoDraft(draft, result)extraction from the now ~155-linehandleExpand. Pure merge logic can be lifted out + unit-tested. Captured in PLAN.md under "Code quality / dedup".world.categorieswithoutworld.canon(PLAN.md backlog item).🤖 Generated with Claude Code