Skip to content

scope FableLoom reformat to one episode per request so a large loom cannot time out - #4814

Merged
atomantic merged 1 commit into
mainfrom
claim/issue-4794
Aug 22, 2026
Merged

scope FableLoom reformat to one episode per request so a large loom cannot time out#4814
atomantic merged 1 commit into
mainfrom
claim/issue-4794

Conversation

@atomantic

Copy link
Copy Markdown
Owner

Summary

Rewriting a loom's scenes into the other format ran every provider call behind a single held HTTP request — 227s on a 13-scene loom, and tens of minutes on a large one, long enough for a proxy or fetch timeout to kill the response while the server kept writing chunks to the record.

  • The endpoint is episode-scoped: POST /api/fableloom/:id/episodes/:episodeId/reformat replaces the loom-wide POST /:id/reformat. Each request is additionally capped at 20 scenes; a run that stops at that ceiling with scenes it never sent answers capped: true.
  • The settings drawer walks the episodes that still have work, names the one in flight ("Rewriting episode 2 of 3 — Pilot…"), and re-asks the same episode while it comes back capped. A response that is not capped is done with that episode even if the model dropped a scene — re-sending a refusal isn't progress, and the leftover rides the existing "run it again to finish" toast.
  • The format pin stays the server's call, and lands only once no episode has an unconverted scene left. That is stronger than the final client PATCH the issue sketched: a browser closed mid-walk can't leave the loom claiming a format half its story isn't in.
  • Resumability is untouched. Each chunk of 5 scenes is still persisted as it lands and stamped with the format it was written in, so a failure keeps what already succeeded and a re-run rewrites only what is left.
  • The rewrite button now counts, and offers itself for, only the scenes not already in the target format — a fully converted loom no longer offers a pass that would do nothing.
  • docs/features/fableloom.md ("Scene format") describes the new transport.

Option A from the issue. Option B (start + SSE + cancel, the Writers Room polish shape) stays available if reformat later grows a per-scene retry or starts rewriting image prompts too; nothing here blocks it.

Test plan

  • server/services/fableLoom/weave.test.js — reworked for the episode-scoped service, plus new coverage: stops at the ceiling and continues where it stopped (capped: true → next request sends only the leftover chunk); does not flag a run as capped when the model, not the ceiling, left scenes behind; holds the loom pin until every episode is converted, not just the one it rewrote; is a no-op with zero provider calls on an already-converted episode; 404s on an episode not in the loom.
  • server/routes/fableLoom.test.js — the reformat route validates the format and forwards (loomId, episodeId, input); the old loom-scoped path is gone (404).
  • client/src/components/fableloom/LoomSettingsDrawer.test.jsx (new) — walks episodes one request at a time, names the episode in flight, shows scenes left while re-asking a capped episode, does not re-ask an episode the model merely dropped scenes in, stops the walk on failure while still clearing the scene selection up front and re-reading afterwards, and offers no rewrite once everything is converted.
  • client/src/components/fableloom/loomFormats.test.js — the pending-scene/pending-episode helpers.
  • Full suites: cd server && npm test (1564 files pass; the one failure, services/loras.test.js > classifies and backfills keyLayout, is an unrelated pre-existing flake that passes in isolation) and cd client && npm test (735 files, 9366 tests, all pass, no act() warnings). cd client && npm run lint clean.

Closes #4794

…annot time out (#4794)

Rewriting a loom's scenes into the other format ran every provider call
behind a single held HTTP request — 227 seconds on a 13-scene loom, and
tens of minutes on a large one, long enough for a proxy or fetch timeout
to kill the response while the server kept writing chunks to the record.

The endpoint is now episode-scoped
(POST /:id/episodes/:episodeId/reformat), and each request is capped at
20 scenes: a run that stops at that ceiling with scenes it never sent
answers `capped: true`, and the settings drawer asks the same episode
again. The drawer walks the episodes that still have work and names the
one in flight, so a multi-minute pass shows where it is instead of
spinning "Rewriting…".

Two properties are unchanged on purpose:

- Resumability. Each chunk of 5 scenes is still persisted as it lands and
  stamped with the format it was written in, so a failure keeps what
  already succeeded and re-running rewrites only what is left.
- No half-and-half loom. The format pin stays the SERVER's call and lands
  only once no episode has an unconverted scene left — a browser closed
  mid-walk can't leave the loom claiming a format half its story isn't
  in, which a client-side final PATCH could not guarantee.

The rewrite button now counts (and offers itself for) only the scenes not
already in the target format, so a loom that is fully converted no longer
offers a pass that would do nothing.
@atomantic
atomantic merged commit 5bf229a into main Aug 22, 2026
7 checks passed
@atomantic
atomantic deleted the claim/issue-4794 branch August 22, 2026 04:16
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.

FableLoom reformat holds one HTTP request open for the whole loom (227s on 13 scenes)

1 participant