Repository navigation
Description
Overview
Phase A0a(i) of the search-extensibility epic (PyAutoMind epics.md; ledger draft/research/autofit/search_extensibility_epic.md, plan §4 A0a, decision D1). A tests-only, parametrised metadata/serialization conformance suite over all 15 public searches that collects and passes on every CI leg, including Heart's unittest-nojax. It freezes a golden identifier table (the epic's §3.2 invariant) so every later refactor phase of track A is checked against it, and it is the only track-A dependency of the autofit_inference harness (B2).
Plan
- Add one parametrised conformance module over the 15 public searches, resolving each class lazily from a class-path string so the module collects on the no-jax leg.
- Freeze a golden table per search: default-construction identifier, identifier field tuple, and constructor-argument name set.
- Assert the
search.jsonround trip and that construction leaves the config untouched, with strict xfails where today's code is known to fail. - Prove the table bites: a deliberate perturbation fails exactly one case.
- Verify on a full-extras env and with the jax family blocked by an import hook, mirroring the
unittest-nojaxleg.
Tier: judge (undeclared) — merge mode: human /prm
Human ruling 2026-10-07: identifiers must stay the same; any change to an identifier is flagged to the human before it lands.
Detailed implementation plan
Work Classification
Library
Affected Repositories
- PyAutoFit (primary)
Branch Survey
| Repository | Current Branch | Dirty? |
|---|---|---|
| fit/PyAutoFit | main (908ca61) | clean |
Suggested branch: feature/search-conformance-metadata
Worktree root: ~/Code/PyAutoLabs-wt/search-conformance-metadata/
Implementation Steps
test_autofit/non_linear/search/conformance_roster.py: asearches_under_test()helper returning 15 entries (class_path string, short name, family,requiresmodule list). Classes are resolved withimportlibinside the test, never at collection. Roster: Emcee, Zeus, BlackJAXNUTS, SMC, DynestyStatic, DynestyDynamic, Nautilus, NSS, Drawer, BFGS, LBFGS, MultiStartAdam, MultiStartADABelief, MultiStartLion, MultiStartProdigy. The module docstring states the roster and the coverage contract.- Skip rule: a construction-dependent case skips only when its
requiresmodule is absent (NSS raisesImportErrorwithout blackjax ≥ 1.6; every other search constructs with defaults even when its backend is missing). No module-levelimportorskip. test_conformance.py, parametrised per search:test_default_construction:cls()succeeds.test_identifier_golden:str(Identifier(search))withPYAUTO_TEST_MODEremoved from the environment (test mode changes Emcee and Zeus identifiers) equals the frozen golden table committed as data in the module. Also freezes each class's__identifier_fields__tuple.test_constructor_argument_set: theinspect.signatureparameter names equal the frozen set.test_search_json_round_trip:af.to_dict→json.dumps/loads→af.from_dict→af.to_dictequal.xfail(strict=True)for BlackJAXNUTS and SMC: the serialisedinverse_mass_matrixis a kind string the constructor rejects. Repair goes to A0b and flips the xfail.test_config_unchanged_by_construction: push a config override withoutput.search_internal: false(the test config has it true and masks the mutation), snapshot the config dict before and after construction, compare.xfail(strict=True)for Emcee, BlackJAXNUTS and SMC until A3.test_family_base: each class subclasses the expectedAbstractMCMC/AbstractNest/AbstractMLE.
- Generate the golden table once from main, commit it, and verify a deliberate perturbation (one default argument) fails exactly one parametrised case.
- Deferred to layer (ii):
samples_infokey sets (only obtainable from live sampler objects) and any backend execution.
Verification
pytest test_autofit/non_lineargreen on the full-extras env.- The same with jax, jaxlib, optax, blackjax blocked via an import hook (mirrors
unittest-nojax, where nautilus, zeus, emcee and dynesty remain installed): NSS cases skip, the module still collects.
Key Files
test_autofit/non_linear/search/test_conformance.py— new suitetest_autofit/non_linear/search/conformance_roster.py— roster helper and golden tablesautofit/mapper/identifier.py— identifier hashing read for reference (not edited)autofit/__init__.py— eager exports and lazyNSS/SMC(_LAZY_ATTRS) read for reference (not edited)
Original Prompt
Click to expand starting prompt
Search conformance suite, layer (i): metadata and serialization for all 15 searches on every CI leg (epic search-extensibility, phase A0a(i))
Type: test
Target: autofit
Repos:
- PyAutoFit
Themes: - searches
- jax
- ci
Difficulty: medium
Autonomy: safe
Priority: high
Epic: search-extensibility
Status: draft
Filed: 2026-10-07
Phase A0a(i) of the search-extensibility epic
(draft/research/autofit/search_extensibility_epic.md; plan in
search_extensibility_epic_report.md §4 A0a, decision D1 in §8.1). Tests
only. It is the safety net every later phase of track A runs against and the
only track-A dependency of B2.
Original request (verbatim from the epic plan)
"A0a, conformance suite in two layers. Repo: PyAutoFit, tests only. Layer
(i), metadata/serialization, all 15 searches, every CI leg (including
unittest-nojax; the module never imports af.NSS/af.SMC at collection
time): construction, search.json round trip and constructor-argument sets,
identifiers against a frozen golden table of all 15 default constructions,
declared capability attributes, and conf.instance unchanged by construction
(xfail for Emcee, NUTS and SMC until A3). Risk: none. Verify: pytest test_autofit/non_linear on a full-extras env and with jax/blackjax/optax
uninstalled. Witness: layer (i) green on all legs."
Scope
Add test_autofit/non_linear/search/test_conformance.py (plus a small
searches_under_test() helper module beside it) parametrised over every
public search exported from af:
- the 15 searches: Emcee, Zeus, NUTS, SMC (mcmc); DynestyStatic, DynestyDynamic,
Nautilus, NSS (nest); LBFGS, BFGS, Drawer, MultiStartLBFGS/BFGS/Prodigy
family (mle). Confirm the exact roster fromautofit/non_linear/search/
and theafexports before freezing the table, and record it in the module
docstring. - Collection never imports an optional backend: resolve search classes lazily
byclass_pathstring inside the test (importlib) so the module collects on
theunittest-nojaxleg; a search whose backend is absent is skipped by
that fact alone for the construction-dependent checks, never by a
module-levelimportorskip. Emulate the no-jax leg locally with an
import-hook block before shipping (memory: nojaxSkip). - Per search: default construction;
search.jsonwrite →from_dict
round trip equals the original (compare the serialised dicts); the
constructor-argument set snapshot; identifier of the default construction
against a frozen golden table committed as data in the test module (this
table is the §3.2 invariant every later phase of the epic must hold);
declared capability attributes present where they already exist today
(assert only what exists; A1 adds the full set);conf.instanceunchanged
by construction, withpytest.mark.xfail(strict=True)for Emcee, NUTS and
SMC until A3. - Snapshot the per-search
samples_infokey sets only if obtainable without
running a backend; otherwise defer to layer (ii).
Out of scope
Backend execution (layer (ii), after A0b), any source edit under autofit/,
the hygiene list (A0b) and the documentation repairs (A0c).
Verification
pytest test_autofit/non_lineargreen in a full-extras env.- The same green with jax/blackjax/optax blocked via an import hook (the
unittest-nojaxleg); JAX-only searches skip, the module still collects. - Golden table: regenerate once, commit, and prove a deliberate identifier
perturbation fails the test.
Witness
Layer (i) green on every CI leg; deleting a row from the golden table or
changing a default argument fails exactly one parametrised case.
🤖 Generated with Claude Code
Activity
Jammy2211 commentedon Oct 7, 2026
Library PR Created
pending-release)API changes: none, tests only (
test_autofit/non_linear/search/{conformance_roster,test_conformance}.py).Local evidence (task worktree, branch head 886ebb2): full
test_autofit3066 passed / 2 skipped / 5 xfailed; conformance module 101 passed / 5 strict xfails; no-jax leg emulated via import hook (97 passed, 4 NSS skips, 5 xfails); perturbation of one golden identifier fails exactly one case.Strict xfails recorded for later epic phases: NUTS/SMC
search.jsonround trip (A0b), Emcee/NUTS/SMCoutput.search_internalmutation at construction (A3). No identifiers changed.Workspace impact: none (option iii). Heart YELLOW acknowledged by the human at ship (manifest drift from the unregistered
autofit_inference/autofit_profilingcheckouts, B1 pending; no release rehearsal for current source).Next: human
/prmonce CI is green (judge tier).Jammy2211 commentedon Oct 7, 2026
Shipped
pending-releaseSummary: layer (i) search conformance suite — 15 public searches, frozen golden identifier table from main 908ca61,
search.jsonround trip, constructor-argument sets,conf.instanceunchanged by construction; collects and passes onunittest-nojax. Five strict xfails record the A0b (NUTS/SMC round trip) and A3 (Emcee/NUTS/SMCoutput.search_internalmutation) repairs. No identifiers changed.Session notes: the merge waited on a Heart release-validation freeze (expired 20:36Z). Epic search-extensibility phase A0a(i) complete; B1 registration is in flight (PyAutoMind#492).
feat: add n_effective to DynestyDynamic; fix truncated runs under a f…