Repository navigation
feat: results-library phase 0 — artefact integrity and honest reads #1734
Description
Overview
Phase 0 of the results-library epic, shared with hpc-campaign (ruling F1, 2026-10-28). Result zips become complete-or-absent, corrupt zips and checkpoints are quarantined instead of failing every rerun, and the aggregator / AggregateCSV report problems instead of hiding them. One issue, two PRs: 0a artefact integrity, then 0b honest reads. The fit() failure record is not here (hpc-campaign phase 1).
Plan
- PR 0a: atomic STORED-media zips; non-destructive
restore()withBadZipFilequarantine; read-onlyopen_result(); zip-aware cachedis_complete; checkpoint validity probe +search_internal.failed_<utc>/quarantine for Nautilus and dynesty; Timer →files/. - PR 0b:
from_directory(on_error=)+agg.errors; loud empty warning;print→logging; AggregateCSV strict by default, union header, atomic save, key-joined labels;row.pyregression tests; docstring fix. - Builds on
open_atomic, the Checkpointer split (refactor: samples adapter and Checkpointer/resume split (search-extensibility A3, stacked on A2) #1680) and archive-before-.completed(fix: A2/A3/A3b post-merge fixes from astra review 05 (search-extensibility) #1683). - Fixture tests +
autolens_workspace_testsmoke; shipspending-release. - Tier: judge — merge mode: human /prm
Detailed implementation plan
Affected Repositories
- PyAutoFit (primary)
Branch Survey
| Repository | Current Branch | Dirty? |
|---|---|---|
| ./PyAutoFit | main @ 8740092 (2026.10.26.1) | clean |
Suggested branch: feature/results-library-p0-integrity
Implementation Steps — PR 0a
autofit/tools/util.py:76-84zip_directory: write<out>.zip.tmp→os.replace;ZIP_STOREDfor.png/.fits/.hdf5, DEFLATED otherwise.non_linear/paths/abstract.py:503-524restore()(today rmtree → extractall →os.remove(zip), BadZipFile re-raised): extract to a sibling temp dir and swap; remove the zip only whenremove_filesand it is this search's own;BadZipFile→ rename<hash>.zip.corrupt_<utc>, warn, treat as not complete. New read-onlyopen_result().non_linear/paths/directory.py:204-208is_complete: whenoutput_path/.completedis absent, check<out>.zipfor a.completedmember + cleantestzip(), cached by (path, size, mtime).fit()(abstract_search.py:302) still restores a complete zip becauseresult_via_completed_fitreads extracted files; extraction-free reads are results-library phase 1.non_linear/checkpoint.py(Checkpointer.load:176,NativeFileCheckpointer.load:347) + resume checks atsearch/nest/nautilus/search.py:535andsearch/nest/dynesty/search/abstract.py:303: shared probe (h5py open for Nautilus; catchOSError/ValueError/RuntimeError/KeyError; samplerLinAlgErroron resume) → movesearch_internal/tosearch_internal.failed_<utc>/, start fresh, log once.non_linear/timer.py: write underfiles/behindhas_timer, atomically; read fallback for legacy output.- Tests: killed-mid-zip resumes; two processes restore one zip concurrently; truncated-HDF5 and poisoned-dill checkpoints quarantine and start fresh; STORED members verified.
Implementation Steps — PR 0b
aggregator/aggregator.py:177from_directory(..., on_error="skip"|"raise"): BadZipFile at :98/:280 →agg.errors(path, exception); prints at :149/:219/:374/:384/:503 →logging; empty result logs a warning naming directory + filters.aggregator/summary/aggregate_csv/__init__.py:strict=Truedefault (:19);fieldnames(:113) = union of rows;save(:138) viaopen_atomic, reports blank columns;add_label_column(:90) key-joined.row.pysigma-1/sigma-3/max_lh regression tests on an asymmetric latent fixture; fix invertedpost_fit_outputdocstring.- Tests: one corrupt zip →
len(agg.errors) == 1; unknown column raises; union header; killed-mid-save leaves previous CSV intact.
Worktree file ownership (concurrent with hpc-campaign phase 1)
This task: tools/util.py, non_linear/paths/, non_linear/checkpoint.py, non_linear/timer.py, aggregator/. Shared touchpoint non_linear/search/abstract_search.py (resume lines only). hpc-campaign phase 1 merges after PR 0a.
Key Files
autofit/tools/util.py,autofit/non_linear/paths/{abstract,directory}.py,autofit/non_linear/checkpoint.py,autofit/non_linear/timer.py,autofit/aggregator/aggregator.py,autofit/aggregator/summary/aggregate_csv/
Original Prompt
Click to expand starting prompt
Results library phase 0: artefact integrity and honest reads in PyAutoFit (shared with hpc-campaign) — one issue, PR 0a then PR 0b
Type: feature
Target: autofit
Repos:
- PyAutoFit
Themes: - results
- aggregator
- hpc
Difficulty: large
Autonomy: human-required
Priority: high
Status: formalised
Consequence: judge
Witness: PR 0a — a killed-mid-zip fixture resumes cleanly; two processes restore one zip concurrently and both read it; truncated-HDF5 and poisoned-checkpoint fixtures start fresh with the old state moved aside (search_internal.failed_<utc>/);.png/.fits/.hdf5members are ZIP_STORED; PR 0b — a fixture tree with one corrupt zip aggregates withlen(agg.errors) == 1; an unknown AggregateCSV column raises; the header is the union of rows; a killed-mid-save leaves the previous CSV intact;row.pysigma-1/sigma-3/max_lh tests pass on an asymmetric latent fixture;autolens_workspace_testsmoke green for both PRs
Review-minutes: 45
Unattended: never
Epic: results-library
Phase: 0
Shared-with: hpc-campaign
Parent: draft/feature/autofit/results_library_epic.md
Filed: 2026-10-28
Phase 0 of the results-library epic (ledger draft/feature/autofit/results_library_epic.md, §Phase 0;
report results_library_epic_report.md P1, P2, P11, P12, P13). Shared with hpc-campaign: its ledger
records this issue as its phase 0 #A and does not re-issue it (ruling F1, 2026-10-28). The fit() failure
record is not here; it is hpc-campaign phase 1. Scoping review:
/mnt/c/Users/Jammy/Workspace/Science/scratch/pyautolens_ii_campaign_results_scoping_2026-10-28/REVIEW.md §D.1.
One PyAutoFit issue, two PRs in order. Ground against PyAutoFit main at or after 8740092dc
(2026.10.26.1); the plan quotes the current lines (the ledger's 710f4b343 references are stale). Build on
open_atomic (tools/util.py), the Checkpointer/internal_state.py split (#1680) and the
"finalise archive then .completed" order (#1683).
PR 0a — artefact integrity
zip_directory: write<path>.zip.tmp, thenos.replace;ZIP_STOREDfor.png/.fits/.hdf5, text
DEFLATED. A zip is complete or absent, never truncated.restore(): extract to a temp dir and swap; never delete the zip unless the caller owns the result
(remove_filesand this search's identifier);BadZipFile→ rename<hash>.zip.corrupt_<utc>, log,
continue as "not complete". Read-onlyopen_result()for downstream stages.is_completefor a zipped result:.completedmember +zipfile.testzip(), cached per path;fit()
consults it beforerestore().- Checkpoint validity probe in
Checkpointer.loadfor Nautilus (h5py open; catchOSError,ValueError,
RuntimeError,KeyError) and dynesty; on failure or a samplerLinAlgError, movesearch_internal/to
search_internal.failed_<utc>/, start fresh, log once. - Timer files →
<output_path>/files/behind thehas_timercapability; atomic write.
PR 0b — honest reads
Aggregator.from_directory(..., on_error="skip"|"raise"),agg.errors(path, exception); an empty
aggregator logs a loud warning naming path and filters;print→loggingacross the aggregator.- AggregateCSV:
strict=Truedefault; union header; atomic save; labels joined by key;save()reports
blank columns. row.pyregression tests (sigma-1, sigma-3, max_lh) on an asymmetric latent PDF fixture; fix the
invertedpost_fit_outputdocstring.
Worktree file ownership (review §D.4, ruling F11)
This lane: autofit/tools/util.py, non_linear/paths/, non_linear/checkpoint.py, aggregator/,
non_linear/timer.py. Shared touchpoint non_linear/search/abstract_search.py (fit() consults
is_complete before restore()) lands in PR 0a; hpc-campaign phase 1 (non_linear/status/) merges after
PR 0a. Ships pending-release. Ledger entry in both epic ledgers on ship.
Activity
Jammy2211 commentedon Oct 29, 2026
Library PRs created
#1737 is held for a decision. The new
AggregateCSV(strict=True)default breaks oneeuclid_strong_lens_modeling_pipelinetest (test_catalogue_missing_assets.py::test_bundle_reaches_later_stages_with_missing_assets):source_sersicasks forgalaxies.source.bulge.centre.centre_0on fits that lack it, which now raisesKeyError. Options: passstrict=Falseexplicitly in the euclid catalogue scripts (works on released and new PyAutoFit), or keepstrict=Falseas the library default.Merge order: #1736 → #1737 → hpc-campaign phase 1 #1738 after #1736. All carry
pending-release.🤖 Generated with Claude Code
Jammy2211 commentedon Oct 29, 2026
Shipped
Results-library phase 0 merged (pending release):
AggregateCSVstrict by default,agg.errors, logging)strict=Falsein the catalogue/workflow CSV scripts, merged first so the strict default breaks nothing there (euclid tests 352/352 against 0b)hpc-campaign phase 1 (#1738) merged after 0a.
🤖 Generated with Claude Code