refactor(managers): C-40 — store construction off the manager class - #227
Merged
Merged
Conversation
C-40's residual read "gated on views-pipeline-core 3.0.0, which is a release signal rather than engineering work". 3.0.0 shipped 2026-08-03 and nothing became possible, because two different gates had been conflated: - the pandas gate (consequence b) was gated on pipeline-core's DataFrame retirement, an epic, not a version — and it lifted on its own on 2026-07-27. - the de-inheritance was never gated on any pipeline-core release. It is gated on views-models: postprocessors/un_fao/main.py:27 constructs our manager directly and the framework's Template Method drives _read/_transform/_validate/_save. The inheritance IS the integration contract, so removing it is a two-repo change. Nobody had written that down, which is how the entry sat parked reading as unblocked. What was actually available is the inbound half of this entry's own prescribed DIP mitigation. The outbound half landed in July as _ContractStorePort; the inbound half never did. _prod_forecasts_datastore, _<partner>_datastore and _<partner>_appwrite_config were methods constructing AppwriteConfig and DatastoreModule inline. They are now module-level functions taking declared arguments. No delivered byte moves. The gain is C-40's consequence (a): store construction, its refusals and their ordering are testable with no manager instance, no views-models path manager and no Appwrite environment — tests/test_store_construction.py, 14 tests, which could not have been written a day ago. First part of the manager seam reachable without the framework. Two things found while in there, both removed: - self.ensemble_path_manager: assigned in __init__ and in the builder, read once four lines later, and nowhere else in either partner package or in views-models. - loa = "pgm" followed by `if not loa: raise` — an unreachable branch guarding a variable never used again, beside the commented-out block that once computed it. Manager class 351 -> 272 lines (16 -> 14 methods); files 440 against the 450 directory budget. New ratchet test_the_manager_class_itself_stays_thin bounds the class at 300 — the existing budget counts the directory and so cannot see a class re-absorbing logic, which is exactly the shape of this refactor. Both bounds stand; neither was relaxed. Four mutations proven: env assert moved after construction, a builder given back its self, a coordinate value pasted in, the class padded past the ratchet. C-40 re-scoped with the corrected gate and trigger; Location rows on C-33 and C-40 updated; the dated ADR-011 assessment banners the two claims this change invalidated. Suite 377 passed / 40 xfailed; ruff clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Advances C-40. No delivered byte changes; no wire module, no manifest, no fixture touched.
The gate C-40 stated was wrong, and that is the main finding
The entry read: "gated on views-pipeline-core 3.0.0, which is a release signal rather than engineering work." 3.0.0 shipped 2026-08-03 and nothing became possible. Two gates had been conflated:
postprocessors/un_fao/main.py:27constructsUNFAOPostProcessorManager(...)directly and the framework's Template Method drives_read/_transform/_validate/_save. The inheritance is the integration contract — removing it means writing a different launcher there, so it is a two-repository change and an operator decision.Nobody had written that down, which is how the entry sat reading as unblocked work waiting on nobody.
What was actually available
This entry's own prescribed mitigation was to wrap the Appwrite I/O behind a DIP adapter. The outbound half landed in July as
_ContractStorePort. The inbound half never did —_prod_forecasts_datastore,_<partner>_datastoreand_<partner>_appwrite_configwere still methods buildingAppwriteConfigandDatastoreModuleinline offself.Now module-level functions with declared arguments:
_build_prod_forecasts_store(ensemble_name),_build_partner_store(model_path),_partner_appwrite_config(model_path).Kept in the same file on purpose. A new module would be a third
views_pipeline_coreimporter and break the allowlist that ADR-015 §2's containment claim rests on. Moving code out of the class gets the testability win without weakening that guarantee.Why this is worth a PR
tests/test_store_construction.py— 14 tests that could not have been written a day ago. Every one runs with no manager instance, no views-models path manager, and with the Appwrite variables explicitly removed from the environment. That is C-40's consequence (a) — "the FAO logic cannot be instantiated or unit-tested without the full framework + Appwrite env" — discharged for this surface, and the first part of the manager seam reachable without the framework.They assert the refusals and their ordering:
assert_env_declaredmust run before anything is constructed, so a missing coordinate produces the declared error naming which store is incomplete, rather than whatever the client raises when handedNonefor an endpoint.Two things found while in there
self.ensemble_path_manager— assigned in__init__and in the builder, read exactly once four lines after being set, and read nowhere else in either partner package or in views-models. A method-local value wearing the costume of manager state.loa = "pgm"followed byif not loa: raise— an unreachable branch guarding a variable never used again, beside the commented-out block that once computed it for real.Measurements
The file grew because the extracted functions carry their reasoning; the class is what C-40 is about.
New ratchet:
test_the_manager_class_itself_stays_thin, bound 300. Added because the existing budget counts the directory and therefore cannot see a class re-absorbing logic — precisely the shape of this refactor. Stated in its docstring as a ratchet, not a target: the right response to it failing is to move something out of the class, not to raise the number. Both bounds now stand; neither was relaxed. Adding a constraint while making a change that the existing one is blind to is the honest version of this.Mutation proofs
..._checked_before_a_store_is_constructedself..._are_functions_not_methods..._no_coordinate_value_is_baked_into_the_builders..._class_itself_stays_thinResidual, stated honestly
The double inheritance itself, and consequences (c) SDP exposure and (d). Not unilaterally actionable, and arguably not a defect — C-40 has always conceded "the dependency itself is correct —
unfao.pygenuinely is a pipeline-core postprocessor", and ADR-015 §1 argues there is nothing to unwind to. Tier held at 2: containment is not removal.Trigger corrected from "a pipeline-core release" to: when views-models changes how postprocessors are launched, or when an upstream base-class change actually breaks a delivery — which
tests/test_framework_contract.pynow catches here rather than on the partner's run.Records disposed of (ADR-014 §5)
C-33's and C-40's
Locationrows named the old symbols. The dated ADR-011 assessment now banners that its §8 describes the.envborrow andensemble_path_manageras current, and both are gone.Verification
🤖 Generated with Claude Code