Skip to content

feat(track-a-source): ADR-013 leg 2b — Hop-A source adapter (Track-A archive → PredictionFrame)#101

Open
Polichinel wants to merge 1 commit into
developmentfrom
feat/track-a-source-adapter
Open

feat(track-a-source): ADR-013 leg 2b — Hop-A source adapter (Track-A archive → PredictionFrame)#101
Polichinel wants to merge 1 commit into
developmentfrom
feat/track-a-source-adapter

Conversation

@Polichinel

Copy link
Copy Markdown
Collaborator

What

The consumer side of ADR-013 §3 (build-sequence leg 2b): views_postprocessing/unfao/track_a_source.py reads a Hop-A Track-A archive from the prediction store and produces the interior PredictionFrame — verified, declared, fail-loud, pandas-free.

Contract obligations discharged

Contract § Obligation Where
§3.2 vpp verifies shard content hashes against the Hop-A manifest on read read_shard(expected_sha256=…)
§3.1 archive is exactly y_pred.npy + identifiers.npz + metadata.json member-set check
§2 / per-hop assert payload matches the header's own declarations: (N, sample_count), dtype, identifier lengths N read_shard
§2.1 major-version gate (MINOR drift accepted, MAJOR rejected) _require_contract_major (manifest + header)
§3.3 manifest content is identity — listed shards all present, months cover exactly expected_months, cell counts match; a torn run is never assembled frames_for_target

Not here by design: the §6 no-collapse policy gate (delivery/draws.py, s_min) runs at the FAO-facing upload (leg 2c), not at ingest — this module is mechanism guards only. Store I/O (Appwrite download) stays in the manager; this module takes bytes.

Tests (15, golden fixture as oracle)

Happy path reads the canonical §10 fixture bytes; every tamper case starts from the real fixture and breaks exactly one declared fact — tampered bytes, missing member, header lying about sample_count/dtype, identifier length mismatch, MAJOR bump (manifest and header), missing shard bytes, target disagreement, wrong month coverage, wrong cell count, missing manifest field. MINOR drift (1.9) explicitly accepted.

Gates: ruff clean, 168 passed / 44 xfailed.

Refs: ADR-013 (docs/ADRs/013_sampled_forecast_wire_contract.md), #45, #88. Next leg: 2c (sink adapter, #91).

🤖 Generated with Claude Code

…→ PredictionFrame)

The consumer side of §3: read a Track-A archive, verify the shard bytes against
the Hop-A manifest's declared sha256 (§3.2 — the verification this repo owns on
read), check the §3.1 member triple, assert the payload matches the header's own
declarations (sample_count, dtype, identifier lengths — the per-hop ingest
assert), gate on contract major version (§2.1), and build the interior
PredictionFrame via frames.build_prediction_frame. frames_for_target() assembles
a (run, target) from its manifest — manifest content is identity (§3.3): listed
shards must all be present, months must cover exactly expected_months, cell
counts must match; a torn run is never assembled.

Pandas-free by construction; mechanism guards only — the §6 policy gate
(delivery.draws, S_min) stays at the FAO-facing upload.

Tests use the §10 golden fixture as oracle: happy path from canonical bytes,
tamper paths from surgically corrupted copies breaking exactly one declared fact
each (hash, member set, S, dtype, major version, target, months, cell count,
identifier length). MINOR header drift (1.9) accepted per §2.1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant