Feature/self-updating-mastra#648
Conversation
Production app (Cloud Run) + live-editable dev service (forced onto Compute Engine via second host-mode port), shared managed Postgres, and a Vertex AI model service for the Mastra coding agent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r glue Hono server (127.0.0.1:4111, container-internal only) hosting a Mastra Agent with Workspace file tools path-scoped to todo-app/. Runs are fire-and-forget with a polled log, and a deterministic tsc --noEmit verify (with one repair round) runs outside the agent's control. Caddy fronts the Next dev server and absorbs its restarts; entrypoint git-inits a runtime baseline so agent edits have history. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ibility pg 8.22+ treats sslmode=require as verify-full and rejects Cloud SQL's internal CA (UNABLE_TO_VERIFY_LEAF_SIGNATURE). Let pg derive TLS behavior from the connection string instead of overriding ssl in code. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ma init - Better Auth: trust proxy headers and disable the origin check for demo mode — the TLS-terminating load balancer made login fail with 'Invalid origin'. Marked TODO(security) for the pre-release security pass. - Serialize ensureSchema() with a pg advisory lock: app + dev racing CREATE TABLE IF NOT EXISTS on a fresh DB hit duplicate pg_type_typname_nsp_index. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… runs on model error gemini-3.5-flash is not a real Vertex publisher model, so the coding agent's Vertex call was rejected by the vertexai.allowedModels org policy. Switch to gemini-3-flash (an allowed, existing model). Also surface model/stream errors as failed runs: previously a model error was logged but the run still typechecked (a no-op, since nothing changed) and reported 'Changes are live', masking the failure. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…erved in europe-west2) gemini-3-flash returns 404 NOT_FOUND in europe-west2. gemini-2.5-flash is GA there and validated end-to-end: the coding agent edits app source and the change hot-reloads live. Keeps the London-demo europe-west2 setup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rver The coding agent edits todo-app/ and can break the Next.js dev app, including the admin page used to send fixes to the agent — a deadlock. Move the admin console out of Next.js into the agent server (Hono), which lives outside the agent's editable tree: - agent server now serves /admin (console), /admin/login (break-glass), /admin/dispatch, /admin/runs/:id; reads feedback + validates admins directly against Postgres (pg added to the agent). - Caddy routes /admin* to the agent (:4111), everything else to Next.js (:3001), so the console survives a Next.js crash. - Auth: reuse the better-auth admin login (session validated against Postgres, works while the app is down) + an ADMIN_TOKEN break-glass fallback. - Remove the old Next.js admin page, /api/agent/* routes, and admin-console component so nothing admin-related remains in the agent-editable tree. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…guardrail Deploy 1 of the trustworthy self-updating app: Run history + model + UX fix (agent server): - Persist every run to Postgres (agent_runs): status, full log, model used, timestamps; survives restarts and redeploys. - Admin console gains a 'Recent runs' list (click to view a past run's log + model), client-rendered feedback + runs that refresh after a run — the run log no longer vanishes with nothing to show. - Runs record which model was used (CHAT_MODEL). Automatic guardrail (per product decision: auth-only for now, no approvals): - beforeToolCall hook hard-blocks the agent writing/editing/deleting lib/auth.ts — enforced in code, not just the prompt. Error capture -> backlog (dev and prod): - Server errors (instrumentation onRequestError) and client errors (window listeners + error boundaries) are recorded as 'error' feedback items the admin can dispatch to the agent. Deduped and length-capped. - feedback gains a 'source' column; user_id nullable for system errors. - Friendly error boundaries (error.tsx, global-error.tsx) that report and offer retry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…demand verdict Every successful agent run now commits its edits (scoped to todo-app/) with Run-Id/Feedback-Id/Model trailers pointing at the Postgres rows it addressed; failed runs restore the working tree to the last good commit. Runs are serialized (409 on concurrent dispatch). The admin console gains a History panel (commits cross-linked to runs), an admin-only revert-as-new-commit with a post-revert typecheck report, and an on-demand 'Grade this run' verdict. Durable history plumbing for the upcoming self-publish: .git is no longer dockerignored (it rides the build context into the next dev image, under the 100 MiB limit since heavy dirs are gitignored) and the entrypoint keeps an existing repo instead of re-initializing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…blish interactive login The Publish panel has the dev container run 'defang compose up --detach' on its own Compose project, overwriting both the dev and app services from the live workspace — intentionally no GitOps. Fabric authorization is a fresh interactive 'defang login' per publish: the console surfaces the auth URL in a new tab (the CLI's redirect goes to the auth server and it long-polls for the code, so no callback to this container is needed) and the admin must complete it every time; the token lives in an ephemeral state dir wiped when the publish ends. GCP credentials are the dev service's own service account (deploy roles granted declaratively via x-defang-roles) through the metadata server — no key files anywhere. Publishes are recorded in a deployments table with streamed CLI logs and a publish marker commit (Deployment-Id trailer, authored as the admin); on boot the next container generation marks the deployment live when its HEAD is that publish commit. Runs and publishes are mutually exclusive. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… publishes failed Set the cancelled phase before killing the login/deploy child so their exit handlers no-op instead of overwriting the deployment row with 'failed'. Found by the headless publish-flow validation against the beta stack. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…never parsed CONSOLE_SCRIPT is authored in a TS template literal, so \n escapes in client-facing strings (revert alert, whoami box, publish log tail) were processed server-side into real newlines inside JS string literals. The served script failed to parse (SyntaxError: Invalid or unexpected token), loadData() never ran, and every admin panel hung at 'Loading…' while the endpoints themselves were healthy. - Escape the three occurrences as \\n so the browser receives \n. - Compile CONSOLE_SCRIPT with new Function() at module load, without running it, so a future unparseable console script fails the deploy instead of shipping a dead console. - Surface loadData() failures in the panels instead of silently leaving 'Loading…' forever. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… in admin Publish/deployment logs were already persisted to the deployments table, but the admin console only showed the in-memory logTail while a publish was in the deploying phase — the moment it failed, the panel dropped to just the error banner and the streamed logs vanished, with no way to see why it failed. - Add GET /admin/deployments/:id returning the full persisted log. - Keep the publish list lightweight (drop log bodies from listDeployments, expose length only) so the 2s panel poll stays cheap. - Make every deployment row clickable to open its stored log, and auto-open a failed publish's log so the error is visible in place. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… publish
The publish flow authenticates to GCP keyless via the VM's metadata-server
service account, whose access token carries no email — the stable CLI cannot
resolve the caller principal there ('no email found in token info') and every
publish died at account-info. The fix (DefangLabs/defang#2174, metadata-server
email fallback) is only in the nightly so far.
Resolve the current nightly asset at build time instead of pinning: nightly
assets are replaced daily, so a pinned asset URL would 404 on the next
self-rebuild and brick publishing. TODO: re-pin to the first stable release
containing #2174.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fig listing The first real publish with working GCP identity got past account-info and died at 'failed to list secrets: secretmanager.secrets.list denied' — compose up validates the stack's config (POSTGRES_PASSWORD etc.) by listing its Secret Manager secrets, and the x-defang-roles set predates that path (every earlier deploy ran as an owner-level key, so it was never exercised). Metadata-only viewer is enough: the CLI lists names; the CD (which has secretmanager.admin) reads the values. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lionello
left a comment
There was a problem hiding this comment.
Comments:
- add a "reboot" (ie. die) button so we come back with old state.
- file changes should ideally be atomic, because changes that touch many files will result in tons of auto-submitted client errors.
| # Managed LLM for the coding agent, using the cloud provider's native | ||
| # inference service (Vertex AI on GCP). Defang provisions an OpenAI-compatible | ||
| # proxy and injects CHAT_URL / CHAT_MODEL into services that depend on it. | ||
| chat: |
There was a problem hiding this comment.
use the new model runner syntax models:
| retries: 10 | ||
| start_period: 10s | ||
| deploy: | ||
| resources: |
There was a problem hiding this comment.
set replicas: 1 to avoid multiple split-brain
Addresses Lionello's review feedback:
- Add a "Reboot environment" (die) button to the admin console. Killing
PID 1 stops the container; its restart:unless-stopped policy brings it
back from the last published image, recovering the environment to the
last-published state when a change has wedged it beyond a git revert.
Refused mid-run/mid-publish so it can't tear down an in-flight write.
- Make agent runs atomic to the observer: a run is already transactional
at the git level (commit on success, revert on failure), but its
one-file-at-a-time edits hot-reload through transient broken states
that flooded the backlog with auto-captured client/server errors. Skip
recording auto-errors while a run is in flight; final-state errors are
still captured (fail-open if agent_runs is unavailable).
- Migrate the `chat` managed LLM from the `provider: {type: model}` block
to the top-level Docker Compose `models:` runner syntax, referenced by
the dev service via endpoint_var/model_var (compose.yaml + compose.dev.yaml).
- Set replicas: 1 on the Postgres db service to avoid a split-brain
second database.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Thanks @lionello — addressed all four points in adc3082:
Note: I validated both compose files parse and the admin console script parses, but did not run a full |
…, replicas Follow-up to PR #648 review: - Atomic edits via a git worktree. The dev server serves the same files the agent edits, so a multi-file run hot-reloaded through half-applied, broken intermediate states — flooding the backlog with transient client errors. The coding agent now edits an ISOLATED git worktree (paths.ts) and typechecks there; a successful run is fast-forwarded into the live served tree in one step (git.ts: syncAgentWorktree/commitRun/applyToLive), so users see a single atomic, already-typechecked update. Failed runs are discarded in the worktree and never touch the live app. Supersedes the earlier error-suppression hack (reverted in report-error.ts). entrypoint.dev.sh creates the worktree and shares the image's node_modules; the stale worktree registration that rides in .git across a self-redeploy is pruned on boot. - replicas: 1 belongs on `dev` (already set) to keep one working copy; removed the incorrect replicas: 1 from `db` — let the platform manage the database. - Multi-cloud model: the `chat` model is now `ai/chat-default` instead of the GCP-only `gemini-2.5-flash`, so Defang resolves it per provider (Gemini on GCP Vertex AI, Claude on AWS Bedrock) and the same Compose file deploys to both. - AWS deploy target: committed `.defang/aws` stack (us-east-1, force-added past the .gitignore) and documented GCP + AWS deployment in the README. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Follow-up on the review feedback (commit b4f302c): 1. 2. Atomicity via worktrees — agreed the real issue is that the dev server serves the same files being edited, not a separate checkout. The coding agent now edits an isolated git worktree and typechecks there; a successful run is fast-forwarded into the live served tree in a single step ( 3. AWS deployment — the deployed Verification status: the agent TypeScript typechecks ( |
… lookup Publish attempt three: past identity, past config listing, died at PrepareDomainDelegation — composeUp resolves the delegate-domain zone via Cloud DNS before launching the CD, and the dev SA had no DNS role (the googleapi 403 surfaces as the generic 'double check the GCP project ID … Forbidden'). The zone already exists from the first deploy, so read-only dns.reader suffices; DNS record writes remain the CD's job. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Samples Checklist
✅ All good!