v0.17.0
Minor Changes
-
deepspace deploy --claim-releasedlets a platform admin take a name still inside its 30-day cooldown. Undeploying releases a hostname into a hold reserved for its previous owner, andreleased_byrecords the app's owner — not whoever ran the undeploy. So an admin taking down an abandoned or abusive app could not then put anything on that name for a month: the reservation belongs to the absent owner, and the admin's own app matches neither the releasing-owner nor the releasing-app leg that normally allows a reclaim. The name was stranded by the mechanism meant to protect it.The flag is explicit rather than an implicit admin bypass. The override is absolute — it beats a release made seconds ago and permanently discards the reservation rather than pausing it — so a routine admin deploy must not be able to seize a held name by accident. This is the same reasoning that makes a rename require
--rename. Non-admin accounts are refused withadmin_required, and because the tier lookup fails closed, a billing outage refuses rather than grants.Two records are written, because neither side knows the whole story: the deploy worker logs the acting admin (it cannot name the displaced owner, since only released rows carry
released_byand no by-host lookup returns them), and the registry logs the displaced owner and the app that lost the name at the moment the row is replaced. -
Removed: GameRoom, document mode, and the identity-migration wire. These
landed in #240 and #241 without a changeset, so this declares them rather than
letting a release drop public exports silently.Gone from the public surface:
useGameRoom, and theGameRoomdurable object withGameRoomConfig,
GamePlayer,UseGameRoomResult,Player, andGameInput. No in-repo app
used it, and it failed the authorization, hibernation, and scheduling
invariants every other room holds. UseRecordRoomfor shared state.CANONICAL_APP_IDENTITY_MIGRATION_IDand theMSG.GAME_*protocol
constants, which had no producer or consumer once the room went.- The RecordRoom document-mode migrations, verified unused across every repo
in the org before deletion.
An app that imported any of these will fail to build. Nothing in the templates
or the feature catalog referenced them.
Patch Changes
-
Fix a set of version-control lifecycle and honesty defects found by
black-box agent live-testing of multi-checkout collaboration — cases where
a verb prescribed recovery that deterministically failed, reported work it
did not do, or handed out a command that could not run where it pointed.- Recovery actions execute verbatim. Every
action.argvthat re-invokes
this CLI is pinned to the running interpreter and entry (resolved through
thenode_modules/.binsymlink) instead of the bare worddeepspace,
which is not on PATH in a linked worktree, a bare clone, or annpx
invocation. One door (executableAction) applies it to refusal actions,
success-path actions, the unknown-command suggestion, anddeploy's own
exit envelope; consumers must not assumeargv[0] === 'deepspace'. - A checkout can follow its own advice.
deepspace clone,workspace new,workspace attach,workspace sync, andworkspace landfill
whichever half of the checkout's repo-local git identity is missing from
the session token — previously thegit pulla divergence refusal handed
back (and any first commit) died onunable to auto-detect email address
in a container with no global git config, or with a half-configured one.
The divergence recovery also pinsgit pull --no-rebase(a fresh clone
has no reconcile config) and exits 2 from workspace publishes exactly as
it does frompush. - A second checkout of a landed workspace is no longer a dead end.
workspace dropcleans up the stale local worktree and branch of a
workspace another clone landed or dropped — proving publication against a
freshly fetched trunk tip when the landed ref is gone — and reports
whether the remote drop actually happened (json.remoteDropped) instead
of claiming a fresh drop on a replay.workspace statusstates the right
fact per state (landed/dropped → drop cleans up; behind → fast-forward;
diverged → integrate first) instead of prescribing aworkspace syncthat
would refuse, and--jsongainssyncRelationso machine callers see the
same distinction.workspace landandworkspace syncon a finished
workspace reportworkspace_not_active(with theworkspace dropcleanup
action) ahead of a checkout mismatch whose attach advice could not
succeed; drop's own unsynced refusal stops prescribing the publish path
once the workspace is finished — nothing can publish those commits, and
the message says so. The server returnsworkspace_not_active(over the
genericconflict) for sync/land/drop against a finished workspace — a
worker-side change that reaches existing CLIs on deploy; no released CLI
branches on the old slug, and this CLI tolerates both. workspace attachis idempotent. Attaching an already-attached
workspace points at the existing worktree instead of refusing
branch_exists— reporting the LOCAL tip and, when it differs from the
published tip, the recovery that relation actually admits (workspace syncwhen ahead;git pull --no-rebasefirst when behind or diverged,
since sync would refuse) — and re-materializes a worktree thatgit worktree removeor a failed cleanup deleted.- Land refusals carry their resume action, and a recorded merge is never
reported as a bare failure.merge_conflict,conflict_markers(now
naming the offending files),validation_failed, and
validation_mutated_treecarry the exact re-run action; when the trunk
push succeeded and only recording the land failed,land_unrecorded
states that the merge IS on trunk and resumes by re-running instead of
implying nothing happened. When a concurrent land or drop finished the
workspace mid-merge, land answersworkspace_not_activeat exit 2 with
pushed: trueand theworkspace dropcleanup action — the merge is on
trunk, and re-running could never record it. statusstops labeling a feature branch's sync line "Trunk". A
non-default branch renders asBranch sync, andjson.trunkgains
branch/isTrunk(isTrunk: nullwhen the default branch is unknown —
GitHub-owned source or an unborn cloud repo — never a guess), so
trunk.state: "in_sync"on a feature branch can no longer read as "local
trunk matches cloud trunk".activity(CLI and dashboard) stops printing
a fabricated "(0 files vs base)" changed-file count on workspace syncs —
the event never carried one.
- Recovery actions execute verbatim. Every
-
deepspace workspace dropandlandno longer treat the genericconflict
error as "already finished".The server refuses a finished workspace with
workspace_not_active;conflict
was the pre-rename slug, and the CLI accepted both. Platform workers deploy
ahead of the CLI release, so nothing still answers with the old slug — and
conflictremains live for workspace-id clashes, which were being re-read as
"already finished" instead of surfacing. An id clash now raises immediately.