Skip to content

Add ephemeral workspaces - #42

Merged
FireSquid6 merged 8 commits into
mainfrom
37-add-ephemeral-workspaces
Aug 4, 2026
Merged

Add ephemeral workspaces#42
FireSquid6 merged 8 commits into
mainfrom
37-add-ephemeral-workspaces

Conversation

@FireSquid6

Copy link
Copy Markdown
Owner

Closes #37.

Create-from-issue already landed in #35, so this adds the second half: a workspace created from an issue can be marked ephemeral, and the bridge deletes it once the work it was opened for is finished.

How it works

POST /workspaces takes ephemeral: true alongside issueNumber (400 without one). The bridge writes a record to a new ephemeral.json{repoName, name, ship, issueNumber, branch, cleanup, blockedReason, blockedAt, pullRequest} — and annotates every workspace it serves with that block, so it rides the existing /events socket into the client. Ships are unchanged and know nothing about it.

A sweep runs every 5 minutes (sweepIntervalMs, 0 disables) plus once at startup, with POST /workspaces/sweep to force a pass and see what it did. Per record it asks a new RepoProvider.pullRequestsForBranch — needed because the existing listPullRequests is unpaginated and cannot see closed PRs past GitHub's first 30 — and cleans up when the branch's PRs are all closed, or when there are none and the issue itself is closed. The branch is the one linked at create time and is pinned: switching branches later neither re-points nor cancels the watch.

It will not delete work you can't get back

Cleanup goes through a new ?force=false on the ship's delete, which refuses with 409 when the clone has a dirty tree (untracked included), commits absent from every remote on any local branch, or a stash. Every existing caller keeps today's unconditional behaviour.

A refusal is not silent: the record turns blocked with the ship's own explanation, shows up wherever the workspace is listed (⚠ EPHEMERAL — issue #41 · PR #118 closed · cleanup blocked: 2 commits not on any remote), and is retried each pass until someone pushes or deletes by hand.

Nothing is written to the forge — no branch deletion, no comments.

Record lifecycle

Dropped when the repo is unregistered, the ship is removed, or the workspace is deleted. A vanished workspace is only forgotten once its ship is online to say so, so a rebooting ship never quietly disarms the watch. An offline ship or a provider that errors leaves the whole repo's records untouched for that pass rather than reading as "no pull request".

Verification

bun test 925 pass / 0 fail and bun run typecheck clean — checked out and re-run at each of the 8 commits individually, not just the tip. New coverage: real-git tests for every refusal case on the ship (including a commit on a branch that is not checked out), the full sweep decision table against a fake provider and fake ships, store round-trips, and the client's form and badge logic. No test waits on the timer; the timer gets one test that it fires and does not overlap.

🤖 Generated with Claude Code

FireSquid6 and others added 8 commits August 3, 2026 19:59
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@FireSquid6 FireSquid6 linked an issue Aug 4, 2026 that may be closed by this pull request
@FireSquid6
FireSquid6 merged commit afb1990 into main Aug 4, 2026
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.

Add ephemeral workspaces

1 participant