Skip to main content
The physics lives in the dbt transform layer. The machine layer reads it. That sentence is the entire design of the feature contract: fct_cliff_prediction_features is the single mart the five models consume, and every feature in the table was computed by the transform layer before ML ever runs.
Read-only mart contract. The ML layer reads fct_cliff_prediction_features read-only. It never writes to the warehouse or the application. driver_skill_residual_s the decomposition’s closure is excluded from the feature matrix; the models must not relearn what the skill layer already attributed.

33 features across 6 physics groups

Every feature has a physics interpretation. The grouping below matches FEATURE_GROUPS in ml/src/schema.py and the features.groups section of the model card the exact same ordering used during training.
Phase 9 (2026-09-05): pruned from 42 to 24. powertrain, telemetry_cliff, weather_air, track and context (18 columns) were dropped on a noise-floor group ablation across all three ablation-bearing model families each dropped group’s contribution to headline accuracy fell inside the seed-refit noise floor in every family scored, so it costs ONNX size and browser payload without a measured accuracy return. powertrain and telemetry_cliff were the mart’s entire consumption of int_lap_telemetry_aggregates; the ingestion and the telemetry data itself are unaffected, only their use as model features. See the execution plan’s Phase 9 section for the full ablation table.
Phase 10a (2026-09-05): 24 → 33, and the nine come from a different sensor. Every traffic feature before this one descends from FastF1’s DistanceToDriverAhead, which int_lap_air_state divides by point speed to get a gap in seconds an approximation that assumes the car ahead is travelling at the same speed as the car behind, which is exactly false in the situation the feature exists to describe. The proximity group instead measures a time: each lap is cut into 100 fractions of relative_distance, the session clock at which a driver first enters a fraction is a crossing time of a fixed point on track, and the gap is the difference between two crossings of the same point. The source is the telemetry stream’s position channel, 58.8M rows that had no consumer at all before this phase.Admitted on an add-ablation, not on the argument: p50 pinball 1.034661 → 1.012128 (1.54× its seed-refit noise floor), cliff macro-F1 0.372960 → 0.380950 (2.17×), stint-life AFT NLL 1.956152 → 1.952005 (0.59×, inside noise). The dirty_air group stays a swap arm that drops it and keeps proximity lands inside noise on all three families, so the two are carrying different things. Validated independently of the telemetry it is built from, against the FIA’s own 4,909 waved blue flags (assert_proximity_agrees_with_blue_flags).A permutation-null arm separates information from capacity nine more columns is nine more split candidates, which can flatter an eval fold on its own. With the proximity columns row-shuffled (capacity preserved, signal destroyed), the classifier’s gain is +0.008406, 2.28× its floor, entirely information, with a capacity effect of 0.11× floor. The p50 regressor’s total gain clears its floor but splits roughly evenly between information and capacity, with neither half clearing on its own so the quantile trio’s improvement is real on this fold but is not cleanly attributable to traffic.

stint_position (4)

Lap number, stint position, fuel mass. The core temporal signal where in the stint this lap falls.

compound (7)

Compound identity, grip peak, wear gradient, optimal temp window, cliff-onset and severity priors. The compound-level physics prior baked into the Reference family.

cliff_prior (5)

Expected pace, degradation rate, whether the onset lap has passed, laps past the cliff, and a cliff candidate flag. The Kaplan-Meier survival prior from the physics layer ML’s starting point, not its conclusion.

thermal (4)

Push residual, cumulative surface and bulk thermal load, and the surface-to-bulk ratio (added under Route C, C3). Encodes how hard the tyre has been working at each of the two thermal layers.

dirty_air (4)

Dirty-air share per lap, surface and bulk thermal load attributed to following traffic, and the dominant air-state label. Encodes the aerodynamic heat penalty from following another car, derived from FastF1’s DistanceToDriverAhead.

proximity (9)

True pairwise track gaps from the position channel: share of the lap within 1 s / 2 s of the car ahead, share spent in a train rather than a single tow, share with a car within 1 s behind, seconds within 1 s, closest and median gap ahead, how stable the car ahead’s identity was, and how many distinct cars came within 3 s. The same physical hypothesis as dirty_air measured from a different sensor and both are kept, because the swap arm says they carry different things.

The read-only contract in practice

Two target spans. The quantile trio and cliff classifier train on 114,270 laps that carry the degradation and cliff targets; the stint-life regressor trains on 120,934 laps the spans differ because a lap can have a valid stint-life count even where its next-lap jump is undefined.
Ordinal encoding handles categoricals: the training map assigns each seen value an integer; NULL or unseen values get −1 (the MISSING_ORDINAL sentinel). Continuous features keep NaN as NaN XGBoost learns the best split direction for missing values natively.

Excluded columns

Read-only mart contract. ML never writes the warehouse or the app. It reads fct_cliff_prediction_features read-only. The columns below are excluded from every model’s feature matrix (EXCLUDED_LEAKAGE_COLUMNS in ml/src/schema.py), asserted by the leakage-spine tests every build.
Excluded (22 columns causal leakage / identifiers / targets / training gate): circuit_key, drift_s_per_lap, driver_id, driver_skill_field_s, driver_skill_loro_mean_s, driver_skill_loro_s, driver_skill_proxy_s, driver_skill_residual_proxy_s, driver_skill_residual_s, is_training_eligible, lap_id, laps_until_cliff_class, next_3_lap_cumulative_jump_s, next_5_lap_cumulative_jump_s, next_lap_degradation_jump_detrended_s, next_lap_degradation_jump_s, race_id, race_year, remaining_stint_life_laps, stint_id, stint_length_laps, survival_weight

Relationships

fct_cliff_prediction_features

The mart this contract reads its full column list, lineage, and the tests that guard its grain.

Features and targets

How a mart row becomes a row of the X matrix encoding, fingerprinting, the forward-window audit, and the three targets.

Leakage spine

The 12 CI tests that assert these exclusions hold every build one idea per guard.

Model overview

Back to the ML front door.