#!/bin/bash # coord-order-claim.sh - CLAIM one pending order out of this repo's queue and # print it. Exactly one session can win a given order. ASCII only, bash 3.2 safe. # # Usage: # coord-order-claim.sh [--repo ] # coord-order-claim.sh [--repo ] --next # oldest pending order # The trailing .md is accepted and stripped, so an id copied off a filename # works as well as one copied out of the injection. # # THE CLAIM IS THE RENAME, and the mutual exclusion comes from the SOURCE, not # from any lock. rename(2) is atomic, so of N processes attempting # orders/.md -> orders/claimed/.md exactly one finds the source; every # other gets ENOENT. There is deliberately no check-then-act step: `[ -e src ] # && mv src dst` is the classic race, and orders-selftest.sh section 5 runs 20 # barriered claimers against exactly that shape as a known-negative control - # it produces many winners, which is what proves the real test is not passing # vacuously. # # Exit: 0 claimed (the order is yours), 1 not claimed - already taken, or no # such pending order (nothing was written either way), 2 usage error. set -u export LC_ALL=C COORD="${CLAUDE_COORD_DIR:-$HOME/.claude/coord}" REPO=""; NEXT=0; ORDER_ID="" while [ $# -gt 0 ]; do case "$1" in # bash 3.2: `shift 2` past the end of $# is a no-op -> would loop forever. --repo) [ $# -ge 2 ] || { echo "coord-order-claim: --repo requires a value" >&2; exit 2; } REPO="$2"; shift 2 ;; --next) NEXT=1; shift ;; -h|--help) grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; -*) echo "coord-order-claim: unknown argument: $1" >&2; exit 2 ;; *) [ -n "$ORDER_ID" ] && { echo "coord-order-claim: one order id at a time" >&2; exit 2; } ORDER_ID="$1"; shift ;; esac done # git toplevel or an explicit --repo, never basename(pwd): guessing here claims # work out of a queue the caller does not own. if [ -z "$REPO" ]; then REPO="$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null)" fi [ -z "$REPO" ] && { echo "coord-order-claim: cannot resolve repo (not inside a git repo); pass --repo " >&2; exit 2; } case "$REPO" in _*) echo "coord-order-claim: $REPO is a reserved engine namespace, not a repo" >&2; exit 2 ;; esac ORDERS="$COORD/$REPO/orders" CLAIMED="$ORDERS/claimed" if [ "$NEXT" -eq 1 ]; then [ -n "$ORDER_ID" ] && { echo "coord-order-claim: use either --next or an order id, not both" >&2; exit 2; } # Oldest first. The id is timestamp-prefixed, so lexical order IS age order - # no stat call, and no dependence on mtimes a copy or a restore may have # rewritten. first="$(ls "$ORDERS"/*.md 2>/dev/null | head -1)" [ -n "$first" ] || { echo "coord-order-claim: no pending orders for $REPO" >&2; exit 1; } ORDER_ID="$(basename "$first" .md)" fi [ -n "$ORDER_ID" ] || { echo "coord-order-claim: order id required (or --next)" >&2; exit 2; } ORDER_ID="$(printf '%s' "$ORDER_ID" | sed 's/\.md$//')" case "$ORDER_ID" in */*|.|..|"") echo "coord-order-claim: invalid order id: $ORDER_ID" >&2; exit 2 ;; esac SRC="$ORDERS/$ORDER_ID.md" mkdir -p "$CLAIMED" 2>/dev/null || { echo "coord-order-claim: cannot create $CLAIMED" >&2; exit 2; } # No `[ -e "$SRC" ]` guard before this line, on purpose - see the header. The # rename is both the test and the action. if ! mv "$SRC" "$CLAIMED/$ORDER_ID.md" 2>/dev/null; then echo "coord-order-claim: could not claim $ORDER_ID - it is already claimed, already closed, or was never in $REPO's queue" >&2 exit 1 fi # Belt on top of the rename's own exit status: assert the destination exists # and the source is gone. mv's status is the contract, but the claim's whole # value is that it is TRUE, and this repo has been burned once already by a # transport asserting success against the call rather than against the world # (coord-send --reply-to, review finding 9). if [ ! -e "$CLAIMED/$ORDER_ID.md" ] || [ -e "$SRC" ]; then echo "coord-order-claim: claim of $ORDER_ID reported success but the order is not where it should be - do NOT act on it" >&2 exit 1 fi # The claim marker's own mtime is the claim time; the order file keeps the time # it was sent. Written after the rename, by the winner alone. printf 'claimed-at: %s\nclaimed-by-pid: %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$$" \ > "$CLAIMED/$ORDER_ID.claim" 2>/dev/null # The D-check lives at the claim moment because that is when a session first # holds both facts - the order and its own STATE. Printing it later would be # after the displacement has already happened silently. echo "coord-order-claim: CLAIMED $ORDER_ID for $REPO. This order is yours until you close it." echo "BEFORE YOU START: read this repo's STATE.md NESTE block and compare it with the order below." echo "If they are different tasks, say so in your FIRST reply, in one line:" echo " \"order $ORDER_ID displaces NESTE ; stands as next after\"." echo "WHEN DONE: coord-order-done $ORDER_ID --commit (or --no-commit --reason \"\")" echo "IF YOU CANNOT: coord-order-done $ORDER_ID --return --reason \"\" - it goes back to the queue." echo "--- order $ORDER_ID ---" cat "$CLAIMED/$ORDER_ID.md" echo "--- end of order $ORDER_ID ---" exit 0