Repository navigation
S1 — Delete the retired delivery path; refuse an incomplete config #149
Description
Epic: #148 · Closes C-63, #145 · Resolves C-29 · Unblocks S2–S8
Background
The frame-native contract path won on 2026-07-27 (run-0, global-land, FAO served since). The pandas path it replaced was kept behind a config fork "until run 0 proves the contract path live" (register C-40) and never removed. This story is that removal. It is not new work — it is the named post-run-0 follow-up, four days overdue, and D-11 warned that if it lingers the duplication becomes permanent.
Two independent dispatch axes, four theoretical modes, one used:
| Axis | Gate | Declared by |
|---|---|---|
| Historical representation | declared_data_format(queryset) == "feature_frame" (unfao.py:130) |
views-models config_queryset.py:62 |
| Delivery protocol | configs.get("wire_contract") (:233, :294, :323, :409, :520) |
views-models config_meta.py:26 |
Production declares both. Omitting either silently selects the retired half — that is the inference ADR-003 forbids.
Work
Delete the legacy leg at every fork:
_read_forecast_data:236-270— legacy body; keep the contract call_read_historical_data:134-146— pandas leg;_read_historical_framebecomes the reader_transform:288-298— legacy leg (geography attaches at artifact build)_validate:300-348— both legacy legs; keep the contract assertion +_check_coverage_check_coverage:414-418— legacy forecast branch_save:523-577— body becomesreturn self._save_contract()_append_metadata:276-286,_delivery_description:619-636— legacy-onlyLEGACY_FORECAST_FILTERS:29-34_historical_coverage_source:579+— collapse to the frame branch- imports
:5, :7, :14—read_dataframe,PGMDataset,pandasif unreferenced
Add the single refusal. One place, at the manager's entry, raising and naming the missing key — not .get() with a silent default. Follow the shape of appwrite_env.assert_env_declared (unfao/appwrite_env.py:41), which is this repo's existing fail-loud-on-missing-declaration idiom.
Reuse, do not rewrite: _ContractStorePort (:37-77) already carries the fail-loud upload guard that #145 asks for; _unfao_appwrite_config() (:499) already builds the config the deleted legacy body duplicated verbatim; _save_contract (:442) is the surviving path.
Do not touch unfao/enrichment.py or unfao/extraction.py in this story — S3 and S4 handle them once the callers are gone.
Acceptance criteria
-
unfao/managers/unfao.py< 450 lines (from 636) - Zero
wire_contract/data_formatbranches outside the single refusal - A launch config missing either key raises, naming the key — new test asserts this
-
#145's silent uploads are gone: nodsm.upload_data(...)whose result is discarded -
test_hop_b_sink_e2e.pybyte-parity unchanged and green — proves the surviving path did not move
Testing / validation
ruff check .;pytest -q --deselect tests/test_datafactory_deploy_readiness.py— the 5 known local pyarrow byte-parity failures only, no new ones- Four test files couple to legacy-only symbols and need rework, not deletion:
test_append_metadata.py(12 hits),test_selection_guard.py(3),test_input_integrity_e2e.py(3),test_falsification_fao_notes_relevance.py(1) test_selection_guard.pypinsLEGACY_FORECAST_FILTERSas an ADR-013 §11.4 golden string. Re-point it at the ADR's post-adoption record — do not drop the guarantee, the historical fact stays pinned after the code goes.
Risk
The legacy path has no end-to-end test, so it could already be broken and nothing would report it. That cuts both ways: it is also why deleting it is low-risk — unreachability is established by config (config_meta.py:26, config_queryset.py:62), not by test.
ADR-013 §11.4 is satisfied, not violated: it required a type-aware guard live before the first contract upload — both guards merged 2026-07-15, run-0 uploaded 2026-07-27. Deleting the legacy reader retires the selector §11.4 was protecting.
Activity
feat(delivery): S1 — one delivery path; refuse an incomplete launch c…
refactor(unfao): S3 — one representation seam; store metadata gets it…
Polichinel commentedon Jul 31, 2026
Done in PR #159 (merged to
development). Manager 636→410 lines; 14 config forks → 0 plus one refusal; pandas/PGMDataset gone from the manager.unfao/launch_config.pyadded — a launcher omitting a key is refused by name and told there is no fallback. #145 closed with it. Register C-63 closed, C-29 resolved. Review found the new module raised without logging (ADR-008) — fixed in-commit with tests asserting the log; C-71 registered for the pre-existing identical gap inappwrite_env.fix(release): address code-review findings on the main merge (#169)
chore(register): review-rr strategic — curation, and a drift class th…
fix(env): S1 #182 — appwrite_env logs before it raises, and a guard o…
docs(accuracy): S11 #197 — living docs still described code #148 dele…
test(validation): S4 #185 — retire the _validate replica; test the ga…