Skip to content

Task Dispatcher as a taOS component: auto-assign claimable cards to shared agents across projects #2141

Description

@jaylfc

Task Dispatcher as a taOS component

Why

The claim lifecycle (ready/claim/claimable/release) is product, but the thing
that MATCHES idle agents to claimable cards is not: it is shell on the lead
box. Every rule it learned on 2026-07-26 (priority-then-oldest pooled across
boards, mechanical blocked-on, fix-forward anti-deadlock, one-claim caps)
benefits exactly one user. Any taOS user with shared agents across projects
should get this for free.

Scope: assignment engine, NOT execution

The dispatcher decides WHICH claimable card goes to WHICH eligible agent and
wakes that agent. Execution stays the agent's business (deployed container
agent via the heartbeat loop, external CLI via its own harness, etc.). This is
what makes it product-shaped: it composes with every agent type instead of
embedding one.

Design

New tinyagentos/projects/dispatcher.py service, started with the app, off by
default per user.

Config (per user, Settings + API):

  • enabled: bool
  • boards: project ids (default: all the user's projects)
  • eligible agents: canonical ids (must hold project_tasks on the board a card
    sits on; the dispatcher never bypasses grants)
  • max_concurrent_per_agent: default 1
  • poll_seconds: default 30 (superseded by event-driven when broker events for
    task-create/close exist; do not build a new event system for this)

Selection policy (the 2026-07-26 rules, now in product):

  • candidate = open/todo/ready/backlog, unclaimed, label claimable or
    fleet:claimable, assignee empty/@any or the specific agent
  • skip while a blocked-on:tsk-X dependency is open (same-board lookup;
    unknown ids fail open)
  • order: priority desc, then created_at asc, pooled across enabled boards
  • respect max_concurrent_per_agent (count of currently-claimed cards)

Dispatch = assign + wake: set assignee_id via the existing task update
path (audit-logged), then wake the agent through the existing heartbeat/wake
machinery. The agent claims on wake via the existing claim endpoint - claim
stays an agent action so the audit trail shows who actually took the work.

Explicitly out of scope: the exec-PR throttle and fix-forward bypass are
git-workflow-specific and stay in external harnesses; the product dispatcher
caps by max_concurrent_per_agent instead, which is transport-agnostic.

Acceptance (S1, backend only)

  • Config CRUD (owner/admin only) + persisted per user
  • With two eligible agents and three claimable cards across two boards,
    assignment follows priority-then-oldest and respects the per-agent cap
  • blocked-on skip proven by test; closing the dependency releases the card
  • An agent lacking project_tasks on a board never gets its cards
  • Disabled by default; enabling requires explicit user action
  • Existing manual claim flows completely unaffected

S2 (later): Settings UI pane + per-project dispatcher status view.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions