Build more than one app at the same time from the UI - #41
Merged
Merged
Conversation
The gateway already accepted three runs at the same time, but the UI disabled the submit button while the selected run was running. It also polled only the selected run, so the history did not show the progress of the other runs. - The UI disables the submit button only while the gateway accepts a prompt. It reads the list of runs every 5 seconds while a run or a delete is in progress, and it renders the history and the selected run from that list. The history shows the stage of each run that builds. - GET /ui/apps reconciles each run that a task owns, as a read of one run does. Thus a run whose task failed releases its concurrency slot when the UI refreshes, also when nobody selects that run. - The 429 response gives a detail that states the limit. The form shows it below the prompt, and the delete dialog shows the error of a delete that fails. Each refresh renders the run panel again, so the run panel cannot keep these errors. - claimRunApp refuses an app name while a different run builds that app. The two runs would write the same directory, and each run would wait for the deploys of the other. - The gateway imports the Render SDK statically. Vitest can give the real module to two dynamic imports of a mocked module that run at the same time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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
The gateway already accepted three runs at the same time (
maxConcurrentRuns), but the UI did not. It disabled Build and deploy while the selected run was running, and it polled only the selected run. Thus a second prompt was not possible, and the history did not show the progress of the other runs.This change lets a user start a run while other runs build, and follow all of them.
Changes
UI (
public/runs.js,app.js,table.js, both pages)GET /ui/appsevery 5 seconds while a run or a delete is in progress. It renders the history and the selected run from that list, and it does not poll each run. When no run is in progress, it stops. A read that fails is tried again at the next interval.Gateway (
app/gateway.ts)GET /ui/appsreconciles each run that a task owns, asGET /ui/apps/:runIddoes. Thus a run whose task failed or was canceled becomesfailedand releases its concurrency slot, also when nobody selects it.detail: "The factory builds at most 3 apps at a time, for all users. Submit the prompt again when a run finishes."Store and workflow (
app/store.ts,app/workflow.ts)claimRunApprefuses an app name while a different run builds that app, under the same advisory lock as before. Two such runs would write the same directory, and each one would wait for the deploys of the other. The refused run ends asfailed: "A different run is building demo/shop. Submit the prompt again when that run finishes."Docs: AGENTS.md (UI, invariants, durability), docs/README.md, and docs/FAQ.md.
Tests
npm run checkpasses: Biome,tsc, and 391 Vitest tests. New tests cover the list reconciliation, the 429 detail, and the refusal of a second run of one app.claimRunAppSQL ran against a throwaway Postgres 18: a claim while a different run builds the app, a claim while a delete is in progress, a new claim after the first run finished, and two claims of one app at the same moment (one wins).prompt-to-appanddelete-app. Both views: three prompts in sequence while runs built, a fourth prompt refused at the cap with the message below the prompt, a run whose task failed changed tofailedwhile not selected, polling stopped when no run was in progress, a refused delete (409) kept its dialog open and a retry deleted the app, focus stayed on a history item across refreshes, a failed read recovered at the next refresh, and the form error at phone width.Notes
pg.Poolinapp/store.tshas noerrorlistener. That fix is in a separate session and PR.🤖 Generated with Claude Code