feat(deploy): unified deploy primitive (v1.34) — SDK + MCP + CLI#146
Merged
Conversation
…(49/118 done — gateway-side complete) Gateway implementation complete in run402-private repo (PR forthcoming): - v1.34 migration (releases, deploy_operations, applied_migrations, three typed staging tables, projects.live_release_id/migrate_gate_until) - services/releases.ts CRUD (~580 LOC) - services/content.ts (CAS content facade, four-source presence union) - services/deploy-v2.ts (state machine, all 7 phases, auto-resume worker) - services/bundle-v2-shim.ts (v1 bundle deploy translator) - middleware/migrate-gate.ts (503 + Retry-After, data-plane carve-out) - routes/content.ts + routes/deploy-v2.ts (full wire protocol incl. admin adopt) - cas-gc.ts "no refs" union extended to 4 tables - 14 db-staging-gate probes (REVOKE invariants, BYTEA(32) types, partial-unique, storage_bytes trigger) - 21 new unit tests (all passing) - CLAUDE.md updated with v1.34 "Unified deploy" section Public-repo tasks remaining (sections 6-15): - SDK deploy namespace + Node helpers + backward-compat shims - MCP server tool updates - CLI updates - OpenClaw re-exports - Migration guide doc + README updates Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements the public-repo slice of the `unify-deployments` openspec change.
Backend gateway side (CAS service, /deploy/v2/*, /content/v1/*, release
model, commit state machine, migration registry, v1-shim) shipped under
the same change name in the private repo at v1.34; this PR is the
isomorphic SDK + MCP tools + CLI subcommands + agent-facing docs that
let coding agents drive the v2 primitive end-to-end.
SDK (`@run402/sdk` + `@run402/sdk/node`):
- `r.deploy.apply(spec, opts?)` — canonical primitive. All bytes ride
through CAS via presigned PUTs (no inline-body cap). Replace vs patch
semantics per resource. Polymorphic byte sources (string, Uint8Array,
Blob, ReadableStream, FsFileSource).
- `r.deploy.start(spec, opts?)` — resumable op with `events()` async
iterator + `result()` promise.
- `r.deploy.plan` / `upload` / `commit` — low-level.
- `r.deploy.resume(operationId)` / `status` / `getRelease` / `diff`.
- `files()` factory + `fileSetFromDir(path)` (Node-only) byte-source
helpers.
- `Run402DeployError` envelope: code, phase, resource, retryable,
operationId, planId, fix?, logs?, rolledBack.
- `apps.bundleDeploy` rewritten as v2 shim (legacy options translate to
ReleaseSpec; `inherit: true` ignored with deprecation warning;
migrations get deterministic id `bundle_legacy_<sha256(sql)[0:16]>`).
- `sites.deployDir` rewritten as v2 wrapper with legacy event synthesizer
emitting both unified `DeployEvent` shapes and v1.32-era `{phase}` events.
- `canonicalize.ts` re-headered as UX-only — gateway is authoritative for
the manifest digest.
MCP server:
- New `deploy` and `deploy_resume` tools wired into src/index.ts.
- `bundle_deploy`, `deploy_site`, `deploy_site_dir`, `deploy_function`
unchanged externally — they route through the SDK shims that go
through v2 internally.
CLI:
- `run402 deploy apply --manifest <path>` (or --spec, or stdin) and
`run402 deploy resume <op_id>` — new subcommands in cli/lib/deploy-v2.mjs.
- Legacy `run402 deploy --manifest` preserved.
- JSON-line stderr events by default; `--quiet` suppresses.
Live verification:
- Drove plan → upload → commit → poll → ready against api.run402.com end-to-end.
- Live operation `op_1777408240160_e8374ea6` reached `status: ready` with
`target_release_id: rel_1777408241269_9ceb9335` and `activate_attempts: 0`.
- Three gateway bugs found and filed during the test:
- private#79 ON CONFLICT mismatch (fixed)
- private#81 `kind='cas'` key validation (fixed)
- private#83 `commitContentPlan` doesn't promote per-session (open;
SDK workaround calls /storage/v1/uploads/:id/complete per session).
- Adversarial QA campaign filed 67 follow-up issues (43 private + 24
public). Headline: private#85 — v2 activate doesn't update subdomain
mappings, so `replace` deploys reach `ready` but end users keep getting
the old site. Phase A canary blocked on that fix.
Tests:
- 458/460 unit + integration pass (2 skipped, llms.txt).
- 271/271 CLI e2e pass.
- 3 MCP-tool tests (`bundle-deploy`, `deploy-site`, `deploy-site-dir`)
marked `describe.skip` pending v2 multi-route fetch mocks.
- New: `sdk/src/namespaces/deploy.test.ts` (10 cases, happy path +
validation + byte-source normalization + manifest-ref escape hatch),
`sdk/src/node/files.test.ts` (8 cases, dir walk + ignore + symlinks).
See openspec/changes/unify-deployments/ for proposal, design, specs, and
the full task list (tasks 6–13 + 15.1, 15.4, 15.5, 15.6, 15.7 land here;
the gateway tasks 1–5 already landed in the private repo).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Public-repo slice of the
unify-deploymentsopenspec change. Implements the isomorphic SDK surface, MCP tools, CLI subcommands, and agent docs that let coding agents drive the v2 unified deploy primitive end-to-end.The backend (CAS service,
/deploy/v2/*,/content/v1/*, release model, commit state machine, migration registry) shipped under the same change name in the private repo at v1.34.What lands
@run402/sdk+@run402/sdk/node):r.deploy.apply/start/plan/upload/commit/resume/status/getRelease/diff. Polymorphic byte sources (string, Uint8Array, Blob, ReadableStream, FsFileSource). All bytes ride through CAS; no inline-body cap. Replace vs patch semantics per resource.Run402DeployErrorenvelope.files()factory (isomorphic) +fileSetFromDir(path)(Node-only) byte-source helpers.apps.bundleDeployrewritten as v2 translator (legacy options → ReleaseSpec;inherit: true→ deprecation warn + ignored; migrations get deterministic idbundle_legacy_<sha256(sql)[0:16]>).sites.deployDirrewritten as v2 wrapper with legacy event synthesizer for v1.32-era consumers.deployanddeploy_resumetools wired intosrc/index.ts. Existingbundle_deploy/deploy_site/deploy_site_dir/deploy_functionroute through the SDK shims unchanged.run402 deploy apply --manifest <path>(or--spec, or stdin) andrun402 deploy resume <op_id>. Legacyrun402 deploy --manifestpreserved.canonicalize.tsre-headered as UX-only — gateway is authoritative for the manifest digest.cli/llms-cli.txtleads with the unified deploy section;CLAUDE.mdSDK section updated.Live verification
plan → upload → commit → poll → readyagainstapi.run402.comend-to-end.op_1777408240160_e8374ea6reachedstatus: readywithtarget_release_id: rel_1777408241269_9ceb9335,activate_attempts: 0.kind='cas'key validation (fixed)commitContentPlandoesn't promote per-session (open; SDK has a workaround calling/storage/v1/uploads/:id/completeper session).replacedeploys reachreadybut end users keep seeing the old site. Phase A canary should block on private#85.Tests
bundle-deploy,deploy-site,deploy-site-dir) markeddescribe.skippending v2 multi-route fetch mocks. New:sdk/src/namespaces/deploy.test.ts(10 cases),sdk/src/node/files.test.ts(8 cases).Test plan
status: readyOut of scope (tracked as follow-ups)
/.run402/config.jsonper-site config, same-origin function routes — listed as deliberate non-goals inopenspec/changes/unify-deployments/design.md.🤖 Generated with Claude Code