Stage 2 replaces structured contacts with human text. Intake converts one contact into one typed report. The dispatcher reads this report.

The design
Intake is not an agent loop. Intake makes one model call for each contact.
flowchart LR
T["one contact"] --> L["intake model"] --> R["typed report"] --> V["code validation"]
The model interprets the text. Code validates the output. Code also copies facts that are already in the contact envelope.
What is provided
The starter provides:
- the model-call code in
extract_report; - the parser for model output;
- the report schema;
- the zero-token path for structured reports; and
- the accepted fixtures from the subagents and evals activity, if your team merged them.
What Claude implements
Claude changes only these parts:
- Claude completes
prompts/intake_system.md. - Claude extends
_finalizeinintake.py. - Claude enables
_finalizefor the human-text path. - Claude adds a small fixture grader.
- Claude adds focused human-text scenarios.
The finalizer must:
- copy
contact_idfrom the contact; - copy
reported_locationfrom the contact; - allow only
fire,unknown, ornoneasincident_type; - use an empty string when notes are absent; and
- set severity and headcount to null for a
nonereport.
What you decide and check
You approve the extraction rules. Review examples before Claude writes the prompt.
Check these distinctions:
- A stated fire or stated flames means
fire. - An explicit denial or a clear non-fire event means
none. - Smoke, smell, or an alarm without proof of fire means
unknown.
Review the prompt as carefully as code. Ask Claude to use clear decision rules. Do not use the sealed instructor labels during prompt development.
What you do not build
Do not add:
- tools to intake;
- memory between contacts;
- incident matching; or
- dispatch rules that hide intake errors.
Prompt Claude
Paste this prompt into Claude Code:
Complete Stage 2 intake as one text-to-JSON model call.
Read
intake.py, the report schema,prompts/intake_system.md,features/intake.feature, and a representative sample ofdatasets/contacts_unlabeled.jsonl. Read the accepted fixtures from the subagents and evals activity if they are present. Do not read the sealed instructor labels.Before you edit, show me three difficult contacts. For each contact, propose values for
incident_type, severity, headcount, and event time. Explain the rule for each value.Create a short decision table for the extraction prompt. Separate stated fire, explicit non-fire, and uncertain evidence. Show me the table before you edit.
After I approve the rules, implement only these items:
- Complete
prompts/intake_system.md.- Extend
_finalizeinintake.py.- Enable
_finalizefor human text.- Add a small grader for our accepted fixtures.
- Add scenarios for a stated fire, a denial, uncertain smoke, and one difficult sentence.
Keep intake stateless. Do not give intake tools. Keep the zero-token path for structured reports. Copy envelope facts in code. Validate model output in code.
Run the intake scenarios. If parsing fails, show me the raw model output before you edit the parser or prompt.
At the end, show one contact, the raw model output, and the finalized report. Mark every field that code changed.
Check the result
Run:
uv run behave features/intake.feature
uv run hadr-runner stage2-language@londone
Run the fixture grader that Claude added. Report results for each field. Do not report only one total score.
Compare the live result with Stage 1. The dispatch code did not change. Trace a new failure from the contact text to the finalized report before you change the dispatch prompt.
Catch-up solution
If intake remains blocked, use the reference intake prompt and implementation.
The intake review prompt gives a more detailed boundary check.
Completion criteria
- Human text produces a valid typed report.
- Code enforces envelope facts and schema rules.
- The prompt contains explicit extraction rules.
- You have per-field evaluation results.