Fail laws are load-bearing — do not unify them
Source:
harness/docs/decisions/fail-laws-are-load-bearing.md· Status: current.
Fail laws are load-bearing — do not unify them
Section titled “Fail laws are load-bearing — do not unify them”Decision
Section titled “Decision”Catalog input and host admit fail differently, by design, and there is no one shared fail-open helper. Each parse site picks its failure shape from what its caller needs:
| Site | Failure | Why |
|---|---|---|
parseWebSearchArgs / parseWebExtractArgs |
substitute defaults ({ query: "", limit: 5 }) |
model-facing catalog row; a bad call still mints a runnable job |
parseJsonRecord |
return null; throws stay at the caller |
host admit decides null vs throw |
write-gate (overwriteExisting spellings) |
.success boolean OR-chain |
read only, never fail the parse on shape |
agentCallBagSchema |
z.unknown() + passthrough, then peel/clamp in TS |
trim is TS logic, not schema logic |
parseWriteArgsJson |
string → JSON.parse → schema |
target shape, not a new dialect |
Rationale
Section titled “Rationale”Throw vs null vs {} vs raw-string fallback each encode a real product decision: a
catalog row that fails to parse still has to produce a job the model can see; a host admit
that fails has to be visible. Collapsing them into one helper deletes that information
and silently changes which paths fail open.
The one convention worth absorbing: empty argsJson → "{}" as preprocess.
Left deliberately untyped: SSE / OIDC JWT, catalog JSON-Schema docs, user
responseSchemaJson, extract markdownFromUnknown. These are unknown on purpose.
See also: schemas-per-package.md,
../runtime/approval.md §8.