Skip to content

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”

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

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.