A run CLAIMS its card — assign to the token owner, and pick up only the TO DO category — because Motir is used by teams
motir run, motir batch and motir auto dispatch a card without claiming it and without checking whether anyone else has. On a single-operator project that is invisible. On a TEAM it is two people's agents working the same card, each unaware, producing two branches for one piece of work.
Two rules
1. CLAIM before dispatch. Assign the card to the token owner — whoami gives the id — before the agent is launched. That is the signal a teammate reads on the board: someone has this.
2. Pick up only the TO DO CATEGORY. The category, never a status key: a project may define its own statuses in that category and a run must take those too. Anything in the in-progress category belongs to whoever moved it there — including a card at Planning, which is how MOTIR-2425 removes a re-planned card from circulation without anything special-casing it.
⚠️ ADVISORY, not a lock — decided, with the cost accepted
Two runs starting together both read the same ranked page, both see the card unassigned, both assign, both dispatch. Last write wins and two agents work one card.
That race is accepted rather than closed. Closing it needs a conditional write — assign only if currently unassigned, and treat the refusal as "someone beat me, take the next" — which is a server capability that does not exist. The window is small (two runs must start within one another's read-to-assign gap) and the failure is loud rather than silent: two branches for one card is noticed immediately.
Do not simulate a lock. A read-then-write that checks first is the same race with more steps and reads like a guarantee. If the window ever matters, the fix is the conditional write, filed as its own card.
⚠️ This ends automatic RESUMPTION — decide what replaces it
Today an interrupted run leaves its card at in_progress, and the next run picks it up again (ensureInProgress skips the redundant flip). Under the category rule that card is no longer pickable, and nothing brings it back — it is stranded until a human moves it.
That is arguably right for a team (in progress means someone has it) and it is a real behaviour change. Settle one of:
- Accept the stranding, and have the run REPORT it: a card assigned to me, in progress, not picked up, named in the summary so it is visible rather than lost.
- Allow
in_progressassigned to ME, which is my own interrupted work rather than someone else's live work.
Either is defensible; silently dropping resumption is not.
Where the filtering happens
The ready set can narrow by ASSIGNEE — a repeatable assigneeId facet with the literal none for unassigned — so "unassigned or mine" may be expressible server-side. ⚠️ Verify the repeated form yields a UNION before relying on it.
It has NO status facet, so the category filter is client-side. A page that is entirely unpickable must therefore follow nextCursor rather than reporting the set drained — the same walk MOTIR-2398 builds for the exclusion skip, and the same way of getting it wrong.
motir run <key> is different
auto and batch PICK; run is GIVEN a key by a person. Decide whether the category rule refuses an explicitly-named card or only warns, and whether --force covers it the way it covers a not-ready item. Claiming, though, applies to all three: a named card is still claimed.
Scope BOUNDARY
The claim and the pickable rule in the three commands. It does NOT add a conditional-write lock. It does NOT change the ready endpoint or its facets — if "unassigned or mine" turns out not to be expressible, filter client-side and report it rather than widening the API. It does NOT change assignment anywhere else in the product. It does NOT unassign on completion: a finished card stays assigned to whoever did it, which is the record.
Acceptance criteria
- Each of the three commands assigns the card to the token owner before launching the agent, asserted per command.
- A card assigned to ANOTHER user is never picked up by
autoorbatch, asserted. - A card outside the to-do category is never picked up, asserted — including one at
Planning. - The rule is expressed on the CATEGORY, not on a status key: a project-defined status in the to-do category IS picked up, asserted with a custom status.
- A page entirely of unpickable rows follows the cursor rather than reporting drained, asserted.
- Whichever resumption answer is chosen is asserted, including the summary line if it is the reporting one.
- A card already assigned to ME is picked up normally — the claim is idempotent.
- No conditional write, no lock, no retry-on-conflict: the claim is a plain assignment.
Context refs
packages/cli/src/commands/dispatch.ts·auto.ts·batch.ts— the three pick/dispatch paths andensureInProgress.packages/cli/src/mcpClient.ts—whoamifor the owner id;updateWorkItemfor the assignment.lib/api/v1/ready/schema.ts— the assignee facet and thenoneliteral; no status facet.- MOTIR-2425 —
Planning, the status this rule removes from circulation for free. - MOTIR-2398 — the cursor-following pick this filter rides on.
- Story: MOTIR-1855.