Data Schema Validation Rules

In modern aviation operations, raw telemetry, maintenance manifests, and scheduling payloads arrive from heterogeneous sources at scale. Without deterministic validation, a single malformed record silently corrupts downstream pairing engines, triggers regulatory violations, and cascades into costly operational disruptions. Within the broader flight data ingestion architecture, schema validation is the primary compliance gatekeeper: it translates regulatory mandates into executable, auditable logic before any record reaches flight dispatch systems or crew scheduling platforms. Everything a duty time validation rule engine later computes inherits the correctness — or the defects — admitted at this boundary.

The Compliance Challenge This Layer Solves

The scoped problem is narrow but unforgiving: given a stream of operational records from feeds that disagree on format, precision, and vocabulary, decide — deterministically and at ingestion latency — which records are structurally sound, semantically meaningful, and legally usable, and quarantine the rest with enough context to remediate them. A validation layer that merely checks “is this valid JSON” is worthless here, because the failures that matter in aviation are not syntactic. They are an ICAO code that parses but names a decommissioned aerodrome, a duty segment whose block-in precedes its block-out, a timestamp that is well-formed but carries no zone, or an aircraft registration that is grammatically correct for one authority and illegal for another.

Three properties make this hard. First, correctness is layered: a payload can be perfectly typed and still be non-compliant, so validation must run in stages rather than as one pass. Second, semantic truth is external — whether an airport code is active, whether a crew member’s medical certificate is current, whether an aircraft is under an open airworthiness directive — which means validation depends on live registry lookups that can fail transiently. Third, the same record feeds regulatory arithmetic under both FAA and EASA regimes, so the schema must preserve every field those calculations need (acclimatisation context, report time, sector count) even when the source feed omits or mangles it. A validation layer that satisfies an authority inspection must model all three concerns as first-class stages, not as branches buried in a parsing loop.

Layered Architecture: Syntactic vs. Semantic Gates

Production-grade validation requires a strict separation between syntactic verification and semantic compliance. Syntactic checks enforce structural integrity — field presence, data typing, and hierarchical nesting — typically expressed as pydantic models or JSON Schema documents. Semantic validation operates at the domain layer, cross-referencing payloads against authoritative registries: ICAO airport codes must resolve to active aerodromes, aircraft type designators must match certified configurations, and timestamps must be timezone-aware ISO 8601 instants rather than ambiguous local strings.

When processing high-volume telemetry through flight log parsing pipelines, a two-stage gate proves operationally resilient. A lightweight schema check at the ingestion edge immediately rejects structurally invalid payloads, while a contextual validation pass verifies operational feasibility against live aircraft status, crew qualification matrices, and historical routing data. This pattern eliminates downstream reconciliation overhead and prevents scheduler fatigue caused by phantom assignments that were never legal to begin with.

Two-stage validation gate An incoming payload enters a syntactic gate that checks type and shape. If it passes, it enters a semantic gate that resolves ICAO codes, fleet membership and timezone-awareness against live registries. A payload that clears both gates becomes legally compliant input to the pairing engine. A failure at either gate is routed to a quarantine holding a dead-letter queue and remediation metadata, never admitted downstream. pass pass fail fail Incoming payload Syntactic gate type · shape Semantic gate ICAO · fleet · time Legally compliant input to pairing Quarantine DLQ + remediation
Two-stage validation gate: a lightweight syntactic check at the edge rejects malformed shapes before a semantic pass resolves ICAO codes, fleet membership and timezone-awareness against live registries. Only a payload that clears both gates reaches the pairing engine; any failure is quarantined with remediation context rather than admitted.

Schema and Data Structure Design

The validation layer’s data model separates three concerns operators routinely conflate: the raw record as received, the versioned rules that judge it, and the structured outcome of that judgement. Keeping the ruleset in its own effective-dated table is what lets a regulatory revision be diffed, reviewed, and rolled back without touching a line of ingestion code — the same discipline the EASA FTL compliance frameworks apply to their FDP tables.

The core entities are the raw_payload as received from a source feed; the effective-dated validation_ruleset that supplies the active field constraints and semantic predicates keyed by revision date; the field_constraint rows that define type, bound, and cross-field rules; the validation_result that records pass/fail per record with its violation tier; the quarantine_record that holds rejected payloads with remediation metadata; and the authoritative reference registries — airport_registry, aircraft_type_registry, and crew_qualification — that the semantic gate resolves against. Every temporal column is stored in UTC with the originating zone retained as metadata, so local report time is derived at evaluation, never persisted ambiguously.

Schema validation domain entity-relationship diagram A raw_payload is judged one-to-one by a validation_result. Each validation_result applies exactly one version of the effective-dated validation_ruleset, which in turn defines many field_constraint rows. A validation_result that fails routes one-to-one to a quarantine_record carrying remediation metadata. The semantic gate resolves each raw_payload against three authoritative registries — airport_registry, aircraft_type_registry and crew_qualification. All timestamp columns are stored in UTC with the originating zone retained as an attribute, and ruleset rows are effective-dated with effective_from and effective_to bounds. judged by 1 1 applies 1 N defines 1 N routes to on fail semantic gate resolves against · N:1 raw_payload payload_idPK source_feed received_utc origin_zone bodyJSONB timestamps stored UTC, zone retained validation_result result_idPK payload_idFK ruleset_idFK verdict tier validation_ruleset ruleset_idPK revision effective_from effective_to ruleset rows effective-dated field_constraint constraint_idPK ruleset_idFK field · type · bound quarantine_record quarantine_idPK result_idFK remediation airport_registry icao_codePK active aircraft_type_registry type_codePK certified crew_qualification crew_idPK valid_to
The schema validation domain: raw_payload is judged one-to-one by a validation_result, which applies exactly one version of the effective-dated validation_ruleset that in turn defines many field_constraint rows. A failing result routes to a quarantine_record, while the semantic gate resolves each payload against the authoritative airport_registry, aircraft_type_registry and crew_qualification. Every timestamp is stored in UTC with the originating zone retained.

Field naming, unit standardisation, and audit lineage across the scheduling, payroll, and compliance modules all follow the shared crew duty time taxonomy mapping. Anchoring the schema to that taxonomy is what prevents the validator from misclassifying a positioning segment as a revenue sector, or a standby callout as the start of a flight duty period — the two most common sources of false violations at this boundary.

Regulatory Mapping and Temporal Constraints

Crew scheduling introduces distinct validation complexity because pairing legality depends on interdependent temporal constraints rather than isolated field values. Schema rules must model the relationships between flight segments, ground-handling windows, and mandatory rest periods, not just the shape of each field. The mapping below is the ground truth the validator encodes.

Because both regimes key their arithmetic on report time and sector count, durations must be consistently represented in UTC or explicitly tagged with IANA time zones, and aircraft registration formats must conform to national authority standards (N-prefix for the FAA, G-prefix for the UK CAA). When these rules are enforced at ingestion, downstream engines receive only legally coherent, temporally unambiguous inputs — and the source records flow in through the crew roster API integration already normalised to this contract.

Python Implementation Walkthrough

Production implementations express the syntactic contract as typed models so a malformed payload fails at the boundary rather than deep inside a compliance calculation. Using pydantic compiles the schema into an optimised validator, keeping per-record latency low during high-throughput ingestion. The model below is the syntactic gate for a duty segment: it enforces field presence, numeric bounds, timezone-awareness, and cross-field ordering in one construction step.

from __future__ import annotations

from datetime import datetime, timezone
from enum import Enum

from pydantic import BaseModel, Field, field_validator, model_validator


class Jurisdiction(str, Enum):
    FAA = "faa"
    EASA = "easa"


class DutySegment(BaseModel):
    correlation_id: str = Field(..., min_length=1)
    crew_member_id: str = Field(..., min_length=1)
    origin: str = Field(..., min_length=3, max_length=4)
    destination: str = Field(..., min_length=3, max_length=4)
    block_out_utc: datetime
    block_in_utc: datetime
    sectors: int = Field(..., ge=1, le=8)
    jurisdiction: Jurisdiction

    @field_validator("block_out_utc", "block_in_utc")
    @classmethod
    def must_be_utc(cls, value: datetime) -> datetime:
        if value.tzinfo is None:
            raise ValueError("timestamps must be timezone-aware")
        return value.astimezone(timezone.utc)

    @model_validator(mode="after")
    def check_temporal_and_station(self) -> "DutySegment":
        if self.block_in_utc <= self.block_out_utc:
            raise ValueError("block_in must follow block_out")
        if self.origin == self.destination:
            raise ValueError("origin and destination must differ")
        return self

The syntactic gate guarantees shape; it cannot know whether origin names a live aerodrome or whether the crew member is qualified on the type. That is the semantic gate’s job, and keeping it a separate, pure function is what makes every verdict reproducible from the audit log months later.

from dataclasses import dataclass


@dataclass(frozen=True)
class Violation:
    field: str
    constraint: str
    tier: str          # "syntactic" | "semantic" | "regulatory"
    record_id: str
    remediation: str


def semantic_gate(segment: DutySegment, registries) -> list[Violation]:
    """Cross-reference a structurally valid segment against live registries."""
    problems: list[Violation] = []
    for field, code in (("origin", segment.origin), ("destination", segment.destination)):
        if not registries.airport_is_active(code):
            problems.append(
                Violation(
                    field=field,
                    constraint="airport must be an active ICAO/IATA aerodrome",
                    tier="semantic",
                    record_id=segment.correlation_id,
                    remediation=f"verify {code} against the current airport registry",
                )
            )
    if not registries.crew_qualified(segment.crew_member_id, segment.origin):
        problems.append(
            Violation(
                field="crew_member_id",
                constraint="crew must hold a current qualification for the assignment",
                tier="semantic",
                record_id=segment.correlation_id,
                remediation="refresh qualification matrix or reassign",
            )
        )
    return problems

Structured violations — never unstructured log strings — are what let compliance dashboards trace non-compliance to its origin. Each carries the offending field, the violated constraint, the tier, the source record identifier, and a remediation directive, so a data steward can act without re-deriving what went wrong.

Rolling Window and Temporal Aggregation Patterns

Field-level validation is stateless, but the regulatory caps are not: §117.23 requires rolling totals over 168- and 672-hour windows, and the EASA equivalents over 7, 14, and 28 days. A validator that ignores these admits records that are individually legal yet collectively illegal. Timezone drift is the main source of cumulative error, so every input is stamped in ISO 8601 UTC before it enters the aggregation layer. Where the data lands in PostgreSQL, frame-bounded window functions express the caps directly; see the PostgreSQL window functions reference for the frame semantics.

-- Rolling 168-hour flight-duty-period total per crew member (FAA §117.23),
-- evaluated at each duty start so an over-cap segment is flagged at ingestion.
SELECT
    crew_member_id,
    block_out_utc,
    SUM(fdp_minutes) OVER (
        PARTITION BY crew_member_id
        ORDER BY block_out_utc
        RANGE BETWEEN INTERVAL '168 hours' PRECEDING AND CURRENT ROW
    ) AS rolling_fdp_minutes_168h
FROM duty_segment
ORDER BY crew_member_id, block_out_utc;

For pre-admission validation inside the ingestion worker, the same logic runs in memory over a Polars or Pandas frame, where a rolling group-by keyed on the duty timestamp reproduces the SQL frame without a database round trip. Whichever engine is used, the window boundaries must be inclusive of the exact regulatory span — an off-by-one on the 168-hour boundary silently under-counts duty and lets an illegal record through the gate it was meant to catch.

Integration Points

This validation layer is one stage in a longer chain and depends on clean contracts with its neighbours:

When validation depends on external registry lookups — verifying crew medical certificates or open airworthiness directives — transient network failures require disciplined retry logic. Exponential backoff with jitter, circuit breakers, and idempotent request signatures preserve resilience without breaching SLA thresholds. Records whose semantic checks cannot be completed are routed to a dead-letter queue for review rather than admitted or dropped, keeping the primary stream clean while maintaining a complete audit trail.

Testing and Edge Cases

Because validation is time-sensitive and registry-dependent, example-based tests miss the cases that matter. Property-based testing with hypothesis generates thousands of synthetic payloads and asserts invariants — for instance, that no segment with block_in <= block_out is ever accepted, and that UTC conversion is round-trip stable. The datetime and zoneinfo handling that underpins these tests is documented in the Python datetime documentation.

from datetime import timedelta
from zoneinfo import ZoneInfo

from hypothesis import given
from hypothesis import strategies as st


@given(
    block_out=st.datetimes(timezones=st.just(ZoneInfo("UTC"))),
    duration=st.integers(min_value=1, max_value=18 * 60),
)
def test_valid_segment_is_temporally_ordered(block_out, duration):
    segment = DutySegment(
        correlation_id="c-1",
        crew_member_id="crew-1",
        origin="KJFK",
        destination="EGLL",
        block_out_utc=block_out,
        block_in_utc=block_out + timedelta(minutes=duration),
        sectors=1,
        jurisdiction=Jurisdiction.FAA,
    )
    assert segment.block_in_utc > segment.block_out_utc

The boundary conditions that most often break aviation validators are specific and must each become a named, manually verified test case:

Predicate logic is validated against the current published regulatory constants before every deployment, with the ruleset version-controlled so a rule revision is a reviewable diff rather than a code change.

Explore This Topic in Depth

Back to Flight Data Ingestion & System Sync.

Explore this section