Skip to content

Latest commit

 

History

History
498 lines (349 loc) · 38.7 KB

reproducibility.md

File metadata and controls

498 lines (349 loc) · 38.7 KB

Moving the Defense — Reproducibility Guide

Repository: moving-the-defense

Project: Measuring Defensive Responses to Attacking Movement in Football

For a concise, linear paper-reproduction path, start with REPRODUCE.md. This document is the deeper technical, protocol, and audit-oriented reference.

Experimental Kloppy equivalence gate

Kloppy is pinned at 3.19.0 for the isolated Metrica Game 1 equivalence experiment. With the existing raw Sample Game 1 files in data/metrica_sample_game_1/, reproduce it with:

MPLCONFIGDIR=/tmp/moving-the-defense-mpl .venv/bin/python src/kloppy_metrica_equivalence.py
.venv/bin/python -m unittest tests.test_kloppy_metrica_adapter

The experiment writes only to outputs/kloppy_metrica_equivalence/. Its B result and adapter rules do not replace the current Metrica loader or authorize scientific reruns through Kloppy.

Governed canonical tracking architecture

Canonical tracking contract v1.0.0 governs new analyses using Kloppy 3.19.0 and Polars 1.44.1. It does not replace historical loaders. With Metrica Game 1 and IDSSE J03WMX raw files already present, reproduce the cross-provider contract gate with:

MPLCONFIGDIR=/tmp/moving-the-defense-mpl .venv/bin/python src/canonical_tracking_contract_audit.py
.venv/bin/python -m unittest tests.test_canonical_tracking_contract

The full logical tables are validated in consecutive Polars chunks. Only bounded schema samples, provenance sidecars, invariant summaries, and downstream equivalence results are committed under outputs/canonical_tracking_contract/.

UnravelSports interoperability audit

UnravelSports 1.2.1 is pinned for the bounded, non-governed interoperability audit. It adds SciPy at runtime; graph/deep-learning extras are not required or installed. With only Metrica Game 1 and IDSSE J03WMX present, reproduce the limit=250 comparison with:

MPLCONFIGDIR=/tmp/moving-the-defense-mpl .venv/bin/python src/unravelsports_interoperability_audit.py
.venv/bin/python -m unittest tests.test_unravelsports_compat

The audit writes only to outputs/unravelsports_interoperability/. It does not authorize using UnravelSports inference, filtering, kinematics, pressing, EFPI, or graph tooling in a governed scientific pipeline.

Environment

The current local checkpoint was audited under Python 3.13.15. The bounded requirements are intentionally lightweight rather than a platform-specific lockfile; exact package versions used by governed executions are recorded in their manifests where applicable.

python3 -m venv .venv
source .venv/bin/activate
python -m ensurepip --upgrade
python -m pip install -r requirements-phase0.txt
jupyter notebook

An equivalent uv setup is:

uv venv .venv
uv pip install --python .venv/bin/python -r requirements-phase0.txt

Python, pandas, NumPy, Polars, Kloppy, UnravelSports, SciPy, Matplotlib, Jupyter, nbclient, and ipykernel cover the tracked source implementations and documentation figures. XML parsing uses the Python standard library. The local environment is ignored by Git.

Raw data

Download Metrica Sample Games 1 and 2 from https://github.com/metrica-sports/sample-data and place the three CSVs for each match under:

data/metrica_sample_game_1/
data/metrica_sample_game_2/

data/* is ignored and data/.gitkeep is retained. Sample Game 2 SHA-256 checksums are recorded in config/phase4a_focal_departure_validation_protocol.json. The 21 IDSSE XML files used for completed external replication belong under ignored data/idsse_raw/; their governed mapping and hashes are recorded in Phase 4C artifacts. Metrica Sample Game 3 is not part of the current evidence and must not be added to a reproduction run.

Coordinate and time conventions

  • Source coordinates are nominally normalized and may slightly exceed $[0,1]$.
  • Physical calculations use 105 × 68 m without clipping.
  • Phase 0 and explicitly raw diagnostics retain source coordinates first.
  • Tracking is 25 Hz; time and frame counters are global across periods in these files.
  • Phase 4 intervals are half-open $[t,t+5)$ with exactly 125 frames.
  • Goalkeepers are excluded as Home11 and Away25 under the frozen Metrica protocol.

Missingness and smoothing

  • No protocol silently imputes or interpolates missing coordinates.
  • Earlier derivative diagnostics use centered 5/7/9-frame rolling-position means and document edge behavior.
  • Phase 4 uses a centered seven-frame mean within each interval, omits the first/last three frames from path accumulation, and freezes 5/9-frame sensitivity.
  • Phase 4 requires a complete ball path and at least nine complete defending outfield players throughout an interval.

Protocol sources of truth

MPLCONFIGDIR=/tmp/moving-the-defense-coverage .venv/bin/python -m pytest -q tests/test_defensive_coverage_redistribution_v1.py tests/test_defensive_coverage_redistribution_v2.py
MPLCONFIGDIR=/tmp/moving-the-defense-coverage .venv/bin/python src/generate_defensive_coverage_redistribution_v1_figure.py

The governed V2 closure can be reproduced without changing any scientific rule:

MPLCONFIGDIR=/tmp/moving-the-defense-coverage .venv/bin/python src/defensive_coverage_redistribution_game1_v2.py --reproduce

V2 remains INVALID before estimation. The v3 protocol changed only exact omission of its explicitly designated constant nuisance indicator. The v3 Game 1 result is a valid period-1-only MIXED development result; it uses the same 281 anchors, omits only period_2_indicator, and commits compact governed output artifacts while retaining provider-linked rows locally. The frozen governance test remains a historical pre-execution assertion; use the estimability and result checks below after the authorized execution:

.venv/bin/python -m pytest -q tests/test_defensive_coverage_redistribution_v3_estimability.py tests/test_defensive_coverage_redistribution_game1_v3.py

Reproduce the governed v3 result and its independent deterministic rerun with:

MPLCONFIGDIR=/tmp/moving-the-defense-coverage .venv/bin/python src/defensive_coverage_redistribution_game1_v3.py --reproduce
MPLCONFIGDIR=/tmp/moving-the-defense-mpl .venv/bin/python src/attacking_continuous_movement_game1_v1.py
.venv/bin/python -m unittest tests.test_attacking_continuous_movement_v1

For the required independent check, rerun with --output to a separate directory, then use --verify-against from the governed output directory. Complete support, feature, frequency-comparison, fixture, summary, provenance, and hash artifacts are under outputs/attacking_continuous_movement_game1_v1/.

MPLCONFIGDIR=/tmp/moving-the-defense-stage-a .venv/bin/python src/attacking_continuous_movement_game2_stage_a.py
.venv/bin/python -m unittest tests.test_attacking_continuous_movement_game2_stage_a

Governed provenance, raw-support inventory, diagnostic triggers, registry, support segments, hashes, and reproduction verification are under outputs/attacking_continuous_movement_game2_stage_a/.

Then reproduce the continuous representation and tests with:

MPLCONFIGDIR=/tmp/moving-the-defense-game2-v1 .venv/bin/python src/attacking_continuous_movement_game2_v1.py
.venv/bin/python -m unittest tests.test_attacking_continuous_movement_game2_v1

For the independent check, rerun with --output to a separate directory and call the governed output with --verify-against. The 19 governed outputs must match byte-for-byte. Feature, support-linkage, 25/10 Hz comparison, mathematical-QC, fixture, invariance, provenance, hash, reproduction, and final-classification artifacts are under outputs/attacking_continuous_movement_game2_v1/.

MPLCONFIGDIR=/tmp/moving-the-defense-bridge .venv/bin/python src/attacker_defender_bridge_game1_v1.py
.venv/bin/python -m unittest tests.test_attacker_defender_bridge_game1_v1 tests.test_attacking_continuous_movement_v1 tests.test_canonical_tracking_contract

For the required independent check, rerun with --output and --figures pointing to temporary locations, then call the governed output with --verify-against. Fifteen governed pre-reproduction files must match byte-for-byte. The saved hard_qc.csv records 24 execution-contract checks; final results, linkage, observations, bootstrap summaries, inheritance, manifests, and hashes are under outputs/attacker_defender_bridge_game1_v1/. That execution froze game2_inheritance.json before Game 2 bridge access. Game 3 and external-provider bridge data remained unauthorized.

The unchanged held-out Game 2 and pooled execution has now completed as FINAL BRIDGE A. With the frozen Game 2 Stage-A support and attacker outputs present, reproduce it with:

MPLCONFIGDIR=/tmp/moving-defense-bridge-game2 .venv/bin/python src/attacker_defender_bridge_game2_v1.py
.venv/bin/python -m unittest tests.test_attacker_defender_bridge_game2_v1 tests.test_attacker_defender_bridge_game1_v1

For deterministic verification, rerun to temporary output/figure paths and use --verify-against. Sixteen governed pre-reproduction files must match byte-for-byte. outputs/attacker_defender_bridge_game2_v1/ contains the Game 2 sample/linkage, model and bootstrap tables, pooled models/comparisons, 32-row hard audit, final criteria, provenance, reproduction record, and hash ledgers. The inherited threshold is exactly 12.198443079831405 m. Game 3 and external-provider bridge execution remain unauthorized.

MPLCONFIGDIR=/tmp/moving-defense-footprint .venv/bin/python src/spatial_defensive_response_footprint_game1_v1.py
.venv/bin/python -m unittest tests.test_spatial_defensive_response_footprint_game1_v1 tests.test_attacker_defender_bridge_game1_v1

For deterministic verification, rerun with --output and --figures pointing to temporary locations, then call the governed output with --verify-against. All 24 governed pre-reproduction Game 1 files must match byte-for-byte. Outputs include the complete anchor/rank linkage, coefficient and contrast tables, rank-distance diagnostics, fixed metric bands, temporal placebo, inherited consistency check, trimming/horizon sensitivities, 28-row hard-QC ledger, provenance, reproduction record, and hashes.

Before Game 2 footprint execution, the prospective held-out execution clarification resolved a reporting gap without changing either frozen scientific artifact. Game 2 receives no standalone coherent/mixed/invalid status: its complete governed outputs must first be saved, hashed, and reproduced, followed by the already-authorized pooled execution, before the original Final Footprint A/B/C rules are applied. The clarification SHA-256 is 60678b0f90128c5905ed2535a81aab37b562fe8a6b8aa6a9c9ff1f7642dcf37e.

The held-out and pooled sequence is now complete at FINAL FOOTPRINT A. Reproduce it with:

MPLCONFIGDIR=/tmp/moving-defense-footprint-final .venv/bin/python src/spatial_defensive_response_footprint_game2_final_v1.py

For independent verification, run the same source with temporary --output and --figures paths, then invoke the governed primary output with --verify-against <temporary-output>. The closed execution reproduced 31/31 governed files byte-for-byte. Game 3 remains unauthorized and untouched.

Local defensive response form v1 is frozen at protocol SHA-256 958c8aa80fe9ea43358c32a42a6be2eea7a41e7f727e23ff137eb3079ee80428 and configuration SHA-256 b120f19c13b86f47f5b73311a4509cbd5de5f95fbaa1369f95dc061c998b8053. Its Game 1 development result is coherent. Reproduce it with:

MPLCONFIGDIR=/tmp/moving-defense-response-form .venv/bin/python src/local_defensive_response_form_game1_v1.py
.venv/bin/python -m unittest tests.test_local_defensive_response_form_v1 tests.test_local_defensive_response_form_game1_v1

The command reads Sample Game 1 and the closed footprint registry, writes outputs/local_defensive_response_form_game1_v1/, and does not access Game 2 response-form quantities or Game 3.

The Tier-3 Game 2 and pooled execution is closed at FINAL RESPONSE FORM B. Reproduce its stages with:

MPLCONFIGDIR=/tmp/moving-defense-response-form-final .venv/bin/python src/local_defensive_response_form_game2_final_v1.py --stage game2
MPLCONFIGDIR=/tmp/moving-defense-response-form-final .venv/bin/python src/local_defensive_response_form_game2_final_v1.py --stage pooled

Game 2 must be serialized, hashed, and independently reproduced before the pooled stage. The closed execution reproduced 12/12 Game 2 and 10/10 pooled governed files byte-for-byte. The pooled addendum hashes are 1ba4e198...e23cf794 and 70f0d6ad...b8a9c813. Game 3 remains unauthorized and untouched.

Where a machine-readable config is listed, documentation explains the rules and the config governs execution. The continuous attacker-movement v1 freeze is currently governed directly by its protocol document; an implementation may transcribe it to config only through a pre-execution exact-consistency check. If prose and config conflict, stop and resolve the literal inconsistency before outcomes.

Development/test separation

Sample Game 1 is development/history. Sample Game 2 completed the first held-out focal-departure validation. Seven IDSSE matches then completed external replication and the Phase 5A/5B predictive tests in one additional provider environment. Historical pre-outcome firewalls remain binding records of how those results were produced; they are not current claims that completed outcomes remain uninspected.

Execution order

For historical reconstruction, execute notebooks in filename phase order. Each notebook is intentionally scoped and many reuse fixed cases. For governed reproduction, run the matching source implementation and protocol rather than assuming every notebook is a standalone pipeline.

The historical Phase 4 sequence was:

  1. verify Game 2 checksums;
  2. read the Phase 4 JSON and protocol;
  3. reproduce outcome-blind support counts;
  4. stop if any frozen support condition fails;
  5. execute held-out outcomes only after the recorded authorization;
  6. preserve all frozen sensitivities and falsification results.

Phase 4B held-out execution

Run the outcome-blind firewall without constructing focal-relative outcomes:

python src/phase4b_focal_departure_validation.py --precheck

After authorization, reproduce the complete frozen analysis and machine-readable outputs with:

python src/phase4b_focal_departure_validation.py

The executed narrative artifact is notebooks/phase4b_focal_departure_heldout_validation.ipynb; detailed results are in docs/phase4b_focal_departure_validation_results.md. The analysis writes derived tables to outputs/phase4b/ and figures to figures/phase4b/.

Phase 4C external replication

Frozen protocol v1.0 is docs/phase4c_external_replication_protocol.md, with machine-readable rules in config/phase4c_external_replication_protocol.json. The completed outcome-blind provider mapping is documented in docs/phase4c_idsse_mapping_audit.md and config/phase4c_idsse_implementation.json. Detailed results are in docs/phase4c_external_replication_results.md.

Place the 21 raw IDSSE XML files under ignored data/idsse_raw/. Then reproduce mapping/support and the frozen execution separately:

python src/phase4c_idsse_external_replication.py --stage mapping
python src/phase4c_idsse_external_replication.py --stage execute

The first command does not construct focal-relative outcomes. The second writes machine-readable tables to outputs/phase4c/ and figures to figures/phase4c/. The narrative notebook is notebooks/phase4c_idsse_external_replication.ipynb.

Phase 5A contextual expectation

Frozen protocol v1.0 is docs/phase5a_contextual_expectation_protocol.md, with machine-readable rules in config/phase5a_contextual_expectation_protocol.json. The implementation uses NumPy's standard Ridge solution and requires only the existing Phase 4C IDSSE raw files/caches.

Run the target-free support/leakage preflight first, inspect its gate, and execute separately:

MPLCONFIGDIR=/tmp/phase5a-mpl .venv/bin/python src/phase5a_contextual_expectation_feasibility.py preflight
MPLCONFIGDIR=/tmp/phase5a-mpl .venv/bin/python src/phase5a_contextual_expectation_feasibility.py execute

The execution writes machine-readable outputs under outputs/phase5a/ and figures under figures/phase5a/. The narrative artifacts are the executed notebook and results report. The execution manifest records frozen protocol hashes and implementation clarifications.

Phase 5B opponent-relational increment

Frozen protocol v1.0 is docs/phase5b_opponent_relational_increment_protocol.md, with machine-readable rules in config/phase5b_opponent_relational_increment_protocol.json. Reproduce its outcome-blind preflight and governed execution separately:

MPLCONFIGDIR=/tmp/phase5b-mpl .venv/bin/python src/phase5b_opponent_relational_increment.py preflight
MPLCONFIGDIR=/tmp/phase5b-mpl .venv/bin/python src/phase5b_opponent_relational_increment.py execute

The execution writes machine-readable outputs under outputs/phase5b/ and figures under figures/phase5b/. The executed reporting notebook reads those saved outputs without refitting; the detailed results report preserves the claim boundary. The manifest records all governing hashes and confirms that Metrica Game 3 was not accessed.

Documentation figures can be regenerated with:

python figures/generate_documentation_figures.py

That script reads Game 1 only for empirical figures.

Post-5B measurement audits

The direction/onset and movement-segmentation audits are exploratory, saved-result analyses rather than frozen validation phases:

MPLCONFIGDIR=/tmp/post5b-mpl .venv/bin/python src/post5b_measurement_audit_direction_onset.py
MPLCONFIGDIR=/tmp/post5b-mpl .venv/bin/python src/post5b_attacking_movement_segmentation_audit.py

Their reports, executed notebooks, figures, and machine-readable outputs are under matching post5b_* paths. The segmentation audit manifest hashes its source, rules, and Game 1 inputs. Reproducing it requires Game 1 only and must not introduce defender coordinates or defensive outcomes.

Attacker Movement Episode v2

The immutable Game 1 rule is governed by protocols/attacker_movement_episode_v2.md and config/attacker_movement_episode_v2.json. Heldout Game 2 additionally requires the prospectively frozen protocols/attacker_movement_episode_v2_game2_replication.md, its matching configuration, and the closed attacker-only Stage A support registry.

MPLCONFIGDIR=/tmp/attacker-episode-v2 .venv/bin/python src/attacker_movement_episode_v2_game2.py
MPLCONFIGDIR=/tmp/attacker-episode-v2 .venv/bin/python src/attacker_movement_episode_v2_game2.py --reproduce

The Game 2 report records a MIXED heldout replication. All 10 governed outputs reproduced byte-for-byte. The command reads Game 2 attacker trajectories, global stoppage clocks, and frozen Stage A support only; it does not read defenders or defensive outcomes.

Local defensive deformation v1

The frozen construct is governed by docs/protocols/local_defensive_deformation_v1.md and config/local_defensive_deformation_v1.json. Game 2 additionally requires the prospectively frozen held-out addendum and config/local_defensive_deformation_v1_game2_replication.json.

Reproduce the closed executions separately:

.venv/bin/python src/local_defensive_deformation_game1_v1.py
.venv/bin/python src/local_defensive_deformation_game2_v1.py

The Game 1 report records development coherence. The Game 2 report records a standalone-unclassified held-out result. Its governed outputs are under outputs/local_defensive_deformation_game2_v1/; an independent run reproduced all 13 governed files byte-for-byte. No pooled deformation analysis is authorized by the held-out addendum.

Opportunity Redistribution v1 — Game 1 development

The scientific design is frozen in protocols/opportunity_redistribution_v1.md and config/opportunity_redistribution_v1.json. Reproduce the closed negative result with:

MPLCONFIGDIR=/tmp/mtd-opportunity .venv/bin/python src/opportunity_redistribution_game1_v1.py
MPLCONFIGDIR=/tmp/mtd-opportunity .venv/bin/python src/opportunity_redistribution_game1_v1.py --reproduce

The result report and outputs/opportunity_redistribution_game1_v1/ record the six-column rank-6 fit, all 2,000 bootstrap replicates, exclusions, frozen robustness checks, and byte-identical reproduction. The script reads Metrica Sample Game 1 only. Game 2, Game 3, and IDSSE are outside this execution.

Concurrent geometry v1 — IDSSE external replication

The external protocol, configuration, and provider-equivalence note govern the seven-match Tier 3 execution. Raw IDSSE files and the established cache/adapter structure are required. Reproduce the complete execution and compare it with the authoritative outputs using:

MPLCONFIGDIR=/tmp/mtd-mpl .venv/bin/python src/concurrent_attacker_defensive_geometry_idsse_v1.py --output outputs/.concurrent_attacker_defensive_geometry_idsse_v1_rerun
MPLCONFIGDIR=/tmp/mtd-mpl .venv/bin/python src/concurrent_attacker_defensive_geometry_idsse_v1.py --output outputs/concurrent_attacker_defensive_geometry_idsse_v1 --verify-against outputs/.concurrent_attacker_defensive_geometry_idsse_v1_rerun

The first command reruns the full provider-equivalence gate and scientific execution. The second records byte identity against the closed result. The result report and outputs/concurrent_attacker_defensive_geometry_idsse_v1/ contain the governed evidence. Game 3 and opportunity outcomes are outside this execution.

Temporal spatial footprint v1 — IDSSE external replication

The frozen external protocol, configuration, hash ledger, and provider-equivalence specification governed the final planned time-ordered external bridge test. Its seven-match IDSSE result is SUPPORTED and commits only compact governed outputs under outputs/spatial_defensive_response_footprint_idsse_v1/; provider-derived rows and local staging remain ignored. Reproduce each match's outcome-blind reconstruction in an isolated process, then the staged fit and its independent rerun:

for match in J03WMX J03WN1 J03WOH J03WOY J03WPY J03WQQ J03WR9; do
  MPLCONFIGDIR=/tmp/moving-defense-footprint PYTHONPATH=src .venv/bin/python \
    src/spatial_defensive_response_footprint_idsse_v1.py \
    --output outputs/spatial_defensive_response_footprint_idsse_v1 --stage-match "$match"
done
MPLCONFIGDIR=/tmp/moving-defense-footprint PYTHONPATH=src .venv/bin/python \
  src/spatial_defensive_response_footprint_idsse_v1.py

The memory-bounded staging reconstructs exactly the frozen native inputs; it does not change the 25 Hz cadence, seven-frame support, fixed 2-second preceding exposure/subsequent response, near/middle ranks, reverse-time control, or transported trim threshold. Independently repeat the commands in an ignored output directory and use --verify-against to require byte-identical governed outputs. The execution used only the seven governed IDSSE matches and did not access Metrica Sample Game 3.

Concurrent Defensive Coordination Form v1 — IDSSE external replication

The original protocol, configuration, and prospective IDSSE status clarification govern the seven-match execution. Reproduce two independent complete executions with:

MPLCONFIGDIR=/tmp/mtd-mpl .venv/bin/python src/concurrent_defensive_coordination_form_idsse_v1.py --output /tmp/mtd-coordination-idsse-run1
MPLCONFIGDIR=/tmp/mtd-mpl .venv/bin/python src/concurrent_defensive_coordination_form_idsse_v1.py --output /tmp/mtd-coordination-idsse-run2
MPLCONFIGDIR=/tmp/mtd-mpl .venv/bin/python src/concurrent_defensive_coordination_form_idsse_v1.py --output /tmp/mtd-coordination-idsse-run1 --verify-against /tmp/mtd-coordination-idsse-run2

The result report and outputs/concurrent_defensive_coordination_form_idsse_v1/ record the supported external result, provider-equivalence gates, exclusions, rank coefficients, manifest, hashes, and reproduction comparison. The 89,719,630-byte observation-level Parquet is intentionally not redistributed: it contains provider-linked identifiers, anchor times, distances, and derived movement measurements. A licensed-data holder can regenerate it deterministically; its authoritative SHA-256, 8a580a2d079fa248a1e8e64578b975b52e9fe790c595a6d16e37811c4a43e8d0, remains in governed_hashes.json. Compact aggregate outputs are sufficient to inspect the reported result. The execution uses only the seven governed IDSSE matches and does not inspect Metrica Sample Game 3.

Defensive Response Expectation v1

The frozen protocol, configuration, and result report govern the seven-match execution. It requires the closed local IDSSE coordination-form observation ledger; that provider-linked input remains ignored. Reproduce the complete execution and independent comparison with:

MPLCONFIGDIR=/tmp/mtd-mpl OPENBLAS_NUM_THREADS=1 .venv/bin/python src/defensive_response_expectation_v1.py --output outputs/defensive_response_expectation_v1
MPLCONFIGDIR=/tmp/mtd-mpl OPENBLAS_NUM_THREADS=1 .venv/bin/python src/defensive_response_expectation_v1.py --output outputs/.defensive_response_expectation_v1_rerun --clean-output
.venv/bin/python src/defensive_response_expectation_v1.py --output outputs/defensive_response_expectation_v1 --verify-against outputs/.defensive_response_expectation_v1_rerun

The ten compact governed outputs reproduce byte-for-byte. prediction_source_rows.parquet and prediction_rows.parquet remain local-only; compact errors, bootstrap/control results, provenance, and hashes are publishable. The execution does not access Metrica Sample Game 3.

Defensive Reorganization Departure v1 and frozen v2 support governance

The frozen protocol and result govern the compact seven-match IDSSE application check. The execution stopped before fitting at the frozen common-sample gate because J03WN1 retained 782 rather than 1,000 off-ball rows. Reproduce its eligibility ledger and byte-identical compact result with:

MPLCONFIGDIR=/tmp/mtd-drd OPENBLAS_NUM_THREADS=1 .venv/bin/python src/defensive_reorganization_departure_v1.py --output outputs/defensive_reorganization_departure_v1
MPLCONFIGDIR=/tmp/mtd-drd OPENBLAS_NUM_THREADS=1 .venv/bin/python src/defensive_reorganization_departure_v1.py --output outputs/.defensive_reorganization_departure_v1_rerun
.venv/bin/python src/defensive_reorganization_departure_v1.py --output outputs/defensive_reorganization_departure_v1 --verify-against outputs/.defensive_reorganization_departure_v1_rerun

No prediction ledger is created: the frozen support gate stops before model fitting. The provider-linked observation-level eligibility ledger remains local-only; its governed SHA-256 is retained in the compact final hash ledger. The execution does not access Metrica Sample Game 3.

The outcome-blind support audit and v2 protocol supersede only future attacking-entity support. V2 derives the current outfield set from starting metadata, substitutions, and confirmed dismissals, then requires exact equality with complete seven-frame tracking support before applying the unchanged ball-nearest exclusion. Focused contract tests are:

.venv/bin/python -m pytest -q tests/test_defensive_reorganization_departure_v2_support.py

The 1,000-row/90% gates, target, E0/E1, model, validation, classifications, retrieval, SkillCorner gate, player prohibition, and stop rule remain v1. The v2 result is MIXED: its support gates passed and it reproduced byte-for-byte, but 2.130% macro improvement missed the frozen 3.0% SUPPORT gate. Therefore its local-only prediction ledger was not used to inspect residuals or DRD, and no retrieval, transport, SkillCorner outcome, player ranking, or Game 3 access occurred.

SkillCorner Spatial Form external replication — executed

The prospective external-replication design is governed by protocols/defensive_reorganization_spatial_form_v1_skillcorner_external.md and config/defensive_reorganization_spatial_form_v1_skillcorner_external.json. The provider-compatibility audit, pre-execution support reconciliation, and license/publication note document the outcome firewall, canonical-rank support correction, and public-artifact boundary. Frozen-file identities are in config/defensive_reorganization_spatial_form_v1_skillcorner_external_hashes.json.

The source release is SkillCorner Open Data at upstream commit c1e17a0cc3e07e1774b52d929c1a0b85115143fc. Native SkillCorner JSONL is authoritative for frame-level support and is_detected; Kloppy 3.19.0 supplies the community parser and normalized roster, pitch, player/team, ball, and possession-team objects. The governed adapter merges support by exact match/frame/player identity. Raw provider data remain ignored and are not committed.

The governed execution is implemented in src/defensive_reorganization_spatial_form_skillcorner_external.py; compact aggregate outputs and the result report are committed. It retained 49,107 rows across 3,382 anchors and nine valid matches. The pooled outward-minus-goalward contrast was 0.048883 m/m with a 95% block-bootstrap interval [0.042940, 0.054707]. All nine match and leave-one-match-out contrasts were positive; the frozen trim and majority-detected quality sensitivity passed. This is a supported observational spatial-form replication, not a tactical or value result.

Focused checks are:

.venv/bin/python -m pytest -q tests/test_defensive_reorganization_spatial_form_skillcorner_external.py tests/test_skillcorner_spatial_form_adapter.py

They verify native period time, physical windows, canonical pitch scaling, goalward/outward transforms, active-roster construction, tracking-quality support, identity-step boundary, raw-unit model design, and the external classification gate. A complete independent rerun reproduced all 14 governed compact outputs byte-for-byte. Native support flags remain authoritative because Kloppy does not retain is_detected.

IDSSE localized-reorganization support preflight

The support-preflight protocol and aggregate result report preserve a closed, response-blind IDSSE support characterization. Its committed grid, summary, manifest, hashes, hard-QC record, and support-only figures can be verified without provider data through:

.venv/bin/python -m pytest -q tests/test_localized_reorganization_heatmap_support_preflight_v1.py -m "not provider_data"

The future near-minus-middle response map is frozen but unexecuted. Do not rerun its provider-backed preflight or read any response field unless separately authorized under current research governance.

Known reproducibility limitations

  • There is no one-command end-to-end workflow or continuous integration check.
  • Requirements are bounded but not fully locked across platforms.
  • Several large derived CSVs are committed for auditability; a future archive policy should preserve hashes and provenance before moving them.
  • Two preflight/result attrition pairs are byte-identical because the governed sample did not change; they are retained as phase-specific evidence rather than deduplicated post hoc.
  • The movement-segmentation audit retained a 56.30 m/s maximum. A later outcome-blind audit traced it to identity/trajectory discontinuity and froze only support-rule directions, not a numeric filter. The separate prominence-refinement protocol subsequently executed once and classified B; it did not repair or exclude tracking discontinuities in the primary analysis.
  • The prominence-refinement execution is governed by config/post5b_attacking_movement_prominence_refinement_rules.json, implemented in src/post5b_attacking_movement_prominence_refinement.py, and reported in post5b_attacking_movement_prominence_refinement_results.md. Its B result selects no threshold and does not authorize Game 2 execution.
  • Directional segmentation v1 is governed by protocols/attacking_directional_segmentation_v1.md, implemented in src/attacking_directional_segmentation_game1_v1.py, and reported in results/attacking_directional_segmentation_game1_v1.md. Its compressed CSV outputs are deterministic and can be read directly by pandas. The Game 1 result is B and does not authorize Game 2 execution.