Skip to content

v1.2.8 — audit fixes + Prompt-node dialog

Choose a tag to compare

@crisng95 crisng95 released this 09 May 17:45
· 78 commits to main since this release

Bundle of audit fixes from the v1.2.7 Storyboard ship + a UX bug discovered post-release.

Bug Fixes

Prompt nodes were opening the image-generation dialog (high-impact UX)

Clicking Generate on a Prompt node opened the same dialog used for image targets — title "Generate image", aspect-ratio chips, variants stepper. Submit then dispatched a `gen_image` request, which is meaningless for a text-only node. Storyboard nodes had a related cosmetic bug — the title also said "Generate image" because the title's fall-through default lumped them with image targets.

Fix:

  • Title now resolves cleanly across all 5 generable types: video / character / storyboard / prompt / image. "Generate image" is the final fallback, no longer a catch-all.
  • New `isPrompt` branch in handleSubmit calls `/api/prompt/auto` on empty text, then `patchNode({data:{prompt}})` to persist. No image dispatch.
  • Aspect-ratio + Variants stepper hidden when `isPrompt` (text-only nodes don't have aspect/variants).

Storyboard refs lost on per-shot retry (from audit)

`retryStoryboardShot` wasn't sending `ref_media_ids` in the request params, and the worker fallback read `data.globalRefMediaIds` which was never persisted. Result: every retry ran with no character / wardrobe refs, defeating the consistency story. Fix collects refs at retry time via `collectUpstreamRefMediaIds(rfId)` (matches the dispatch path) and pins the worker-side contract with a regression test.

Internal / Chores (audit follow-ups)

Backend (commit `7197485`)

  • S2 — Tightened parents validation in `_handle_gen_storyboard`: `isinstance(v, int)` accepts `bool` because bool subclasses int in Python. Now rejects explicitly via `not isinstance(v, bool)`. A direct API caller posting `shot_parents=[null, true]` no longer slips through.
  • S4 — Worker now persists `Node.data.shots[]` progressively after each phase (Phase A roots, then each Phase B BFS level), so the frontend sees roots populate before continuations finish. Final result still returned to `req.result` for the polling path. Added `_persist_storyboard_progress` helper.

Frontend (commit `a1d376e`)

  • S1 — `dispatchStoryboard` now persists `globalRefMediaIds` in its patchNode payload so the worker's retry-handler fallback is real, not dead code.
  • S3 — `retryStoryboardShot` now registers in the `active[rfId]` poll registry, so `cancelGeneration(rfId)` (called when a node is deleted) actually stops the retry poll. Previously it leaked.
  • S5 — Post-retry aggregate compute returned `partial` whenever any shot was still queued/running. Now returns `running` when an in-progress shot exists, and only `partial` for the genuine mixed-success-and-failure case.

Tests

  • 8 worker tests: bool-parents rejection (`test_gen_storyboard_rejects_bool_parents`), progressive persistence (`test_gen_storyboard_persists_progress_between_phases` + multi-call counter), refs propagation regression (`test_retry_storyboard_shot_forwards_ref_media_ids`).
  • pytest `347 → 367` (Storyboard ship was `+24`, audit fixes `+4`).
  • `tsc --noEmit` clean.

Behind the scenes

This release was driven by an end-to-end audit of every input/output boundary in the Storyboard feature (frontend → POST /api/requests → worker → flow_sdk → result → frontend persist), then a 3-worker /team execution to fix all findings in parallel without touching each other's lanes.