Skip to main content
Back to notes
Still life of a closed leather ledger and a fountain pen on a dark wooden surface.
EngineeringJun 15, 20266 min read· Engineering team (Engineering)

Refuse rather than approximate: the engine's hardest design choice

When data is insufficient to compute a defensible euro figure, Provenship's engine returns REFUSED_NEEDS_VERIFIER_INPUT instead of an approximation. This is the same posture that makes the rest of the engine trustworthy. Three real scenarios.

#refusal#engineering#methodology

TL;DR

Provenship's calculation engine refuses to produce a number when its inputs are too sparse to defend in front of a verifier. The refusal carries a code, a trigger, and a remediation path. We walk through three real scenarios.

Most software bugs come from a function that should have refused returning a number instead. CLAUDE.md Core Principle #7, the one we go back to most often, is the simplest discipline we ship: refuse rather than approximate. This note walks through the three real scenarios where the engine returns REFUSED_NEEDS_VERIFIER_INPUT and what each refusal means for the operator.

Scenario 1 — missing bunker LCV

A vessel reports HFO consumption but the bunker delivery note does not declare the lower calorific value. The Annex II default exists (40,200 MJ/t) but the operator's BDN is supposed to override it where measured.

A naive implementation reads 'no LCV → use Annex II default'. Provenship does that — but emits a refusal-track event for the operator's dashboard. The penalty is still computed, but the audit trail flags the substitution. The verifier reviewing the report sees 'BDN missing LCV; Annex II default used' rather than 'computed value, trust it'. That visibility is the refusal.

Scenario 2 — voyage with no port pair

A vessel reports fuel consumed at sea but the voyage record carries neither departure nor arrival port. The EU MRV scope test (intra-EEA vs extra-EEA, with 50% scope for the latter) cannot be evaluated.

The engine refuses outright. The vessel-year is set aside in a REFUSED_NEEDS_VERIFIER_INPUT bucket with a code that says: 'scope cannot be evaluated; port pair required'. The operator's dashboard surfaces the row with a remediation prompt. The verifier never sees a guessed scope.

Scenario 3 — pool mate not in the dataset

An operator declares membership in a FuelEU compliance pool with a counterparty whose vessels are not in the operator's account. The pool-side surplus that would offset the operator's deficit is unknown.

The standalone penalty is computed (the operator sees their own deficit). The pooled net is refused — REFUSED_NEEDS_VERIFIER_INPUT, with a remediation prompt: 'pool counterparty data not on file; provide pool agreement or upload counterparty year'. The honest answer is the only useful answer here.

Why refusal buys trust

Approximation is what a verifier walks back into the room to argue about. Refusal is what the operator and verifier sit down to resolve together with a fresh data input. The shape of the conversation is the difference between losing the contract and renewing it.

Related notes