deploy-runbook
Skill264 starsChanged 43 days agoApache-2.0View on GitHub
The exact three.ws production deploy runbook (clean worktree, build:gcp chain, submit gates, cron sync, CDN purge, verify). Load before any deploy.
- Reads credentials
Files
Deploy runbook (API/frontend)
Remember stop-and-ask gate 2: production deploys need owner approval unless the owner's current instruction is itself the approval. Full runbook incl. LB/DNS/TLS/env/rollback/recovery: docs/ops/gcp-production.md. GPU workers deploy from their own workers/<name>/cloudbuild.yaml.
- Reclaim disk first:
npm run clean:worktrees(add--applyto act). Deploy worktrees are created by every deploy and removed by nobody. On 2026-08-04 ten had accumulated and filled the 126 GB disk to 100%, which fails in a way that reads like a different bug entirely:git worktree adddies mid-checkout withNo space left on deviceand leaves a half-written tree behind. The script only reclaims a linked worktree that is detached, clean, and idle past--min-age-hours(default 2, so a concurrent agent's live build is never deleted); anything with uncommitted work is reported and kept. It applies the same rules to the subagent worktrees under.claude/worktrees/, which additionally must have theirworktree-agent-*branch already merged into HEAD; the merged branch is deleted withgit branch -d(on 2026-10-08 nine of those held 31 GB). - Prepare a CLEAN worktree:
npm run prep:worktree -- --apply. That is the whole step; it encodes everything below, and running it beats staging by hand. Plan first with a barenpm run prep:worktree(writes nothing),--path <dir>to stage your own tree instead of colliding with a concurrent agent on the default/workspaces/.deploy-wt,--forceto replace an existing tree (refused if it holds uncommitted work). What it does:git worktree add --detach <path> HEAD, then hardlinks every artifact in theARTIFACTSlist ofscripts/prepare-deploy-worktree.mjswithcp -alon the same filesystem (not just the first). Three of them are build-critical,node_modules,chat/node_modulesandcharacter-studio/build; the optional ones (walk-sdk/dist,multiplayer/node_modules,animation-sources) are what let the worktree also run the repo's own test suite, so a pre-shipnpm testcan be taken at the exact commit being shipped instead of in the shared tree. It then copies BOTH.envand.env.local. Omitting either nested one kills the build minutes in: an OOM exit 144 on the avatar-studio rebuild, orCannot find package '@sveltejs/vite-plugin-svelte'on the chat build. A nested artifact that is missing from the source tree is BUILT first (deps:chat,build:avatar-studio) rather than skipped, which is the failure a hand-runcp -alproduces on a machine that has never deployed..env.localis the one that carriesDATABASE_URL, and without it step 3's migration gate cannot read the schema state and refuses to submit. Remove it when the deploy lands (git worktree remove --force <path>); that is what step 0 exists to clean up after. Seedocs/ops/gcp-production.md. Build:
npm run build:gcp. ORDER IS LOAD-BEARING andbuild:gcpalready encodes it. Never hand-run the steps in a different order. The full chain, in order:check:conflicts→check:browser-graph→check:tdz-bootstrap→ensure:avatar-studio→build:info:snapshot→docs:freshness→build:lib:full(the UMD lib) →build:avatar-sdk→build:chat→build(the frontendvite build) →publish:lib→build:info→check:dist→check:pagesWhy the order is load-bearing:
check:conflictsruns first so an unresolved merge-conflict marker fails in seconds instead of after a full build.check:tdz-bootstrapsits beside it for the same reason: it refuses a browser module whose top-level bootstrap call runs above thelets that call writes. JavaScriptCore checks an assignment target's temporal dead zone before evaluating the right-hand side and V8 does not, so that ordering ships green on Chrome and throws "Cannot access uninitialized variable." on every Safari, which is how/avatars/:idwent dead on iOS and macOS while every desktop check passed.build:lib:fullwritesdist-lib/agent-3d.js,build:avatar-sdkcopies that bundle intoavatar-sdk/dist/index.mjs, and the frontendvite buildresolvesavatar-sdk/src/agent.jsagainst it, so both must come BEFOREbuild; skipping them shipped whatever staleavatar-sdk/distthe build machine happened to hold, and in a fresh worktree failed the build outright withCould not resolve "../dist/index.mjs"(2026-08-17). The frontendvite buildruns withemptyOutDir, so it WIPESdist/and everything that writes intodist/must come after it: that is whypublish:lib, which mirrors the UMD lib intodist/, followsbuildrather than sitting next tobuild:lib:full, andcheck:distfails without that bundle present.publish:libwrites only the moving channels (latest,1,1.5) from the fresh build; every released version directory is downloaded from the write-once archivegs://three-ws-lib-releasesand checked againstdata/agent-3d-releases.json, so the build needs network access to storage.googleapis.com, and it fails ifpackage.jsonnames a version that was never cut. A versioned URL is immutable and SRI-pinned by embedders, so it must never change bytes: cut a new version withnpm run release:libafter bumpingpackage.json, never by rebuilding an old one (2026-10-01: rebuilding 1.5.2 on every deploy broke every published SRI pin).check:distalso fails when a documented SRI pin disagrees with the ledger;npm run check:sri-pins -- --fixrewrites stale pins.ensure:avatar-studioconsumescharacter-studio/buildandbuild:chatconsumeschat/node_modules, which is exactly why step 1 stages worktree artifacts.docs:freshnessmeasures documentation drift against git history and writespublic/docs-freshness.jsonplus its summary, so it must run beforebuildcopiespublic/intodist/; running it here is what keeps/docs/freshnessand every docs-page freshness badge describing the commit actually deployed instead of whenever someone last ran it by hand. Do NOT hand-runbuild:vercelas the frontend build: it builds the SDK/lib sub-artifacts, NOT the static HTML pages, sodist/ends up without/,/create, etc. andcheck:distfails.- Submit:
npm run deploy:gcp:submit. That isdb:check(exit 4 if any migration is pending), thencheck:gcloudignore, thenaudit:deploy(no committed symlinks, no unsatisfied peer deps, no undeclaredapi/imports, decoder assets present, and an installednode_modulesthat matchespackage-lock.json), thencheck:api-imports(imports every module underapi/and fails on any that throws while loading, since the server imports a handler on first hit and a load-time throw is a 500 on every request to that route rather than a build error), then the samegcloud builds submit --config server/cloudbuild.yaml --region us-central1 --project aerial-vehicle-466722-p5.check:gcloudignoresimulates the upload and fails when a file the server imports at runtime would be excluded from the build context: an omittedservices/home-relay/src/token.jsshipped revision 00412 answering/api/healthzwithERR_MODULE_NOT_FOUNDon every request (2026-09-04). The lockfile match exists because the image installs from the lockfile while every local test, and every deploy worktree that hardlinks thisnode_modules, runs whatever is on disk: a dependency bump nobody reinstalled shipped@x402/extensions2.25.0 untested and every paid x402 route answered 500 on import (2026-09-17). If it fails, runnpm install, thennpm test, before deploying. Do NOT hand-run the baregcloud builds submit: it is the only step that would have caught new code shipping over an old schema, and skipping it is how production ended up serving handlers that query columns their migration never applied (column "blocks_behind" does not exist, live on 2026-08-14). If the gate says migrations are pending, apply them first (npm run db:status, thennpm run db:migrate); if it saysDATABASE_URL is not set, step 1 did not copy.env.local. EVERY cloudbuild config must pinserviceAccount: .../three-ws-build@...(the default compute SA was deleted), and manual submits need--substitutions=SHORT_SHA=manual$(date +%s)when the config tags images with$SHORT_SHA. - Sync new crons, then purge CDN:
npm run deploy:gcp:sync-crons, thennpm run deploy:gcp:purge-cdn. The sync creates a Cloud Scheduler job for every cron invercel.jsonthat has none and touches no existing job; without it a new cron never fires (four sat dead for up to three weeks until 2026-10-08). The purge is synchronous on purpose. Do not re-add--async, or post-deploy checks read stale edge content and report phantom failures. - Verify the deploy landed.
curl -s https://three.ws/api/versionreturns the live commit SHA + Cloud Run revision, andnpm run smoke:prodsweeps every page declared indata/pages.jsonagainst the live site (deploy:gcpruns it automatically after the purge).
The whole build + submit + purge in one command: npm run deploy:gcp:full.
In this repository62 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- docs/llms-full.txtllms.txt
- docs/llms.txtllms.txt
- llms-full.txtllms.txt
- llms.txtllms.txt
- public/llms-full.txtllms.txt
- public/llms.txtllms.txt
- anatomySkill
- authenticate-walletSkill
- build-an-agent-skillSkill
- connect-three-ws-mcpSkill
- create-3d-avatarSkill
- create-a-three-ws-agentSkill
- embed-three-ws-avatarSkill
- find-3d-assetsSkill
- fundSkill
- gcp-triageSkill
- generate-3d-modelSkill
- hire-an-agentSkill
- metamask-agent-walletSkill
- metamask-agent-workflowsSkill
- monetize-serviceSkill
- okx-agent-chatSkill
- okx-agentic-walletSkill
- okx-agent-identitySkill
- okx-agent-payments-protocolSkill
- okx-agent-taskSkill
- okx-ai-guideSkill
- okx-ai-supportSkill
- okx-audit-logSkill
- okx-dapp-discoverySkill
- okx-defi-investSkill
- okx-defi-portfolioSkill
- okx-dex-bridgeSkill
- okx-dex-marketSkill
- okx-dex-signalSkill
- okx-dex-socialSkill
- okx-dex-strategySkill
- okx-dex-swapSkill
- okx-dex-tokenSkill
- okx-dex-trenchesSkill
- okx-dex-wsSkill
- okx-growth-competitionSkill
- okx-how-to-playSkill
- okx-onchain-gatewaySkill
- okx-securitySkill
- okx-task-watchSkill
- okx-wallet-portfolioSkill
- pay-for-serviceSkill
- query-onchain-dataSkill
- rig-a-modelSkill
- search-for-serviceSkill
- sell-an-agent-skillSkill
- send-usdcSkill
- tradeSkill
- x402Skill
- changelog-and-docsSkill
- engineering-standardsSkill
- maintain-claude-mdSkill
- The first 60 shown.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

