Applying updates
When you ship a new Shmastra version (new widget features, new skills, new adapters), you update every user's sandbox to the latest origin/main through this pipeline. Users stay signed in; their dev server keeps running during most of the update thanks to a git worktree.
Where to trigger it
- Sandbox table → select rows → Update selected or Update all.
- CLI:
npx tsx manage/index.mts <sandbox_id>(one sandbox) ornpx tsx manage/index.mts(every sandbox, sequential).
Web-UI updates run in parallel with a concurrency cap of 5.
The 8 phases
Every update runs through these phases in order. The UI shows a phase progress bar per sandbox and streams logs over SSE.
- fetch — configure git identity, commit any local sandbox changes (so no work is lost), fetch
origin, and check whether we're behind the update branch (default:main; configurable viaSANDBOX_UPDATE_BRANCH). - merge — create a git worktree on the update branch, merge any local commits into it. The dev server keeps running in the main working tree throughout this phase.
- install —
pnpm installinside the worktree. - build —
pnpm dry-runto verify the build compiles. If it fails, the pipeline bails out without touching the main tree. - migrate — stop pm2 briefly to flush DuckDB's WAL, snapshot the observability database, and run any required schema migrations against the worktree copy. If migration fails, the original snapshot is restored and the update aborts — the sandbox stays on its current schema and code.
- apply — fast-forward the main branch to the update branch tip and run
pnpm installin the main directory. - patch — run any pending scripts from
scripts/patches/(see Patches). Each sandbox tracks itsversionin Supabase; only newer patches are applied. This phase also unconditionally re-syncs cloud-managed artifacts (MCP server config, skill files) to ensure every sandbox has the latest versions even if no code update was needed. - restart — stop pm2, swap migrated databases into place (if a migration ran in phase 5), and restart pm2 processes (
shmastra,healer). The UI polls the/api/versionendpoint and waits until the server reports the new version before calling the update complete.
Phases 7 and 8 always run, even when the sandbox is already up to date with the update branch. This keeps MCP configuration, skill files, and other cloud-managed artifacts in sync on every pass.
Only phases 6–8 mutate production state. The pipeline is safe to cancel or retry at any earlier phase.
Rollback on failure
If phase 6 (apply) succeeds but any later phase fails, the pipeline automatically rolls the sandbox back to its exact pre-update state:
git reset --hardto the commit that was HEAD before the merge.- Restore the DuckDB snapshot taken in phase 5 (if a migration ran).
- Re-run
pnpm installto syncnode_moduleswith the restoredpackage.json/ lockfile. - Restart pm2 on the now-consistent old code and schema.
The result is that a failed update leaves the sandbox byte-equivalent to its state before the update started. Users experience a brief downtime window (the restart in step 4) but return to a working sandbox on the previous version.
Conflict resolution
Because users (or the healer) can edit sandbox files between updates, merges can conflict. The pipeline resolves them automatically:
- Lockfiles (
package-lock.json,pnpm-lock.yaml,yarn.lock) — deleted and regenerated on install. - Config files (
package.json,tsconfig.json, others) — resolved via a direct call to the Claude API that picks a merged result. - Source files — resolved by a Mastra agent with full workspace tools; it reads both versions and writes a unified result.
You can watch the resolutions in the streaming log.
Stopping a running update
The Stop button on the row. The pipeline completes the current phase (so it doesn't leave the sandbox in a partially-applied state) and exits. Use stop if you notice something fishy in the logs; you can re-trigger later.
CLI output
npx tsx manage/index.mts <sandbox_id>Prints phase-by-phase logs to stdout. Exit code is 0 on success, non-zero on any phase failure — suitable for a cron or GitHub Action.