Skip to content

environments.create() declares one response key; the control plane sends four (warnings / durationMs / conditional hostnameAssignment undeclared) #12883

Description

@hotlong

Found while implementing #12866 (ADR-0006 D2, SDK half). Filed unassigned. Request-side sibling: #3739 (projects.create() declares 13 body fields, the control plane reads 4) — this card is the RESPONSE side of the same method.

The gap

client.environments.create() (packages/client/src/index.ts; client.projects.create() before #12866) declares its unwrap shape as the single key environment. Measured 2026-08-28 against objectstack-ai/cloudorigin/main, the handler — packages/service-cloud/src/routes/environment-lifecycle.ts, POST ${prefix}/cloud/environments — answers 201 with:

ok({
environment: result.environment,
warnings: result.warnings,
durationMs: result.durationMs,
...(result.hostnameAssignment ? { hostnameAssignment: result.hostnameAssignment } : {}),
})

So three keys the route really sends are undeclared. warnings in particular is the channel a partially-degraded provision uses to tell the caller what it could not do, and no SDK caller can reach it without an as any.

hostnameAssignment is conditional by design — the handler's own comment says it is forwarded only when the control plane renamed a colliding hostname, "so absence stays absence" — so it would have to be declared optional, and declaring it at all means declaring the shape ProvisionEnvironmentResponseSchema (spec cloud/environment) gives it.

Why this is its own card and not part of #12866

#12866 corrected two falsehoods in this declaration (the key was project, which this route has never sent; and a non-optional database block the route does not send at all) because both were the card's own defect class — a declared key contradicting the wire. ADDING keys is different: it widens published surface, it needs the optionality of hostnameAssignment decided, and it needs a decision on whether to bind the spec schema or keep the erased any. That is a decision, not a rename.

Note the neighbouring constraint recorded on the namespace's docblock (#11925 / #12036): @objectstack/spec/cloud's camelCase row contracts are deliberately NOT bound here, because the /api/v1/cloud/* control plane speaks snake_case. Whatever is decided for these three keys has to answer that first.

Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions