Engineering

AI Agent Dispatcher: Packets, Not Spawns

Our ai agent dispatcher never starts sibling chats. It prints a copy-paste packet and keeps a ledger, while a separate supervisor runs bounded workers inside its own session.

Jonathan Wage

Founder / CEO

10 Min Read Reviewed by Mike Christensen Fact-checked by Mike Christensen
BluSky — The Future of Trading. Prop firm futures trading. Sign up at BluSky.pro.

Bottom Line

  • We wanted an agent that hears a review request and starts the right reviewer, but nested agents can run inside their own chat and cannot open sibling chats, so the dispatcher prints a packet instead of starting a chat.
  • The dispatcher decides one of four outcomes (packet, continue, noop, retire) with a small deterministic check that runs before any diff is loaded.
  • A packet is only printed when a review request names the human individually; team-only requests and code-owner implication are noops.
  • A local, untracked ledger enforces at most one active reviewer chat per identity key, so a re-request becomes a continue-the-existing-chat packet.
  • A separate supervisor role does run background workers, but only from its own session, one per claim, under a concurrency cap, with a verbatim brief and a draft review that a human submits.
  • Sibling chats, a shared team store, and timers stay out of scope, and the spec says so in a feasibility table.

We wanted an AI agent dispatcher that listens for review requests, decides who should look at a pull request, and starts a fresh reviewer session. Written against what agent platforms expose today, the spec lost its last step. A nested agent can run inside its own chat. It cannot open a sibling chat.

So the v1 dispatcher starts no chats. It decides, prints a copy-paste packet, and keeps a small ledger. A human pastes the packet into a new chat, and that chat is the reviewer. Background workers run elsewhere, inside a supervisor's own session, which is a narrower thing. This is one part of the larger AI software factory we are describing in this series.

What the platforms let us do

The spec contains a table titled "Feasibility (today)", kept on purpose because a design that quietly assumes a missing capability fails in production, usually at 2 a.m. and without a clear error.

  • Dispatch is itself an agent session. Yes. A human starts it, it subscribes to events through the host, ends its turn, and wakes when something happens.
  • Listening for my review requests. Yes on one host family with a repository-scoped GitHub subscription; we filter the payload down to the named human.
  • Claude Code and Codex as dispatch hosts. Weaker. A remote Claude Code session can use a pull request activity subscription if one covers the repository. Codex has no subscribe doorbell, so it needs re-prompting.
  • Opening a sibling chat automatically. No. The task and agent tools stay inside the current chat, and a worker launched from it is still its child.
  • The child staying on its PR. Yes, using whatever subscription its host offers or a local poller.
  • Platform one-shots (scheduled automations, routines, a review mention in a comment). Yes, but a different product: each event starts a new session, with no way to resume the same child.

Listening and deciding are solid; starting a second conversation is not. Browser automation, an undocumented launch command, or a webhook that creates sessions would each make the demo work and the system brittle. We designed to what the platforms can do and said so in a spec section, "What this is not".

Packets, not spawns

The dispatcher is a long-lived agent session that one human starts for themselves, scoped to one repository. It is not a GitHub Action, an organization bot, or a way to dispatch for teammates. The start prompt gives the repository, the human's login, prepare mode, and a pointer to the skill and workflow doc. It arms listening and ends its turn, with no polling loop and no review work in that chat.

On every wake, the first thing it runs is a small decide command with the notification JSON piped in. The result is one of four outcomes.

  1. packet: an eligible review request with no active reviewer for that key. Print a block to paste into a new chat.
  2. continue: an eligible request where a reviewer is already active. Print a block that says to paste into the existing chat.
  3. noop: the event is not for this human, was already handled, or is noise. Re-arm and end the turn.
  4. retire: the pull request merged or closed. Mark the ledger row retired, re-arm, end the turn.

The packet is plain text. It names the role (reviewer), the mode (prepare), the human's login, a ledger key, and the pull request URL, and tells the child to run its wake gate first and not load the full diff or post a review until required CI is green. The dispatcher prints immediately.

The packet never contains review text. A description or comment is untrusted input, and an embedded instruction should not travel through a dispatcher into the next prompt. The child fetches what it needs from GitHub itself.

Named means named

The eligibility rule is deliberately narrow. A packet is printed only when the wake is a formal review request on a pull request in this repository and the requested individual reviewers include the human's login. That rejects three cases that feel like they should count:

  • A request to a team where the human is a member but is not named individually.
  • A code-owner file that implies the human should look, when GitHub did not request them.
  • A review the human already started by hand, which the ledger turns into a continue or a noop.

This is stricter than the manual loop, where team membership is a fair trigger. Automation should be stricter than a person: a false positive is a stray chat, a false negative is just the manual path. The login is compared case-insensitively, and a leading at sign is stripped.

The login comes from the start prompt and nowhere else. On a cloud host the GitHub CLI identity can be a bot installation, so inferring from it would listen for requests addressed to a bot. A missing login is a noop with a reason, not a guess. See the GitHub as the bus post for that split.

Noop has to be cheap

A repository-wide subscription is noisy: every pull request event can wake the dispatcher, and loading a diff each time would burn its budget on other people's work.

So the decision is a small deterministic function, not a prompt. The decide command normalizes the cloud subscription and local poller payloads onto one contract, then runs a short chain of checks. No login: noop. A Linear event: noop. A closed or merged pull request: retire if there is an active row, noop if not. A review request that names someone else: noop. Anything unrecognized: noop with an unhandled-event reason. It reads and prints one JSON object and touches neither network nor diff.

This is the wake gate idea from zero-token wakes: a cheap, testable decision in front of the expensive model turn. Unit tests cover named-user packets, non-named noops, active-row continues, retirement, case-insensitive matching, and the cloud payload shapes.

The ledger and the key

The identity key for a reviewer is the reviewer's login plus the pull request number, with at most one active child per key. When a request arrives for a key with an active row, the outcome is continue. Re-requests are normal (an author pushes fixes and asks again), and a second reviewer on each would create the duplicate chatter we work to prevent in stopping agents from arguing forever.

The ledger is a local JSON file that git ignores. Each v1 row stores an id, a state, and a timestamp; the state is packet ready after a packet or continue, and retired after a merge or close. The spec lists more states for later (waiting on CI, working, waiting on the author), but we did not build what we cannot yet use.

A row is retired when the pull request merges, and by default when it closes unmerged, but not when a review is submitted: the reviewer child stays on the pull request, and a later re-request should produce a continue.

When we let agents run workers

The limit is about sibling chats, not delegation. Our watch workflow also has a queue-wide supervisor whose executor hands work to background workers (Claude Code background agents or Codex processes) launched inside its own session. The supervisor itself launches neither; a worker is a child of one live session, not a chat nobody owns.

The supervisor reads every open pull request through GitHub's command-line tool, and pure rules choose one next step each. In prepare mode every step is a recommendation; in auto mode the executor does this for a review:

  1. Claim. It claims a review for one pull request at one head. The claim returns a fixed brief.
  2. Hand over the brief verbatim. One background worker gets exactly that brief per claim.
  3. Record the run. A local ledger row tracks each run, and the executor marks the wake handled.
  4. Cap concurrency. The default is 2 live reviews, configurable from 1 to 10, because each end-to-end review may bring up its own Docker stack.
  5. Stage one draft. The worker leaves exactly one unsubmitted draft review at the current head, either inline author feedback or an approval-intent body with evidence.

The worker never submits, approves, requests changes, merges, or resolves threads. The human submits the draft, and the supervisor reacts to the review state GitHub then shows. GitHub is the source of truth; the ledger records only what this checkout launched.

Briefs pass verbatim

The brief carries the whole worker contract. When the executor added words like "triage by risk" or "e2e optional", workers skipped the evidence their review skill requires. So: no wrapper, summary, priority or scope advice.

Ledger lessons

One poller owns a queue, with one ledger per worktree, because a second poller would race the first on claims and marks. The executor never stops a listener with a live parent, since it belongs to another session; it stops only orphans, by exact process id, and attaches its own.

So the designs do not conflict. The dispatcher opens nothing. The supervisor runs bounded workers it can account for, in one session, under a cap, and a person owns every submitted review.

What v1 does not do

In v1:

  • The dispatcher does not start sibling chats. The child starts when a human pastes the packet. The supervisor's background workers are a separate role.
  • There is no shared team store; the ledger lives in the dispatch session's workspace and belongs to one person.
  • There are no timers. The spec mentions nudging the human if an author stalls for around a day after a review, but the host has no timer, so the dispatcher acts only when woken.
  • Author sessions are not dispatched; the human starts them. Issue tracker events are a noop with a specific reason, so an accidental subscription cannot open an author chat.
  • The dispatcher is always in prepare mode; auto mode exists only inside a child, if the human says so there.
  • The dispatcher has no cap on concurrent children; the human picks which packets to act on.
  • Nothing merges, marks a pull request ready, or bypasses review policy.

Why we did not build around it

Is copy and paste an acceptable place to stop? For now, yes, for two reasons. First, the human stays in the loop exactly where a new autonomous process would appear. A new reviewer chat is a decision about attention, cost, and which checkout to use, and pasting a packet is a cheap way to make it.

Second, if a platform later supports opening sibling sessions, the packet is already the right payload and only the deliverer changes; the decide function, key format, ledger, and child stay the same. The spec names that optional later work (a command or SDK that starts a session) and marks it out of scope.

Lessons

  1. Write the feasibility table first. Let what each host can do today, not ambition, set the scope, and keep the table so the next reader sees which assumption to revisit.
  2. Decide before you load anything. On a noisy subscription, a cheap deterministic function returns noop for most events before any diff or model turn.
  3. Make eligibility narrower than a human's. A team request or code-owner hint is not consent to start work, and a false negative has a manual fallback.
  4. Key the work and cap it at one. A stable key with at most one active child turns re-requests into continues without any coordination service.
  5. Keep untrusted text out of handoffs. A packet carries identifiers and pointers; the next agent fetches content itself and treats it as data.
  6. Say what is out of scope, in the spec. Sibling chats, shared stores, and timers are listed, so nobody builds on them by accident.

Humans own every outcome, and the children the dispatcher points at run in prepare mode by default and never merge on their own. If you are building something similar, read the platform's real capabilities first and draw the boundary there. The packet is a small feature. The discipline of not pretending is the larger one.

All In Engineering
  • Engineering Oct 13, 2026

    How to Build an AI Software Factory

    An AI software factory is a pipeline where agents propose and deterministic gates and accountable humans dispose. Here is how we follow one change through seven stages.

  • Engineering Oct 13, 2026

    Browser Verification for AI Agents

    Passing tests do not prove a page works. Here is how we make AI agents open the real page, read the screenshot, and fix the browser path until proof is fast.

  • Engineering Oct 13, 2026

    Compliance as an AI Agent Constraint

    Some product boundaries are too important for an agent's judgment. Here is how we encode them as ai agent compliance guardrails: status labels, a checklist, escalation by default, and a golden test.

  • Engineering Oct 13, 2026

    Risk-Proportional Review for AI Agents

    Risk based code review for AI agents means deterministic path rules set the floor, agent evidence can only raise it, and humans stay required where a miss costs the most.

Start trading at scale today. Sign up for free.

Free 7-day trial

Set-up in 3 minutes

Paper account for testing

TradersPost operates as a non-custodial automated trading platform, enabling users to connect alerts from their preferred trading platforms to their selected brokerage or exchange accounts. It abstains from the transmission, custody, or management of customer funds, covering both traditional and cryptocurrency assets. Typically, registration requirements set by regulatory entities such as the SEC, FINRA, or FinCEN apply to entities that hold or transmit customer funds. To ensure ongoing compliance, TradersPost regularly engages with regulatory authorities to confirm its adherence to all relevant local and federal laws.

TradersPost does not provide alerts, signals, research, analysis, or trading advice of any kind. It is designed to assist traders and investors in making their own trading decisions based on their alerts. The platform does not offer recommendations regarding securities to buy or sell, nor does it provide trading or investing advice. The platform and its features, capabilities, and tools are provided 'as-is' without any warranty.

Risk Disclosure: The use of automated trading systems involves inherent risks, including the potential for significant financial loss. These systems operate based on predetermined algorithms that may not fully adapt to changing market conditions, possibly making them unsuitable for some investors. Individuals are advised to thoroughly assess their financial situation and risk tolerance before using this platform.

Testimonials appearing on this website may not be representative of other clients or customers and is not a guarantee of future performance or success.

Broker, exchange, trading platform, company names, product names, service marks, trademarks, and logos appearing on this website are the property of their respective owners and are used solely to identify supported connections and technical compatibility. TradersPost is an independent third-party platform and is not affiliated with, endorsed by, sponsored by, or authorized by any of these organizations unless expressly stated. Technical compatibility does not imply a commercial partnership, sponsorship, endorsement, or authorization. See Important Disclosures for trademark information.

© 2026 TradersPost, Inc. All rights reserved.