sd update
Change one or more seeds. All the named seeds change in one transaction, or none do.
sd update sd-a3f --status in_progress --add-label parser
sd update sd-a3f --claim # atomically take it
sd update sd-a3f sd-b7c --priority 0
sd update sd-a3f --defer 2026-10-15 # hide from ready until then
| flag | meaning |
|---|---|
--title, -d, --description, --notes, --design, --acceptance-criteria (alias --acceptance) | set the text (see below for replacing existing text) |
--force | allow replacing a non-empty description, notes, design, acceptance criteria or agent context with different content |
-s, --status | open, in_progress, blocked, deferred, closed |
-p, --priority | 0-4 or P0-P4 |
--assignee NAME | set the assignee ("" clears it) |
--owner WHO | set the owner ("" clears it) |
--external-ref REF | a reference to the same work elsewhere, br’s external_ref ("" clears it) |
--agent-context JSON | br’s agent_context: inline JSON, @path or @path.yaml/.yml, stored as compact JSON. "" clears it; replacing or clearing a non-empty one needs --force |
--due WHEN | due date, the forms of --defer; a bare date or tomorrow is 09:00 local, stored as a UTC instant ("" clears it) |
--estimate MIN | time estimate in minutes, 0 to 525960 |
--check-acceptance ITEMS | tick acceptance checklist items in place: 1-based numbers (1,4,5) or a text selector matching exactly one item (case-insensitive; an exact match wins, else a unique substring). Repeatable; needs no --force; the rest of the field is kept byte for byte |
--uncheck-acceptance ITEMS | untick items; the same selectors |
--add-acceptance TEXT | append - [ ] TEXT to the checklist (repeatable). Checklist edits and --acceptance-criteria are not combined in one update |
--claim | set assignee to the actor and status to in_progress, only if the seed is open, unclaimed and unblocked |
--add-label, --remove-label | repeatable |
--defer DATE | hide from ready until this date or instant ("" clears it) |
--workflow-run RUN | the shuttle run driving it ("" clears it); see Formulas |
--claim is a compare-and-set. It succeeds for exactly one caller: a
second claimer, or a claim on a blocked or non-open seed, exits 4 and
writes nothing. Claiming your own claim again is a no-op. The actor is
--actor, else SEEDS_ACTOR, else $USER.
Setting status to closed records closed_at; setting it back to anything
else clears closed_at and close_reason. An update that changes nothing
writes nothing.
Explicitly setting a status other than deferred (or using --claim) also
clears an old defer date and retires resume_when: notes markers to
historical resume condition: in that same transaction. The condition and
surrounding evidence remain in the notes. This also repairs stale parking
metadata when the requested status already matches. A non-empty --defer
in the same request deliberately establishes a new hold and preserves its
resume marker. Edits without a status or claim preserve existing holds.
Existing text is not replaced by accident. Setting a description, notes,
design, acceptance criteria or agent context
that already has text to different text (including "") is refused with
exit 5, naming the field, and nothing is written, unless --force is
given. This is br’s guard (br’s --force). Filling an empty field, or setting
the same text again, needs no --force, so re-running an update is safe. The
replaced text stays in the seed’s history (sd history). The check runs in the
shared update path, so a local store and a quipu server refuse alike.
--json: an array of the updated seed objects, each with the tx.
Exit codes: 0; 2 for a bad value or --claim combined with --assignee
or --status; 3 for an unknown id; 4 for a lost claim or a concurrent change;
5 for replacing existing text without --force.
sd update ID -a owner is the short form of --assignee owner.
Rank within a priority
sd update ID --rank 10 assigns an exact decimal ordering key; --rank ""
clears it. --before OTHER and --after OTHER insert only ID beside a ranked,
live item of the same priority, without editing neighbors or deadlines. Moves
accept one ID and cannot change priority or claim work in the same command.
Ranks are canonical decimal strings in JSON (null when absent), with up to nine
fractional digits and magnitude at most 9000000000000000000. No binary floating
point is used. Tied anchors, exhausted precision and boundary exhaustion refuse
without renumbering. Explicit rank assignments may tie; ordering then uses due
date, creation time and ID. Moving is relative to the observed cohort: concurrent
independent moves may receive the same rank. The moved item’s revision is checked
atomically; neighbors are never rewritten.
sd ready defaults to priority,rank; sd list --sort priority,rank requests the
same order. Unranked items sort last within their priority, oldest first. Due
dates remain time limits and are never changed by ranking.
A reviewed bulk migration can use scripts/migrate-ranks.py. Its plan is a JSON
object with quipu, graph and rows; each row names id, canonical rank
(or null), and expected priority, revision, rank and due_at. The default is a
read-only rehearsal. Execution requires --execute --plan-sha256 SHA; every edit
uses --if-revision and touches only rank. The receipt records each completed
edit and a revision-guarded rollback plan. A refusal or unknown outcome stops
without retry or automatic rollback. Inspect pending_id before continuing.
Rehearsal and execution need distinct receipt paths; an existing receipt is
never overwritten. Do not derive ordering from current deadlines implicitly:
review an explicit order and its current target preconditions first.