swarmops ref set#
Point ref NAME at REV: move it from --expect, or --create it.
- Group
refs- Since
0.1.0- Mutates
- yes
- Identity required
- yes
- MCP tool
ref_set- Envelope op
ref_set- Exit codes
- 01245
Syntax#
swarmops ref set [OPTIONS] <--expect <REV>|--create> <NAME> <REV>Required arguments#
Syntax: <NAME>.
main or release/<name>.
Syntax: <REV>.
The revision to point it at.
Exactly one of --expect <REV> or --create is required.
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, land your change instead (swarmops land merges it onto the ref's head), or re-apply your edits to the content at the error's actual, commit with --parent <actual>, then retry with --expect <actual>. Do not re-send your old whole-file puts: they would revert the winner's edits to shared files.
Syntax: --create.
Create the ref; if it holds REV already that is a replay (already_applied), at another target E_CAS_MISMATCH.
Optional arguments#
The command also takes the global options.
Examples#
swarmops --repo [tmp]/store --as agent/worker-17 ref set main [c1] --create{
"ok": true,
"op": "ref_set",
"api": 1,
"result": {
"name": "main",
"new": "[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.
No recorded example yet.
Details#
ref set moves one ref to a changeset, or creates it, by compare-and-swap: you say what the ref
holds now (--expect) or that it does not exist yet (--create), and the store compares that
with the ref under its write lock. It is the low-level move. To publish a change on main, agents
land it, which merges onto whatever main holds by then; ref set is for operators and
scripts that point a ref at a changeset they have already chosen, such as a release ref.
What happens, in order#
- Arguments. Exactly one of
--expectand--createis required; both, or neither, isE_INVALID_ARG(exit 2). - Identity. Without
--asorSWARMOPS_AGENTthe command fails withE_IDENTITY_REQUIRED(exit 5) before it opens the store. - Revisions.
REVand the--expectrevision are resolved in one snapshot of the store. A revision that names nothing isE_REV_NOT_FOUND(exit 6), a malformed oneE_INVALID_ARG(exit 2), a prefix more than one changeset sharesE_AMBIGUOUS_ID(exit 1). Both appear inmeta.resolvedwith the full ids they named. - The name.
NAMEmust bemainorrelease/<x>, wherexis one or more of letters, digits,.,_and-, does not start with., and the whole name is at most 255 bytes; anything else isE_REF_NAME_INVALID(exit 1). - The compare-and-swap, under the store's write lock, after the write has checked the journal's tail, so against the ref as committed, never as a reader's snapshot showed it:
| The ref holds | With --expect E | With --create |
|---|---|---|
E, which is not REV | Moved to REV; old is E | E_CAS_MISMATCH, actual is E |
REV already | Replay: already_applied: true, nothing written | Replay: already_applied: true, nothing written |
Another changeset X | E_CAS_MISMATCH, actual is X | E_CAS_MISMATCH, actual is X |
| Nothing (no such ref) | E_CAS_MISMATCH, actual is null | Created at REV, version 1 |
A replay writes no journal frame: op log does not list it and the ref's version stays. So the
same ref set run twice is safe, whichever run gets there first.
E_CAS_MISMATCH is exit 4 and retryable: true, but do not rerun the same command: it would
fail again. Read details.actual (the hint, swarmops show <actual>, only reads) and decide
with that. The --expect help above says what to do for each case, and
When a compare-and-swap fails
walks through it.
What ref set does not check#
- No ancestry.
REVcan be any changeset in the store: a descendant of the ref's target, an ancestor (moving the ref back), or one on another line of history. Movingmaindrops from its history every changeset the new target is not built on. Those changesets stay in the store, readable by id, andop undocan move the ref back. - No merge. Nothing of the ref's old target is carried over.
landis the command that merges.
After the move#
- Durability. The move commits when its journal frame is durable. If a step after that fails
(such as listing the frame in
journal.off), the move still stands,warningssays what failed, and the next write to the store finishes it.meta.recoveredreports what this write repaired of an earlier writer that died mid-write (Monitoring). - The record. The journal records the move with the agent's identity and the time, in
seconds;
op logshows it as an operation of kindref_move, whichop undocan take back. - The lock. A write waits up to 10 s for the store's lock. If another writer holds it the
whole time, the command fails with
E_BUSY(exit 4, retryable) and nothing is written: run the same command again.
Patterns#
Move a release ref to main's head, expecting the target you read:
OLD=$(swarmops resolve release/1.0 | jq -r '.result.id')
NEW=$(swarmops resolve main | jq -r '.result.id')
swarmops ref set release/1.0 "$NEW" --expect "$OLD"Create a ref, safe to retry: a second run with the same REV is a replay, and one with another
REV fails with E_CAS_MISMATCH, so no ref is ever silently re-pointed:
swarmops ref set release/2.0 "$NEW" --createBranch on the outcome in a script:
out=$(swarmops ref set release/1.0 "$NEW" --expect "$OLD")
case $? in
0) echo "$out" | jq -r '.result.already_applied' ;;
4) echo "$out" | jq -r '.error.code, .error.details.actual' ;;
*) echo "$out" | jq -r '.error.code, .error.message' ;;
esacCaveats#
- Expect a full id. A ref name or a relative revision as
--expectis read when the command runs, so it does not pin what you saw:--expect release/1.0onrelease/1.0always matches, and the compare-and-swap protects nothing. An id prefix pins the id only while no other changeset shares it. The CLI accepts all three; the--expecthelp warns. - Changing the trunk. If
mainis exported to a git trunk, moving it by hand is a change of the trunk: keep to One writer per trunk.
Through MCP#
The MCP tool is ref_set, with name, new, and either expected_old or create: true. The
server refuses an expected_old that is not a full 64-hex changeset id with E_INVALID_ARG
(hint swarmops ref list), so a ref name, a relative revision or a prefix never reaches the
store.
Performance#
A ref move is one durable write transaction. The performance ledger counts its syncs: two for the
journal (the frame and its entry in journal.off) and two for each ref it moves (the ref's new
file and its directory)
(Sync calls per commit).
Guides#
- Commit and land: Move a ref yourself
- Reading and repairing history: See every write
- Scripting the CLI: Retry rules
- Concepts: Compare-and-swap
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_set.
Exit codes:
0: Success.1: The operation failed.2: The request or the command line is malformed.4: The operation can succeed when it is run again.5: An identity or policy requirement refused the operation.
Errors#
- E_IDENTITY_REQUIRED (exit 5): A mutation was asked for without an identity.
- E_CAS_MISMATCH (exit 4): The ref no longer holds the expected target (or, with
create, exists at another one);details.actualholds what it does,nullwhen the ref does not exist. - E_REF_NAME_INVALID (exit 1): The ref name is not
mainorrelease/<name>. - E_INVALID_ARG (exit 2): A malformed argument or request, including a command-line usage error.
Any command that opens a store may also fail with a store error, such as E_NOT_A_REPO, E_IO or E_STORE_CORRUPT; a command line the grammar refuses is E_INVALID_ARG (exit 2).