Outcomes: failure as a value
This page explains why an unsuccessful Run is still a successful protocol interaction.
An outcome is the structured classification of a Run, including refusals and partial execution.
Pilotage separates four channels: carrier errors, request errors, diagnostics, and outcomes. A malformed JSON-RPC message is a carrier error. An unknown run is a request error. An invalid program produces diagnostics. A Run returns an outcome.
The six reserved outcome statuses are:
| Status | Meaning |
|---|---|
success |
The program completed. |
validation_error |
Execution was refused after validation; no side effects occurred. |
catalog_drift |
The pinned catalog no longer matches; no side effects occurred. |
promotion_drift |
A promoted program's baseline no longer matches; no side effects occurred. |
partial |
Execution began but did not complete; side effects may have occurred. |
error |
The Run failed and no more specific status applies. |
When Harborview refuses a triggered flow in immediate mode, the tool call itself is not an error. It returns:
{
"run_id": "run_0003",
"outcome": {
"ok": false,
"status": "validation_error",
"error": "this flow declares a trigger; mode \"immediate\" would silently ignore it"
}
}The response also carries a bad_trigger diagnostic explaining the repair. On MCP, this result rides isError: false. That distinction keeps failure inside the convergence loop: the Client reads the status and diagnostics instead of treating the interaction as a broken transport.
One boundary matters here: “verified” is not an outcome status and not a layer of the loop. It is the conclusion the agent reaches after reading the outcome and the trace against the original goal. The protocol reports what happened; the agent decides whether the mission was achieved.
Formal rules: Part II §5.7, Outcome, Part II §8, Error model, and Part III §7, Error mapping.
Next: Drift and recovery · Validation