Build1 publisher3 min readPublished
Telling the caller to evacuate stopped the agent from ever asking for an address
In a 30-call test suite for a home services intake agent, guardrail G4 told the model to stop collecting fields the moment it heard a gas smell and never told it when to resume. The dispatcher got the result.
The Engineer · Build desk

What happened
- An AI phone intake agent for home service contractors told a caller with a suspected gas leak to leave the house and call 911, then wrote an escalation record with null address fields and a null callback number.
- While debugging something else, the developer found the agent had answered "Okay hang on" with raw JSON three times in one call, a fault that all nine scoring criteria miss.
- Eight of the thirty scripted test calls failed the four criteria that plain code can check, with the five judge-scored criteria still unrun.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- exposure Voice intake moves the failure downstream to people who cannot fix it: a dispatcher holding an escalated gas call with no location and no number has nothing to act on, and the caller is off the line.
- constraint In a prompt, a path to a schema is just a path. Only injecting the same bytes the validator parses stops the contract the agent reads and the contract it is graded on from drifting apart.
- decision The suite has to be fixed before its score can be used as a model judgement, since some of its emergency cases are unpassable as written.
G4, the emergency guardrail, is three imperatives and every one of them terminates: "If any of these appears at any point in the conversation, stop the intake immediately. Do not finish your question. Do not collect remaining fields." [17] There is no clause describing a state to come back to. So when the scripted caller said "Okay, I'm outside," safe and still on the line, the agent asked for nothing [18].
The record it wrote at the end of that call carried urgency "emergency", emergency_type "gas", outcome "escalated", and nulls in callback_number, street, city and postal_code [7]. The on-call dispatcher could not send anyone and could not phone back [10]. "The model did exactly what I told it to. That was the problem," the developer wrote [11].
The repair went into the prompt text. G4 now carries a section called the safety-critical minimum: once the caller confirms they are out, ask exactly two things, one at a time, the address and a number to call back, and nothing else; if they panic or hang up, write what you have and mark it abandoned [19].
The earlier failure in the same case was a path. The prompt said: "At the end of every conversation, emit a single JSON object conforming to 01-intake-agent/schema/intake.schema.json." [12] A model cannot open a file, so it never read the schema and assembled a shape out of the field tables in the prompt instead, with trade fields at the top level, "gas_smell" where the enum requires "gas", and no outcome field at all [13]. The checker identifies a record by its schema_version field, which the model had not written, so the run reported "no intake object found" while a valid-looking object sat in the transcript [14]. Adding schema_version would have left three more ways to fail [15], four distinct mismatches in one record [3].
The fix took a few lines in the runner. The schema file is read with read_text(encoding="utf-8-sig"), injected into the prompt as intake_schema, and the same text is parsed into Draft202012Validator [16]. Generation and grading now load one file. The encoding argument is utf-8-sig.
Eight of the thirty deterministic runs failed, 27 percent [6][1]. Three of the eight belonged to the suite: emergency cases that demanded a phone number and an address no line in the script ever supplied, so no agent could have passed them [24]. Drop those and five of twenty-seven runnable cases failed, about 19 percent [2].
That figure describes one prompt, one schema and gpt-5.5 through a local gateway [5], scored on the four criteria plain code can check [3]. Five of the nine need a second model as judge and have not been run [4]. For the count to mean anything on another voice stack, the rubric would have to catch what this one missed. The agent replied to "Okay hang on" with raw JSON, three times in one call [20]. On a voice line the text-to-speech layer speaks whatever the agent returns [21]. C1 passed that call, because the checker reads the agent's last turn first and that turn happened to hold exactly one object [22]. The prompt now states that the record is written once, when the call is actually over, and that "hang on" and "one sec" are not the end of a call [23].
What to watch
- Whether a rerun after the G4 and schema-injection changes clears the five agent-attributable failures or moves them.
- Whether the rubric gains a turn-level check for records emitted mid-call, instead of scoring only the last turn.
- Whether the three unpassable emergency scripts are rewritten to contain an address and a callback number.