Repository navigation
S1: Read regression_targets in the catalog; migrate the 46 fixture sites #86
Description
Epic: #85 · Order: Story 1 of 8 · Gate: blocks S2, S3, S6 · Closes: #84
Background
views_baseline/model/catalog.py reads self.config["targets"] at 7 sites — lines 57, 62, 67,
75, 85, 98, 112, one per _get_* builder. views-pipeline-core retired that key on 2026-08-02
(507ae11, refs #380/#381, shipped in 3.0.0); the replacement vocabulary is regression_targets
(+ classification_targets, which no baseline uses). Every baseline model has raised
KeyError: 'targets' since. See #85 for the full incident.
The reason the suite did not catch it is tests/conftest.py:12-13, which supplies both keys:
"targets": ["synth_target"],
"regression_targets": ["synth_target"],That fixture is not merely stale — it is illegal upstream. pipeline-core's combined_targets()
raises ValueError on any config carrying targets, and the manager's config reaches the forecast
stage (manager.config is a property aliasing self.configs, passed as context.configs and read
by combined_targets(context.configs) at forecasting/stage.py:153,173). A fixture that pipeline-core
would reject is not a realistic merged config, and leaving it in reproduces the exact blind spot
that caused this outage.
Work
-
views_baseline/model/catalog.py— derive the target list once in__init__:def __init__(self, config: dict, partition_dict: dict, loa: str): """Catalog of available baseline models. Reads the views-pipeline-core config vocabulary: `regression_targets`. (`targets` was retired in pipeline-core #380 and is now rejected by `combined_targets()`.) Classification targets are not read — no baseline is wired for them; see ADR-012 when that changes. """ self.config = config self.partition_dict = partition_dict self.loa = loa self.targets = list(config["regression_targets"]) ...
Then replace all seven
targets=self.config["targets"]withtargets=self.targets.Do not import
combined_targets—model/may not import views-pipeline-core at module
level (ADR-013). See [EPIC] Restore baseline runnability — migrate off the retiredtargetsconfig key #85 for why the manager-passes-it-down variant was also rejected. -
tests/conftest.py— delete line 12 ("targets": ["synth_target"],). Line 13
("regression_targets") already exists and stays.MANAGER_BASE_CONFIGis then the only
already-compliant fixture in the suite. -
Migrate the 46 config-key sites. Rename the dict key
"targets"→"regression_targets"in:tests/test_catalog.py— 17 configs (lines 14, 31, 45, 59, 74, 91, 101, 111, 123, 144, 162,
179, 191, 203, 215, 241, 276)tests/test_baseline_manager.py— 11 configs (29, 55, 87, 120, 272, 285, 310, 336, 374, 403, 432)tests/test_reproducibility_gate.py— 12 inert configs (37, 48, 64, 79, 97, 115, 126, 139, 205,
219, 231, 244) plus the manager config at 172tests/test_baseline.py— 5 configs (572, 581, 591, 600, 609)tests/test_falsification_seed_wiring.py— 2 configs (38, 76)
-
Do NOT touch the ~115 non-config uses. These are a different thing that happens to share the
word:- the
targetspytest fixture —tests/conftest.py:73-75 - model constructor kwargs —
ZeroModel(targets=...)etc., ~50 sites intest_baseline.py
plustest_golden.py,test_parametric.py,test_pooling.py,test_dual_input.py,
test_grid.py,test_helpers.py,test_closeness.py,test_ensemble_consumption.py to_feature_frame(..., targets=...)— throughouttest_input.py,test_pooling.pytests/test_protocol.py:13—_BASEis a**kwargsbag for direct model construction
(it containspartition_dictandloa, which no real config does). Leave it alone.- parameter names —
_frame_config(targets=…)attest_baseline_manager.py:115,
make_dummy_ff(targets=…)atconftest.py:59,_pandas_pool_reference(…, targets, …)
- the
-
Five assertions read both sides —
tests/test_catalog.py:39, 53, 67, 82, 133assert
model.targets == config["targets"]. The model attribute keeps its name; only the config
subscript changes.
Order within the PR (this is the point of the story)
Do step 2 and 3 first and run the suite. It must go red. That red is the evidence the tests
now describe the config shape production actually has. Only then apply step 1 and watch it go green.
A PR that was never red at this commit has not proved anything.
Acceptance criteria
-
grep -rn 'config\["targets"\]' views_baseline/returns nothing -
grep -rn '"targets"' tests/returns nothing (all remaining uses aretargets=kwargs or the fixture) -
grep -n 'targets' tests/test_protocol.pystill shows_BASEunchanged -
conda run -n views_pipeline python -m pytest -q→ 248 passed, 1 xfailed -
ruff check .clean - The PR description records that the suite was red between the fixture commit and the catalog commit
Validation
conda run -n views_pipeline python -m pytest -q
ruff check .
# the fix, proven directly:
conda run -n views_pipeline python -c "
from views_baseline.model.catalog import BaselineModelCatalog
c = BaselineModelCatalog(config={'regression_targets':['lr_os_best'],'window_months':18,'lambda_mix':0.05,'n_samples':256,'seed':42}, partition_dict={'test':(493,540)}, loa='pgm')
m = c.get_model('MixtureBaseline'); print('OK', m.targets)
"Relevant files
views_baseline/model/catalog.py, tests/conftest.py, tests/test_catalog.py,
tests/test_baseline_manager.py, tests/test_reproducibility_gate.py, tests/test_baseline.py,
tests/test_falsification_seed_wiring.py
Labels: story, implementation, bug
Activity
Polichinel commentedon Sep 9, 2026
Implemented in the working tree (not yet committed).
The red step, which is the point of the story. After migrating the fixtures and deleting the
retired key from
conftest.py, but before touchingcatalog.py:Those 34 failures are the production failure, reproduced in the suite for the first time. The
fixture was the blindfold.
After the catalog fix:
248 passed, 1 xfailed in 1.56s,ruff check .clean.Direct proof of the path that was dead:
What changed
views_baseline/model/catalog.py—self.targets = list(config["regression_targets"])derivedonce in
__init__; all seven_get_*factories now forwardtargets=self.targets. Docstringrecords the vocabulary, the
#380retirement, and whyclassification_targetsis not read.tests/conftest.py— deleted"targets": ["synth_target"].MANAGER_BASE_CONFIGis now a configpipeline-core would actually accept (
combined_targets()raises on the old shape).test_catalog.py(17 keys + 5config["targets"]assertions),test_baseline_manager.py(11),test_reproducibility_gate.py(13),test_baseline.py(5),test_falsification_seed_wiring.py(2).tests/test_catalog.py:179re-wrapped — the longer key pushed the line past the 99-char limit.Correction to one acceptance criterion
I wrote "
grep -rn '"targets"' tests/returns nothing". That is not achievable and should not be:tests/test_protocol.py:13keeps_BASE = {"targets": ["y"], "partition_dict": ..., "loa": ...},which is a constructor
**kwargsbag, not a config — it carriespartition_dictandloa,which no real config does. The correct criterion is "no config-key
"targets"remains", and thatholds: the only surviving occurrence in
tests/is that one line, deliberately.The other criteria are met as written.
Polichinel commentedon Sep 9, 2026
Closed by #94, merged to
development(2b3d3b4). CI green:check (3.11),lint,build,check-branchall pass — the first green Lint and Test since 2026-08-02. Suite 248 → 295 passed.