From 00ccb9ec74d7fabcb84376bff69d19cf3cb5720c Mon Sep 17 00:00:00 2001 From: Alastair Drong Date: Tue, 28 Jul 2026 13:48:26 -0700 Subject: [PATCH 1/6] Seed initial build prompt for new apps only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a user creates a new app, the form description reached the build session only as the APP_DESCRIPTION env var, which the pod's CLAUDE.md merely *instructs* Claude to read — not guaranteed on turn one, so a carefully-described app could sit idle until the user re-typed it. Seed the description as Claude's opening prompt so a new app builds to spec immediately. Gate it strictly to brand-new apps: an existing-app "Build" session has no form, its description is back-filled from the app's stored sus.json (possibly stale), and the user is about to say what they want — auto-kicking a build there would risk clobbering intentional changes or nagging about outgrown specs. Detection uses the absence of sus.json (the existing scaffold gate), which is restart-safe: a recreated pod re-clones the committed scaffold so NEW_APP reads 0 and we don't re-kick on restart. The untrusted description is passed via an exported env var and expanded by the inner shell as a single quoted arg (never spliced into the command string), so it cannot break quoting or inject shell — verified with a hostile description containing quotes, $(...), backticks, and newlines. Also soften CLAUDE.md so resumed sessions treat the description as background rather than a spec to enforce. Co-Authored-By: Claude Opus 4.8 (1M context) --- build-pod/CLAUDE.md | 5 ++++- build-pod/entrypoint.sh | 32 +++++++++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/build-pod/CLAUDE.md b/build-pod/CLAUDE.md index 6759d0d..f98f22d 100644 --- a/build-pod/CLAUDE.md +++ b/build-pod/CLAUDE.md @@ -61,7 +61,10 @@ Check these environment variables for context about what you're building: - `APP_NAME` — the human-readable app name (if set) - `APP_DESCRIPTION` — what the app should do (if set) -If `APP_DESCRIPTION` is set, use it as your starting context. The user expects the app to match this description. If the current state of the app doesn't match, proactively offer to fix it. +If `APP_DESCRIPTION` is set, treat it as helpful background about what the app was originally meant to do. + +- **Brand-new app:** you'll receive an initial instruction to build it — follow that and get a working first version into the preview right away. +- **Existing app you're resuming:** the description may be the *original* spec, and the app may have intentionally moved on from it. Don't assume any difference is a mistake, and don't start changing things unprompted. Wait for the user to tell you what they want; only offer to align the app with the description if it seems genuinely relevant. --- diff --git a/build-pod/entrypoint.sh b/build-pod/entrypoint.sh index 6210e27..5c8e5e1 100755 --- a/build-pod/entrypoint.sh +++ b/build-pod/entrypoint.sh @@ -70,8 +70,13 @@ fi APP_DIR="/repo/${APP_TEAM:-_new}/${APP_SLUG:-_new}" mkdir -p "$APP_DIR" -# If this is a new app, create a minimal sus.json. +# If this is a new app, create a minimal sus.json. The absence of sus.json is +# also our "brand-new app" signal (an existing app's clone brings its own down), +# and it's restart-safe: a recreated pod re-clones the now-committed scaffold, so +# NEW_APP reads 0 and we don't re-kick a build on restart. +NEW_APP=0 if [ ! -f "$APP_DIR/sus.json" ]; then + NEW_APP=1 cat > "$APP_DIR/sus.json" < Date: Wed, 29 Jul 2026 10:49:30 -0700 Subject: [PATCH 2/6] Fix seed re-firing on reconnect; write scaffold sus.json via json.dump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses the #96 review findings. 1. BLOCKING — seed was not one-shot. ttyd spawns a fresh `claude` per *client connection*, and the frontend reconnects on its own (terminal iframe reload on a not-ready blip, full page reload after ~15s, manual refresh, second tab). Baking the prompt into the ttyd command re-fired "build the initial version" on every reconnect — potentially over an app the user had been iterating on for an hour, with autosave committing the clobber. NEW_APP is fixed for the pod lifetime so it can't gate this. Fix: drop the prompt in a seed file and have the inner per-connection shell claim it with an atomic `mv` that succeeds exactly once; later connections fall through to a plain interactive session. Verified one-shot across sequential reconnects and exactly-once under 8 concurrent connections; injection safety preserved (hostile description reaches claude as a single "$(cat)" word). 2. Scaffold sus.json now uses python3 json.dump instead of a heredoc. APP_DESCRIPTION is a free-form