Interface Guardrail


public interface Guardrail
One safety-envelope term: something that must stay true no matter how good the objective gets (docs/process/scorer-interface.md §3).

The clause this whole class exists for: a guardrail that cannot check itself says UNKNOWN

A guardrail evaluates a finished run and returns a Guardrail.Verdict. There are three of them, not two, and the third is the point:

Several of §3's envelope terms are not log facts. "All invariant-tier tests green" is a Gradle exit code. "R8: this code passed in sim first" is a fact about history. "Logging overhead stays under its loop-time budget" needs a budget nobody has measured yet. A guardrail for one of those, handed only an RLOG, cannot answer — and if it quietly returns zero penalty, the envelope silently degrades into "whatever happened to be loggable", which is exactly the reward hack the envelope exists to stop. So it says UNKNOWN, loudly, and E2's acceptance gate decides what to do about it.

UNKNOWN is not a pass and not a failure. It carries no penalty — inventing one would be a made-up number — and it must never be read as "holds". Scorer keeps unknowns in its breakdown and counts them separately for precisely this reason.

Invariants only, never implementation facts

kP > 0 is not an envelope term (§3, §4): it belongs to the implementation tier, and putting it here would block a legitimate controller swap. An envelope term survives a redesign.
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Interface
    Description
    static enum 
    What a guardrail concluded about one run.
    static final class 
    A guardrail's conclusion about one run: a status, a penalty, and why.
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final double
    The penalty applied to a violated envelope term.
  • Method Summary

    Modifier and Type
    Method
    Description
    Checks one finished run.
    What this guardrail is called, for the score breakdown a human reads.
  • Field Details

    • ENVELOPE_VIOLATION_PENALTY

      static final double ENVELOPE_VIOLATION_PENALTY
      The penalty applied to a violated envelope term.

      Double.POSITIVE_INFINITY rather than a large finite number, so no objective — however good — can ever buy its way past a violation. A finite penalty is a price; this is a wall.

      See Also:
  • Method Details

    • name

      String name()
      What this guardrail is called, for the score breakdown a human reads.

      Abstract on purpose, which is also why this interface is not a @FunctionalInterface. It was briefly a default returning getClass().getSimpleName(), and that is a trap: a lambda cannot override a default method, so every lambda-built guardrail would have taken the JVM's synthesized lambda class name instead. Those names vary by JVM and by run, which would make the breakdown's keys unstable across a replay (R5) and would let two unrelated custom guardrails collide on one name. Two abstract methods means the compiler rejects the bare lambda instead, and the author has to say what the term is called.

      Returns:
      a stable name for this term, used as its key in the score breakdown
    • evaluate

      Checks one finished run.

      Return a verdict; do not throw. If this term needs a key the run does not have, return Guardrail.Verdict.unknown(String) — that is what UNKNOWN is for. Reaching straight for RunResult.metric(String) without checking RunResult.hasKey(String) first throws RunResult.MetricNotFoundException out through Scorer.score(RunResult), which stops the loop rather than scoring the run ineligible. That is loud and safe — nothing gets promoted on a crash — but it is not the behaviour anybody wants, and the fix is one hasKey check.

      Parameters:
      run - the finished run to check
      Returns:
      this term's conclusion about run; never null