Part of epic #105. Depends on S1. Parallel-eligible with S3/S5.
Problem
§5 pins a 10-column sidecar (priogrid_id first; codes ALWAYS float64; NaN/null rows preserved; ascending rows; column order normative) built from this repo's ADR-011 lookup — no builder exists. §5.2's gid-set parity (sidecar cells == forecast cells) has no invariant.
Work
views_postprocessing/unfao/wire/sidecar.py: build_sidecar(lookup: pa.Table, gids) -> pa.Table — pyarrow-only, lookup injected (DIP: production loads data/gaul_lookup.parquet; tests inject synthetic). Restricted to the forecast gid set; a gid absent from the lookup FAILS LOUD (never a dropped row). Reuse unfao/gaul_schema.py column SSOT — do not restate column lists.
views_postprocessing/delivery/parity.py: assert_gid_set_parity(forecast_gids, sidecar_gids, *, label) + GidParityError — pandas-free primitives, names the symmetric difference. (Lands under the existing delivery/ no-pandas design-contract enforcement automatically.)
Acceptance criteria
- Injected synthetic lookup equal to the fixture's rows → output byte-equals
fixture_run_0__sidecar.parquet (pyarrow pin as in S3).
- Property tests vs the real
gaul_lookup.parquet: pinned schema incl. float64 codes, ascending priogrid_id, NaN row preserved, column order.
- Parity raises with named gids on any asymmetry.
Tests: tests/test_wire_sidecar.py, tests/test_parity.py. Oracle: fixture sidecar.
🤖 Generated with Claude Code
Part of epic #105. Depends on S1. Parallel-eligible with S3/S5.
Problem
§5 pins a 10-column sidecar (priogrid_id first; codes ALWAYS float64; NaN/null rows preserved; ascending rows; column order normative) built from this repo's ADR-011 lookup — no builder exists. §5.2's gid-set parity (sidecar cells == forecast cells) has no invariant.
Work
views_postprocessing/unfao/wire/sidecar.py:build_sidecar(lookup: pa.Table, gids) -> pa.Table— pyarrow-only, lookup injected (DIP: production loadsdata/gaul_lookup.parquet; tests inject synthetic). Restricted to the forecast gid set; a gid absent from the lookup FAILS LOUD (never a dropped row). Reuseunfao/gaul_schema.pycolumn SSOT — do not restate column lists.views_postprocessing/delivery/parity.py:assert_gid_set_parity(forecast_gids, sidecar_gids, *, label)+GidParityError— pandas-free primitives, names the symmetric difference. (Lands under the existing delivery/ no-pandas design-contract enforcement automatically.)Acceptance criteria
fixture_run_0__sidecar.parquet(pyarrow pin as in S3).gaul_lookup.parquet: pinned schema incl. float64 codes, ascending priogrid_id, NaN row preserved, column order.Tests:
tests/test_wire_sidecar.py,tests/test_parity.py. Oracle: fixture sidecar.🤖 Generated with Claude Code