Skip to content
Classification & Routing

Critical vs Non-Critical Routing Logic

Splitting Driver Vehicle Inspection Report (DVIR) defects into critical and non-critical streams is the single decision that determines whether an asset is legally allowed to move. Under 49 CFR § 396.11©(2), a driver-reported condition that affects safe operation must be corrected — and certified corrected — before the vehicle returns to service; the Federal Motor Carrier Safety Administration’s out-of-service (OOS) criteria then dictate which defects immobilize the unit outright. When that branch is made by hand, dispatchers clear held trucks under schedule pressure and carriers accrue Vehicle Maintenance BASIC violations. This page specifies the deterministic routing layer inside the Defect Classification & Repair Order Routing pipeline that consumes a scored defect, maps it to a compliance action in imperative terms — trigger an OOS hold or defer to a scheduled window — and emits the state transition to downstream maintenance and telematics systems without manual triage.

Two-lane critical versus non-critical DVIR defect routing flow A scored DVIR defect enters a threshold gate that compares its 0-100 severity score against the fleet-aware major floor of 35 and critical floor of 70. Scores of 70 or higher take the critical lane: an out-of-service hold with compliance_hold true, an immediate dispatch freeze, a P1 high-priority repair order, and a certified mechanic sign-off gate the asset cannot leave until repair and verification are recorded. Scores below 70 take the non-critical lane: major defects of 35 to 69 go to an expedited 24-hour clearance queue, minor defects of 0 to 34 are batched into a scheduled preventive-maintenance window. Both lanes converge on a single append-only, SHA-256 hash-chained audit log. Scored defect severity_score 0–100 + threshold_version Threshold gate score ≥ 70 score < 70 CRITICAL LANE · compliance_hold = true · oos_immediate OOS hold dispatch freeze P1 repair order high priority Mechanic sign-off gate repair + verification recorded → cleared NON-CRITICAL LANE · compliance_hold = false Major 35–69 expedited_24h queue Minor 0–34 deferred_pm batch Scheduled PM window · batched work order grouped by component system · staged once per batch Append-only audit log · SHA-256 chained

Prerequisites and Environment Setup

Anchor link to "Prerequisites and Environment Setup"

The routing service is a thin, deterministic consumer that sits between the scoring stage and the maintenance execution layer. It does not compute severity itself — it receives a normalized score produced upstream by the Severity Scoring Algorithms for DVIR Defects and applies fleet-aware cut points supplied by Dynamic Threshold Tuning for Fleet Types. Target Python 3.10+ (the code below uses match statements and structural typing) with:

  • Pydantic 2.x — schema validation and immutable routing payloads.
  • enum / datetime (stdlib) — severity tiers and UTC timestamps.
  • Celery 5.x or Prefect 2.x — asynchronous emission of alert and scheduling tasks.
  • A message broker (Kafka, RabbitMQ, or AWS SNS/SQS) — the transport the routing decision is published to.

The routing service assumes the inbound defect already conforms to the canonical contract defined in the Standardized DVIR JSON Schema Design; any payload that fails that contract must be rejected before it reaches this layer, not silently defaulted to non-critical.

The routing layer operates on two records: the scored input it consumes and the routing decision it emits. Field names and the 0–100 severity scale are held identical to the scoring and threshold pages so a single defect can be reconstructed across the pipeline during a DOT audit.

Field Type Enumeration / Range Compliance tag
defect_id string (UUID) Immutable key for audit-trail reconstruction
asset_id string (17-char VIN) Asset-level OOS and recall cross-reference
component_code string SAE J1939 SPN / OEM fault tree Maps to OOS component tables
severity_score integer 0–100 Output of the scoring engine
fleet_type enum long_haul_tractor, regional_step_van, refrigerated_box, last_mile_sprinter Selects the threshold profile
severity_tier enum minor, major, critical Routing decision label
compliance_hold bool true / false true binds an OOS/dispatch freeze
routing_lane enum oos_immediate, expedited_24h, deferred_pm Downstream queue selector
decided_at ISO8601 UTC State-transition timestamp for § 396.11 recordkeeping
threshold_version string semver Which cut-point matrix produced the decision

The score-to-tier mapping is the same three-band table reproduced across the defect-classification pages; the routing layer only adds the lane and hold columns:

Score Tier Compliance action Routing lane
0–34 Minor Log for next scheduled PM deferred_pm
35–69 Major Flag for pre-trip clearance within 24h expedited_24h
70–100 Critical / OOS Trigger an OOS hold; freeze dispatch immediately oos_immediate

The routing engine is a pure function over the scored defect plus its fleet-specific thresholds: given the same inputs it must always emit the same lane and hold flag. Fold no I/O, clocks, or random values into the branch itself — resolve those before or after, so the decision is reproducible from the audit log.

Defect routing state machine with the mandatory critical-clearance path A defect moves SUBMITTED to SCORED to ROUTED. From ROUTED the decision branches: when compliance_hold is true it enters HELD, otherwise it enters DEFERRED, which is a terminal non-critical state. A HELD asset can only reach the terminal CLEARED state by passing in order through REPAIRED and then VERIFIED — a certified mechanic sign-off with post-repair verification. Any attempt to skip a transition, for example clearing a held asset without a recorded repair or verification, raises a COMPLIANCE_EXCEPTION that escalates to the safety director rather than silently clearing the vehicle. SUBMITTED SCORED ROUTED compliance_hold = true compliance_hold = false DEFERRED terminal · non-critical HELD asset frozen · OOS REPAIRED certified mechanic VERIFIED post-repair evidence CLEARED terminal · return to service COMPLIANCE_EXCEPTION any skipped transition → escalate to safety director skip repair / verify
python
import logging
from enum import Enum
from datetime import datetime, timezone
from pydantic import BaseModel, Field


class SeverityTier(str, Enum):
    MINOR = "minor"
    MAJOR = "major"
    CRITICAL = "critical"


class RoutingLane(str, Enum):
    DEFERRED_PM = "deferred_pm"
    EXPEDITED_24H = "expedited_24h"
    OOS_IMMEDIATE = "oos_immediate"


class ScoredDefect(BaseModel):
    defect_id: str
    asset_id: str
    component_code: str
    severity_score: int = Field(ge=0, le=100)
    fleet_type: str
    threshold_version: str


class RoutingDecision(BaseModel, frozen=True):
    defect_id: str
    asset_id: str
    severity_tier: SeverityTier
    routing_lane: RoutingLane
    compliance_hold: bool
    threshold_version: str
    decided_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))


# Cut points arrive from Dynamic Threshold Tuning; defaults mirror the
# site-wide 0-34 / 35-69 / 70-100 bands and must never be hard-coded elsewhere.
def classify(score: int, major_floor: int = 35, critical_floor: int = 70) -> SeverityTier:
    if score >= critical_floor:
        return SeverityTier.CRITICAL
    if score >= major_floor:
        return SeverityTier.MAJOR
    return SeverityTier.MINOR


def route(defect: ScoredDefect, major_floor: int = 35, critical_floor: int = 70) -> RoutingDecision:
    tier = classify(defect.severity_score, major_floor, critical_floor)
    match tier:
        case SeverityTier.CRITICAL:
            lane, hold = RoutingLane.OOS_IMMEDIATE, True   # 49 CFR 396.11(c)(2): correct before return to service
        case SeverityTier.MAJOR:
            lane, hold = RoutingLane.EXPEDITED_24H, False
        case SeverityTier.MINOR:
            lane, hold = RoutingLane.DEFERRED_PM, False

    return RoutingDecision(
        defect_id=defect.defect_id,
        asset_id=defect.asset_id,
        severity_tier=tier,
        routing_lane=lane,
        compliance_hold=hold,
        threshold_version=defect.threshold_version,
    )

Because RoutingDecision is declared frozen=True, a decision cannot be mutated after it is emitted — an amended score must produce a new, separately logged decision rather than overwrite the original, which is exactly what an append-only audit trail requires.

Critical lane: atomic OOS enforcement

Anchor link to "Critical lane: atomic OOS enforcement"

When route() returns compliance_hold=True, the system must execute one indivisible sequence: freeze the asset’s dispatch status, generate a high-priority repair order, and fan out notifications. The alerting fan-out is handled by Automating Critical Defect Alerts to Dispatchers, which pushes structured payloads to dispatch dashboards, ELD gates, and mobile technician apps.

python
async def enforce_critical(decision: RoutingDecision, publish) -> None:
    if not decision.compliance_hold:
        raise ValueError("enforce_critical called on a non-holding decision")
    # Order matters: freeze dispatch BEFORE the repair order so no window exists
    # in which the asset is both flagged critical and still dispatchable.
    await publish("dispatch.freeze", {"asset_id": decision.asset_id, "reason": "OOS_396_11"})
    await publish("repair_order.create", {"defect_id": decision.defect_id, "priority": "P1"})
    await publish("alert.dispatcher", decision.model_dump(mode="json"))
    logging.info("COMPLIANCE_LOCK asset=%s defect=%s", decision.asset_id, decision.defect_id)

The asset stays frozen until a certified mechanic signs the repair and uploads post-repair verification. Premature dispatch clearance, a missing signature, or a skipped state transition must raise a compliance exception and escalate to the safety director — never silently clear.

Non-critical lanes: deferred and batched work

Anchor link to "Non-critical lanes: deferred and batched work"

Major and minor defects follow a different lifecycle. Minor findings are aggregated into batched work orders aligned to upcoming preventive-maintenance windows; major findings carry a hard 24-hour clearance clock but do not immobilize the unit mid-shift. The engine groups deferred items by component system (lighting, HVAC, cosmetic trim) so technician tooling and parts staging are prepared once per batch rather than per defect. High-volume deferral batches are handed to the same batching machinery described in Async Batching for High-Volume Ingestion.

Compliance Thresholding and Routing Obligations

Anchor link to "Compliance Thresholding and Routing Obligations"

Every branch of the router maps to a concrete FMCSA obligation. The table below is the authoritative mapping compliance officers should reference when reconciling routing logs against inspection records:

Computed value Routing action DOT / FMCSA obligation
severity_score >= 70 oos_immediate, compliance_hold=true 49 CFR § 396.11©(2): defect affecting safe operation must be corrected and certified before return to service; unit held per FMCSA OOS criteria
35 <= severity_score < 70 expedited_24h, compliance_hold=false 49 CFR § 396.11(a): documented defect requiring pre-trip correction within the carrier’s clearance window
severity_score < 35 deferred_pm, compliance_hold=false 49 CFR § 396.3(b): systematic maintenance record; defect logged for scheduled service
Missing or unparseable score reject payload, raise exception Never default to a non-holding lane — an unroutable defect is a compliance gap, not a minor one

The imperative rule that governs the whole layer: when the score is ambiguous, missing, or the threshold version is unknown, reject the payload and escalate rather than deferring by default. A false negative here is an unsafe vehicle on the road.

Production Integration and Platform Synchronization

Anchor link to "Production Integration and Platform Synchronization"

The routing decision is a fact that other systems subscribe to, not a call the router blocks on. Emit each RoutingDecision to the broker as a single event keyed by a deterministic identifier so retries during cellular dropouts or broker redelivery do not create duplicate repair orders. Derive the idempotency key from stable fields only:

python
import hashlib


def idempotency_key(decision: RoutingDecision) -> str:
    # Deterministic across retries: same defect + same threshold version => same key.
    material = f"{decision.defect_id}:{decision.threshold_version}:{decision.routing_lane.value}"
    return hashlib.sha256(material.encode()).hexdigest()

The Computerized Maintenance Management System (CMMS) consumes repair_order.create events, ELD and telematics platforms (Samsara, Geotab) consume dispatch.freeze to enforce the OOS gate on the vehicle side, and the audit store consumes every decision. Chain audit entries cryptographically — each record includes the SHA-256 hash of the previous decision for the same asset — so a deleted or reordered clearance is detectable during a DOT audit. Reconciliation jobs periodically diff the routing ledger against CMMS and ELD state to catch assets cleared without a mechanic sign-off or major defects left open past their 24-hour clock. Assignment of the resulting repair orders to technicians is owned by Mechanic Assignment & Workload Balancing; the router only guarantees the correct lane and priority reach it.

  • Schema validation — reject any inbound payload that fails the canonical DVIR contract; never coerce a missing score to zero.
  • Deterministic execution — the branch is a pure function of severity_score and threshold version; no clocks or randomness inside route().
  • Immutable decisions — emit frozen records; corrections create new logged decisions, never overwrites.
  • Idempotent emission — every published event carries a deterministic key derived from stable fields.
  • Fail-closed defaults — ambiguous or unscorable defects escalate as exceptions, not silent deferrals.
  • Versioned thresholds — persist threshold_version on every decision so a past routing call can be replayed exactly.
  • Cryptographic chaining — hash-link consecutive decisions per asset for tamper-evident audit packages.
What severity score triggers an out-of-service hold?

A normalized severity_score of 70 or higher maps to the critical tier, which sets compliance_hold=true and routes to the oos_immediate lane. That band corresponds to defects meeting FMCSA out-of-service criteria under 49 CFR § 396.11©(2), so the asset’s dispatch status is frozen until a certified repair and post-repair verification are recorded.

How is a major defect different from a critical one in routing terms?

A major defect (score 35–69) carries a documented 24-hour pre-trip clearance obligation but does not immobilize the vehicle mid-shift, so it routes to the expedited_24h lane with compliance_hold=false. A critical defect immobilizes the unit immediately. Both are logged, but only the critical lane freezes dispatch.

What happens when a defect arrives without a valid severity score?

The router must reject the payload and raise a compliance exception rather than defaulting it to a non-critical lane. Defaulting an unscorable safety defect to deferred processing is the failure mode that puts an unsafe vehicle on the road, so unroutable defects escalate to the safety director for manual classification.

Why must routing decisions be immutable and cryptographically chained?

DOT audits require a verifiable, append-only record of every state transition. Freezing each RoutingDecision and hash-linking consecutive decisions per asset makes tampering — a deleted clearance or a reordered sign-off — detectable, which is what turns the routing log into a defensible audit package.

Back to Defect Classification & Repair Order Routing.