Skip to content

Feature/self-updating-mastra#648

Open
raphaeltm wants to merge 22 commits into
mainfrom
feature/self-updating-mastra
Open

Feature/self-updating-mastra#648
raphaeltm wants to merge 22 commits into
mainfrom
feature/self-updating-mastra

Conversation

@raphaeltm

@raphaeltm raphaeltm commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Samples Checklist

✅ All good!

raphaeltm and others added 10 commits July 16, 2026 07:45
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>
@raphaeltm
raphaeltm had a problem deploying to deploy-changed-samples July 17, 2026 11:50 — with GitHub Actions Failure
…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>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 17, 2026 12:10 Failure
…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>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 17, 2026 13:00 Failure
@lionello lionello changed the title Feature/self updating mastra Feature/self-updating-mastra Jul 17, 2026
…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>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 17, 2026 16:47 Failure
…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>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 17, 2026 17:34 Failure
… 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>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 17, 2026 17:58 Failure
…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>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 18, 2026 14:18 Failure
… 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>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 18, 2026 16:08 Failure
…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>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 18, 2026 17:29 Failure

@lionello lionello left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the new model runner syntax models:

retries: 10
start_period: 10s
deploy:
resources:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 18, 2026 17:55 Failure
@defang-sam

defang-sam Bot commented Jul 18, 2026

Copy link
Copy Markdown

Thanks @lionello — addressed all four points in adc3082:

  1. Reboot / "die" button — added a Reboot environment control to the admin recovery console. It kills PID 1, so the container's restart: unless-stopped policy brings it back from the last published image (returning to the last-published state and dropping edits made since). It's refused mid-run/mid-publish so it can't tear down an in-flight write, and the UI treats the dropped connection as success.

  2. Atomic file changes — 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 errors. recordError now skips auto-captured client/server errors while a run is in flight, so a run is atomic from the observer's side too. Errors from the final committed state are still captured, and it fails open if agent_runs isn't available.

  3. models: runner syntax — migrated the chat managed LLM from the provider: { type: model } block to the top-level Docker Compose models: key, referenced by the dev service via endpoint_var/model_var (in both compose.yaml and compose.dev.yaml).

  4. replicas: 1 on the db — set on the Postgres service to avoid a split-brain second database.

Note: I validated both compose files parse and the admin console script parses, but did not run a full tsc/deploy from this environment (Mastra deps not installed here) — the sample build/CI covers that.

…, 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>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 18, 2026 18:17 Failure
@defang-sam

defang-sam Bot commented Jul 18, 2026

Copy link
Copy Markdown

Follow-up on the review feedback (commit b4f302c):

1. replicas: 1 — you're right, it belongs on dev (already set) to keep a single working copy; removed the one I'd wrongly added to db and left DB scaling to the platform.

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 (agent/src/paths.ts, git.ts: syncAgentWorktree/commitRun/applyToLive), so the dev server sees one atomic, already-typechecked update. Failed runs are discarded in the worktree and never touch the live app. This supersedes the error-suppression hack from the last commit, which I reverted. entrypoint.dev.sh creates the worktree, shares the image's node_modules, and prunes the stale worktree registration that rides in .git across a self-redeploy.

3. AWS deployment — the deployed chat model was GCP-only (gemini-2.5-flash, which resolves to a non-existent bedrock/gemini-2.5-flash on AWS). Switched it to ai/chat-default so Defang resolves it per provider (Gemini on Vertex, Claude on Bedrock) and the same Compose file deploys to both. Committed a .defang/aws stack (us-east-1) and documented GCP + AWS deploys in the README.

Verification status: the agent TypeScript typechecks (tsc --noEmit) and both compose files + the admin console script parse. I could not run an actual deploy from this environment (no CLI/creds), and the worktree + self-redeploy path really wants a live deploy to confirm — particularly (a) the worktree recreation across a publish self-redeploy and (b) ai/chat-default on Bedrock. Happy to dispatch a deploy-and-verify run to GCP beta and AWS if you want.

… 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>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 18, 2026 18:20 Failure
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.

2 participants