<!-- Generated by crates/swarmops-cli/src/reference.rs from the code: do not edit. Change the code, then run SWARMOPS_REGEN_REFERENCE=1 with the regen_reference test. -->

# swarmops ref delete

Delete ref NAME.

```yaml
title: "swarmops ref delete"
description: "Delete ref NAME."
group: "refs"
since: "0.1.0"
mutates: true
identity_required: true
mcp_tool: "ref_delete"
envelope_op: "ref_delete"
exit_codes: [0, 4, 5]
errors: [E_IDENTITY_REQUIRED, E_CAS_MISMATCH]
syntax: "swarmops ref delete [OPTIONS] --expect <REV> <NAME>"
```

## Syntax

```text
swarmops ref delete [OPTIONS] --expect <REV> <NAME>
```

## Required arguments

### `NAME`

Syntax: `<NAME>`.

The ref.

### `--expect`

Syntax: `--expect <REV>`.

The changeset the ref holds now.

Give the full changeset id you read (from ref list, show, log or a commit's result). A ref name or a relative revision such as main\~1 is read from the store when the command runs instead of pinning the id you read: the ref's own name always matches (a deleted ref errors instead), so the compare-and-swap protects nothing, and main\~1 on main never matches. An id prefix pins the id you read only until another changeset shares it; then it is E\_AMBIGUOUS\_ID.

After E\_CAS\_MISMATCH, the ref holds the error's `actual`, which someone else moved it to: look at it (swarmops show &lt;actual&gt;) before you delete it with --expect &lt;actual&gt;.

## Optional arguments

The command also takes the [global options](../cli.md#global-options).

## Examples

### CLI

```sh
swarmops --repo [tmp]/store --as agent/worker-17 ref delete release/1.0 --expect [c1]
```

```json
{
  "ok": true,
  "op": "ref_delete",
  "api": 1,
  "result": {
    "name": "release/1.0",
    "old": "[c1]",
    "already_applied": false
  },
  "meta": {
    "repo": "[tmp]/store",
    "elapsed_ms": "[elapsed_ms]",
    "resolved": {
      "[c1]": "[c1]"
    },
    "auth": "trust"
  }
}
```

Exit code 0. Recorded by the CLI tests. A name in square brackets, such as `[tmp]` or `[c1]`, stands for what differs from run to run: the test's temporary directory, an id, a time.

### MCP

No recorded example yet.

## Details

`ref delete` removes one ref by compare-and-swap: it deletes the ref only if it still names the
changeset you give as `--expect`. Deleting a ref removes the name only. Every changeset stays in
the store, readable by id, and `op undo` of the deletion creates the ref again.

### What happens, in order

1. **Identity.** Without `--as` or `SWARMOPS_AGENT` the command fails with
   `E_IDENTITY_REQUIRED` (exit 5) before it opens the store.
2. **The expected target.** `--expect` is resolved as a revision first. A revision that names
   nothing is `E_REV_NOT_FOUND` (exit 6): so `--expect release/1.0` on a ref that is gone fails
   here, before the delete is tried.
3. **The name.** `NAME` must be `main` or `release/<x>` (letters, digits, `.`, `_`, `-`, not
   starting with `.`, at most 255 bytes); anything else is `E_REF_NAME_INVALID` (exit 1).
4. **The compare-and-swap,** under the store's write lock, against the ref as committed:

| The ref holds | Result |
| --- | --- |
| The expected changeset | Deleted; `old` is that changeset, `already_applied: false` |
| Nothing (no such ref) | Replay: `already_applied: true`, nothing written |
| Another changeset | `E_CAS_MISMATCH` (exit 4), `details.actual` is what it holds |

A ref that does not exist counts as deleted already, whoever deleted it, so a retry of a delete
that succeeded is safe. This holds for a name that never existed too: `ref delete release/typo
--expect <id>` succeeds with `already_applied: true` as long as `<id>` is a changeset in the
store. Read `already_applied` when it matters whether this run deleted the ref.

On `E_CAS_MISMATCH`, someone moved the ref after you read it. The hint, `swarmops show
<actual>`, only reads: look at what the ref holds now before you delete it with
`--expect <actual>`.

### Deleting `main`

`main` can be deleted like any ref. Afterwards `log` without a revision, and every command given
`main` (`ls main`, `cat main:…`), fails with `E_REV_NOT_FOUND` (exit 6, hint `swarmops ref list`),
and a `commit` without `--parent` records a changeset with no parent, the root of a new history. To
bring `main` back, `op undo` the deletion, or `ref set main <id> --create`.

### After the delete

The journal records the deletion as a ref move with no new target, with the agent's identity
and the time; `op log` lists it as a `ref_move` whose move has `old` and no `new`. A ref created
again under the same name starts at `version` 1. Durability, `warnings`, `meta.recovered` and the
10 s lock wait (`E_BUSY`) work as for [`ref set`](../commands/ref-set.md#after-the-move).

### Patterns

Delete a ref you just read:

```sh
OLD=$(swarmops resolve release/1.0 | jq -r '.result.id')
swarmops ref delete release/1.0 --expect "$OLD"
```

Take a branch imported from git into its own ref, land it on `main`, then delete the ref:
[One writer per trunk](../../operate/one-writer-per-trunk.md#change-the-trunk-without-breaking-the-rule)
runs this sequence.

Undo a deletion: find the newest operation that deleted the ref (a move with no `new`) and undo
it. `op log` lists 50 operations unless `-n` says otherwise; page with `--before` for older ones.

```sh
OP=$(swarmops op log | jq -r '[.result.ops[] | select(any(.ref_moves[]; .name == "main" and .new == null))][0].op')
swarmops op undo "$OP"
```

### Caveats

- **Expect a full id.** A ref name or a relative revision as `--expect` is read when the
  command runs: `--expect release/1.0` always matches the ref it deletes, so the compare-and-swap
  protects nothing. An id prefix pins the id only while no other changeset shares it.
- **No cleanup.** Deleting a ref frees no space: the changesets it named, and their objects,
  stay.

### Through MCP

The MCP tool is `ref_delete`, with `name` and `expected_old`. The server refuses an
`expected_old` that is not a full 64-hex changeset id with `E_INVALID_ARG` (hint
`swarmops ref list`).

### Guides

- [Commit and land: Move a ref yourself](../../guides/commit-and-land.md#move-a-ref-yourself)
- [Reading and repairing history: Undo a ref's deletion](../../guides/history-and-undo.md#undo-a-refs-deletion)
- [Concepts: Ref](../../concepts.md#ref)

## Return information

`result` is a `RefSetResp` (`swarmops schema RefSetResp` prints its JSON Schema).

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `already_applied` | `boolean` | yes | The ref was already in the requested state, so nothing changed and a replay is safe; `old` and `new` are then the request's `expected_old` and `new`, resolved to full ids. |
| `name` | `string` | yes | The ref. |
| `new` | `string or null` | no | Its target after; omitted when the ref was deleted. |
| `old` | `string or null` | no | What the ref held before a move that was applied. On a replay (`already_applied`) it is `expected_old` as resolved, which the ref need not have held; omitted when `create` was given. |
| `warnings` | `array of string` | no | Problems found after the move became durable, such as failed housekeeping; the move itself succeeded. Omitted when empty. |

Envelope `op`: `ref_delete`.

Exit codes:

- `0`: Success.
- `4`: The operation can succeed when it is run again.
- `5`: An identity or policy requirement refused the operation.

## Errors

- [`E_IDENTITY_REQUIRED`](../errors.md#e_identity_required) (exit 5): A mutation was asked for without an identity.
- [`E_CAS_MISMATCH`](../errors.md#e_cas_mismatch) (exit 4): The ref no longer holds the expected target (or, with `create`, exists at another one); `details.actual` holds what it does, `null` when the ref does not exist.

Any command that opens a store may also fail with a store error, such as [`E_NOT_A_REPO`](../errors.md#e_not_a_repo), [`E_IO`](../errors.md#e_io) or [`E_STORE_CORRUPT`](../errors.md#e_store_corrupt); a command line the grammar refuses is [`E_INVALID_ARG`](../errors.md#e_invalid_arg) (exit 2).

## See also

- [Operating SwarmOps: One writer per trunk](../../operations.md#one-writer-per-trunk)
- [`swarmops ref list`](ref-list.md)
- [`swarmops ref set`](ref-set.md)
- [CLI reference](../cli.md)
- [Error codes](../errors.md)
