Skip to content

fix: live-update a commission's freshly-run render instead of requiring a reload (#4149) - #4270

Merged
atomantic merged 2 commits into
mainfrom
claim/issue-4149
Aug 15, 2026
Merged

fix: live-update a commission's freshly-run render instead of requiring a reload (#4149)#4270
atomantic merged 2 commits into
mainfrom
claim/issue-4149

Conversation

@atomantic

Copy link
Copy Markdown
Owner

Summary

After "Run now" on a Creative Commission, the fire creates the Creative Director project and returns immediately — the actual render lands minutes later, once the planner/render loop finishes. The detail page had no way to notice, so the new run sat as a "No render yet" card until the user reloaded, and the toast said as much: Run started — its render appears below once generation finishes (reload to refresh).

There is no server-side completion signal for a CD project today (no socket channel, and a commission run row is written once with status: 'started' and never revisited). So the page takes the second option from the issue's fix shape: a lightweight poll of the referenced projects while any of them is still generating.

  • The existing batch ?ids= fetch (getCreativeDirectorProjectsByIds, Resolve a commission's render projects without fetching every CD project #4148) is refactored into a single fetchProjects callback driven by both the id-set change and the poll, with a sequence guard so a tick that resolves after the id set changed can't reinstate the previous set's projects.
  • The poll runs through the shared useAutoRefetch hook at 5s (matching CreativeDirectorDetail), so it pauses while the tab is hidden and re-fires on return.
  • The gate: some run with status === 'started' whose project is either unresolved (a just-created project can postdate the last batch) or in a non-terminal CD status (draft / planning / rendering / stitching). draft counts as in-flight here — unlike CreativeDirectorDetail, where a draft is a project the user hasn't started, a commission fire creates and advances the project in the same breath.
  • Hard ceiling: a started run older than 6h is treated as stalled rather than in flight, so a crashed or pruned project can't poll forever on a tab left open. The clock is the last fetch attempt, so the bound keeps re-evaluating even while the endpoint is failing.
  • "loading…" only shows on the first attempt at a given id set, so a poll tick can't flash a resolved (or known-pruned) card back to the placeholder every 5s. A failed fetch keeps the last good map instead of blanking it.
  • The (reload to refresh) clause is dropped from the toast.

An idle detail page — every referenced project settled — issues no polling traffic at all.

Test plan

  • cd client && npx vitest run src/pages/CreativeCommissionDetail.test.jsx — 7 passed (3 pre-existing Resolve a commission's render projects without fetching every CD project #4148 cases + 4 new).
  • New cases, each verified against a bypass probe:
    • swaps a still-generating run to its finished render without a reload — fails when enabled is forced to false.
    • stops polling once every referenced project has settled — fails when enabled is forced to true.
    • never polls for a started run past the in-flight age ceiling — fails when enabled is forced to true.
    • no longer tells the user to reload when a run starts.
  • cd client && npx vitest run — full client suite: 648 files / 7895 tests passed.
  • npx biome lint --error-on-warnings on both changed files — clean.

Closes #4149

…ng a reload (#4149)

A commission fire creates the Creative Director project and returns; the render
lands minutes later, and the detail page had no way to notice — so the Run-now
toast told the user to reload.

There is no server-side completion event for a CD project today, so the page now
polls the referenced projects (the existing batch ?ids= route, via useAutoRefetch)
while any 'started' run still points at one in a non-terminal status, and stops
as soon as they all settle. A run older than 6h is treated as stalled rather than
in flight, so a pruned or crashed project can't poll forever on an open tab.
…roject batch as pruned, not in flight (#4149)

The poll gate counted any unresolved projectId as still generating, so a
commission with a pruned project polled every 5s until the 6h age ceiling.
Track the id set the batch last resolved SUCCESSFULLY: once it matches the
current set, a missing id is a pruned project (settled); a failed fetch is an
attempt but not a load, so it still retries. Also key the fetch effect on the
newest run id, so a run that ever reused a project id can't be judged against a
cached 'complete' snapshot.
@atomantic

Copy link
Copy Markdown
Owner Author

Reviewed with antigravity (agy), two rounds against the PR diff.

Round 1 — REQUEST_CHANGES. Two logic findings, both fixed in a5702f3:

  • The poll gate counted any unresolved projectId as still generating, so a commission holding a pruned project polled every 5s until the 6h age ceiling. Now the page tracks the id set the batch last resolved successfully — once it matches the current set, a missing id is a pruned project (settled), while a failed fetch is an attempt but not a load and still retries.
  • The batch refetch keyed only on the project-id set, so a run that ever reused a project id would be judged against a cached complete snapshot and never start polling. The newest run id is now a dep too.

Round 2 — APPROVE. No further findings: dependency completeness, the sequence-counter race guard, the start/stop/ceiling behavior of the gate, and the pruned-vs-failed-vs-empty sentinel separation all check out.

Both gate branches carry bypass probes: forcing enabled to true/false, and forcing the unresolved-id branch to true/false, each fails exactly the case that pins it.

@atomantic
atomantic merged commit 0c82db6 into main Aug 15, 2026
7 checks passed
@atomantic
atomantic deleted the claim/issue-4149 branch August 15, 2026 05:15
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.

Live-update a commission's freshly-run render instead of requiring reload

1 participant