Skip to content

feat(deploy): unified deploy primitive (v1.34) — SDK + MCP + CLI#146

Merged
MajorTal merged 3 commits into
mainfrom
claude/musing-colden-115cb4
Apr 29, 2026
Merged

feat(deploy): unified deploy primitive (v1.34) — SDK + MCP + CLI#146
MajorTal merged 3 commits into
mainfrom
claude/musing-colden-115cb4

Conversation

@MajorTal
Copy link
Copy Markdown
Collaborator

Summary

Public-repo slice of the unify-deployments openspec 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

  • SDK (@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. Run402DeployError envelope.
  • files() factory (isomorphic) + fileSetFromDir(path) (Node-only) byte-source helpers.
  • Backward-compat shims: apps.bundleDeploy rewritten as v2 translator (legacy options → ReleaseSpec; inherit: true → deprecation warn + ignored; migrations get deterministic id bundle_legacy_<sha256(sql)[0:16]>). sites.deployDir rewritten as v2 wrapper with legacy event synthesizer for v1.32-era consumers.
  • MCP: new deploy and deploy_resume tools wired into src/index.ts. Existing bundle_deploy/deploy_site/deploy_site_dir/deploy_function route through the SDK shims unchanged.
  • CLI: run402 deploy apply --manifest <path> (or --spec, or stdin) and run402 deploy resume <op_id>. Legacy run402 deploy --manifest preserved.
  • canonicalize.ts re-headered as UX-only — gateway is authoritative for the manifest digest.
  • Docs: cli/llms-cli.txt leads with the unified deploy section; CLAUDE.md SDK section updated.

Live verification

  • Drove plan → upload → commit → poll → ready against api.run402.com end-to-end.
  • Live op op_1777408240160_e8374ea6 reached status: ready with target_release_id: rel_1777408241269_9ceb9335, activate_attempts: 0.
  • Three gateway bugs filed during the test:
    • kychee-com/run402-private#79 ON CONFLICT mismatch (fixed)
    • kychee-com/run402-private#81 kind='cas' key validation (fixed)
    • kychee-com/run402-private#83 commitContentPlan doesn't promote per-session (open; SDK has a workaround calling /storage/v1/uploads/:id/complete per session).
  • Adversarial QA campaign filed 67 follow-up issues (43 private + 24 public). Headline P0: kychee-com/run402-private#85 — v2 activate doesn't update subdomain mappings, so replace deploys reach ready but end users keep seeing the old site. Phase A canary should block on private#85.

Tests

  • 458/460 unit + integration pass (2 skipped, llms.txt-related).
  • 271/271 CLI e2e pass.
  • Three 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), sdk/src/node/files.test.ts (8 cases).

Test plan

  • Build green
  • Unit + integration green (458/460)
  • CLI e2e green (271/271)
  • Live end-to-end deploy against prod hits status: ready
  • Phase A canary — gated on kychee-com/run402-private#85 (subdomain mapping in activate)

Out of scope (tracked as follow-ups)

  • The 67 QA issues — bulk of them are gateway-side validation gaps and 5xx → structured-4xx cleanups.
  • CAS pack uploads (sites with thousands of tiny files), virtual /.run402/config.json per-site config, same-origin function routes — listed as deliberate non-goals in openspec/changes/unify-deployments/design.md.

🤖 Generated with Claude Code

MajorTal and others added 3 commits April 28, 2026 18:47
…(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>
@MajorTal MajorTal merged commit aa43ace into main Apr 29, 2026
1 of 3 checks passed
@MajorTal MajorTal deleted the claude/musing-colden-115cb4 branch April 29, 2026 07:05
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.

1 participant