Report only the verdict you reached

Report only the verdict you reached

A check that could not conclude must say so, not emit a conclusion.

Every check has at least three outcomes, not two: pass, fail, and I could not tell. The third is the one implementations forget. When it is missing, the inconclusive case gets folded into one of the other two — and the check starts producing confident answers it never actually computed.

This is the dual of [[make-silent-failures-observable]]. That principle says silence must speak. This one says the noise must not lie.

Why this exists

A wrong verdict is worse than no verdict, because a verdict gets acted on.

Four instances, all measured on this platform within one working session:

None of these errored. Each produced a plausible wrong answer, and each cost real time to a reader who had no way to tell it from a real one.

A fifth, measured 2026-09-12, is worse than the other four because it was not a transient. The merge-through-gates signal asks git whether a branch reached the trunk, probing a list of candidate repository roots. On every install the first root was the installed runtime directory, not a checkout, so no root resolved and the signal returned a silent, unconditional “did not merge” — for as long as it had shipped. Two capabilities that depend on it were dead platform-wide with nothing anywhere saying so, and the suite stayed green because every test injected a stub in place of the probe.

The lesson is not “add a third state to gates”. It is that anything a decision reads is a check — a signal, a probe, a lookup — and inherits this obligation. An inconclusive branch that can never succeed in production is not a fail-safe, it is a blind spot wearing a fail-safe’s clothes. If a code path’s honest answer is always “I could not tell”, someone has to be told that, once, loudly, rather than every caller being told “no” forever.

What to do

Give “I could not tell” its own outcome. A tri-state (true / false / null), a distinct exit code, a separate log verb — whatever the surface allows. If a caller cannot distinguish “no problem found” from “the check did not run”, the check is not finished.

Make the inconclusive branch fail in the safe direction, and say which direction that is. Safe is not always “stop”: for a liveness probe, an unreachable control plane must NOT read as dead, because unreachability is precisely what kills the thing being probed — so unknown keeps waiting. For a policy gate, unknown blocks. The direction differs; stating it explicitly is what does not.

Never let a crash surface as a finding. A non-zero exit with no finding printed is a crash. Runners must distinguish the two, and a runner that cannot should report the ambiguity rather than pick.

Do not report a verdict about state you did not read. A record from a previous run is evidence about that run, not this one.

Ask which direction of the answer needs fresh state — it is often only one. Some properties are monotone: once a commit is an ancestor of the trunk it stays one, so a positive reachability answer read from a stale clone is still true, while a negative from the same clone is worthless — it cannot distinguish “not merged” from “not fetched since”. Gating both answers on freshness costs availability for nothing; gating neither ships confident wrong negatives. Measured 2026-09-12: the workspace the merge signal reads is refreshed when a build starts, not when an item completes, and was found ten days behind.

An inconclusive result never withdraws a verdict already reached. “I could not tell” is not evidence that the earlier answer was wrong – it is the absence of evidence, and absence cannot overturn a measurement. When an infrastructure event lands after a check has already concluded, record it beside the verdict, not on top of it. Measured 2026-09-10: a local-CI gate PASSED, published its result, and twenty minutes later the same record was rewritten to blocked_control_plane_starvation with the verdict and its evidence id erased – one lease, one run, and nothing had re-examined the tree. Whoever writes the result record owns this: a downgrade guard belongs at the write, because every caller that could clobber a verdict is a caller who did not mean to.

The consequence clause

A check that reports verdicts it did not reach gets disabled — by a switch, or by people learning to ignore it. Either way it is gone before the day it would have mattered. This is why a permanently-red check is worse than no check at all: it trains its audience out of the habit the check exists to create.

That failure mode is why enforcement is sequenced last. A guard shipped before its signal is trustworthy will sit red against genuinely healthy data, and be switched off long before it could catch anything.