# Workspaces

The cloud repo, guarded push and pull, durable workspaces for parallel work, and the status and activity feed.

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](/guides/source-control) 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.

```bash
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](#the-refusal-contract) below).

Keep `DEEPSPACE_ENV` consistent for a session. It selects which platform (production or staging) owns the remote, workspaces, releases, and activity — the remote is even named for it, `space` versus `space-staging` — so switching it mid-session points the same checkout at a different owner. `DEEPSPACE_DEPLOY_URL` does **not** select the platform; it only overrides one service's base URL.

## 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:

```bash
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 `sync` and `land` from the workspace checkout.** `-w`/`--workspace` selects *identity* only — it does not make another checkout a safe place to operate from.
* **`land` is 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-worktree` opts 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_modules` across worktrees.
* **Never plain-push a `ws/*` branch to `space`.** Publish with `workspace sync` so 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

* **`pull` fetches 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 --force` still 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 plain `git push --force space` through 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.example` excepted) is refused with `secret_in_history` before 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 raw `git push` is 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](/cli-reference/commands#push).

## 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](/cli-reference/overview#the-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`

```bash
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**:

```bash
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](/cli-reference/commands#activity).

Events are **facts, not instructions** — pushes, workspace lifecycle, releases. A peer's `land` event tells you trunk moved; it does not tell you to rebase. Decide what to do from your own state.

## See also

* [Source control](/guides/source-control) — DeepSpace versus GitHub authority, and moving between them
* [Collaborators](/guides/collaborators) — who can push and deploy
* [Command reference: `workspace`](/cli-reference/commands#workspace) and [version control](/cli-reference/commands#version-control)
* [CLI overview](/cli-reference/overview) — exit codes and the JSON action contract
