Skip to content

feat: idempotent workspace creation — same name+owner returns existing#115

Merged
khaliqgant merged 3 commits intomainfrom
feat/idempotent-workspace-clean
Mar 31, 2026
Merged

feat: idempotent workspace creation — same name+owner returns existing#115
khaliqgant merged 3 commits intomainfrom
feat/idempotent-workspace-clean

Conversation

@khaliqgant
Copy link
Copy Markdown
Member

@khaliqgant khaliqgant commented Mar 31, 2026

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 createWorkspace
  • packages/server/src/routes/workspace.ts — 200 for existing, 201 for new
  • packages/sdk-typescript/src/relay.ts — simplified ensureWorkspace
  • packages/local/src/main.rs — matches server behavior
  • packages/types/src/workspace.ts — CreateWorkspaceResult type
  • openapi.yaml — 200 added, 409 removed
  • README.md — updated docs
  • Tests updated

Depends on

Test plan

  • Two workspaces with same name + same API key → returns existing
  • Two workspaces with same name + different API keys → both created
  • SDK ensureWorkspace works without 409
  • CI passes

🤖 Generated with Claude Code


Open with Devin

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>
@github-actions
Copy link
Copy Markdown

Preview deployed!

Environment URL
API https://pr115-api.relaycast.dev
Health https://pr115-api.relaycast.dev/health
Observer https://pr115-observer.relaycast.dev/observer

This preview shares the staging database and will be cleaned up when the PR is merged or closed.

Run E2E tests

npm run e2e -- https://pr115-api.relaycast.dev --ci

Open observer dashboard

https://pr115-observer.relaycast.dev/observer

devin-ai-integration[bot]

This comment was marked as resolved.

khaliqgant and others added 2 commits March 31, 2026 17:13
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>
@khaliqgant khaliqgant merged commit 5560181 into main Mar 31, 2026
4 checks passed
@khaliqgant khaliqgant deleted the feat/idempotent-workspace-clean branch March 31, 2026 15:25
Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 7 additional findings in Devin Review.

Open in Devin Review

Comment thread openapi.yaml
Comment on lines +62 to +64
api_key:
type: string
description: Workspace API key (only returned on first creation)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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."

Suggested change
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.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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