Striker comparison evidence
Build notes, deal evidence, expected-minutes evaluation, coverage audit, independent review and reproducibility hashes for the corrected striker comparison.
Build report
# Striker alternatives — first comparison build **[Open the interactive workbench](index.html).** It is self-contained and works locally; no external services or login required. [Full CSV](comparison.csv) · [versioned data](comparison.json). Built 10 September 2026 with an explicit analysis date of **9 September 2026**. This is a reconstructed summer decision comparison, not a pre-window forecast or a currently available shopping list. The output horizon is the **full 2026/27 domestic league season**, evaluated under a 38-match Serie A opportunity. Performance inputs stop at complete 2025/26; partial 2026/27 output never enters this build. ## What the product does - Search **859 entries**, covering the 700 historical CF/second-striker lineup candidates, additional source-native feeder players and explicit internal/defer strategies. There is no arbitrary 14-player or fee-band limit. - Compare **npxG and xA separately**, with a consistent output horizon. **551** have a shared Understat Marcel baseline. An unavailable projection does not remove a player from discovery. - Switch between empirical expected minutes and a common reserve/rotation/starter scenario. These are possible recruitment jobs, not fitted labels for player intent. - Plot annualized partial commitment against football output, retaining unknown-price alternatives outside the plot and in the table. **11** candidates have numeric partial fee-and-fixed-wage references; no inference converts missing cost to zero. - Inspect financial components, dated source links, position history, performance trends and limitations. Enter and export your own fee/wage/term scenarios without overwriting evidence. ## First comparison at the same 1,800-minute job The empirical-minutes column is a separate alternative to the shared job, not an additional multiplier. Price ranges are incomplete contractual scenarios, not confidence intervals or Juventus offers. | Reference | npxG/90 | xA/90 | npxG at 1,800 min | Empirical minutes | Annualized partial cost range | |---|---:|---:|---:|---:|---| | Randal Kolo Muani | 0.2905 | 0.1325 | 5.81 | 1,568 | €17.79m–€18.43m | | Dušan Vlahović | 0.5280 | 0.1144 | 10.56 | 1,511 | unpriced–unpriced | | Jonathan David | 0.5032 | 0.1683 | 10.06 | 2,192 | unpriced–unpriced | | Arkadiusz Milik | unavailable | unavailable | unavailable | unavailable | unpriced | | Emmanuel Emegha | 0.5384 | 0.1037 | 10.77 | 1,319 | €7.14m–€10.64m | | Rasmus Højlund | 0.3607 | 0.1057 | 7.21 | 2,427 | €20.26m–€20.26m | | Santiago Castro | 0.3129 | 0.1201 | 6.26 | 1,817 | €11.27m–€11.27m | These numbers identify trade-offs, not a final purchasing verdict. Vlahović has a higher baseline scoring rate than Kolo Muani; their creation rates and playing-time evidence differ. Retention economics for Vlahović are unresolved, and the historical rate-error envelopes overlap. Neither the point estimates nor the plot establish a clear, feasible upgrade at a known price. Milik remains visible with an abstention: insufficient qualifying prior-season Understat exposure means the shared rate model cannot provide his next-season rate. Feeder-only players show FotMob components separately, without inventing a league conversion. ## Material financial corrections The official-source review distinguishes RKM's €38m disclosed fee from an ancillary-cost ceiling of €3.2m and contingent bonuses. His annualized partial proxy is €17.79m at the disclosed fee floor, €18.43m with the ancillary ceiling, using the stored gross-wage reference. Those bounds omit further unresolved components; the base is a floor scenario, not a most-likely complete cost. Vlahović's Juventus contract expired in June 2026; a keep comparison is a hypothetical new contract. David and Openda had been loaned out by the analysis date, so retaining them is a historical alternative with unresolved salary-sharing and forgone proceeds. Emegha's advance agreement and the earlier Malen/Højlund arrivals also change which decisions were genuinely open in the summer. [Primary-source evidence and full 18-candidate cost audit](COSTS.md). No original acquisition fee is recharged automatically to a retained player. The displayed two financial measures are: - **Annualized partial commitment:** fee / stated contract years + one year of fixed gross wage. - **Full-term partial commitment:** fee + fixed gross wage × stated years. They are not actual cash schedules, complete liabilities, net resale results or fair values. Missing bonuses, ancillary charges, wage changes and retention terms remain visible. User cost edits are assumptions only. ## Validation and its practical limits The rate model reuses `scripts/site/marcel.py` rather than introducing another implementation. It aggregates split-league events/exposure, uses prior-only positional means and targets exactly 2026. Historical rate diagnostics contain **2,396** player-season pairs with an observed prior CF/SS start and at least 450 Understat minutes in both seasons. These seasons were already inspected; no untouched holdout is claimed. | Rate RMSE | Shared baseline | Last season | Prior positional mean | |---|---:|---:|---:| | npxG/90 | 0.126 | 0.141 | 0.171 | | xA/90 | 0.077 | 0.088 | 0.098 | The minutes baseline is deliberately simple: prior three-season minutes, weighted 2/1/1. Historical recorded early-arrival cohorts retain covered zero-minute outcomes. Fitted mean/shrinkage comparators use only earlier completed seasons. The fixed baseline does **not** beat every comparator; linear shrinkage remains an exploratory challenger, not a winner selected on these same diagnostics. | Minutes model | Temporal test rows | RMSE minutes | MAE minutes | Zero-minute outcomes | |---|---:|---:|---:|---:| The broad minutes evaluation includes return signatures, with an excluding-return view and majority/minority-CF histories reported separately. Only five zero-minute outcomes remain in the excluding-return test cohort, limiting confidence about transfer failures. Kolo Muani's prior mixed-role starts are explicit rather than forced into a majority-CF label. [Minutes model and coverage](MINUTES.md). The displayed historical residual envelopes are not individually calibrated prediction intervals. Multiplying separate rate and minutes means gives an output proxy; their joint dependence is unmodeled. Multiplying marginal endpoints is a stress envelope, not an 80% joint interval. Injury, selection, adaptation and manager effects are not independently identified. ## Coverage, identity and availability [Independent data audit](DATA-AUDIT.md) documents three important constraints: 1. Transfermarkt position `apps` counts lineup listings, including bench. Actual starts are shown separately; neither is within-match role-minute allocation. 2. Feeder extracts already exclude players below 900 minutes and use profile position labels. Unlinked identities remain source-native and receive abstentions. Emegha's existing lower-confidence source bridge is flagged for identity review. 3. The bulk transfer snapshot is incomplete around deadline day and contains future-dated transactions. Prior-season club labels are historical; the workbench does not infer current availability from a maximum transfer date or market value. There is no validated Juventus-context or cross-league output adjustment. The rate stress factor is user-set and applies equally; it is not estimated league translation. Whole-season source rates include all observed playing roles. Current official terms and historical performance snapshots have separate provenance. ## Reproduce ```sh python3 scripts/recruitment/minutes.py --candidates reports/striker-comparison/candidate-ids.csv python3 scripts/recruitment/build_comparison.py python3 scripts/recruitment/render_comparison.py python3 scripts/recruitment/write_report.py python3 -m unittest discover -s tests/recruitment -p 'test_*.py' node tests/recruitment/test_workbench.cjs ``` `--reuse-rates` requires matching input/model/output hashes. Minutes caches also require matching source, candidate, code and output hashes. [Manifest](manifest.json) records the consumed snapshots, caches and frontend sources. The independent review and final verification are recorded alongside this report. **Release status:** usable local comparison with explicit abstentions, assumptions and evidence. New transfer grades, monetary Deal Alpha and deployment remain pending. Browser-based visual QA is unavailable in this session; DOM-adapter interaction tests are not visual verification. The original production data and sealed registers remain unchanged.
Deal and cost evidence
# Striker comparison: cost evidence v1 **Analysis cutoff: 9 September 2026.** This is a late reconstructed summer comparison. The cost evidence includes information published after the old August register. It is not a claim that any current offer was available to Juventus, nor a forecast sealed before the window. ## What the numbers mean `annualized_commitment_proxy = fee scenario / stated employment years + fixed annual gross wage scenario` `full_contract_proxy = fee scenario + stated employment years × fixed annual gross wage scenario` Both are partial accounting-style comparison proxies. The first is not actual annual cash paid or audited amortization. The second assumes constant wages, excludes unpriced commitments and is not a verified full liability, discounted cash flow or resale-adjusted cost. No original acquisition fee is charged again to a retained player. Contingent purchase obligations, performance bonuses, loan receipts and options stay separately labeled. Low/base/high are explicit scenarios, not confidence intervals. A single salary figure repeated across scenarios does not establish its precision. The base RKM fee is the disclosed minimum, not an estimated most likely fee. ## Material corrections from primary evidence - **Kolo Muani:** this is an actual Juventus acquisition, signed2August2026, with availability status `signed_by_juventus`; it does not carry the peers’ missing-Juventus-offer flag. Juventus disclosed a €38m permanent fee, ancillary costs up to €3.2m, and additional contingent bonuses up to €12m. Employment runs through June2031; the fee is payable over four financial years. Using the archived €10.19m gross fixed wage, the fee-plus-fixed-wage proxy is €17.79m yearly at the disclosed fee floor and €18.43m with the full ancillary cap. Constant-wage full-term proxies are €88.95m and €92.15m. Neither includes the €12m conditional transfer bonus or reported wage bonuses. The legacy register’s €41.2m is the ancillary-cap scenario, not a verified fixed acquisition payment. [Juventus release, 2 August2026](https://www.juventus.com/images/image/private/fl_attachment/dev/rmqn9frsqodxlg8wrvxf.pdf). - **Vlahović:** the original Juventus contract expired June2026. Retention means a hypothetical renewal, with unknown new term, wages and signing costs. His old €22.22m gross wage is a reference only. Beşiktaş announced a three-year deal on13August at €7.5m guaranteed annually, without stating gross/net tax basis; that is not substituted for Juventus gross wages. [Original Juventus agreement](https://www.juventus.com/images/image/upload/dev/rbyns9djqekerapuzioe.pdf), [Beşiktaş agreement](https://bjk.com.tr/tr/haber/94879). - **Malen:** Roma’s initial loan dates16January2026; the option exercise was announced26May, with employment through2030. He is an earlier decision benchmark, not a new summer arrival. The €25m fee in this version remains a legacy secondary report. [Initial loan](https://www.asroma.com/en/news/74665/donyell-malen-signs-for-roma), [option exercise](https://www.asroma.com/en/news/75420/donyell-malen-statement). - **Højlund:** Napoli confirms he originally arrived on loan in summer2025 and the purchase conditions had been met by3June2026. The comparison must not relabel that obligation as a fresh summer decision. [Napoli announcement](https://sscnapoli.it/en/sscn-hojlund-e-a-tutti-gli-effetti-un-calciatore-azzurro/). - **Emegha:** Chelsea announced an advance agreement for his2026arrival. The retrieved primary text does not expose an exact publication date. This proves an advance deal, not that he remained available during summer2026. [Chelsea announcement](https://www.chelseafc.com/en/news/article/chelsea-agree-deal-to-sign-emanuel-emegha). - **David:** the old register’s incumbent label is stale at the new cutoff. Juventus announced an Atlético loan on1September2026, fee-free with a €25m purchase option; salary allocation is not disclosed. Original2025 ancillary registration costs were €12.5m, not zero. A keep scenario before departure cannot count the option as guaranteed forgone proceeds. [2025registration](https://www.juventus.com/images/image/private/fl_attachment/dev/zok6u6ehh096iczjy0ci.pdf), [2026loan](https://www.juventus.com/images/image/private/fl_attachment/dev/zdy5kqwzkeujpncfqrm5.pdf). - **Openda:** Juventus announced a Lyon loan on28July2026 for €3.5m. He is a hypothetical retained-before-loan baseline. The €3.5m is a documented foregone receipt in that scenario; salary sharing remains unverified. The2025 loan, subsequent conditional obligation and2026 disposal must not be collapsed into a new fee-free player. [Lyon loan](https://www.juventus.com/images/image/private/fl_attachment/dev/pwcjmxqa8hcmjvshnpmi.pdf), [2025acquisition terms](https://www.juventus.com/images/image/private/fl_attachment/dev/oubp0gexcep2znxampgt.pdf). ## Evidence limits that change the comparison - The remaining external fees and contract terms are archived Transfermarkt/Capology reports; they have not all been independently confirmed by primary club contracts. This product does not silently promote those reports to verified offers. - Five legacy destination salaries are Capology point estimates, four with its own verified marker. That marker is provider provenance, not our access to a contract. - Seven wage scenarios use the old model fitted to only eight priced signings. Its error quartiles are not calibrated prediction intervals, particularly outside the calibration leagues. The stored LOO APE descriptions also disagree (36.3% versus35.0%); no single validation figure is asserted here. - The two club-ceiling-only wages (Delap and Emersonn) are removed from the comparable numeric axis. A new recruit could exceed an incumbent’s salary; the old ceiling is no factual upper bound. - Rodríguez’s five-year term was a pool-median imputation despite a one-year Capology reading and missing Transfermarkt expiry. The base term is now null. The UI may expose an explicit user assumption. - Emersonn’s origin conflicts inside the archives: pool Toulouse versus a Transfermarkt note Göztepe. This version flags the conflict. - David, Openda and Milik have wage-only references. Their aggregate retention economics remain null while future obligations and opportunity costs are unresolved. Milik has a supported existing contract to2027; David/Openda had already left on loan by the cutoff. - Unknown candidates stay searchable with unknown costs. Explicit scenario entry can fill fee, gross wage and term; it does not create a verified bid or establish feasibility. ## Coverage and arithmetic output Amounts below are millions of EUR. The annual column is a partial fee-plus-fixed-wage proxy; wage-only reference is separate. Missing totals are deliberate. | Player | Annual proxy base | Annual scenario low–high | Full-term proxy base | Wage-only reference | Evidence basis | |---|---:|---:|---:|---:|---| | E. Emegha | 8.37 | 7.14–10.64 | 58.62 | — | imputed_wage_scenario | | C. Espí | 9.80 | 8.57–12.07 | 49.02 | — | imputed_wage_scenario | | F. Torres | 23.34 | 23.34–23.34 | 116.70 | — | capology_point_estimate | | D. Pejcinovic | 9.80 | 8.57–10.66 | 49.02 | — | imputed_wage_scenario | | Donyell Malen | 13.66 | 13.66–13.66 | 54.64 | — | capology_point_estimate | | G. García | 15.68 | 13.71–15.91 | 78.42 | — | imputed_wage_scenario | | N. Jackson | 20.23 | 19.03–20.23 | 101.17 | — | imputed_wage_scenario | | Emersonn | — | —–— | — | — | wage_unknown | | L. Delap | — | —–— | — | — | wage_unknown | | Rasmus Højlund | 20.26 | 20.26–20.26 | 81.04 | — | capology_point_estimate | | Á. Rodríguez | — | —–— | — | — | imputed_wage_scenario | | Santiago Castro | 11.27 | 11.27–11.27 | 56.35 | — | capology_point_estimate | | M. Abline | 9.60 | 8.57–9.60 | 48.00 | — | imputed_wage_scenario | | Randal Kolo Muani | 17.79 | 17.79–18.43 | 88.95 | — | primary_fee_secondary_wage | | Dušan Vlahović | — | —–— | — | 22.22 | partial_internal_or_renewal_scenario | | Jonathan David | — | —–— | — | 11.11 | partial_internal_or_renewal_scenario | | Loïs Openda | — | —–— | — | 7.41 | partial_internal_or_renewal_scenario | | Arkadiusz Milik | — | —–— | — | 2.24 | partial_internal_or_renewal_scenario | ## Integration and verification Import `load_evidence()` and `candidate_cost(tm_id, evidence=None, overrides=None)` from `scripts/recruitment/costs.py`. Evidence is keyed by canonical Transfermarkt ID. Unknown IDs return explicit null costs. Only `fee_eur`, `annual_gross_wage_eur`, and `contract_years` can be overridden; values must be finite and nonnegative, terms positive. The function never mutates evidence and imports without network/file reads. Source asset SHA256 values are embedded in the versioned JSON. `python3 -m unittest discover -s tests/recruitment -p test_costs.py -v`:9tests passed. Tests cover contingent-fee separation, unknown values, ceiling rejection, missing term, Vlahović renewal scope, explicit nonmutating scenarios, invalid inputs, timing corrections and retained provenance. No sealed register, legacy grade, historical ledger, model or production file was modified.
Expected-minutes evaluation
# Empirical minutes baseline — first striker comparison ## What it forecasts One full domestic-league season of minutes at the receiving club, capped at3420 for a38-match SerieA scenario. The numeric baseline is a2/1/1 weighted average of a player’s observed minutes in the previous three complete big-five seasons. Missing older seasons are omitted from weighting; missing or uncovered latest-season data triggers an abstention. It is a descriptive usage forecast, not an injury forecast, guaranteed starting role or estimate of the minutes Juventus would specifically select him for. Current target: **2026/27** using complete2025/26 and earlier inputs. No partial2026/27 observations enter. Since the season has already begun, this is a full-season hypothetical comparison built now, not a forecast sealed before the2026/27 kickoff. ## Cohort and zero minutes The local Transfermarkt table supplies recorded early summer arrivals into event-dated big-five clubs: arrivals fromJuly1 through the receiving club’s first domestic league fixture. Each player/receiving-club/season keeps its earliest incoming event. Self-transfers are excluded. Reverse transfers within730days identify return signatures, which remain visible and are reported separately; that signature is not a verified legal deal classification. A target zero is retained only when the receiving club has its expected34/38 completed fixtures, observed appearance rows pass the club coverage check, and the player has no contradictory missing-starter record or missing minute values. A missing player row on an adequately covered fixture is evidence of zero club delivery; it is not evidence of no football elsewhere. Missing club fixture/appearance coverage is censored, never silently zero. Source checks cannot independently prove every substitute was recorded. Prior-player histories sum all big-five club stints. Latest-season histories must have covered source club-seasons. Players whose prior league is unobserved are abstentions, not zero-minute players. Historical lineup starts define observed CF/second-striker usage; current snapshot position, age, fees and wages do not enter. The initial primary diagnostic required at least50% of prior starts atCF/second striker. This cutoff is an explicit cohort convention, not learned intent. It excluded RKM at10/21 prior starts. Following review, the comparison additionally supports **any observed prior CF/second-striker start**, with majority and minority-CF histories reported separately. Numeric baseline weights did not change. The first primary-only scores are retained in`minutes/primary-scores-initial.csv`; broader diagnostics do not replace them with a claim of preregistered success. The source cohort contains **6416** early-arrival records. **688** have covered target/prior data and some prior CF usage; **59** have zero receiving-club minutes. There are **204** target-coverage failures and **3285** missing/uncovered latest-season histories across the full source cohort. Filters overlap. These counts do not establish complete transfer-market coverage: this repository’s separate ledger audit found transfer-table omissions. ## Temporal diagnostic Each evaluated season fits mean/shrinkage baselines only on earlier target seasons whose final fixture was before that test season’s earliest opening fixture. Last-season and weighted-history baselines have fixed rules. Evaluated years have already been inspected elsewhere in this project, so results are historical walk-forward diagnostics, not an untouched holdout or proof of recruitment edge. | Baseline | Population | N | RMSE minutes | MAE minutes | Zero outcomes | |---|---|---:|---:|---:|---:| | historical_mean | any_prior_CF_start_early_arrivals | 557 | 923.8 | 788.0 | 52 | | historical_mean | excluding_return_signature | 382 | 834.2 | 705.0 | 5 | | historical_mean | majority_prior_CF_starts | 370 | 948.4 | 816.9 | 38 | | historical_mean | minority_prior_CF_starts | 187 | 873.1 | 731.0 | 14 | | last_season | any_prior_CF_start_early_arrivals | 557 | 1038.1 | 843.9 | 52 | | last_season | excluding_return_signature | 382 | 963.8 | 787.4 | 5 | | last_season | majority_prior_CF_starts | 370 | 1049.8 | 846.4 | 38 | | last_season | minority_prior_CF_starts | 187 | 1014.7 | 839.1 | 14 | | linear_shrinkage | any_prior_CF_start_early_arrivals | 557 | 863.7 | 724.3 | 52 | | linear_shrinkage | excluding_return_signature | 382 | 761.2 | 634.7 | 5 | | linear_shrinkage | majority_prior_CF_starts | 370 | 878.4 | 745.2 | 38 | | linear_shrinkage | minority_prior_CF_starts | 187 | 834.0 | 682.9 | 14 | | weighted_history | any_prior_CF_start_early_arrivals | 557 | 959.6 | 776.3 | 52 | | weighted_history | excluding_return_signature | 382 | 871.6 | 701.1 | 5 | | weighted_history | majority_prior_CF_starts | 370 | 969.7 | 780.7 | 38 | | weighted_history | minority_prior_CF_starts | 187 | 939.2 | 767.6 | 14 | **Reading:** the weighted baseline improves on last season, but does not beat the historical mean on every metric. Linear shrinkage has better retrospective errors here; it remains an exploratory challenger because selecting and validating it on these same seasons would overstate the evidence. The first release retains the fixed weighted baseline with its limitations visible. The broad diagnostic has557 held-out rows including52 zeros. Excluding return signatures leaves382 rows with only5 zeros; selection into early recorded CF transfers is a material limitation. It is not a representative injury/benching risk sample of every potential Juventus target. Minority-CF usage has187 held-out rows including14zeros, and majority-CF usage370 including38zeros. ## Current candidates and uncertainty All699 canonical candidate IDs are retained: **516** descriptive forecasts, **63** latest-season coverage/history abstentions, and **120** with no supported priorCFstart. Missing forecasts must remain null in the UI. - RKM:1567.75 weighted historical minutes; mixed-role history,10/21 CFstarts. - Vlahović:1510.75 weighted historical minutes;11/11 CFstarts. These similar historical-usage numbers do not imply similar player quality or the same Juventus playing role. The main comparison must keep shared starter/rotation/depth opportunity scenarios separate. The `empirical_lo80`/`empirical_hi80` columns add the observed10th/90th percentile walk-forward residuals in the relevant broad role-history group, then clip to the destination’s0–3420 opportunity. They are **pooled empirical residual ranges**, not calibrated individual80% confidence or prediction intervals. They do not model uncertainty in medical status, new team selection or a change of role. ## Reproduce and integrate ```sh python3 scripts/recruitment/minutes.py --candidates reports/striker-comparison/candidate-ids.csv python3 -m unittest discover -s tests/recruitment -p test_minutes.py -v ``` Import-safe API: `read_data(db) -> history, cohort`; `forecast(player_ids, history, cohort, target_season=2026, destination_games=38) -> predictions, walk_forward, scores`. The CLI writes`minutes/history.csv`, `cohort.csv`, `predictions.csv`, `walk_forward.csv`, `scores.csv` and`model.json` with input/script hashes. No sealed forecast or production file is edited. Six behavior tests cover future-input exclusion, unknown latest season, source-coverage abstention, missing older seasons, opportunity clipping, and genuinely temporal training with retained zero outcomes.
Coverage audit
# Striker comparison: independent data and time audit
**Decision frame:** retrospective reconstruction of summer 2026 with comparison cutoff **2026-09-09**; audit written **2026-09-10**. **Forecast target:** the full **2026/27** domestic-league season, using completed seasons through **2025/26** only. This is a newly corrected baseline, not an untouched pre-window prediction or a replacement for a sealed bet.
## Immediate findings
- The broad 2025/26 Transfermarkt CF/second-striker query produces **700 candidates**. **551** have one linked shared-baseline forecast for target year 2026; **149** remain visible without a forecast. No duplicate forecast mappings were found within those 700.
- `tm_positions.apps` counts **lineup listings, including bench listings**, not actual appearances or minutes at a position. **524** candidates have at least one CF/SS start; **176** have none. Label the inclusion evidence accurately and retain the two counts separately.
- Current-season Understat files exist **inside `data/understat/`**, despite a separate live-fetch script whose documentation says current data belongs elsewhere. Explicit target/input filters are necessary.
- The committed Transfermarkt extract cannot establish closing-window availability or current clubs. Only **one** recorded nonfuture transfer is dated after 2026-08-01; market values stop at **2026-06-11**. There are **32 future transfer rows**, extending to **2030-06-30**.
- The feeder data is a selected high-minute attacker panel, not a full feeder scouting universe. The 2025/26 files contain **161 source-labeled strikers**, of whom **24** join the existing canonical bridge.
Evidence: [candidate-audit.csv](candidate-audit.csv), [data-audit-evidence.json](data-audit-evidence.json). Reproduce with `python3 scripts/audit/audit_striker_snapshot.py` after the isolated explorer/Marcel integration rebuild.
## Season semantics and snapshot limits
Understat's integer `season` is the **starting calendar year**: 2025 means 2025/26, 2026 means 2026/27. The local rows are internally consistent with complete prior seasons and early current seasons:
| League | 2025-file players | Maximum prior-season minutes | 2026-file players | Maximum current-season minutes |
|---|---:|---:|---:|---:|
| Premier League | 537 | 3,420 | 358 | 180 |
| La Liga | 600 | 3,420 | 382 | 270 |
| Bundesliga | 499 | 3,060 | 285 | 90 |
| Serie A | 586 | 3,420 | 361 | 180 |
| Ligue 1 | 553 | 3,060 | 357 | 180 |
RKM has 1,704 Tottenham minutes and 2.700563 npxG in the EPL 2025 file. The Serie A 2026 file instead has two Juventus games, 180 minutes and 0.646923 npxG. Those latter observations must not enter this summer comparison. Vlahović's Serie A 2025 file has 972 minutes and 6.776332 npxG. These are inspected local values, not an independent live-source verification.
The JSON top-level metadata contains `league`, `season`, `playerCount` and `players`, with **no retrieval timestamp or observation cutoff**. Local file modification dates and Git commit dates do not prove when every underlying observation was available. The inspected files entered the durability history by the 2026-08-30 commit; that establishes a repository snapshot, not a pre-window information vintage. Record hashes and mark exact source retrieval time unknown.
Do not remove current-season data from the archive as part of this product. Build a filtered view with `season < 2026`; keep the raw evidence intact. The shared `project(..., target=2026, ...)` uses target-minus-one/two/three history and `prior_rate` filters seasons before the target. Query the generated CSV with **`target_season == 2026`**, never “latest forecast.” RKM also has a target-2027 row because a partial 2026 observation exists.
An independent adversarial check replaced every current-season rate with **1,000,000**: RKM and Vlahović's target-2026 projections remained byte-for-byte equal at the returned-record level. [Temporal check](temporal-perturbation-check.json). This verifies the feature/prior cutoff for these cases; it does not turn retrospective model development into an untouched forecast evaluation.
## Correct baseline outputs
These values come from the rebuilt `marcel-understat-v2-exposure-2026-09-09` CSV, with target 2026 and complete 2025/26 as the latest source season:
| Player | Understat ID | npxG/90 | xA/90 | Historical minutes basis |
|---|---|---:|---:|---:|
| Randal Kolo Muani | 5743 | 0.2905 | 0.1325 | 4,640 |
| Dušan Vlahović | 7084 | 0.5280 | 0.1144 | 5,111 |
| Pape Sarr, cross-check only | 9021 | 0.1507 | 0.1232 | 5,401 |
These rates are descriptive and conditional on the existing baseline's context assumptions. They are **not destination-translated Serie A predictions**, do not predict minutes, and do not establish an edge over the transfer market. Multiplying by a user-selected minutes scenario is a scenario calculation, not a fitted availability forecast. A pooled historical RMSE band does not supply a calibrated player-specific interval or a valid joint uncertainty band for npxG+xA.
Preserve the original 14-player pool forecasts and sealed registers as historical evidence. The legacy pool's quoted RKM ranking must not be reused as the ranking of this corrected model.
## Candidate universe and identity
The mechanical core query is `season == 2025`, `position in {Centre-Forward, Second Striker}`, `apps > 0`, without a 900-minute cutoff. It includes minor and bench roles, so provide a “has CF/SS starts” filter rather than implying every included player is an established centre-forward. The query covers big-five lineup data only.
One listed player, **TM445606**, is absent from `tm_players.parquet`. Keep an unresolved-identity row or source-name fallback; do not silently lose it in an inner join. `candidate-audit.csv` records this status.
The existing 14-player pool defines actual big-five arrivals with fees €25–60m and explicitly documents an old **surname + first initial, highest-minutes fuzzy join**. For the new comparison, all 14 full `marcel_name` values map uniquely through the current Understat bridge. Record those IDs rather than reusing the old fuzzy rule. Emegha's bridge is **US11817 → TM559328**, method `fuzzy_name`, confidence **0.6**; the other 13 are `name_in_season`, confidence **0.8**. A unique bridge match is useful evidence, not an independently verified identity. The exact links are in the evidence JSON.
Add the named incumbents/internal/loan scenarios explicitly if the core query misses them. Do not silently redefine the role rule just to make every anchor eligible. Add feeder strikers with source-native identities (`fotmob:<id>`) when there is no canonical join, with a separate source-position evidence label.
| Feeder league | Attacker rows | Source-labeled strikers | Strikers linked to TM |
|---|---:|---:|---:|
| Austria | 58 | 30 | 3 |
| Belgium | 82 | 35 | 5 |
| Netherlands | 91 | 31 | 4 |
| Portugal | 89 | 31 | 4 |
| Turkey | 104 | 34 | 8 |
The feeder extractor defaults to **≥900 league minutes**; observed minima range from 902 to 930. It filters the player's **current `primaryPosition` from playerData**, then fetches the historical season's stats. Consequently the “Striker” label is a source snapshot label, not historical role-minute evidence. These files cannot support a claim that low-minute feeder prospects are comprehensively covered. They also do not include Serie B, Championship, Brazil or Argentina.
For feeders, show their observed source-specific npxG/xA components and exposure. Do not append them to an Understat forecast ranking by relabeling FotMob values, applying an unvalidated universal multiplier, or imputing a zero for the missing forecast.
## Availability, club and price vintages
The committed TM extractor records a maximum game date of **2026-07-06**. Its transfer slice has 1,076 rows between June 1 and September 9, but only one from August onward, dated August 15. Much of the apparent late coverage in an unfiltered maximum date is future loan/contract-related rows. It cannot reconstruct the completed summer market.
Use prior-season club in the performance-history column. A historical valuation row's `current_club_name` is a snapshot attribute, not proof the player belonged to that club on the valuation date. A current contract-expiry field is likewise not automatically a pre-signing contract vintage. The 14-player manually assembled pool is a later, separately sourced record; label those rows accordingly rather than treating it as a correction to the whole TM database.
For most broad candidates, feasibility, asking price and destination wage demand are **unknown**. A currently affordable fee assumption is a user scenario, not an observed quote or proof Juventus could close the deal. A realised transfer to another club shows a transaction occurred; it does not establish equal terms or willingness to join Juventus. Retaining Vlahović must distinguish an avoidable acquisition fee from remaining wages and any forgone sale proceeds; zero acquisition fee is not zero economic cost.
## Recommended product contract
```json
{
"analysis_as_of": "2026-09-09",
"decision_frame": "retrospective_summer_2026_reconstruction",
"forecast_target": "2026/27_full_domestic_league_season",
"feature_season_max": 2025,
"partial_current_season_used": false,
"model_version": "marcel-understat-v2-exposure-2026-09-09",
"forecast_context": "historical_context_baseline_not_Serie_A_translated",
"candidate_id": "tm:487969",
"identity": {"source_ids": {}, "join_method": "name_in_season", "review_status": "unverified_bridge"},
"role_evidence": {"basis": "historical_lineup_listings", "season": 2025, "cf_ss_starts": 0},
"forecast_status": "available_or_missing_history_or_identity_conflict",
"minutes_basis": "explicit_scenario_or_separately_validated_forecast",
"cost_basis": "sourced_terms_or_user_scenario_or_unknown",
"feasibility": {"status": "unknown", "evidence_date": null},
"sources": [{"path": "...", "sha256": "...", "retrieved_at": null}]
}
```
The illustrated zero starts is a schema placeholder, not RKM's observed count. Each candidate's exact source dates, inclusion reason, metric source and abstention reason should travel with its JSON row. Unknown field values remain null, distinct from a measured zero.
## Independent acceptance checks for the finished comparison
1. Target year is exactly 2026 in every shared forecast; no `season >=2026` performance rows enter its features or priors. The 2026/27 observed outcome is absent from comparative scoring.
2. RKM/Vlahović rates reproduce the table above and identify the corrected model. Legacy pool ranks remain legacy evidence.
3. The core universe reconciles to 700 IDs, with all 149 forecast abstentions visible; extra anchors/feeders carry explicit extra inclusion reasons and canonical deduplication evidence.
4. Lineup counts are never labeled actual appearances or role minutes. Snapshot feeder positions remain explicitly identified.
5. Cost, forecast and minutes statuses remain separate. Missing price/history does not become zero, a bargain or a failed player.
6. Rows do not advertise closing-window completeness or confirmed availability from the stale TM extract. Any manually supplied 2026 terms show their separate source.
7. Browser runtime/visual QA is reported separately from data validation. This audit has not performed browser verification.
Independent review
# Independent acceptance review — striker workbench **Reviewed:** 2026-09-10. **Outcome: PASS for a local, explicitly reconstructed comparison.** No remaining blocking defect was found in the reviewed data contract, source arithmetic or exercised JavaScript paths. This is not approval of an overall recruitment ranking, predictive edge, monetary Deal Alpha or production deployment. This review was performed independently of the product builder. It inspected `build_comparison.py`, `performance.py`, `costs.py`, `render_comparison.py`, `workbench.js`, the generated JSON/HTML, the minutes contract and the source audit. The tests below were run against the artifacts, rather than inferred from the builder's success message. ## Findings resolved during review The initial reusable-rate path accepted cached forecasts without checking whether their inputs and target still matched the requested build. The manifest also omitted some actual cache and frontend dependencies. These were reported to the builder and corrected: - Rate reuse now checks target/model/input-and-code fingerprints and the hashes of the forecast, validation and validation-row outputs. - Minutes reuse checks the source database, candidate list, producer code and output hashes. It also checks unique player IDs, target 2026, feature cutoff 2025, 38-match opportunity and 0–3,420-minute bounds. - Individual rate rows are checked for the same target/model and pre-2026 histories. - The manifest includes consumed rate/minutes artifacts, shared Marcel code, JavaScript and the HTML template. In an isolated temporary directory, the real builder **rejected** both a cache whose target contract was changed to 2027 and a forecast file whose bytes were changed. The unmodified source contract and output hashes were independently verified to match first; these were meaningful negative tests, not failures caused by an already-stale cache. No production file was altered by the tests. An intermediate rendered HTML copy lagged a corrected RKM availability statement in the JSON while the builder was editing. The final HTML now embeds data equal to `comparison.json`; its embedded JavaScript matches the reviewed source. The final manifest has **zero hash mismatches** against the files on disk. ## Data and numerical acceptance | Check | Observed result | |---|---| | Candidate identity | 859 rows, 859 unique candidate IDs | | Broad historical CF/SS core | All 700 audited IDs retained | | Shared rate forecasts | 551; all target 2026 and history maximum 2025 | | Empirical minutes | 516 numeric estimates; target 2026 throughout | | Feeder coverage | 160 unique candidates carrying 161 separate source observations | | Internal/defer strategies | Two explicit strategy rows; no invented player rates or zero-cost verdicts | | Numeric cost references | 11; all remain labeled partial | | Cost arithmetic | All 33 low/base/high numeric scenarios reproduce fee ÷ term + annual gross fixed wage and fee + wage × term | | Missing cost operands | Annualized/full-term costs remain null rather than zero | | Canonical anchor rates | RKM 0.2905 npxG/90 and 0.1325 xA/90; Vlahović 0.5280 and 0.1144 | | Fixed opportunity | Full 2026/27 scenario, not a remaining-season projection | The separate source audit's adversarial test also confirmed that replacing post-cutoff Understat rates with 1,000,000 does not alter RKM/Vlahović's target-2026 results. These values are the corrected v2 model's outputs. They must not inherit the legacy 14-player pool's rankings or be described as forecasts sealed before the window. The cost plot compares a one-year annualized partial proxy with a one-season football-output scenario. Full-term partial cost is shown separately with the stated contract duration. Neither measure is described as actual cash paid, complete liability, discounted lifetime economic cost or a fair acquisition price. Unknown internal-option terms stay unpriced, including Vlahović's hypothetical renewal and David/Openda retention scenarios; their original acquisition fees are not silently recharged as new cash costs. ## JavaScript behavior exercised A Node `vm` run with a minimal DOM shim exercised the actual workbench script and current JSON: - Initial table and SVG generation succeeded; SVG coordinates contain no NaN or Infinity. - Switching to empirical minutes produced finite chart markup and the explicit product-of-means/joint-model limitation. - The feeder filter returned 160 unique candidates, preserving 161 source observations in their records. - An unpriced Vlahović remained in the table and outside the plot. - Entering a complete zero-fee, €13m annual-wage, three-year scenario priced that alternative and placed it on the plot. Restoring source evidence returned it to the unpriced state. - Null football forecasts remained null; unknown costs returned “Unpriced,” not a zero-cost bargain. - Independent scenario arithmetic reproduced the expected fee-allocation-plus-wage calculation. **This was not a browser test.** No connected browser runtime was available. The DOM shim verifies JavaScript execution and generated strings; it does not verify actual layout, responsive behavior, label collisions, browser accessibility, download behavior or visual presentation. Browser runtime and visual QA remain pending and must not be described as complete. ## Limits to carry with the product 1. Rates are whole-player-season observations, not striker-only role-minute measures. TM lineup listings can include bench entries; the product correctly exposes starts and role evidence separately. 2. Feeder records are selected high-minute, source-position observations. There is no validated conversion from their FotMob values into the Understat/Serie A projection. 3. Rate validation conditions on at least 450 minutes in both seasons. The empirical minutes study has a different, incomplete recorded-arrival population. Multiplying those models is a scenario/output proxy, not a validated joint expectation. 4. The central residual ranges are pooled historical envelopes. Their combinations do not represent calibrated individual 80% probability intervals. 5. Most broad candidates have unknown costs and availability. The 11 numeric references do not make the other candidates buyable, and terms agreed with another destination are not Juventus quotes. 6. The historical transfer extract is incomplete around the closed window. Pre-window publication vintages are not fully reconstructed. The page's reconstructed-comparison label is essential. 7. No empirical evidence here supports an overall player-quality grade, a market-beating purchase policy, a defensive valuation, a resale model or euro-denominated alpha. These are material boundaries of a usable first comparison, not reasons to hide the available data. The artifact can support inspection and explicit scenario discussion while these limits remain visible. ## Reviewed artifact hashes - `comparison.json`: `9c0663a7f824634f76f5c4111e6ed84483f17620762daa3e1b035e42d52dfecc` - `index.html`: `0edbb87ae7e5adb7c7a96ea4c626e26e0ae352fc52bb19e8b32b823a5e01e1b9` - `manifest.json`: `e16bd2cebe197798232b0b2e1b993b32b9128a74a18d28474664a6eeb3464855` Later source/data changes require checking the regenerated artifacts again; this review does not automatically cover them.
Input manifest and hashes
{
"schema_version": "striker-comparison-v1",
"candidate_count": 859,
"rate_covered": 551,
"input_sha256": {
"data/site/tm_positions.parquet": "56b762fbd523f7e0c7addb8c86e45cd73f27a29b764e6989095e2b8763cffca9",
"data/site/tm_players.parquet": "13925a6ca53790c645ab399d850193cf78654df4b60b84032f40bff232bc145c",
"data/bridge/players.csv": "009c37c69b59cfe880ed8237eb8f05a2594f7883aba9530ebbb85569081b4afc",
"data/fm/fotmob_feeder_player_season_2025-2026.csv": "55329a4834dc9246a209574fcdd0c7a837596b43e01e073be750968db7df705a",
"data/recruitment/cost-evidence.json": "df56cb08c397182472e2609cdfc6d475fcd635c163399958e4b285d60566b7c4",
"reports/striker-comparison/minutes/predictions.csv": "4cf5f2887d33d69ca53f807cdbf469bf9a04119346edd02e649ba37bc15197c1",
"data/tm/transfermarkt-datasets.duckdb": "808959f5b5b16bb698180c348b269d9ec26e1d1a5538767ffe9d971b96796d1c",
"reports/striker-comparison/candidate-ids.csv": "50e2b6f6dd4aa0e0465e2fe84822b4697647be183b6b5d52299620855ead9f15",
"reports/striker-comparison/minutes/model.json": "8300c39d18262cf7c3007251f7723018262e4ed1223c2c1621f8687853a70ef2",
"reports/striker-comparison/rate-forecasts.json": "e29691ab2f1d041cf1ea3a96802c9f112d0944ad3c82afdc709caec6467ddcbd",
"reports/striker-comparison/rate-validation.json": "10e27216fc955ac7a6fedcefa0ff95ae7b6f37a06ba1d96aaa11d0ceddbd46b4",
"reports/striker-comparison/rate-validation-rows.csv": "5f221a738d7e79bdf47df9a73f68bcd698848433b8c04b9f1f715d684567d7df",
"reports/striker-comparison/rate-cache-contract.json": "d8acebb772e935796226ea8c5d0a69b085f8d1292d01da3ab1f95fbeab6b400e",
"data/understat/Bundesliga_2014.json": "7e9a3f1983cc8d5d84944fa1a71bc9ee42e96f99cf2e564ab0f69aeaba51e194",
"data/understat/Bundesliga_2015.json": "10d7f3e080d99fbb4c45cba8b2ff339a2efbf118fe118c636e05e9cbf99f870a",
"data/understat/Bundesliga_2016.json": "08f8a208bf93b1768e36c5f6865274d3c5f12b1744090eb50e3302133389a9ec",
"data/understat/Bundesliga_2017.json": "2c5b57a7f455f6b2259a8a5e010a4abc269e4ccf1d4efeb60f151e23cd85c371",
"data/understat/Bundesliga_2018.json": "21959f48dcc07437d5258c9069b721a76d0495d5a88b0014b0cc39ab786bd3d9",
"data/understat/Bundesliga_2019.json": "98f144b92a2492df886b9898cf6362cd742e1ce6457092180091b689236f1e81",
"data/understat/Bundesliga_2020.json": "c31816eab0b4c99c69c3195beb993d9dd0ece8bcd77106ecb20c053e74a64a3c",
"data/understat/Bundesliga_2021.json": "2257351b379dd50f8e20c2bcae0cd5417e1f9b5fc1cf81c74abfc584ef4cefa7",
"data/understat/Bundesliga_2022.json": "0ff3bc567832ebf3205e320153f21a015cb86b3ffbb9cc5b4c3276dbbf21251b",
"data/understat/Bundesliga_2023.json": "bcd9469b4d40cafb92c981ae38043a3b084dd6abbacc6d07e7538d044948bbde",
"data/understat/Bundesliga_2024.json": "b21f4bcfee77bb348e55b39490fd4818e29f6a331ae63cfa877e7e1925c2d43b",
"data/understat/Bundesliga_2025.json": "f5fdd3e7737aded7ca1ff0524be1457c0a585677e61e47d601b16597d517b439",
"data/understat/EPL_2014.json": "c268ca9b930fca5439411a67081f1924fb07382af5e819c05d43dac41517f25b",
"data/understat/EPL_2015.json": "cbe4ffcff33d3e8422db6c35bf02976666b4e9113c1655f3e642a8860770dc40",
"data/understat/EPL_2016.json": "3b116ece1cc58eae91d9dbdc8f50f62a62c7eeb261440342860445e15aa705e9",
"data/understat/EPL_2017.json": "4221203e4a8670cb7d8c8050a0eb1e9557188f5833c9c737a5cc6802a94bb906",
"data/understat/EPL_2018.json": "de20252557b61eaf95f9b39f57c8cb33f95770f2ca4b5f617abfd22916ea028c",
"data/understat/EPL_2019.json": "b7e8539600b789ddf4a8edad4490ade1a6bcd0c3dbd23366ee276db3eeff6998",
"data/understat/EPL_2020.json": "64696d5499ba894ec11f87cf75080b9673daa254458871ac5640ffd798e024e0",
"data/understat/EPL_2021.json": "2488591574a88b84241221eb19f75b0b8fb387f216d6f7762f339bfd7650a34c",
"data/understat/EPL_2022.json": "548ece655e6d29bc7ebd80fbd946626a97b78f1df7cb2448fd82db4b6ad1c17d",
"data/understat/EPL_2023.json": "08aa9f566b5e4c178779642990890fd64c802f5a113183d7aa18fb2205490b6c",
"data/understat/EPL_2024.json": "ab5e1a9e2abeb99f515b47b1310fef195db68af3b3d882abc0c938b1ef305fb0",
"data/understat/EPL_2025.json": "cf8bde50fdd6d867722b4aa271618be77ab84b4932cca569b4c157d5b816f2ec",
"data/understat/La_liga_2014.json": "db486f47dad2ca95f92575a05f5cc96ad775dbbbacec10ea03e5dd9c2762d901",
"data/understat/La_liga_2015.json": "24e92e0664b51f529258944adb20d786c8bab12328738000f15e6eb131a6ef91",
"data/understat/La_liga_2016.json": "f5835a8575de38c0034397c60f773706e0489a43c0bed73c287795e7a1a5bc50",
"data/understat/La_liga_2017.json": "9ddb731d4985876540a07864c6e1fb14c44b8c819e065295ce35e4a6791e227a",
"data/understat/La_liga_2018.json": "075598f1c8f3f50ac9aaf85f2a1468793e7d6c7af2347a76dcde0c023892a07f",
"data/understat/La_liga_2019.json": "61cb12bfa68dae9c58b319360046fd4ac8f871d82984ac187de3eb4ad9d408b4",
"data/understat/La_liga_2020.json": "9eef996ec55eef31a2db99eed980950300758e49606bd4ae353647d1a82d84c5",
"data/understat/La_liga_2021.json": "37a2de1c5fe20eed058cfd30e73d182b14ae9d0f5cceb885586bf231a9cb9177",
"data/understat/La_liga_2022.json": "978e2980f4223af14e3e0b6ffbabf9be454324de54f03a06d5141ec90a26cc9a",
"data/understat/La_liga_2023.json": "2e78b469bce4403b25fe65aa6fe0b9decd7e3eadc147c71b4b261a3a63b4c417",
"data/understat/La_liga_2024.json": "839ffaa852bf26eab0decc88ed39d0aa64dcdf802659f32c00f019850a47ca53",
"data/understat/La_liga_2025.json": "7a6ab80a5ec007abc0c86ed9ea914004a2355412689090071e6097d14ef62055",
"data/understat/Ligue_1_2014.json": "94cd2e1047728c1a43455da524ab77affa8a75b0984814f2dd5853e5a35815b1",
"data/understat/Ligue_1_2015.json": "0b0a4207174742d7d0c1b8a886df134208fb44240e34afb5ff36c2b5ceb0f184",
"data/understat/Ligue_1_2016.json": "6afa930189cdd0ca2a9271d41624bc654f42cd8cd17fa3da53e5be2751111658",
"data/understat/Ligue_1_2017.json": "c21d5cfaaf5cd7feb4af81650e5d81cd82624661642030dee11c25f651a5faf7",
"data/understat/Ligue_1_2018.json": "906dcaf8ef18e67514473deeb2917188b814a3982c6cd730ddea15ea75ba7ed9",
"data/understat/Ligue_1_2019.json": "fbd0b592db16a55cff0f3de1c7211bc7034870e782e6f81505ba5d215ecebaf3",
"data/understat/Ligue_1_2020.json": "077e2f41f591792a79d29d5454f97689cd1de98574e14e91e544abc27a0c4129",
"data/understat/Ligue_1_2021.json": "d0d8c60660b0065f792f54bca5d5bd5699c25b825fed37a22155d74d18de6d48",
"data/understat/Ligue_1_2022.json": "65232201a546d5a98d1007410cc8f6801271dca4ba1c0c6a354a66162db14247",
"data/understat/Ligue_1_2023.json": "18834adc44a1d4294c8f4a61056b14bd597c3003e9b6222ba09b6036bf3564b8",
"data/understat/Ligue_1_2024.json": "4420fb35026174620fcef8fd216aa1212d08c998ce4176247a3a3f3f946a900d",
"data/understat/Ligue_1_2025.json": "19bdb44471e3490c667b7368442424bec0ee8e2cdf78f24e3970ef071df901f6",
"data/understat/RFPL_2014.json": "d5984a1de47b1f3d0b79c858121b0ac198324089adec45d69ab858b24e776a7b",
"data/understat/RFPL_2015.json": "52825da3be48e193cf5a5ea09a64836e0095cd1d7dcd41f7de5f398d1c736fb5",
"data/understat/RFPL_2016.json": "0f6e8a4ae04c358a7eca779b493fcd24618852899a530b5a2197a8ef914ca5e8",
"data/understat/RFPL_2017.json": "c09ba31f2fdf1bd74e7cf5b380a8f6e847f2bd3a59bb148f6fdf82cee7a94ea5",
"data/understat/RFPL_2018.json": "22223fd0fdb2a33ba9b67a65bc7b856ff2473c3ad9cea44078b78ea14e166a5d",
"data/understat/RFPL_2019.json": "1e52abcfc98b76f7e1683bf3e03d6979a039c624596b0300ded90e56876b4797",
"data/understat/RFPL_2020.json": "d32cd78840ad2efd63fc78cd6b5fc7f4f2b034d734ff0885b7b6a6cdeba18456",
"data/understat/RFPL_2021.json": "6f65a46d80078a4638ae79b9ecf43151b8ffab368c7003b2a1a8220a6cdb3fd6",
"data/understat/RFPL_2022.json": "e998bae46f47e111416aa1e866d19c90ea6cf66fcdf3837c12c40ba502b41c44",
"data/understat/RFPL_2023.json": "ebfa2dde1d40f05bfb02a48c25cdf12bc356da6c51ea80d5d524ab8e33d0d504",
"data/understat/RFPL_2024.json": "c186564f536c868c274dad1d22a98b871605af029f2bc43034a66c26170db68b",
"data/understat/RFPL_2025.json": "cb62c1181ec01fa066c65838425a9fd4735ec302caa778ef54ce0959cdc0ca54",
"data/understat/Serie_A_2014.json": "cdd743095e0bcf363077b1a6270dc072fdafe93d9e2b64cfc5f7dd69e38bc57b",
"data/understat/Serie_A_2015.json": "1fdbc29f110be1dec64ee5ca239db3106aff4156e63eff398a3f60f1e7cde2fb",
"data/understat/Serie_A_2016.json": "38f04565701f5b73e0f9bc98ce2e9a4c8e74b1fd98498e5d3680631a625b238f",
"data/understat/Serie_A_2017.json": "c2f1a579b538e09702fbb5f555c09db8c7324ad3ad13cd239758efa73c138c01",
"data/understat/Serie_A_2018.json": "51e96deee925b5f6c1c5631e2d324390cc01efb4db524a6f5dc8bc020a7caae8",
"data/understat/Serie_A_2019.json": "37ebf933f317572a97d84864c92bb94978c7f517ff2a3d53129938796dd9f944",
"data/understat/Serie_A_2020.json": "ef4e7ace23350dbd40478a69abfaf52e853f1c403c161286e8476dc4796c57aa",
"data/understat/Serie_A_2021.json": "0093cf82dcc4f1d94a746e466ca5cac8489477ff39569c6558af315ff76cf494",
"data/understat/Serie_A_2022.json": "977fbdcd2a4d4b9221136831f3f7123bc8120537b7937f029bb39f5d9658373c",
"data/understat/Serie_A_2023.json": "6735a87a5a08565c7574071c2153353fdc0dc2cebeb1786641abf460e4944d11",
"data/understat/Serie_A_2024.json": "e042760bec22e50145b10d724ccb7473d33f1d4d60fb4a4e44f11182e23e30c5",
"data/understat/Serie_A_2025.json": "40329861df0634b890f478045dcfb1ba9a94ccfa6b035ca0bc6ae45ca9ab6343"
},
"source_sha256": {
"scripts/recruitment/build_comparison.py": "8e40e7841791e2639a95f67b1b507f67fc128a43fcd05f667eb4d27f0a1b9a6f",
"scripts/recruitment/costs.py": "2f282fd452f9a65d6df47093bd945ad008516aa6f50bdb4b7ec73636e5ee3777",
"scripts/recruitment/minutes.py": "fbf87376a11b9b84b8c60a693cebd96494cedcd3b9ea5d997b10150c3a09888d",
"scripts/recruitment/performance.py": "00193c69c86cec6293b3e4fabc84b176f367f53e451ae7a21b7e523959361c3f",
"scripts/recruitment/render_comparison.py": "e302b2a6990586d44eaeed742b4054cc0fc8e53ccd5dacca0b5eab8454147031",
"scripts/recruitment/write_report.py": "8c409b9a513ec8a26e24027993b7f8fe56a08696049c540fbc5b3db684b5bd7f",
"scripts/recruitment/workbench.js": "7cc5eb1fe931f0645710d1c4c82ead7898ecece6e1dc7a11ff87d146879f2c77",
"scripts/recruitment/workbench.html": "0f9f4e7fe7ad13999488d923f23ff5d648c10f59fd16c48a76927ea5824e2b3f",
"scripts/site/marcel.py": "81eb712553961293f3eb4192b7f87cafe555e1e2290afdc6608f45d9f839e9b8"
},
"future_season_excluded": true,
"deployment": "not attempted"
}