Repository navigation
CP-2a: file runs read through the preprocessing run's RunInputs - #69
Merged
Merged
Conversation
Folder and tree runs need common.preprocessing_run and open it with RunInputs.open(run, experiment_root=<image root>, masks_root=<masks folder>), so a moved or copied experiment is checked and read where it is now. The images patched are the run's FOVs with their recorded keys; images and masks are read with RunInputs.read_plane/read_mask in one canonical layout per run (cellpatcher.file_runs, cellpatcher.run_reading). The former file-run settings (image_axes/mask_axes, mask_source, mask_label_name, labels) raise a migration message. The session-wide statistics cache is replaced by one opened run per call (cellpatcher.run_session): a mask or unpatched image changed between calls is refused. Run opening moved to cellpatcher.run_open (re-exported). 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
…ne images) 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 07:06
…run session The combined run_config.json was written after the call's run session closed, so its normalization_inputs opened the preprocessing run again where it recorded the experiment. A moved experiment with a CSV normalization scope was refused after every leaf had been patched (and a copy was checked at the original). Regression test in test_file_runs.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
run_reading kept its own copy of morphobricks_preprocessing.masks.FOLDER; import it (lazily, as the other preprocessing imports here) instead of duplicating it (§9). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
State that an image added after the preprocessing run is skipped silently today, and that the gap closes when CellPatcher calls the check preprocessing will give bricks (owner decision of 2 November 2026; not in v0.4.0rc1). The OME-Zarr section no longer says folder and tree runs read stores themselves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
statistics.py re-exported cellpatcher.run_open's functions under a new "# noqa: F401", growing the noqa count from 1 to 2 (§9 ratchet: it may only shrink). Re-export them with plain assignments, and import the private _refuse_other_file in normalization.py from run_open, where it lives. Ruff findings unchanged (62). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
… runs still use The file-run refusal said every refused setting "still describes in-memory arrays", but mask_source and mask_label_name are not used by run_patching. Say which ones are. 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 was referenced Nov 2, 2026
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-2a of the CellPatcher v0.2.0 plan (
docs/restart/preprocessing-design-v0.4.md§6 and §8 on MorphoFeatureclaude/restart-doc). Folder and tree runs now read files only through the preprocessing run'sRunInputs. This builds on CP-1 (#68,27fa182) and the rc1 pin (morphobricks-preprocessing@v0.4.0rc1,bd4a5dc). It does not depend on preprocessing PR #113 (PP-7).Behaviour changes
A preprocessing run is required for file runs. This applies to
run_patching_pipeline,run_patching_condition_repeat_treeandrun_patching_from_folders, in every mode, with normalization on or off.common.preprocessing_run(the run's output folder).no_preprocessing_run, and the message gives themorphobricks-preprocessing check/runcommands.The experiment root is where the experiment is now. The run is opened with
RunInputs.open(run, experiment_root=<image folder or root>, masks_root=<mask_folder_path or mask_root_path>).check_sources/check_planes, and its masks were read with no check at all. A mask changed in a copy was used; it is now refused.load_plane_statistics,recorded_image_type,cached_statisticsandopen_runtakeexperiment_root=/masks_root=.Images, FOV keys and masks come from the run (
cellpatcher.file_runs,cellpatcher.run_reading):RunInputs.fovs(), with their condition, biological and technical repeat andposition_idas recorded. No name is parsed.RunInputs.read_plane, masks withRunInputs.read_mask(the run's pairing; the brick checks the labels). With plane policies, only the chosen planes are read.discover_*,pair_masks,read_image, header axis check oriter_file_planes(a test makes them raise).One canonical layout per run. Arrays are built T, Z, C, then Y, X, in the recorded roles:
run_config.jsonrecords this layout ascommon.image_axes/mask_axes, pluscommon.preprocessing_run.Masks:
matching.mask_typepicks among the run's mask types. A label image's type is its name.Nonetakes the only type.cellmode always reads masks.randommode reads them when a masks folder ormask_typeis given.Migration (old settings raise). For file runs, these raise a
ValueErrorsaying what replaces them:common.image_axes/mask_axesother than"YX";mask_source="ngff_labels"andmask_label_name;condition/biological_repeat/technical_repeatother than"single".They still describe in-memory arrays for
run_patching. CP-2b removes them for file runs.run_patching_pipelinepatches one condition and biological repeat. A run with more is refused, pointing to the tree run.Normalization (CSV scopes):
normalization.plane_histogramsmay be left unset, or must name that table (not_the_run_output).Statistics cache. The session-wide
_STATISTICS_CACHE(keyed on table and manifest) is removed.cellpatcher.run_sessionkeeps the opened run for one call only (run_patching, a folder or tree run). Within that call, a tree run opens it once, and every file it reads is fingerprinted again byRunInputson each read.cached_statistics) nothing is cached.Kept and documented from rc1: a folder mask and an OME-Zarr label image of the same type for one image are refused by the preprocessing run (
duplicate_mask), so no clean run has them (test intest_ome_zarr_images.py).Module moves. Run opening moved to
cellpatcher.run_open(statistics.pywas over 600 lines). It is re-exported fromcellpatcher.statistics.Docs.
configuration_reference.md, and corrected rows (preprocessing_run, the axes, masks, labels,mask_type,image_read_transposed, OME-Zarr).normalization_contract.md§2/§5,normalization.mderror table, notebook CHANGELOG.Tests
tests/test_file_runs.pyhas 18 tests (17, plus one added in review):run_config.json;run_config.jsonwith a CSV scope is written from the call's run.tests/helpers.py:preprocess,file_run_config,preprocessed_folder,preprocessed_tree,as_file_run).test_a_swap_the_preprocessing_run_did_not_declare_is_refused_with_its_statistics: file runs can no longer declare a swap (common.image_axes='ZXY'is refused by the migration check, tested intest_y_and_x_declared_swapped_are_refused_never_read_transposed).test_tree_no_condition_folders_raises,test_colliding_positions_are_refused_before_any_read,test_a_tree_refuses_a_later_leaf_before_reading_any_leaf,test_a_tree_refuses_a_later_leaf_mask_object_set_before_reading_any_leaf(one mask type per run, checked once before any read),test_without_the_plane_reader_planes_come_from_the_whole_image(no fallback reader for file runs)...._never_reaches_a_file_run), multi-series TIFF.mainsource (62d2a73, run against rc1), 95 tests fail (94 + the review test), including all 18 oftest_file_runs.py. For the key ones:mask_folder_pathand the old API.Gate (Python 3.11, uv venv, rc1):
pytestwith.[test,zarr]: 1013 passed, 7 skipped (after review). CI is the same. All 7 skips are torch (test_torch_dataset.py1,test_biological_repeat.py1,test_delivery.py5).pytestwith.[test]: 990 passed, 30 skipped (23 need the zarr extra, 7 torch), locally and in CI.ruff check src tests: 62 findings, all in main's set. Main has 67; 5 old ones are gone and none are new.# noqa: 1, as on main (review removed a new one).run_session.py,run_open.py,run_reading.py,file_runs.py, the rewrittenrun_patching_pipeline/run_patching_condition_repeat_tree/_patch_leaves/_checked_config, andcached_statistics.ruff --select C901,PLR0913,PLR0915,ANNwith the §9 limits passes on the new modules.ruff --select ANNis clean on the new modules, and the rewritten entry points are fully annotated. Every module hasfrom __future__ import annotations, and public names have NumPy docstrings.RunInputscalls.orchestration.py938→625,statistics.py689→526,normalization.py836→832,pipeline.py1068→1066,config.py613→611.test_condition_repeat_tree.py668→658,test_patch_identity.py677→609,test_normalization.py951→951,test_io.py600→592.file_runs.py387,run_open.py245,run_reading.py186,run_session.py83,tests/test_file_runs.py222.Left for later PRs
image_axes/mask_axesfor file runs,image_read_transposed(alwaysfalsefor file runs now), the transposition plumbing,pair_masks/find_masks,ids.py, and the now-unusedplanes.iter_file_planes;image_axes;mask_folder_pathto say where a moved masks folder is (owner question 3);run_patchingwith a CSV scope (owner question 4).configuration_reference.md,normalization_contract.md§2 and the notebook CHANGELOG.patch_array/iter_patch_arrayswith paths re-read files withcommon.image_axes);validate_image_and_mask_folders.mask_sourceand labels),quick_start/api_examples/patching_modes/plan.md, and the installers.Owner decisions (2026-11-02)
test_a_mask_file_whose_header_names_x_before_y_is_refused).Review fixes
5145e39: a tree run's combinedrun_config.jsonwas written after the call's run session closed, so itsnormalization_inputsreopened the run where it recorded the experiment: a moved experiment with a CSV scope was refused after every leaf was patched. Now written inside the session; regression test fails without the fix.77b9af4:run_readingimports preprocessing'smasks.FOLDERinstead of a copy (§9 duplication).66b080b: docs state the added-images gap as a known limitation with its planned fix; the OME-Zarr section no longer says folder/tree runs read stores themselves.4831de1:statistics.pyre-exportedrun_openunder a new# noqa: F401(ratchet 1 → 2); now plain assignments, and_refuse_other_fileis imported fromrun_open.75866f2: the migration message no longer saysmask_source/mask_label_namedescribe in-memory arrays.Owner questions as asked by the implementer
discover_experimentfor file runs, so CellPatcher patches the run's FOVs only. An image added to the experiment after the run (or a PNG beside the TIFFs) is not patched, and nothing is said. Before, the CSV scopes refused it (images_without_rows) and folder runs refused non-TIFF images by name.configuration_reference.mdandnormalization_contract.md§2.metadata={"axes": "XY"}) gets the run written anyway, with that mask recorded asaxes: ""andshape: [].RunInputs.read_maskthen calls it "changed or missing since the run".test_declared_axes.py::test_a_mask_file_whose_header_names_x_before_y_is_refused).mask_folder_path(unchanged), so their run cannot be told where a moved masks folder is.mask_root_pathfor them and uses it only to open the run.run_patchingwith a CSV scope still opens the run where it recorded the experiment. There is noexperiment_rootin the config.normalization.experiment_rootwanted, or is that left to the bricks' migrations?https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Generated by Claude Code