Stage 1 changes the general agent loop into a fire dispatcher. The loop stays the same. A system prompt, a state message, and typed tools give the loop its task.

flowchart LR
    subgraph Intake["Intake (model extraction)"]
        i1["'smoke near the<br/>school on 5th'"]
        i2["'building fire on Elm St,<br/>people inside'"]
        i3["'fire is out at<br/>the school now'"]
    end

    r{"Reconciliation (Code)<br/>match to known incidents"}

    subgraph Incidents["Incidents (Memory)"]
        n1["1: School, 5th Ave"]
        n2["2: Elm St"]
    end

    subgraph Dispatch["Dispatch loop (Agent)"]
        plan["plan"] --> act["act"]
        act --> plan
    end

    subgraph Trucks["Fire trucks"]
        t1["truck A"]
        t2["truck B"]
    end

    i1 --> r
    i2 --> r
    i3 --> r
    r -- "update" --> n1
    r -- "new" --> n2
    n1 --> plan
    n2 --> plan
    act <--> t1
    act <--> t2

All contacts in this stage are structured reports. Each report describes a separate incident. The dispatcher must assign the trucks to the incidents.

What is provided

The starter provides:

  • the agent loop;
  • the report and incident stores;
  • the dispatch tools and tool runner;
  • structured contact data; and
  • the DISP-* and INT-* scenarios.

What Claude implements

Claude changes only these parts:

  1. Claude completes the minimum _finalize code in intake.py.
  2. Claude connects structured reports to incidents in runner._process_tick.
  3. Claude writes _state_message in dispatch.py.
  4. Claude completes prompts/dispatch_system.md.
  5. Claude runs the focused scenarios and fixes failures in this stage.

The state message must:

  • rank open incidents by risk to people;
  • mark each incident as COVERED or UNCOVERED;
  • mark each truck as FREE or committed; and
  • give one clear allocation instruction.

What you decide and check

You decide the dispatch policy. Review these choices before Claude edits the prompt:

  • How does the system rank people, severity, and uncertainty?
  • When does the system hold a free truck?
  • How does the system prevent duplicate coverage?
  • What information must a dispatch rationale contain?

Check the final state message. Ask Claude to show why the first incident has the highest rank. Do not accept a policy that you cannot explain.

What you do not build

Do not add:

  • natural-language intake;
  • duplicate reconciliation;
  • replanning rules; or
  • a new agent loop.

Later stages add these functions.

Prompt Claude

Open the HADR repository in Claude Code. Paste this prompt:

Complete Stage 1 of the HADR dispatcher.

First, read CLAUDE.md, dispatch.py, runner.py, intake.py, both stores, and the DISP-* and INT-* scenarios.

Before you edit, show the path from one structured contact to one dispatch tool call. List the code holes on this path. Stop if a required part is outside Stage 1.

Implement only these items:

  • Complete the minimum _finalize path for structured reports.
  • Connect each fire or unknown report to one incident in runner._process_tick.
  • Write _state_message in dispatch.py.
  • Complete prompts/dispatch_system.md.

Keep the general agent loop unchanged. Copy contact_id and reported_location from the contact envelope. Make each dispatch refer to a filed incident.

In _state_message, read truck coordinates from position. Permit a null route. Do not treat the old incident_id on an idle truck as a current commitment.

Show me the dispatch policy before you put it in the system prompt. Use this initial rule unless I change it: assign one free truck to each highest-priority uncovered incident. Do not issue a command that changes nothing. Wait for my approval.

Run uv run behave features/dispatch.feature features/intake.feature. Inspect the recorded request when a scenario fails. Do not change code before you understand the failure.

At the end, show one complete state message. Explain what code decides and what the model decides.

Check the result

Run:

uv run behave features/dispatch.feature features/intake.feature
uv run hadr-runner stage1-basic@londone

Record:

  • casualties;
  • buildings lost;
  • dispatch rounds; and
  • dispatch tokens.

Read one dispatch rationale. If both trucks go to the same fire, first check the state message and the system prompt. Do not change the model first.

The skeleton progress checker gives a more detailed review.

Completion criteria

  • A structured contact can cause a valid dispatch.
  • The state message prepares the facts before the model call.
  • You can explain the dispatch policy.
  • You have a live baseline for the next stages.