Planning bug: `motir run <id>` has NO claim, so two sessions built MOTIR-2952 at once — and the tell was a `git status`, not anything the MCP said
Repo: motir-meta (the fix is a run.md rule + a notes.html lesson).
What happened (2026-08-17)
Two motir run MOTIR-2952 sessions ran concurrently. Session A created
/workspace/prodect/motir-core-subtask-2952 on subtask/MOTIR-2952-fourth-enumeration at 15:37 and
began editing at 15:45. Session B started at ~15:41 and every signal it is told to read said the card
was untouched:
| signal | what it said |
|---|---|
get_work_item status | todo |
readiness | { ready: true, openBlockers: [] } |
validate_work_item | valid: true (two advisories, both about other cards) |
git status in the worktree at 15:43 | clean |
Session B then spent ~10 minutes on setup it did not need — a private Postgres install, migrate deploy, prisma generate, and a 10-file reproduction run — before git status at 15:52 returned
nine modified files, and a 75-second watch showed two more appear. The diff was unmistakably 2952's
own scope, including a comment reading "BoardNotFoundError is the DECIDED answer … (MOTIR-2952)".
Session B came within one command of destroying that work. Its next planned step was
git reset --hard origin/main in that worktree, to re-base off main — which would have discarded
eleven files of a sibling's uncommitted work with no recovery path.
The defect
run.md's claim is claim_next_ready, and motir run <id> explicitly skips it: "Optional motir run <id> skips the claim/selection and runs that specific item." MOTIR-1330 closed the TOCTOU window
on the bare path and left the explicit-id path with no serialization at all. Step 7 then compounds it —
"Status is ALREADY In Progress — the claim flipped it" — which is false on the <id> path, so a
run that follows the text literally never flips the status and never leaves a mark another session
could read. That is exactly what session A did, and why todo was still todo ten minutes in.
And a status check alone would not have saved session B either, because the status was never written. The only artefact that told the truth was the worktree.
Acceptance criteria
run.md'smotir run <id>path states that the explicit-id form has NO server-side claim, and that its FIRST action — before any guard, worktree, install or read — istransition_statustodo → in_progressas the claim, with a rejection treated as a STOP (a sibling holds it), matching the bare path's fail-to-retry shape.run.mdcarries a pre-flight sibling check on the<id>path: an existingsubtask|design|docs|parent/MOTIR-<id>-*worktree is inspected before it is touched — clean ⇒ reuse or re-base it; DIRTY ⇒ a live sibling owns it, STOP and report, and neverreset --hard/checkout --/cleana worktree you did not dirty. The criterion names the two-sample form (samplegit statustwice ~60s apart) for the case where the tree is clean but a session is mid-thought.- Step 7's "Status is ALREADY In Progress — the claim flipped it" is corrected to hold only on the bare path.
- A
notes.htmlentry records the near-miss with its numbers (clean at 15:43, nine files at 15:52, eleven by 15:53) and the lesson: on a shared box the worktree is a claim and the card is not — read the filesystem before trusting a status field. - The card records whether a PRODUCT fix is owed (an MCP claim op keyed by identifier, so the
explicit-id path gets the same
FOR UPDATE SKIP LOCKEDguaranteeclaim_next_readygives the bare one) or whether the rule is sufficient, with the reason.
Context refs
motir-meta/prompts/run.md— themotir runsection (the<id>note) and step 7.- MOTIR-1330 —
claim_next_ready, which fixed this for the bare path only. - MOTIR-2952 — the card both sessions were dispatched on.