Skip to content

S3 — forecast convert-at-the-door (internal PredictionFrame) #88

Description

@Polichinel

Epic: #85 · S3 · [UNILATERAL] · depends on #86, #87

Background

The forecast enters at _read_forecast_data as pd.read_parquet(io.BytesIO(...))PGMDataset (unfao.py:125-127), whose object-dtype cells already hold the sample arrays. Today those samples are never represented as a frame — the interior runs on pandas and the values are effectively collapsed. This story makes the interior sample-aware without touching either wire.

Work

  • Immediately after the PGMDataset is built, construct an internal PredictionFrame (via S1) from the forecast.
  • Route the interior computations that consume the forecast (coverage cell counts, observed-range decisions where applicable, provenance counts) through the S2 frame-native readers.
  • Both wires stay pandas: the Appwrite read is unchanged, and _save still emits the same object-dtype parquet (the outbound change is S6). This step changes only vpp's internal representation.

Acceptance criteria

  • The forecast is held internally as a (N,S) PredictionFrame; interior invariants are computed from it.
  • The emitted delivery (parquet bytes) is byte-identical to today on the same input — no wire/behavior change.
  • ruff + full suite green; delivery/ untouched.

Parity / validation

Head-to-head on a captured forecast fixture (object-dtype-cell parquet): run the existing pandas path and the new frame path over the same bytes; assert identical coverage counts, identical fabricated-month clip decisions, identical provenance dict, and byte-identical emitted parquet (outbound unchanged this step).

Dependencies

Depends on S1 + S2. This is the unilateral slice of the gated forecast problem — it de-risks S6 by proving the frame path produces identical deliveries before any wire change.

Files

views_postprocessing/unfao/managers/unfao.py (_read_forecast_data, the interior call sites), tests (a captured forecast fixture + head-to-head).


Review amendment (expert-code-review, 2026-06-28) — don't deepen the manager

Introduce the frame conversion behind a small delivery source adapter the manager calls, rather than inlining frame-construction into _read_forecast_data.

  • Why: _read_forecast_data/_save already interleave infra + logic (register C-40); adding representation logic inline accretes a third responsibility (violates SRP/DIP). A thin source/sink seam is exactly C-40's thin-shell mitigation.
  • Cross-ref C-40; keep the manager a thin orchestrator.
  • Acceptance criteria addition:
    • Forecast→PredictionFrame conversion lives behind a callable seam (a delivery source adapter), not inline in the manager method.

Metadata

Metadata

Assignees

No one assigned

    Labels

    implementationCode implementation workstoryA single reviewable unit of an epic

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions