R3 — Degradation is defined
Any new code path with a hard (required) external dependency has an explicit failure/degradation behavior defined - fallback, circuit breaker, or a documented fail-fast - rather than an unhandled exception propagating to the caller.
Python Implementation
R3: any new code path with a hard (required) external dependency has an explicit failure/degradation behavior defined — fallback, circuit breaker, or a documented fail-fast — rather than an unhandled exception propagating to the caller. rubric_store/definitions/reliability.yaml tags this `both`. The hardest of the ten criteria added this pass to verify from syntax alone — "is this fallback value a *real* degradation strategy" is fundamentally a semantic question. This rule only commits to two shapes with genuinely clear syntactic signal: - The catch block contains a `return` statement -> an explicit value is handed back to the caller instead of letting the exception propagate. That's a real, visible degradation path -> pass. - The catch block is empty or a bare `pass` -> the failure is silently absorbed with nothing defined at all -> fail (distinct from L1's "swallowed with no log" question; a block can log and still define no degradation behavior, which is exactly this case). Everything else — a bare re-raise (could be a documented fail-fast, could be unhandled propagation; only a human or the ticket text can tell which), or a handler that does something else without returning or raising — is "unclear" rather than a guess, on purpose.
Java Implementation
R3 for Java: any new code path with a hard external dependency has an explicit degradation behavior defined, rather than an unhandled exception propagating. Same two-clear-shapes-plus-honest-unclear design as the Python version (degradation_defined_rule.py): a `return` statement in the catch block is a real, visible fallback -> pass; an empty/bare-rethrow handler around a real dependency defines nothing -> fail; anything else is "unclear" rather than a guess.
Go Implementation
R3 for Go: any new code path with a hard external dependency has an
explicit degradation behavior defined. Not a straight port of the Python/
Java versions: Go has no exceptions to "propagate unhandled" in the first
place — every error must be explicitly returned by the function signature,
so `if err != nil { return err }` is itself Go's normal, idiomatic
fail-fast, not the failure mode R3 is really trying to catch. This rule
narrows to what's actually checkable:
- An `if err != nil` block with no statements at all -> the failure is
discarded with nothing defined -> fail, same as L1's identical shape.
- A block whose return statement propagates the error (see
ast_helpers.block_returns_error) -> "unclear", not a guessed pass or
fail: this is Go's idiomatic path, but whether propagation vs. a
fallback is the *right* call for this specific hard dependency needs
human judgment this pass can't make.
- A block with a return statement that does NOT mention the error at all
-> read as an explicit fallback value being returned instead of the raw
error -> pass, at moderate confidence (this can't distinguish a real
default value from a zero-value that happens not to say "err").
- Anything else (no return, does something else) -> unclear.
Only applies to error-check blocks whose enclosing function also makes an
outbound call somewhere (the "hard dependency" gate) — function-level
containment, not the tight per-try-block nesting Python/Java can check,
since Go's control flow is flat rather than block-scoped around a
try/except.