Skip to content
Classification & Routing

Defect Classification & Repair Order Routing

Driver Vehicle Inspection Reports are the legal hinge between a vehicle being operationally ready and a carrier being liable. Under 49 CFR § 396.11(a)(3) a driver must record any defect that would affect the safety of operation or result in a mechanical breakdown, and under § 396.11©(2) the motor carrier must certify that the reported defect was repaired — or that repair was unnecessary — before the vehicle is dispatched again. § 396.13(a) then requires the next driver to review that certification and sign the prior report before operating the vehicle. Those three clauses are not paperwork; they are state-transition invariants. A defect that is discovered but never classified, routed, and certified is a Vehicle Maintenance BASIC violation waiting to surface at the next roadside inspection.

This section is the engineering reference for the layer that consumes a normalized inspection record and turns it into a certified repair. It sits downstream of the Core DVIR Architecture & FMCSA Compliance Mapping reference that defines the canonical record and the gated state machine, and it is fed by the DVIR Ingestion & Digital/Paper Parsing Workflows pipeline that produces validated payloads. What this section owns is everything between “a defect exists in the record” and “the record is certified return-to-service”: deterministic severity classification, routing to the correct maintenance pathway, idempotent work-order generation, technician dispatch, and the append-only audit trail that survives a DOT audit.

The design principle throughout is that compliance decisions must be deterministic, auditable, and imperative. When a score crosses a threshold, the system does not “flag for review” — it triggers an out-of-service hold. When a payload is missing a certification signature, the system does not warn — it rejects the payload. Every rule that maps an observation to a regulatory action is expressed in code, backed by database constraints, and mirrored in an immutable ledger.

Why § 396.11 Classification Cannot Be Discretionary

Anchor link to "Why § 396.11 Classification Cannot Be Discretionary"

Every defect-routing system exists to satisfy a federal mandate, and the mandate does not tolerate discretion at the boundary that matters. Under § 396.11(a), the report must cover at minimum the service brakes, parking brake, steering mechanism, lighting devices and reflectors, tires, horn, windshield wipers, mirrors, coupling devices, wheels and rims, and emergency equipment. When any of those components is reported defective, the carrier is obligated under § 396.11©(1) to repair it or determine that repair is unnecessary, and to keep the original report, the certification of repairs, and the certification of the reviewing driver for at least three months per § 396.11©(2)(iii).

The failure mode carriers actually get cited for is not missing a defect — it is dispatching a vehicle before the defect was certified as resolved. That is why classification must be deterministic. If a brake-adjustment defect scores as critical on one run and major on another because the logic depends on a model’s confidence or a race in the scoring service, the audit trail contradicts itself, and a contradictory audit trail is worse than none. The Out-of-Service (OOS) criteria published in the CVSA North American Standard are the ground truth: a brake violation that exceeds the adjustment limit, a steering component with excessive lash, a tire with tread below 2/32" on a steer axle — each is a binary, defensible condition. The classifier’s job is to reproduce those binary conditions faithfully and then layer graded severity on top for the conditions the regulations leave to carrier judgment.

The consequence of getting this wrong is measured in CSA points and roadside inspection frequency. A defect that should have triggered an OOS hold but was routed to a scheduled-maintenance queue becomes an operating-a-vehicle-with-a-known-defect violation the moment that vehicle rolls. Treating the severity threshold as a hard gate the data cannot bypass — rather than a suggestion a dispatcher can override — is the only defensible posture during an audit.

Architecture Overview: From Classified Defect to Certified Repair

Anchor link to "Architecture Overview: From Classified Defect to Certified Repair"

The routing layer is a directed state machine, not a queue of tickets. A validated DVIR record enters with one or more defect entries, each defect is scored, the score is mapped to a severity band, and the band selects exactly one routing pathway. From there the record cannot reach a RETURNED_TO_SERVICE state without a repair-certification event that references the specific defect code, and it cannot reach a FINALIZED state without the mechanic certification required by § 396.11©(2). Every transition is appended to the audit ledger before the next transition is allowed to begin.

Defect-routing pipeline from classified defect to certified repair A left-to-right gated state machine. A validated DVIR record enters the deterministic severity classifier, which emits a 0 to 100 score and splits on a severity band: critical 70 to 100 routes to an out-of-service hold with an immobilization flag, major 35 to 69 routes to a regulated 24-hour repair queue, and minor 0 to 34 routes to scheduled maintenance. All three bands converge on the idempotent work-order generator, keyed on VIN, component code, and inspection timestamp. The generator upserts into the CMMS across a webhook acknowledgement loop; a duplicate or schema-invalid payload is diverted to a dead-letter store. Accepted orders pass to technician dispatch, matched on certification and capacity, then to the repair-certification gate requiring the mechanic.certify role, and finally every transition is appended to a write-once audit ledger. A nightly reconciliation job cross-checks CMMS state against that ledger. Validated DVIR record ≥1 defect entry Deterministic classifier score 0–100 Critical 70–100 OOS hold · immobilize = true Major 35–69 regulated 24 h repair queue Minor 0–34 scheduled maintenance Idempotent work-order generator key = VIN·code·inspection_ts CMMS upsert webhook ack loop circuit breaker · backoff Dead-letter store rejected / duplicate payload Technician dispatch ASE cert + capacity match OOS jumps the queue Repair-certification gate § 396.11(c)(2) role: mechanic.certify WORM audit ledger append-only · SHA-256 chain Reconciliation job nightly CMMS ⇄ ledger cross-check one route per band upsert reject assign repaired certified log OPEN
Every classified defect follows a gated path: the band selects one route, all routes converge on an idempotent work-order key, and no order reaches return-to-service without a mechanic certification appended to the WORM ledger.

The critical design property is that the transitions are gated, not implicit. A dispatcher cannot manually move a critical record to a scheduled queue; the transition table simply has no legal edge for it. This is what makes the pipeline auditable: the set of paths a record can travel is finite and enumerable, and every path terminates in either a certified repair or an explicit, logged rejection. The severity mathematics that assigns the score feeding this diagram is developed in full in the Severity Scoring Algorithms for DVIR Defects reference; the branching that selects the pathway is developed in Critical vs Non-Critical Routing Logic.

Schema-Driven Data Standardization

Anchor link to "Schema-Driven Data Standardization"

Classification is only as reliable as the record it operates on. The routing layer consumes the canonical DVIR defect record produced upstream — the same field names and types defined in the Standardized DVIR JSON Schema Design — and never accepts free-text as an input to a compliance decision. The canonical defect record used across every page in this section is:

Field Type Compliance annotation
dvir_id str (UUID) Immutable idempotency key for audit-trail reconstruction
vehicle_vin str (17-char) Asset identity; cross-referenced against recall and OOS history
component_code str Standardized taxonomy (SAE J1939 SPN / OEM fault tree); see defect taxonomy mapping
defect_description str Sanitized driver input; never an input to the score, only to the record
driver_severity_flag enum{minor,major,critical} Driver’s § 396.11(a)(3) initial classification
inspection_ts datetime (tz-aware) § 396.11 inspection time; drives SLA and retention windows
odometer_reading int Wear context for scoring adjustments
compliance_tags list[str] Regulatory markers, e.g. FMCSA_396.11, DOT_OOS_CRITERIA

Enforcing this contract in code with Pydantic 3.10+ syntax makes the schema the first compliance gate. A record that does not validate never reaches the classifier:

python
from datetime import datetime
from enum import StrEnum
from uuid import UUID

from pydantic import BaseModel, Field, field_validator


class SeverityFlag(StrEnum):
    MINOR = "minor"
    MAJOR = "major"
    CRITICAL = "critical"


class DefectRecord(BaseModel):
    """Canonical DVIR defect record consumed by the routing layer.

    Validation here is the first compliance gate: a record that does not
    satisfy the schema is rejected at the API boundary (49 CFR § 396.11).
    """

    dvir_id: UUID
    vehicle_vin: str = Field(min_length=17, max_length=17)
    component_code: str
    defect_description: str
    driver_severity_flag: SeverityFlag
    inspection_ts: datetime
    odometer_reading: int = Field(ge=0)
    compliance_tags: list[str] = Field(default_factory=list)

    @field_validator("inspection_ts")
    @classmethod
    def require_timezone(cls, ts: datetime) -> datetime:
        # Naive timestamps break SLA and retention math; reject them.
        if ts.tzinfo is None:
            raise ValueError("inspection_ts must be timezone-aware (UTC)")
        return ts

    @field_validator("vehicle_vin")
    @classmethod
    def normalize_vin(cls, vin: str) -> str:
        vin = vin.strip().upper()
        # I, O, Q are not valid VIN characters; a misread here mis-routes the record.
        if any(c in vin for c in ("I", "O", "Q")):
            raise ValueError("VIN contains invalid characters (I/O/Q)")
        return vin

The component_code field is the join key between the driver’s observation and the regulatory meaning of the component. Mapping raw SPNs and OEM codes onto a single taxonomy is handled by the Defect Taxonomy Mapping for Heavy Trucks reference; the routing layer assumes that mapping has already run and that component_code is canonical.

The classifier converts a validated DefectRecord into a 0–100 severity score and a band. The score is computed by rule-based logic over component criticality, the driver’s own flag, and wear context — never by a stochastic model whose output cannot be reproduced during an audit. The band boundaries are fixed and reused on every page that references them: 0–34 minor, 35–69 major, 70–100 critical.

python
from dataclasses import dataclass

# Component criticality weights are the deterministic core of the score.
# Steer-axle brakes and steering are OOS-critical under CVSA criteria.
COMPONENT_WEIGHT: dict[str, int] = {
    "service_brakes": 45,
    "steering": 45,
    "tires_steer": 40,
    "coupling": 38,
    "lighting": 20,
    "wipers": 12,
    "horn": 8,
}

DRIVER_FLAG_FLOOR = {"minor": 0, "major": 35, "critical": 70}


@dataclass(frozen=True)
class Classification:
    score: int
    band: str  # "minor" | "major" | "critical"


def classify(record: DefectRecord) -> Classification:
    base = COMPONENT_WEIGHT.get(record.component_code, 15)

    # Wear escalation: high-mileage assets carry more residual risk.
    wear_bonus = 10 if record.odometer_reading > 500_000 else 0

    # The driver's § 396.11(a)(3) flag is a floor, never a ceiling: a driver
    # may under-call severity, but the system must not under-route it.
    floor = DRIVER_FLAG_FLOOR[record.driver_severity_flag]

    score = min(100, max(base + wear_bonus, floor))
    band = "critical" if score >= 70 else "major" if score >= 35 else "minor"
    return Classification(score=score, band=band)

Treating the driver’s flag as a floor rather than an override is the single most important compliance decision in the classifier: a driver who under-reports a critical brake defect as “minor” must not be able to route the vehicle back to service. The way carrier-specific thresholds are tuned around these fixed bands — so a marginal brake-lining observation on a steep-grade route escalates while the same reading on a flat regional lane does not — is developed in Dynamic Threshold Tuning for Fleet Types.

Idempotent work-order generation

Anchor link to "Idempotent work-order generation"

Every band maps to exactly one routing action, and work-order creation must be idempotent so that a network retry, an offline replay, or a duplicate DVIR submission never opens two tickets for the same defect. The deterministic key is derived from the fields that uniquely identify the defect event:

python
import hashlib
from enum import StrEnum


class Route(StrEnum):
    OOS_HOLD = "oos_hold"          # 70-100: immobilize, § 396.11 known-defect gate
    REPAIR_24H = "repair_24h"      # 35-69: regulated repair window
    SCHEDULED = "scheduled"        # 0-34: next maintenance cycle


def route_for(band: str) -> Route:
    return {
        "critical": Route.OOS_HOLD,
        "major": Route.REPAIR_24H,
        "minor": Route.SCHEDULED,
    }[band]


def work_order_key(record: DefectRecord) -> str:
    """Deterministic id so retries and replays are idempotent, not duplicated."""
    seed = f"{record.vehicle_vin}:{record.component_code}:{record.inspection_ts.isoformat()}"
    return hashlib.sha256(seed.encode()).hexdigest()[:32]


def build_work_order(record: DefectRecord, cls: Classification) -> dict:
    route = route_for(cls.band)
    return {
        "work_order_id": work_order_key(record),
        "dvir_id": str(record.dvir_id),
        "vehicle_vin": record.vehicle_vin,
        "component_code": record.component_code,
        "severity_score": cls.score,
        "route": route.value,
        "immobilize": route is Route.OOS_HOLD,
        "compliance_tags": record.compliance_tags,
    }

Because work_order_key is a pure function of the defect event, the CMMS write can be an idempotent upsert keyed on work_order_id: a duplicate submission resolves to the same row instead of a second ticket. This is what lets the pipeline tolerate the retry storms that follow a cellular dropout without corrupting the ledger.

Routing a work order into the CMMS does not guarantee a timely repair. The dispatch layer matches each open order to a technician by ASE certification, current shift status, geographic proximity, and active workload, and it refuses to assign a complex electrical fault to a technician who lacks the certification for it. Production dispatch runs on async task orchestration (Celery, RQ, or AWS Step Functions) with priority queues and circuit breakers for CMMS downtime, and it factors mean-time-to-repair baselines into queue priority so an OOS hold never waits behind routine work. The certification-weighted assignment model and capacity-aware scheduling are developed in Mechanic Assignment & Workload Balancing.

The routing layer is where several § 396.11 invariants are physically enforced, and enforcement is expressed as gates the data cannot bypass rather than checks a user can waive. The same principles that govern the upstream ingestion boundary are formalized for cloud workflows in the Compliance Boundary Enforcement in Cloud Workflows reference; within this section they take the following concrete form.

  • Severity-to-action gate. A critical band deterministically produces an OOS_HOLD with immobilize = True. There is no code path and no role that can route a critical defect to a scheduled queue.
  • State-transition invariants. The legal transitions are OPEN → IN_REPAIR → CERTIFIED → RETURNED_TO_SERVICE. A record cannot skip CERTIFIED; return-to-service without a certification event that references the specific component_code is not a representable state.
  • RBAC on certification. Only an identity holding the mechanic.certify role may emit the certification event required by § 396.11©(2). The driver who reported the defect cannot certify their own report.
  • Append-only audit logging. Every accepted transition writes a structured JSON event — payload, rule evaluation, resulting score, actor identity, and the previous event’s hash — before the next transition is permitted.

Encoding the transition table explicitly keeps the state machine auditable:

python
LEGAL_TRANSITIONS: dict[str, set[str]] = {
    "OPEN": {"IN_REPAIR"},
    "IN_REPAIR": {"CERTIFIED"},
    "CERTIFIED": {"RETURNED_TO_SERVICE"},
    "RETURNED_TO_SERVICE": set(),
}


def assert_transition(current: str, nxt: str, actor_roles: set[str]) -> None:
    if nxt not in LEGAL_TRANSITIONS[current]:
        # Illegal edge: reject rather than coerce the record into a bad state.
        raise ValueError(f"illegal transition {current} -> {nxt}")
    # § 396.11(c)(2): only a certifying mechanic may finalize a repair.
    if nxt == "CERTIFIED" and "mechanic.certify" not in actor_roles:
        raise PermissionError("certification requires the mechanic.certify role")

Database-level enforcement backs the application logic: repair-order creation and status updates run at serializable isolation so two concurrent workers cannot both advance the same record, and a unique constraint on work_order_id makes duplicate creation impossible even under a race.

Edge Resilience and Failure Modes

Anchor link to "Edge Resilience and Failure Modes"

Real fleets generate DVIRs from mobile clients on intermittent cellular links, so the routing layer must assume out-of-order, delayed, and duplicated events are normal rather than exceptional.

  • Idempotency. Every write is keyed on work_order_key, so a replayed submission upserts the same row. The CMMS integration uses the same key as its idempotency token so a webhook redelivery never opens a second ticket.
  • Offline reconciliation. A DVIR captured offline may arrive hours after its inspection_ts. Retention and SLA windows key off inspection_ts, never wall-clock arrival time, so a late-arriving critical defect is still correctly gated as of the moment of inspection.
  • Retry logic. CMMS and dispatch calls run behind a circuit breaker with exponential backoff. When the CMMS is unreachable, work orders queue in a durable dead-letter store rather than being dropped, and a reconciliation job drains the queue when the CMMS recovers.
  • Rejection handling. A payload that fails schema validation, carries an unresolved safety defect without a certification, or references an unknown component_code is rejected at the boundary and returned to the client for correction — it is never partially processed.
python
def ingest(raw: dict) -> dict:
    try:
        record = DefectRecord.model_validate(raw)
    except Exception as exc:
        # Reject at the boundary; do not admit an unvalidated record.
        return {"status": "rejected", "reason": str(exc)}

    cls = classify(record)
    order = build_work_order(record, cls)
    # Upsert is idempotent on work_order_id, so retries are safe.
    upsert_work_order(order)
    append_audit_event("OPEN", order, record)
    return {"status": "accepted", "work_order_id": order["work_order_id"], "route": order["route"]}

Every state transition in the routing pipeline must be reconstructable years later on demand, because a DOT audit can request the full chain of custody from defect discovery through repair certification. The retention strategy has three parts.

Immutability. The signed defect record and its certification are written to Write-Once-Read-Many storage — AWS S3 Object Lock in compliance mode or Azure Immutable Blob — so that no actor, including an administrator, can alter a finalized record. Each record is hashed with SHA-256 at write time and each audit event carries the hash of the prior event, producing a tamper-evident chain: altering any historical record breaks every downstream hash.

Retention scheduling. § 396.11©(2)(iii) sets a floor of three months for the original report, the repair certification, and the reviewing-driver certification. The pipeline keys retention off inspection_ts and, because many carriers extend retention for insurance and litigation holds, the retention policy is configuration, not a hard-coded constant — but the configured value can never fall below the federal floor.

Audit access. Records are indexed by vehicle_vin and preparation date so an auditor’s most common query — “show every DVIR and its certification for this unit over this window” — resolves without a full scan. OpenTelemetry instrumentation across the classification, routing, and dispatch services traces latency and error rates, and a nightly reconciliation job cross-references DVIR submissions, CMMS work orders, and driver sign-off records, flagging any discrepancy for compliance review before it becomes an audit finding.

By enforcing deterministic classification, idempotent state management, and a cryptographically chained audit trail, the routing layer turns § 396.11 from a recurring liability into a defensible, queryable record.

What severity score triggers an out-of-service hold?

A severity score of 70–100 places a defect in the critical band, which deterministically produces an OOS_HOLD with an immobilization flag. This band aligns with CVSA North American Standard Out-of-Service criteria, and the transition is a hard gate: no role or manual action can route a critical defect to a lower-priority queue.

How does the routing layer prevent duplicate repair orders?

Work-order creation is keyed on a deterministic identifier derived from the VIN, component code, and inspection timestamp via SHA-256. The CMMS write is an idempotent upsert on that key, so a network retry, an offline replay, or a duplicate DVIR submission resolves to the same order instead of opening a second ticket.

Can a driver’s own severity flag lower the routed severity?

No. The driver’s § 396.11(a)(3) flag is treated as a floor, never a ceiling. A driver may under-call a defect, but the classifier will never route below the severity the component and wear context justify, so an under-reported critical brake defect cannot be returned to service.

How long must DVIR defect records be retained?

49 CFR § 396.11©(2)(iii) sets a floor of three months for the original report, the repair certification, and the reviewing-driver certification. The pipeline stores these in WORM storage with SHA-256 hashing and keys retention off the inspection timestamp; carriers commonly configure longer windows for insurance and litigation holds, but never below the federal minimum.

Back to Defect Classification & Repair Order Routing is the top of this section; from here, continue up to the Core DVIR Architecture & FMCSA Compliance Mapping reference that defines the invariants this layer enforces.