fct_player_week
player_id × season × week × model_version × candidate. Prediction rows without actuals remain visible but do not qualify.
Python produces predictions and evaluation artifacts. dbt separately turns their file families and statistics into tested player-week facts and versioned decision marts; FastAPI serves both provenance paths without pretending dbt trains the model.
The pre-scoring silver gate and the post-scoring warehouse build are distinct execution stages.
TRACE A METRIC
The trace keeps evaluation window, season, model version, candidate and cohort aligned. It never pools ALL with positional cohorts.
fct_player_weekplayer_id × season × week × model_version × candidate. Prediction rows without actuals remain visible but do not qualify.
fct_weekly_evalRows with actual is not null are aggregated by eval_window × season × week × model_version × candidate × cohort. within_3 is inclusive.
independent pathsA singular test reconciles n exactly and MAE/within rates to 0.0001 against evaluation artifacts. /performance serves artifacts; /marts/* serves exports.
The fifth fixture row has no actual and is excluded. Result: n=4, MAE=3.625, within-3=2/4 and within-5=3/4. This defined unit test is not a claim about its most recent execution.
DECISIONS BEHIND THE MODELS
Problem. Published statistics can be restated after a game.
Chosen approach. Incremental delete+insert at player × season × week reprocesses the newest four distinct loaded periods plus new periods.
Tradeoff / limit. A correction outside that conservative, uncalibrated window waits for a full refresh. The fixture tests use a two-period window; they do not prove universal equivalence.
Downstream effect. Recent corrected scores flow downstream; fct_player_week stays a full table because its causal baseline depends on earlier observations.
Problem. Latest-seen player attributes cannot truthfully describe every earlier scoring period.
Chosen approach. snp_player checks name, position and team; the as-of view uses a capture when period > start and period ≤ next capture.
Tradeoff / limit. History begins at first capture. Earlier periods fall back to current attributes with is_exact_asof=false; this is warehouse knowledge, not a transaction ledger.
Downstream effect. The snapshot-backed views are not exported, and the current player API still reads latest-seen dim_player.
Problem. New interval metrics should not silently change an existing API contract.
Chosen approach. v1 retains alias fct_decision_policy; latest v2 exports as fct_decision_policy_v2 and adds within-3 rate and interval coverage.
Tradeoff / limit. An unversioned dbt ref resolves to latest, while /marts/decisions deliberately pins v1. Exported does not mean served.
Downstream effect. The response now reports mart_version from the serving constant; it remains a backwards-compatible v1 API.
Problem. Overlapping prediction families and incomplete outcomes can distort a headline rate.
Chosen approach. Weekly predictions win deterministic source precedence; model version and candidate stay in the key, and unplayed outcomes remain null.
Tradeoff / limit. Rates require their actual denominators. A narrowed population with a higher hit rate is not automatically a better policy.
Downstream effect. Evaluation artifacts and exported marts remain independent provenance paths that are reconciled under aligned scope.
VALIDATE + PUBLISH
Main builds the full development project. Pull requests may select state:modified+ and defer unchanged nodes to cached main development DuckDB relations; if the cache is unavailable, CI falls back to a full dev build. This is never production deferral. Full-history checks can also be conditional on vars, so a successful workflow is not interchangeable with full-data proof.