feat: idempotent workspace creation — same name+owner returns existing#115
feat: idempotent workspace creation — same name+owner returns existing#115khaliqgant merged 3 commits intomainfrom
Conversation
Same API key + same name returns existing workspace (200). Different API keys can share workspace names. Changes: - engine/workspace.ts — createWorkspace returns existing if same name+apiKeyHash - routes/workspace.ts — returns 200 for existing, 201 for new - sdk relay.ts — ensureWorkspace simplified (no 409 dependency) - local main.rs — matches server idempotent behavior - types/workspace.ts — CreateWorkspaceResult type - openapi.yaml — updated responses (200 added, 409 removed) - README.md — updated docs - tests updated Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Preview deployed!
This preview shares the staging database and will be cleaned up when the PR is merged or closed. Run E2E testsnpm run e2e -- https://pr115-api.relaycast.dev --ciOpen observer dashboard |
Tests with real SQLite DB (via FakeD1): - New workspace returns created: true - Same name + same API key returns existing (created: false) - Same name + different keys creates separate workspaces - ownerApiKeyHash lookup works - Anonymous creates always produce new workspaces - Conflicting key + hash throws - getWorkspaceByName handles duplicates Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| api_key: | ||
| type: string | ||
| description: Workspace API key (only returned on first creation) |
There was a problem hiding this comment.
🟡 openapi.yaml api_key description contradicts actual server behavior on idempotent returns
The CreateWorkspaceResponse schema's api_key field description says "Workspace API key (only returned on first creation)" (openapi.yaml:64), but the actual server and local daemon behavior echoes back the api_key on idempotent 200 returns too. The server route at packages/server/src/routes/workspace.ts:155 extracts the raw API key from the Authorization header and passes it to the engine, which includes it via buildWorkspaceResponse(existing, providedOwnerApiKey) at packages/server/src/engine/workspace.ts:55. The local daemon similarly returns existing_workspace.api_key at packages/local/src/main.rs:660. Since CreateWorkspaceResponse is used for both the 200 and 201 response schemas, this description is misleading — a consumer reading the OpenAPI spec would incorrectly expect api_key to be absent from 200 responses. This violates the AGENTS.md Docs Hygiene rule: "Update README.md and openapi.yaml together when API behavior changes."
| api_key: | |
| type: string | |
| description: Workspace API key (only returned on first creation) | |
| api_key: | |
| type: string | |
| description: Workspace API key. Returned when a new workspace is created (201) and echoed back on idempotent returns (200) when the caller provides their key. |
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
Replaces #112 and #114 with a clean PR (no noise files).
Same API key + same name → returns existing workspace (200).
Different API keys → can share workspace names (201).
Changes
packages/server/src/engine/workspace.ts— idempotent createWorkspacepackages/server/src/routes/workspace.ts— 200 for existing, 201 for newpackages/sdk-typescript/src/relay.ts— simplified ensureWorkspacepackages/local/src/main.rs— matches server behaviorpackages/types/src/workspace.ts— CreateWorkspaceResult typeopenapi.yaml— 200 added, 409 removedREADME.md— updated docsDepends on
Test plan
🤖 Generated with Claude Code