Skip to content

feat(sdk): add CreateStack/GetStack, ProvisionVector, DeploymentEvents#22

Merged
mastermanas805 merged 1 commit into
masterfrom
sdk-endpoint-coverage
Jun 10, 2026
Merged

feat(sdk): add CreateStack/GetStack, ProvisionVector, DeploymentEvents#22
mastermanas805 merged 1 commit into
masterfrom
sdk-endpoint-coverage

Conversation

@mastermanas805

Copy link
Copy Markdown
Member

What

Closes the SDK endpoint-coverage gap surfaced by the live SDK durability probe. The SDK could provision db/cache/nosql/queue/storage/webhook + Deploy, but was missing methods for endpoints that exist and are live on prod — most importantly it could not deploy anonymously (DeployPOST /deploy/new is RequireAuth; the documented anonymous deploy path is POST /stacks/new, which the SDK lacked).

New methods

All mirror the existing provision-method pattern, the 120 s provisioning-timeout client (build pods are slow), and the full APIError envelope handling.

Method Endpoint Notes
CreateStack(ctx, CreateStackOpts) (*Stack, error) POST /stacks/new The anonymous deploy path. Synthesises instant.yaml from []StackServiceSpec and uploads it + per-service tarballs as multipart/form-data. Async (Status:"building").
GetStack(ctx, slug) (*Stack, error) GET /stacks/:slug Poll status + per-service URLs. 404 → IsNotFound.
ProvisionVector(ctx, *VectorOpts) (*VectorResult, error) POST /vector/new pgvector; optional Dimensions hint (0 → server default 1536). VectorResult embeds ProvisionResult + Extension/Dimensions.
DeploymentEvents(ctx, id, limit) (*DeploymentEventList, error) GET /api/v1/deployments/:id/events Rule-27 failure-autopsy timeline (kind/reason/exit_code/last_lines/hint). ExitCode is *int32 (null-distinguishable).

Contract verification (against api/internal/router/router.go + handlers)

Each request/response shape was verified against the live api before wiring.

One deviation from the brief, implemented to the real contract: the brief suggested /stacks/new takes a JSON {services/name/env} body. The api actually takes multipart/form-data — a manifest field (instant.yaml) + one tarball field per service (keyed by service name) + name + optional env. The SDK builds the manifest client-side from the service specs. The create response key is stack_id (mapped to Stack.Slug); there is no top-level url on create — per-service URLs come from GetStack (services[].url).

Tests (mirror existing httptest contract-test style)

  • vector_test.go — happy path (body assertions), dimensions-omitted-when-zero, error envelope, preflight + empty-token/url branches.
  • stack_test.go — happy path (multipart parse + manifest assertions), env-omitted, error envelope, preflight (no services / empty name / nil tarball / duplicate), tarball read error, two-service sort, transport/build-request/decode error arms, GetStack happy + 404 + empty-slug.
  • stack_internal_test.go — white-box sweep of writeStackMultipart failing-writer arms (manifest/name/env/CreateFormFile/io.Copy/Close).
  • deploy_events_test.go — happy path (nullable exit_code), default-limit, 404, 401.

A writeStackMultipart(io.Writer, ...) seam makes the multipart error arms reachable (a *bytes.Buffer can never fail them).

Gate (all green)

  • go build ./... ✓ · go vet ./... ./examples/...
  • go test ./... -short -race -count=1 ✓ (CI command)
  • gofmt (real, via $(go env GOROOT)/bin/gofmt) ✓ · golangci-lint run ./...0 issues
  • diff-cover patch coverage: 100% (185 changed lines, 0 missing) · project floor 98.4% (≥95%)

Docs

README method tables + anonymous-deploy/autopsy usage examples + "what's not covered" trim; CHANGELOG Added entries.

🤖 Generated with Claude Code

Close the SDK endpoint-coverage gap surfaced by the live durability probe —
the SDK could provision db/cache/nosql/queue/storage/webhook + Deploy but was
missing live, documented prod endpoints, most importantly the ANONYMOUS deploy
path (Deploy/POST /deploy/new is RequireAuth; anon agents deploy via stacks).

New methods (all mirror the existing provision-method pattern, the 120 s
provisioning-timeout client for slow build pods, and the full APIError
envelope handling):

- CreateStack  -> POST /stacks/new   (multipart manifest + per-service tarballs;
  the documented anonymous deploy path). Returns *Stack (slug/status/tier/env/
  expires_in). Synthesises instant.yaml from []StackServiceSpec.
- GetStack     -> GET /stacks/:slug  (poll status + per-service URLs).
- ProvisionVector -> POST /vector/new (pgvector; optional Dimensions hint;
  returns *VectorResult embedding ProvisionResult + Extension/Dimensions).
- DeploymentEvents -> GET /api/v1/deployments/:id/events (rule-27 failure
  autopsy: kind/reason/exit_code/last_lines/hint; ExitCode is *int32 for null).

Verified each request/response shape against api router.go + the stack/vector/
deploy handlers before wiring. Brief said /stacks/new takes a JSON
{services/name/env} body — the api actually takes multipart/form-data (manifest
YAML + tarball-per-service); implemented to the real contract and synthesise the
manifest client-side. Create response key is stack_id (mapped to Stack.Slug);
there is no top-level url on create (per-service URLs come from GetStack).

Tests: httptest contract tests for each method (success + error-envelope +
preflight + reachable transport/build/decode arms). A writeStackMultipart seam
takes an io.Writer so the multipart error arms are reachable via a failing
writer. 100% patch coverage (185 diff lines, 0 missing); project floor 98.4%.
go build/vet, go test -short -race, gofmt, golangci-lint (0 issues) all green.
README method tables + anonymous-deploy/autopsy examples + CHANGELOG updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mastermanas805
mastermanas805 merged commit db0c58a into master Jun 10, 2026
11 checks passed
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