Skip to main content

Compliance Overrides

A compliance override is the audit-grade resolution of a charge event that failed one of TuffOps' regulatory gates. Rather than letting a tech save a non-compliant event silently — or refusing the save entirely and losing the operational record — TuffOps holds the event in pending review and waits for a supervisor to decide.

The model is built around three constraints: the decision must be made by a human, by a different human than the technician, and the decision must come with a reason a regulator can read.

The three kinds

Each gate produces its own override request kind, and each kind has its own approver permission. The split is intentional: the supervisor authorised to overlook an expired cert is not the same role as the supervisor authorised to bypass a recovery-equipment requirement.

KindTriggerApprover permission
cert_gatingTech logged a charge event without a current §608/§609 cert on file at save time.compliance.override_cert_gating
topoff_gatingTech topped off a unit that had no documented leak repair on the active episode.compliance.override_topoff_gating
recovery_equipment_gatingTech logged a recovery without a registered TuffOps cylinder.compliance.override_recovery_equipment_gating

A supervisor with two of the three permissions can decide two of the three kinds. They cannot decide the third.

Gating modes

Each gate runs in one of two modes, configurable per gate under Settings → Compliance:

  • Block — the tech cannot save the event without a supervisor present. The save is rejected; the event never enters the ledger as pending. Use this for high-discipline crews where the audit trail must be clean before the event is recorded.
  • Warn — the tech saves the event, the event enters the ledger as pending_compliance_review, and an override request is opened for a supervisor. Use this when blocking the save would leave the operational record incomplete (e.g. you'd rather have a recorded recovery with an audit gap than no record at all).

The two modes have different audit shapes. Block leaves no trace in the ledger when the gate fails. Warn leaves a pending event and an override request, both visible to inspectors.

The four-eyes rule

The subject technician of an override request cannot decide it. The page they see if they try shows a "four-eyes blocked" alert. This is enforced server-side, not just on the UI — the decision endpoints reject self-decisions.

The rule applies even when the technician has the approver permission. A field tech who happens to also be a supervisor still can't approve their own override.

The rule does not require two separate approvers. One non-self supervisor with the right permission is sufficient. The "four eyes" is the technician plus the supervisor.

What a decision writes

Approve writes a row to compliance_overrides:

  • request_id — links back to the override request.
  • overridden_by_user_id — the deciding supervisor.
  • decision_reason — the text the supervisor typed (minimum 20 characters).
  • decided_at — server timestamp.

The originating charge event is then posted as compliant. The unit's leak-rate calc includes it from the next computation onwards.

Reject voids the originating charge event. The void reason is the rejection text. The event stays in the ledger but its voided_at and void_reason columns are set, and the leak-rate calc excludes it.

Both decisions are terminal. There is no edit, no undo, no reopen. To restore a rejected event's effect, the technician must record a new charge event from a new work-order line item.

Why the request can't time out

Override requests don't auto-resolve. A pending request sits forever until a supervisor decides. The reason: silently approving or silently rejecting after a delay would create the worst kind of audit gap — a record changed by no one in particular at no particular time.

The operational consequence is that pending requests are visible in the queue, and the queue is the supervisor's worklist. If the queue grows, that's a process problem (the right supervisors aren't reading the queue) rather than a compliance problem (the events haven't been silently committed).