Repository navigation
CP-2b: file-run removals, run-config version 2, moved experiments everywhere - #70
Merged
Merged
Conversation
Pure removal, no behaviour change: pipeline.check_source_identities, ids.object_set_from_label_path, io.read_ngff_mask, io.refuse_mask_channel_axis and planes.iter_file_planes have had no caller since file runs read through the preprocessing run's RunInputs (CP-2a). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…s pairing Design v0.4 §6: CellPatcher pairs no file itself. The function moves to cellpatcher.file_runs (still cellpatcher.validate_image_and_mask_folders) and takes preprocessing_run=: the run is opened with RunInputs.open with the image and mask folders as where they are now, and each image's recorded mask of the type is returned. Without a run it is refused (no_preprocessing_run, with the preprocessing command); an image without a recorded mask of the type, a type the run did not record, and a changed mask are refused. The tests of CellPatcher's own pairing (find_masks through pair_masks: ambiguous names, formats, header problems, a masks folder inside the images) are removed: those checks are the preprocessing run's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Pure removal: io.pair_masks, MaskPairing, the find_masks header-issue filtering (_refuse_header_problems and its codes), MASK_CHANNEL_HELP, io.source_relative_path and io.discover_folder_images had no caller left once validate_image_and_mask_folders read the preprocessing run's pairing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…s manifest SHA-256 run_config.json now has run_config_version 2 and, when a preprocessing run was read, a preprocessing section: the run's folder, the SHA-256 of its run_manifest.json taken when this call opened it (refused if the manifest changed while it was opened), and for a folder or tree run the array layout built from the run's recorded roles. A file run no longer records common.image_axes, mask_axes or its labels (they are the run's, not settings), so its run_config.json loads back as a config a file run accepts; before, the layout recorded as image_axes was refused by the migration check. load_config and the exported-patch readers refuse any run_config_version but 1 (no version: before v0.2.0, or a plain config file) and 2. run_config_data and the config reader move to cellpatcher.run_config_file (pipeline.py and orchestration.py shrink). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
New cellpatcher.iter_run_patch_arrays(patches, config, experiment_root=, masks_root=) delivers the patches of a folder or tree run one at a time: the run is opened with RunInputs.open (where the experiment and masks are now), each patch's image is the run's image at its source_relative_path with the FOV key the run recorded (another image or key is refused), and only its plane is read with RunInputs.read_plane, its mask with read_mask. patch_array, iter_patch_arrays and PatchTorchDataset take arrays only: a file path, which they read themselves with common.image_axes and CellPatcher's own header check, now raises a TypeError naming iter_run_patch_arrays. The tests of CellPatcher's header-axis check for lazy delivery from a path (refuse_image_header_axes, refuse_mask_header_axes, header_axes_disagreement) are removed with that path; the file-run refusals of declared axes stay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…now unused Pure removal: io.read_image, io.conform_axes, io.refuse_image_header_axes, io.refuse_mask_header_axes, io.refuse_header_axes, io.header_axes_disagreement and planes.read_file_plane with its plane reader (_read_choice, _plane_reader, _file_layout, _read_plane; the last transposition plumbing: plane.T for a file whose X came before Y) had no caller once lazy delivery stopped reading files. Their tests go with them; read_array still reads a store in its stored order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Breaking, listed in the CHANGELOG: - images.csv loses its last column image_read_transposed (and ImageRecord its field): since preprocessing v0.4 refuses every Y/X swap it was always false for a file and empty in memory. - common.mask_source and common.mask_label_name are removed: folder and tree runs read the masks their preprocessing run paired (label images included, chosen with matching.mask_type), and run_patching never used them. CommonConfig(mask_source=...) is a TypeError; a config or run_config.json file holding their defaults (every file before v0.2.0) still loads, and one with another value is refused naming the setting and what replaces it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…ff F401) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…ed masks folder Owner decision 3 (2026-11-02): as the tree run already did, run_patching_pipeline (and run_patching_from_folders) accept mask_folder_path in grid and whole_fov mode and use it only to open the preprocessing run, whose masks are checked there and never read. Before, it was refused, so the run of an experiment whose masks folder had moved could not be opened. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
… CSV scope Owner decision 4 (2026-11-02): in-memory run_patching with the image or biological_repeat scope opened the preprocessing run where it recorded the experiment, so a moved experiment was refused. normalization.experiment_root and masks_root now say where they are now; the run is opened there with RunInputs.open. A folder or tree run is told by its arguments and refuses these settings with the migration message. cached_statistics moves from cellpatcher.normalization to cellpatcher.run_open (normalization.py was over 600 lines), beside the new settings_statistics that both CSV-scope readers use. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…isions Fixes what this PR makes false, and nothing for CP-3: the CHANGELOG entry (with the breaking removals), configuration_reference.md (file runs, common and normalization tables, matching, delivery, images.csv, OME-Zarr, run_config.json), AGENTS.md §4/§5, normalization_contract.md §4/§12/§13, normalization.md, the validate_image_and_mask_folders example in api_examples.md, and docstrings and the in-memory mask message that named removed functions or mask files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
read_patch_table (the reader MorphoRelate uses) reported any run_config.json it could parse, and the exported-patch loaders checked the version only for raw exports. Both now refuse a run_config_version other than 1 or 2 first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
guijacquemet
marked this pull request as ready for review
November 2, 2026 08:31
run_config.json records the manifest's SHA-256 taken when the call opened the run. A manifest rewritten after that (preprocessing run again on the same output folder) was recorded as if unchanged. Now the SHA-256 is checked again when run_config.json is written (before the file is created, so a refusal leaves no file and no export), and a second open in the same call refuses a manifest changed since the first. Regression tests for these two and for the existing "changed while it was opened" branch, which had none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…(ruff PLR0402) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
The path refusal of patch_array reached PatchTorchDataset only when an item was loaded, inside a DataLoader worker, although its constructor checks its inputs up front. Images and masks are now checked in the constructor, with the same TypeError naming iter_run_patch_arrays. Regression test (needs torch). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…ivery test test_lazy_delivery_refuses_a_mask_declared_swapped covered a mask file with a swapped header; its file half is now test_lazy_delivery_refuses_a_mask_file, but nothing covered lazy delivery refusing an array mask declared 'XY' any more. Test only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…e manifest refusal The CHANGELOG now says plainly that CellPatcher's own notebook does not work until CP-3 (no shim, owner decision of 2026-11-02), that the check for images added after the run is wired once preprocessing v0.4.0rc2 provides it, and names the downstream items for the brick migrations (MorphoRelate's axes check, MorphoEmbed's PatchTorchDataset, fiberfeatures' reference patch_config.json). It also records this review's two fixes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
patch_array, iter_patch_arrays and _normalize_on_load (their contract or body changed here), load_config, refuse_transposed_mask_axes, _read_run_config and Normalizer.for_config. Annotations only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Only a file run's preprocessing section was tested. An in-memory run at a CSV scope records the run and its manifest's SHA-256 without a layout and keeps common.image_axes and its labels; at the patch scope it has no section. Test only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Contributor
Author
Independent review: merged as
|
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.
CP-2b of the CellPatcher v0.2.0 plan (
docs/restart/preprocessing-design-v0.4.md§6, §8 and §9 on MorphoFeatureclaude/restart-doc). It builds on CP-2a (#69, merged as6a35426). It stays pinned tomorphobricks-preprocessing@v0.4.0rc1(bd4a5dc). After this PR, CellPatcher discovers, pairs and reads no file itself, for any run.Behaviour changes
run_config.jsonversion 2 (cellpatcher.run_config_file):run_config_version: 2.preprocessingsection:run: the run's folder;manifest_sha256: the SHA-256 ofrun_manifest.json, taken when this call opened the run. A manifest that changes during the call is refused: while it is opened, between two opens in one call, or beforerun_config.jsonis written (then norun_config.jsonis left; review fix);layout(image,mask): the order CellPatcher built the arrays in from the run's recorded roles.common.image_axes,mask_axesor its labels. They are the run's values, not settings, and each patch's labels are inpatches.csv.run_config.jsonnow loads back withload_configas a config a file run accepts. On main, the layout recorded asimage_axes(for example'CYX') and the leaf labels made the migration check refuse it.load_config,read_patch_tableand the exported-patch readers (iter_exported_patches,load_exported_patch,ExportedPatchTorchDataset) refuse anyrun_config_versionother than 1 and 2. Version 1 means no version: a file written before v0.2.0, or a plain config file.validate_image_and_mask_folders(image_folder, mask_folder, *, preprocessing_run=, mask_type=):RunInputs.openwhere the two folders are now.no_preprocessing_run, with the command).mask_type;cellpatcher.file_runsand is stillcellpatcher.validate_image_and_mask_folders.cellpatcher.iter_run_patch_arrays(patches, config, *, experiment_root=, masks_root=)delivers a folder or tree run's patches.source_relative_path. A patch whose image is not in the run, or whose FOV key differs from the recorded one, is refused.RunInputs.read_plane; its mask is read withread_mask.patch_array,iter_patch_arraysandPatchTorchDatasettake arrays only. A path raises aTypeErrornamingiter_run_patch_arrays(PatchTorchDatasetwhen it is built; review fix). On main, CellPatcher read the path itself, withcommon.image_axesand its own header check.run_patching_pipelineandrun_patching_from_foldersacceptmask_folder_pathingridandwhole_fovmode, as the tree run already did. It only says where the run's masks folder is now: the masks are checked there and never read. On main this was refused.normalization.experiment_root/masks_rootsay where a moved experiment and its masks folder are now, forrun_patchingat theimageorbiological_repeatscope.images.csvloses its last column,image_read_transposed, andImageRecordloses the field. Since preprocessing v0.4 it was alwaysfalseor empty.common.mask_sourceandcommon.mask_label_nameare removed:CommonConfig(mask_source=...)is aTypeError;run_config.jsonthat holds their defaults (every file written before v0.2.0) still loads;matching.mask_type.cellpatcher.io:pair_masks,MaskPairingand the find_masks header filtering;read_image,conform_axesandread_ngff_mask;refuse_image_header_axes,refuse_mask_header_axes,refuse_header_axesandheader_axes_disagreement;refuse_mask_channel_axis,source_relative_pathanddiscover_folder_images.cellpatcher.planes:iter_file_planesandread_file_plane, with the last transposition plumbing (plane.Tfor a file whose X came before Y);cellpatcher.ids:object_set_from_label_path;cellpatcher.pipeline:check_source_identities.run_config_data: frompipelinetocellpatcher.run_config_file;cached_statistics: fromcellpatcher.normalizationtocellpatcher.run_open.common.image_axes/mask_axes. Masks with X before Y are still refused, withrefuse_transposed_mask_axes.ids.py: patch identity, andposition_idfromimage_file. That uses preprocessing'sposition_idand only for in-memory images; file runs give the recordedposition_id.read_array.discover_condition_repeat_folders/list_supported_files, which the notebook uses; no run uses them.discover_condition_repeat_folderswraps preprocessing'sdiscover_experiment;list_supported_fileslists one folder's entries itself (iterdir, with preprocessing'sis_system_name), a listing helper for in-memory use.claude/pp-8b-brick-checks, which is not merged and not in rc1. It is wired once a preprocessing release candidate includes it. The known-limitation docs are unchanged.Tests
test_file_runs.py:load_config,iter_exported_patchesandread_patch_table(3 cases);normalization.experiment_root/masks_rootfor a moved experiment, and their refusal in a tree run.test_io.py:validate_image_and_mask_foldersneeds a run;test_types_and_config.py: themask_sourcesettings are removed, and old files still load;test_declared_axes.py: no transposition column; a mask path refused;test_plane_policies.py: a file's planes delivered through the run;test_delivery.py: equality of the lazy arrays now viaiter_run_patch_arrays.origin/mainsource (ce0379c).test_io,test_delivery,test_ome_zarr_images).test_dependency_pinsartefacts of thePYTHONPATHswap. They pass normally.test_an_opened_run_is_not_reused...fails only because of the movedcached_statisticsimport.test_io/test_ome_zarr_imagesfail 11 more tests:grid/whole_fovwith a masks folder: main raises "not supported";validate: main returns its own pairing, or rejectspreprocessing_run=as an unknown option.test_the_run_config_records...findsimage_axesrecorded and no version, and no reader checks a version.validate_image_and_mask_folders. These are now the preprocessing run's checks:find_masksheader-code tests, the deprecated-name test and the multi-axis mask advice (2 cases).test_the_shared_rule_is_the_preprocessing_brick_sandtest_other_disagreements_are_refused(3 cases).YXC,conform_axes(2), theread_file_planemulti-series/multi-image tests (2) andtest_mask_source_is_validated.Gate (Python 3.11, uv venvs, rc1
bd4a5dc; review heade0137b1)pytestwith.[test,zarr]: 988 passed, 7 skipped. All 7 skips need torch. The torch-gated modules (test_torch_dataset,test_delivery,test_biological_repeat) were also run with CPU torch 2.15: 54 passed.pytestwith.[test]: 967 passed, 28 skipped: 21 need the zarr extra, 7 need torch.e0137b1gives the same counts and is green.ruff check src tests: 58 findings, against 62 on main. None is new; 4 of main's are gone.# noqacount: 1, the same as main.e61dd6e: 983 passed / 7 skipped and 962 / 28.Review fixes (independent review, 2026-11-02)
001f059,ca77cff:run_config.jsonrefuses arun_manifest.jsonthat changed during the call. Before, only a change whileRunInputs.openran was refused, and that branch had no test. Now the SHA-256 is checked again whenrun_config.jsonis written; the data is built before the file is created, so a refusal leaves no file and no export. A second open in one call also refuses a manifest changed since the first. 3 regression tests, which fail on main.00d5c4b:PatchTorchDatasetrefuses a path when it is built, not in a DataLoader worker. Regression test (torch).35874de: the in-memory half of the removedtest_lazy_delivery_refuses_a_mask_declared_swapped: an array mask declaredXYin lazy delivery. Nothing covered it any more.e0137b1: a test of the version 2run_config.jsonof an in-memory run (a CSV scope: run and SHA, no layout, axes kept; the patch scope: no section).01390bb: annotations on the functions this PR changed (§9 ANN).44e6c74: the CHANGELOG says plainly that the notebook is broken until CP-3, with no shim. It notes that the added-images check is wired after preprocessing v0.4.0rc2, and it names the downstream items for the brick migrations.§9 figures (measured)
ruff --select C901,PLR0913,PLR0915,ANNwith limits 15, 8 and 50, plus an AST count of executable lines.run_config_file.py(182 lines) andrun_delivery.py(104 lines).from __future__ import annotations.run_config_file,run_delivery,run_session,run_openandfile_runs;delivery._image_plane/_mask_plane/_refuse_file.run_config_data);run_patching_condition_repeat_tree, unchanged);open_run,open_file_run,iter_run_patch_arrays,run_patching_condition_repeat_tree);open_run, each line of a wrapped call counted).io.py1090→582,pipeline.py1066→1030,normalization.py832→804,orchestration.py625→591,config.py611→599,test_patch_identity.py609→583.planes.py389→243,test_io.py592→406,file_runs.py388→436,run_open.py245→301,test_file_runs.py242→344. No file grows past 600.file_sha256andMANIFEST_FILENAME.RunInputscalls.Downstream (inspected, not edited)
ea19bb0),io/cellpatcher.py:check_image_axesreadscommon.image_axesfrom CellPatcher'srun_config.json. A file run's v2 file no longer has it, so MorphoRelate reports "not checked" (axes_check_state); there is no error. Its migration PR should take the axes from the preprocessing run.read_patch_tablenow refuses an unknownrun_config_version.a8e88df):provider_crops.pybuildsPatchTorchDataset([self.image], ...). That is fine ifself.imageis an array; a path would now be aTypeError.362f1ba):modeandgridfromdata/experiment_001/reference/patch_config.json.mask_source/mask_label_nameat their defaults. It loads withload_config, butCommonConfig(**common)would be aTypeError.docs/make_example_results.py,asdict) would lose the two keys.4e6970f): itsimage_read_transposedtest is MorphoFeature's owninputs.read_transposed. It uses no CellPatcher name that changed (border_labels,object_sides).ca548cd): it usescheck_patch_bounds/check_region_maskonly. It is unaffected.pair_masks/read_ngff_maskandcellpatcher.normalization.cached_statistics, and offers the removed settings, so its import cell fails until CP-3. It already could not run a file run withoutpreprocessing_runsince CP-2a.Owner decisions (2026-11-02)
normalization.masks_rootstays besideexperiment_root, because a moved masks folder needs it.https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Generated by Claude Code