Skip to content

Non-linear generation: backport across pipeline stages & Story Builder steps#566

Merged
atomantic merged 7 commits into
mainfrom
feat/non-linear-stage-generation-wt
May 30, 2026
Merged

Non-linear generation: backport across pipeline stages & Story Builder steps#566
atomantic merged 7 commits into
mainfrom
feat/non-linear-stage-generation-wt

Conversation

@atomantic

Copy link
Copy Markdown
Owner

Summary

Makes both the Create Series Pipeline and the Story Builder non-linear, so a creator who authored work out of order (e.g. started a series as a drafted comic book) can backfill the prior steps to evaluate whether the story is complete.

Pipeline — per-stage source picker. Each text stage (idea → prose → comicScript → teleplay) was forward-only: it could only be generated from its conventional upstream. Now each stage's Generate button shows a "Generate from:" picker listing every other stage that has content, pre-checked with the conventional forward source. The server renders the chosen stages through a generic {{#sourceMaterials}} block instead of a hardcoded single-source slot, so prose can be written from a comic script, the beat sheet reverse-engineered from finished prose, etc. Omitting sourceStageIds preserves exact prior behavior (auto-run and legacy callers fall back to the forward source).

Story Builder — start anywhere + backfill. Navigation is now advisory rather than gated: setCurrentStep accepts any valid step, and the step rail flags an unlocked/stale upstream with a warning icon instead of disabling it. Lock enforcement stays at the generators (a locked arc/readerMap record still refuses regeneration). The idea and plotArc generators take an optional fromDownstream flag that synthesizes the step from the series' existing issue content (richest stage per issue: comicScript → teleplay → prose → idea). plotArc routes through a new arcPlanner.generateArcFromSource that reuses the importer's importer-arc-extract prompt and returns the same {arc, seasons} shape, committed through the identical commitSeasonsWithRemap path; idea passes the corpus as {{sourceMaterial}} in a new optional prompt block. A "Backfill from existing issues" button appears on the idea + plot-arc steps whenever any issue has content.

Compatibility. Two hash-gated prompt migrations ship the updated templates to existing installs (054 for the four pipeline stage prompts, 055 for the story-builder idea prompt); customized prompts are left intact and warned about. No sync/schema-version payload changes (Story Builder sessions are local-only; pipeline issue shape is unchanged — only generation inputs change).

Test plan

  • cd server && npx vitest run for the touched suites — 294 tests pass: textStages (incl. new backport cases), pipeline route (new sourceStageIds schema), storyBuilder service (new fromDownstream backfill cases + relaxed navigation), storyBuilder route, arcPlanner, and both migration suites (054/055, each verifying the accepted-old hash matches git-HEAD and that customized prompts are skipped).
  • cd client && npx eslint on TextStagePanel.jsx + StoryBuilder.jsx — clean.
  • Manual (npm run dev): open an issue that has only a comic script → the Prose stage's "Generate from:" lists Comic Script (Idea absent); generate produces prose from the script. In Story Builder, a session whose series has drafted issues but no arc → enter Plot Arc out of order, click "Backfill from existing issues", get a populated arc; a locked step still refuses regeneration.

@atomantic atomantic force-pushed the feat/non-linear-stage-generation-wt branch from d9bd733 to 3157cde Compare May 30, 2026 20:41
atomantic added 2 commits May 30, 2026 13:50
…(backport)

The Create Series Pipeline was strictly forward-only: each text stage
(idea → prose → comicScript → teleplay) could only be generated from its
conventional upstream, so a series that started life as a drafted comic
script had no way to backfill the prose / beat sheet from it.

Each stage's Generate button now offers a "Generate from:" picker listing
every other stage that has content, pre-checked with the conventional
forward source. The server renders the chosen stages through a generic
{{#sourceMaterials}} block instead of a hardcoded single-source slot, so
prose can be written from a comic script, the beat sheet reverse-engineered
from finished prose, etc. Omitting sourceStageIds preserves the exact prior
behavior (auto-run + legacy callers fall back to the forward source).

The forward-source map and a shared stageContentOf accessor live in
apiPipeline.js / textStages.js so the client picker and server resolver read
one definition instead of duplicating it across the boundary.

Migration 054 rewrites the four shipped stage prompts in existing installs
to the source-agnostic templates, hash-gated so customized prompts are left
intact and warned about.
…fted issues

The Story Builder forced a strict forward order: setCurrentStep hard-threw a
409 unless every earlier step was locked-and-fresh, and the generators only
ever pulled from their conventional upstream. A user who began a series by
drafting the comic book had no way to walk the wizard backward and reconstruct
the idea / plot arc from the work that already existed.

Two changes:

- Navigation is now advisory, not gated. setCurrentStep accepts any valid step;
  the step rail still flags an unlocked/stale upstream with a warning icon but
  no longer disables the step. Lock enforcement remains at the generators
  (a locked arc/readerMap record still refuses regeneration).

- The idea and plotArc generators take an optional `fromDownstream` flag. When
  set, they synthesize from the series' existing issue content (richest stage
  per issue: comicScript → teleplay → prose → idea) instead of the empty
  upstream. plotArc routes through a new arcPlanner.generateArcFromSource that
  reuses the importer's importer-arc-extract prompt and returns the same
  {arc, seasons} shape, committed through the identical commitSeasonsWithRemap
  path. idea passes the collected corpus as {{sourceMaterial}} in a new
  optional prompt block. The forward (seed-only) paths are unchanged.

A "Backfill from existing issues" button appears on the idea + plot-arc steps
whenever any issue already has text content.

Migration 055 upgrades the shipped story-builder-idea-expand prompt in existing
installs (hash-gated; customized prompts are left intact and warned about).
@atomantic atomantic force-pushed the feat/non-linear-stage-generation-wt branch from c492b64 to 06435f9 Compare May 30, 2026 20:53
…e-prompt migrations

Making the pipeline + story-builder prompts source-agnostic (migrations 054/055)
changed the live data.reference bodies. Per the repo convention, every prior
migration that ships a NEW_SHIPPED_MD5 for one of those prompts must keep that
hash pointing at the current reference and accumulate the prior hash in its
accepted-old lineage — otherwise the drift-catch tests fail and an install at
the pre-054 hash wouldn't recognize the upgrade path.

Bumped NEW + appended the prior hash for the four pipeline stage prompts in
migrations 003/004/013/025/027 and the story-builder idea prompt in 043, and
mirrored both into the setup-data.js drift table.
@atomantic atomantic force-pushed the feat/non-linear-stage-generation-wt branch from 9b37021 to 5d567da Compare May 30, 2026 21:05
atomantic added 4 commits May 30, 2026 15:32
…als prompts; fix dead goToStep args

- Switch all four pipeline sourceMaterials blocks from triple-backtick fences to
  tilde fences + the importer's injection-guard sentence, matching the established
  pattern in importer-arc-extract and siblings.
- Add the missing injection-guard sentence to story-builder-idea-expand.md (which
  already had the tilde fence).
- Remove stale idx argument from StoryBuilder Back/Next buttons — goToStep was
  narrowed to (id) in this branch but the footer calls kept the old second arg.
- Fix arcPlanner.js comment that overstated shapeSeasonOutlines: synopsis is
  present in importer-arc-extract output but is not forwarded to buildSeason.
- Cascade new prompt hashes through migrations 003/004/013/025/027/054/055 and
  setup-data.js drift table so existing installs auto-upgrade cleanly.

Note: z.enum(['idea','prose','comicScript','teleplay']) in pipeline.js:365,
TEXT_STAGE_IDS in issues.js, and PIPELINE_TEXT_STAGES in apiPipeline.js are
three separate literals that must stay in lockstep — they agree today but a
future text-stage addition must update all three; the route enum is the one
most likely to be forgotten and would silently 400 valid sourceStageIds.
…st, story-builder prompt drift entry

- idea backfill now throws ERR_VALIDATION when fromDownstream:true but no issue
  has content, consistent with the plotArc backfill guard (was silently falling
  through to a seed-only expansion)
- Add regression test covering the new guard
- Add server/routes/pipeline.test.js case asserting sourceStageIds is accepted
  by the route schema and an unknown stage id 400s (plan doc listed this test
  as expected but it was missing)
- Add story-builder-idea-expand.md to setup-data.js SHIPPED_PROMPT_OLD/NEW_MD5
  drift tables so the boot-time drift warning covers it (CLAUDE.md: stage-prompt
  changes need migration + drift-table mirror)
…traction

importer-arc-extract returns synopsis per season, but shapeSeasonOutlines
wasn't forwarding it to buildSeason — so Arc Canvas synopsis fields stayed
empty after a plotArc backfill even though the LLM populated them. One-line
fix: add synopsis to the shapeSeasonOutlines buildSeason call. Extend the
backfill test to assert the persisted season carries the extracted synopsis.
… literal

- TextStagePanel: remove availableKey join-proxy; availableSources is already
  stable from useMemo so it can be used directly as a useEffect dep, removing
  the workaround variable and its eslint-disable comment
- storyBuilder plotArc branch: replace 5 uninitialized let declarations with
  a single arcGenResult intermediate, then one const destructure — both branches
  write the same shape, so the let pattern was unnecessary
- collectIssueSourceText: drop the spread-and-sort; listIssues already returns
  issues in number order when filtered to a single seriesId
- resolveSourceStageIds: build the Set once from requested, then single-pass
  filter TEXT_STAGE_IDS — eliminates the intermediate filtered array
- pipeline route sourceStageIds: drive z.enum from issuesSvc.TEXT_STAGE_IDS
  instead of a hand-duplicated literal, matching the STAGE_STATUSES pattern
  already used elsewhere in the file

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Makes the Create Series Pipeline and Story Builder non-linear so creators can backfill earlier steps from later ones. Pipeline text stages gain a multi-select source picker (any populated stage can feed any other) rendered via a generic {{#sourceMaterials}} block, and Story Builder relaxes step gating to advisory while letting the idea and plotArc generators synthesize from existing issue content via the importer's arc-extract prompt.

Changes:

  • Pipeline: per-stage source picker (client + server sourceStageIds) with hash-gated migration 054 rewriting four prompt templates to a source-agnostic block.
  • Story Builder: setCurrentStep becomes advisory; generateStep({fromDownstream}) adds backfill paths for idea (new optional {{sourceMaterial}} block, migration 055) and plotArc (new arcPlanner.generateArcFromSource reusing importer-arc-extract).
  • Drift table in setup-data.js plus old migrations 003/004/013/025/027 updated to accept the new hashes and target the post-054-fence shipped versions.

Reviewed changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated no comments.

Show a summary per file
File Description
server/services/pipeline/textStages.js Adds sourceMaterials context + resolveSourceStageIds, exports stageContentOf.
server/services/pipeline/textStages.test.js New backport / default-source / drop-invalid cases.
server/services/pipeline/arcPlanner.js New generateArcFromSource using importer-arc-extract; adds synopsis to season outline shape.
server/routes/pipeline.js Adds sourceStageIds enum array to generate schema.
server/routes/pipeline.test.js Verifies accept/reject of sourceStageIds.
server/services/storyBuilder.js Removes lock-gate from setCurrentStep; adds collectIssueSourceText + fromDownstream paths for idea/plotArc.
server/services/storyBuilder.test.js New backfill tests + relaxed-nav test.
server/lib/validation.js Adds fromDownstream to story step generate schema.
client/src/services/apiPipeline.js Exports PIPELINE_DEFAULT_FORWARD_SOURCE.
client/src/components/pipeline/stages/TextStagePanel.jsx Renders "Generate from:" chip picker, sends sourceStageIds.
client/src/pages/StoryBuilder.jsx Free navigation with warning icons; adds Backfill button on idea + plotArc.
data.reference/prompts/stages/pipeline-{idea-expansion,prose,comic-script,teleplay}.md Replace hardcoded slot with {{#sourceMaterials}} block + injection-guard fences.
data.reference/prompts/stages/story-builder-idea-expand.md Adds optional {{sourceMaterial}} backfill block.
scripts/migrations/054-source-agnostic-stage-prompts.{js,test.js} New hash-gated migration for the four pipeline prompts.
scripts/migrations/055-story-builder-idea-backfill-prompt.{js,test.js} New hash-gated migration for the idea-expand prompt.
scripts/migrations/003,004,013,025,027-*.js Extend ACCEPTED_OLD_MD5 chains + retarget NEW_SHIPPED_MD5 to post-054-fence hashes.
scripts/setup-data.js Drift table updated with the new old/new prompt hashes.
docs/plans/2026-05-30-non-linear-stage-generation.md Planning doc.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@atomantic atomantic merged commit d5afa7b into main May 30, 2026
3 checks passed
@atomantic atomantic deleted the feat/non-linear-stage-generation-wt branch May 30, 2026 23:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants