Workspaces
The cloud repo, guarded push and pull, durable workspaces for parallel work, and the status and activity feed.
On this page
DeepSpace-source apps have a real platform Git repository. This page covers how your checkout talks to it, how workspaces let several people or agents work on one app without colliding, and how to read the app's coordination feed. It all applies to DeepSpace source only — GitHub-source apps intentionally refuse these writes and workspaces; use ordinary GitHub branches and worktrees there.
The space remote#
The platform remote is named space (under the production platform; space-staging when the staging environment is selected — see the Note below). The CLI's wrappers install or repair it and configure a Git credential helper for the platform host — global when the CLI is a durable executable, worktree-private when it is transient. After that, plain git fetch and git push work from the configured checkout.
For a new checkout, use deepspace clone; an arbitrary git clone only authenticates automatically when the durable global helper is present.
npx deepspace clone <app-or-id> [dir]
npx deepspace push [-b <branch>]
npx deepspace pull [-b <branch>]
clone creates a normal checkout. push and pull are guarded Git operations with stable refusal codes — they do not replace Git's history model, they wrap it with platform policy (see the refusal contract below).
Workspaces#
A workspace is a durable server ref plus task metadata, materialized locally as a ws/<id> branch and worktree. Use one for each independent line of work:
npx deepspace workspace new -t "wire RBAC into billing"
npx deepspace workspace attach ws_… [dir]
npx deepspace workspace sync
npx deepspace workspace status
npx deepspace workspace list [--all]
npx deepspace workspace land [--into <branch>] [--validate]
npx deepspace workspace drop [ws_…]
new creates the ref and the local worktree; attach materializes an existing workspace into a checkout on another machine; sync publishes your commits to the server ref; land merges into trunk and retires the workspace; drop discards one.
Operating rules#
These are the rules that keep parallel work safe. They are worth internalizing rather than rediscovering through refusals:
- Commit WIP, then
workspace sync. Sync is what makes work durable and visible to collaborators. An unsynced workspace exists only on your disk. - Run
syncandlandfrom the workspace checkout.-w/--workspaceselects identity only — it does not make another checkout a safe place to operate from. landis an ordinary merge. It preserves the workspace commits, then deletes the server workspace ref. Local cleanup removes only a checkout carrying DeepSpace's private ownership marker — worktrees you (or another tool) created by hand are retained.--keep-worktreeopts out of cleanup entirely.- Overlap reports are advisory. They compare live peer tips so you can coordinate; they are not file-ownership locks. Read them, then talk.
- Managed workspace directories anchor under the primary checkout, even when you invoke the command from another linked worktree. Each checkout installs its own dependencies — never symlink
node_modulesacross worktrees. - Never plain-push a
ws/*branch tospace. Publish withworkspace syncso ref, metadata, and activity stay coherent. - Deploying from a workspace requires its exact HEAD to be synced first. A refusal here means the server would ship a tip you have not published — follow the refusal's action rather than deploying an older tip.
Dropping a workspace#
workspace drop refuses to delete unpublished commits. It verifies the tip is represented in cloud workspace, base, or landed history, then deletes the ref with compare-and-swap — so a branch that advanced concurrently survives the delete. When it refuses, follow its workspace sync action (publish, then drop) or pass --keep-worktree. Never force-clean a workspace by hand; the guard exists precisely because "I'm sure it's merged" is the famous last words of lost work.
Guardrails on push and pull#
pullfetches and fast-forwards. If dirtiness, divergence, or another worktree holding the branch prevents the local update, it stops safely — and may return one local action to run.push --forcestill refuses to discard a remote commit your local branch does not contain. Force is for deliberate ref moves, not conflict recovery — integrate the remote tip instead. This guard lives in the CLI wrapper only: a plaingit push --force spacethrough the credential helper bypasses it, and the server does not re-check it.- Push preflight rejects committed secret files. A branch tracking
.dev.vars,.env*,.npmrc,.envrc, or.mcp.json(templates like.env.exampleexcepted) is refused withsecret_in_historybefore any bytes leave your machine. Unlike the force guard, this rule is also enforced server-side — the platform refuses a pushed pack that introduces a secret file, so rawgit pushis no bypass either. Fix the branch — untrack the file, gitignore it, commit — rather than looking for a bypass flag; there deliberately isn't one. - Size ceilings: 20 MiB per object, 32 MiB of compressed history per push, refused with
push_too_large— why untracking an oversized file doesn't fix it, and what does, is in the command reference.
The refusal contract#
Guarded operations refuse with stable codes, and when exactly one deterministic recovery exists they attach an executable action:
| Code | Meaning |
|---|---|
non_fast_forward | The remote ref moved past your local branch |
behind_trunk | Deploy only: your branch is strictly behind its cloud-repo branch - deploying would take already-landed work off the live app |
diverged | Local and remote histories have split |
dirty_worktree | Uncommitted changes block the operation |
workspace_unsynced | The workspace tip is not published to the server ref |
A refusal's action follows the CLI-wide action contract — execute it as given; when there is none, the recovery requires judgment (inspect the Git state and decide), never an invented command.
Status and activity#
Two read-only commands orient you — after a context switch, in a fresh shell, or when resuming automation.
status#
npx deepspace status
npx deepspace status --json
status reports present facts: session, app, install state, branch/workspace, sync relation, and the live release where reachable. It does not derive a workflow or print a synthetic next step. Interpret the facts, then run the relevant command.
activity#
Activity is stateless on the server; the caller owns the cursor:
npx deepspace activity # events after cursor 0
npx deepspace activity --since <cursor> # strictly after the cursor
npx deepspace activity --follow # tail from now
npx deepspace activity --follow --since <cursor>
Persist the last returned cursor whenever continuity matters. Omitting --since in one-shot mode replays from the beginning; omitting it in follow mode starts at the current tail.
activity --follow --json emits NDJSON — the frame grammar (ready, activity, transport) is in the command reference.
See also#
- Source control — DeepSpace versus GitHub authority, and moving between them
- Collaborators — who can push and deploy
- Command reference:
workspaceand version control - CLI overview — exit codes and the JSON action contract