NAME

App::karr::Cmd::Pick - Atomically find and claim the next available task

VERSION

version 0.500

SYNOPSIS

karr pick --claim agent-fox
karr pick --claim agent-fox --status todo --move in-progress
karr pick --claim agent-fox --tags backend,urgent --json

DESCRIPTION

Selects the next available task for an agent, taking class of service, priority, blocked state, and claim expiry into account. When the board lives in a Git repository, the command also uses lock refs so concurrent agents do not pile onto the same candidate.

SELECTION RULES

  • Eligible statuses

    If --status is omitted, tasks in the board's terminal statuses are excluded -- its final configured status and archived, which on the default board means done and archived.

  • Claim timeout

    Already claimed tasks are ignored unless their claim timestamp has expired according to claim_timeout. A claimed_by of the empty string is not a claim; it is how kanban-md spells "unclaimed".

  • Ordering

    Candidates are sorted by class of service, then by priority, then by task id. The class and priority lists come from the board's own configuration (priorities and classes in config.yml), not from a hardcoded table -- so a board imported from kanban-md with a longer priorities list ranks according to its own list. Lower class index is more urgent; higher priority index is more urgent (matches kanban-md's pick.go).

  • --move

    Optionally updates the picked task to a new status such as in-progress.

JSON OUTPUT

With --json a successful pick prints the picked task as a JSON object, and picking nothing prints {"picked":null}. Either way the exit status is 0, so a polling agent decodes the payload and tests for a task rather than reading the exit code or the message text.

EXCLUSIVITY

The board is read once to rank candidates, but nothing is decided on that reading. Every candidate is re-read from its ref after its lock is taken, tested against the same predicate a second time, and written back under a compare-and-swap on the OID it was just read from. An agent that loses that swap has picked nothing and moves to the next candidate.

That belt-and-braces shape is not defensive programming, it is the fix for #86. The lock ref alone cannot make a pick exclusive: its holder identity is the clone's user.email, which every agent on one machine shares, so twelve parallel picks each acquired the lock quite legitimately, each acted on a snapshot taken before any lock existed, and each wrote its claim over the previous one -- nine agents were told they owned task 1, and the card named only the last of them. The lock now only keeps agents off each other's candidates; the compare-and-swap is what binds the claim.

LOCK EXPIRY

The lock is taken, used, and released within one command, and (since #45) it is released before the push rather than after it, so it is never published to the remote on the success path.

An agent that dies in between still leaves one behind, so locks expire: the board's lock_timeout (default 5m) is how long one may be held before another agent takes it over. It is deliberately a separate knob from claim_timeout (default 1h) -- a claim covers a work session, a lock covers the few milliseconds this command spends writing one card, and reusing the claim window would leave a crashed agent's task unpickable for an hour. App::karr::Cmd::Unlock is the manual escape hatch.

SEE ALSO

karr, App::karr, App::karr::Cmd::List, App::karr::Cmd::Move, App::karr::Cmd::Handoff, App::karr::Cmd::AgentName, App::karr::Cmd::Unlock

SUPPORT

Issues

Please report bugs and feature requests on GitHub at https://github.com/Getty/karr/issues.

IRC

Join #langertha on irc.perl.org or message Getty directly.

CONTRIBUTING

Contributions are welcome! Please fork the repository and submit a pull request.

AUTHOR

Torsten Raudssus <getty@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2026 by Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.