Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ brick**.
- **Statistics code (from the normalization brick):** the per-plane histogram, the CSV
reader and writer, pooling (`PooledHistogram`) and normalization parameters (`min_max`,
`percentile_range`, `z_score`, `robust_z_score`) live in the required dependency
`morphobricks_normalization` (pinned to its release v0.5.0; first pinned at v0.2.0, plan §12 questions 27 and
`morphobricks_normalization` (pinned to its release v0.6.0; first pinned at v0.2.0, plan §12 questions 27 and
28). This brick runs them over
the experiment. Later bricks such as patch extraction use them to normalise any group of
planes without rereading pixels. Import them from `morphobricks_normalization`; the
Expand Down Expand Up @@ -770,7 +770,7 @@ Technical details go to `logging` (shown in the notebook's "Technical details" p
| Topic | Decision |
|---|---|
| Binning | Per-plane min–max, same bin count per experiment, pooled counts for groups (binning study) |
| Core dependencies | `numpy`, `tifffile`, `scipy` (the mask step's piece counts, Q32) and `morphobricks-normalization` (git dependency pinned to its release tag v0.5.0, whose reader takes the schema version 5 table (channels by canonical ID, ADR 0003; since v0.5.0), and whose CSV reader checks each row's stored `plane_id`, `image_file` and `bit_depth` (since v0.3.2), refuses a blank `position_id` and tables of schema versions 1 to 3 (v0.4.0), and whose `PlaneRecord` requires a keyword-only `position_id` and which refuses corrupt or ambiguous statistics and parameters (v0.3.3); `[tool.hatch.metadata] allow-direct-references`). `ipywidgets` and `matplotlib` in the `notebook` extra; `zarr` in the `zarr` extra; `pytest` and `ruff` in `dev` |
| Core dependencies | `numpy`, `tifffile`, `scipy` (the mask step's piece counts, Q32) and `morphobricks-normalization` (git dependency pinned to its release tag v0.6.0, which only adds the `stain` module and the optional `histology` extra (neither used here), whose reader takes the schema version 5 table (channels by canonical ID, ADR 0003; since v0.5.0), and whose CSV reader checks each row's stored `plane_id`, `image_file` and `bit_depth` (since v0.3.2), refuses a blank `position_id` and tables of schema versions 1 to 3 (v0.4.0), and whose `PlaneRecord` requires a keyword-only `position_id` and which refuses corrupt or ambiguous statistics and parameters (v0.3.3); `[tool.hatch.metadata] allow-direct-references`). `ipywidgets` and `matplotlib` in the `notebook` extra; `zarr` in the `zarr` extra; `pytest` and `ruff` in `dev` |
| OME-Zarr (Q12) | Input may be TIFF and/or OME-Zarr (folders ending in `.zarr`), mixed. OME-Zarr axes come from their metadata and are matched by role; level 0 of a single image only, several images per store is `multiple_series`. *Removed in v0.4.0 (plan Q46): TIFF images are always read directly; the following describes v0.3.* TIFF choice once per experiment (`convert_tiffs`): read directly (default) or convert to `<output>/ome_zarr` (OME-NGFF 0.4) and compute rows from the copies; switching recomputes TIFF rows and removes unused copies. Reads OME-NGFF 0.4 and 0.5 (0.5 needs zarr 3). In the notebook: its own step-3 card "(Optional) Image conversion" after Settings (Q13): radio buttons (read directly = recommended), a disk-space warning (amber, red when the estimate exceeds free space, never blocking), and conversion settings (chunk size, compression + level, pyramid levels) shown only when converting. CLI `--convert-tiff` with `--chunk-size`, `--compression`, `--compression-level`, `--pyramid-levels`. No overlap setting: chunks never overlap |
| System files (Q5) | Ignored silently; `single` check and sibling uniqueness are case-insensitive |
| Too-deep folders (Q7) | Error |
Expand Down Expand Up @@ -1094,15 +1094,15 @@ jupyter lab notebooks/MorphoBricks_Preprocessing.ipynb

---

## 11. Status (current state, v0.5.2)
## 11. Status (current state, v0.5.3)

- **Released:** v0.5.2 (12 November 2026), with the normalization brick pinned at v0.5.0. The
- **Released:** v0.5.3 (13 November 2026), with the normalization brick pinned at v0.6.0 (v0.5.2,
12 November 2026, pinned it at v0.5.0). The
per-release history is in [CHANGELOG.md](CHANGELOG.md); the milestones PP-0 to PP-13 are in
[docs/archive/plan-milestones-v0.1-v0.4.md](docs/archive/plan-milestones-v0.1-v0.4.md); the older
status log of this file is in
[docs/archive/agents-status-history-v0.1-v0.3.md](docs/archive/agents-status-history-v0.1-v0.3.md).
- **Unreleased:** the faster *Looks right: confirm the design* click (see the changelog's
"Unreleased" section).
- **Unreleased:** none yet; later changes go in a new "Unreleased" section of the changelog.
- **Scope:** 2-D per plane (a Z-stack is refused as `z_stack_not_supported`; since v0.5.2 the
notebook saves Z-stacks as recorded projections); acquisitions may have different channel
lists (v0.5.2, Stage 3 S3.3: manifest version 5, histogram table schema version 5; units of
Expand Down
18 changes: 17 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,23 @@ The design is `docs/restart/preprocessing-design-v0.4.md` in MorphoFeature, on b
are in [docs/plan.md](docs/plan.md) §12. (The v0.3 to v0.4 migration guide was removed in
v0.5.1: no run before v0.5.1 is read.)

## Unreleased
## v0.5.3 (13 November 2026)

A pin release: normalization is pinned at v0.6.0, so that every core package (and CellPatcher,
fiberfeatures and MorphoFeature downstream) pins the same normalization URL. Nothing in
preprocessing changes because of it, and no format changes: run manifest version 5,
`plane_histograms.csv` schema version 5, resolver contract version 2 and conversion record
version 4 are those of v0.5.2, and a v0.5.2 run is read as it is. The release also carries the
faster *Looks right: confirm the design* click of #293, merged after v0.5.2 (Fixed, below).

### Changed

- **Normalization pinned at v0.6.0** (`@v0.6.0`, tag on `95e1d6d`; was `@v0.5.0`, `1291b09`).
Normalization v0.6.0 only adds: a new module `morphobricks_normalization.stain` (RGB-to-scalar
presets: `to_scalar`, `rgb_to_scalar`, `PRESETS`) and an optional extra `histology`
(scikit-image), which preprocessing neither uses nor installs. Its legacy API,
`NormalizationParameters`, `rgb.py`, the `plane_histograms.csv` schema version 5 and every
output of v0.5.0 are unchanged, and it still imports with NumPy only.

### Fixed

Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ from there.

## Status

Pre-alpha; the current release is v0.5.2 (what is planned next is in
Pre-alpha; the current release is v0.5.3 (what is planned next is in
[docs/roadmap.md](docs/roadmap.md)). Implemented and tested:

- Folder discovery and validation, TIFF and OME-Zarr header inspection without reading pixels
Expand All @@ -41,7 +41,7 @@ Pre-alpha; the current release is v0.5.2 (what is planned next is in
Every feature, message code and output file is described in the [user guide](docs/user_guide.md).

From the normalization brick (`morphobricks_normalization`, a required dependency, pinned
to its release v0.5.0): per-plane histograms with the robust range and the range check
to its release v0.6.0): per-plane histograms with the robust range and the range check
(`histogram`, `ranges`), the histogram table (`csv_io`, schema version 5: channels by canonical
ID, so acquisitions may have different channels), pooled group statistics
(`pooling`) and normalization parameters (`normalization`); import them from
Expand All @@ -61,7 +61,7 @@ converted. See the [CHANGELOG](CHANGELOG.md).
## Installation

```bash
pip install "git+https://github.com/CellMigrationLab/morphobricks-preprocessing.git@v0.5.2"
pip install "git+https://github.com/CellMigrationLab/morphobricks-preprocessing.git@v0.5.3"
```

For development, from a clone:
Expand All @@ -71,7 +71,7 @@ pip install -e ".[dev]"
```

The optional `notebook` extra installs the widget dependencies for the notebook
(`pip install "morphobricks-preprocessing[notebook] @ git+https://github.com/CellMigrationLab/morphobricks-preprocessing.git@v0.5.2"`).
(`pip install "morphobricks-preprocessing[notebook] @ git+https://github.com/CellMigrationLab/morphobricks-preprocessing.git@v0.5.3"`).
The notebook's first code cell (**1. Install**) can set this up automatically: it uses a compatible nearby
checkout or installed package, and installs missing requirements into the active kernel. A standalone
download installs from GitHub if needed (internet access and Git required).
Expand Down Expand Up @@ -228,7 +228,7 @@ ruff format .
pytest
```

Coding conventions are described in [docs/development.md](docs/development.md). Planned work after v0.5.2 is in [docs/roadmap.md](docs/roadmap.md).
Coding conventions are described in [docs/development.md](docs/development.md). Planned work after v0.5.3 is in [docs/roadmap.md](docs/roadmap.md).

## License

Expand Down
4 changes: 2 additions & 2 deletions docs/colab-release-check.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Write the date, the commit and any failure (as an issue number) next to each ite
- **Time-lapse:** a small experiment of time-lapse images (optional; the tests cover it).

**Before opening it.** `RELEASE_REF` in cell 1 is the release preparation commit, and cell 3's
`PreprocessingNotebook(...)` call is that commit's API (v0.5.2: `PreprocessingNotebook()`). The
`PreprocessingNotebook(...)` call is that commit's API (v0.5.3: `PreprocessingNotebook()`). The
release PR moves `RELEASE_REF` and records the pair in `RELEASE_CALLS` of
`tests/test_notebook_file.py`, which fails for a `RELEASE_REF` without one.

Expand All @@ -30,7 +30,7 @@ release PR moves `RELEASE_REF` and records the pair in `RELEASE_CALLS` of
- [ ] One line: "… run cell 1 (Install MorphoBricks preprocessing) first …", no traceback;
nothing is installed.
3. Run **cell 1**.
- [ ] It prints **Ready** with the preprocessing (0.5.2), normalization (0.5.0) and
- [ ] It prints **Ready** with the preprocessing (0.5.3), normalization (0.6.0) and
ipywidgets versions and the pinned source, then names cells 2 and 3. No restart or
ipywidgets upgrade is asked for; Drive is not mounted.
- [ ] Run it again: nothing is installed again, the same versions.
Expand Down
23 changes: 23 additions & 0 deletions docs/conformance.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,29 @@ real run; a skip is never counted as a pass. Owner rulings of 8 November 2026 (i
"no skips" applies to the Spec conformance modules (`tests/test_spec_*.py`); a skip elsewhere
in the suite is listed as **not run**, with its reason.

## Release record: v0.5.3 (13 November 2026)

A G1-style record of the v0.5.3 release candidate (as for v0.5.2 below), a pin release:
normalization moves from v0.5.0 to v0.6.0, which only adds the `stain` module and the optional
`histology` extra. It does not decide a gate; deciding a gate is the owner's.

| | |
|---|---|
| Release candidate | Measured on the tree of the v0.5.3 preparation commit (commit A of branch `claude/release-0.5.3`, from `main` at `3c91e7c`: v0.5.2 and #293, the faster *Looks right: confirm the design* click) before this record was added; the record changes only this file. A keeps `RELEASE_REF` at v0.5.2's `cadbed4`. Commit B then points the notebook's `RELEASE_REF` at A and records the pair in `RELEASE_CALLS` (the notebook and two test files only), so `git diff A B -- src pyproject.toml` is empty: the installed package is the one `RELEASE_REF` names |
| Spec pin | `tests/spec_fixtures/PIN.toml`: MorphoBricks-Spec `69a58b3e2f89b318364c4c1252c54caf676fb6bb` (unchanged), checked by `test_spec_pin` (16 passed) |
| Environment | Linux, Python 3.13.18, `pip install -e '.[dev,notebook,zarr]'` in a fresh venv (normalization 0.6.0 from its tag `v0.6.0` = `95e1d6d`, as pip's `direct_url.json` records; scikit-image not installed, the `histology` extra is not requested; numpy 2.5.4, tifffile 2026.11.12, ipywidgets 8.1.9, zarr 3.4.2, pytest 9.2.0); run as root on a case-sensitive file system |
| Commands | `python tools/check.py` (Ruff, Ruff format, pytest over `tests/`, code rules); `python -m pytest -q -p no:cacheprovider -rsxX --junitxml=… tests/test_spec_*.py` |
| `tools/check.py` | Ruff clean, Ruff format 461 files already formatted, pytest **5493 passed, 4 skipped** (not run, below), 0 failed, code rules passed (exit status 0) |
| Spec modules (`tests/test_spec_*.py`) | **1799 passed, 0 skipped, 0 xfailed, 0 failed**, from all 25 modules of `tests/spec_gate.py` `EXPECTED_MODULES` (JUnit: no `skipped`, `failure` or `error` element in any case); the cases per module are those of v0.5.2 below |

**Not run (4 environment skips, outside the Spec modules):** the four of v0.5.2 below
(`tests/test_cli_log.py:166`, `tests/test_folder_rules.py:199` and `:230`: the file system is
case-sensitive; `tests/test_discovery.py:665`: run as root). These four behaviours are **not
verified by this run**.

**Still the owner's:** the Colab and JupyterLab browser check (`docs/colab-release-check.md`),
before tagging.

## Release record: v0.5.2 (12 November 2026)

A G1-style record of the v0.5.2 release candidate (as for v0.5.1 below). It does not decide a
Expand Down
6 changes: 3 additions & 3 deletions notebooks/MorphoBricks_Preprocessing.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@
"from pathlib import Path\n",
"\n",
"# LabConstrictor version identifiers.\n",
"current_version = \"0.5.2\"\n",
"current_version = \"0.5.3\"\n",
"notebook_name = \"MorphoBricks_Preprocessing\"\n",
"\n",
"# A downloaded/Colab notebook installs this immutable source revision.\n",
"RELEASE_TAG = \"v0.5.2\"\n",
"RELEASE_REF = \"cadbed4bb049995b2385e3b8499c8d247d3754bd\"\n",
"RELEASE_TAG = \"v0.5.3\"\n",
"RELEASE_REF = \"14f6b1a0e37474d38f3dd54138bbfdcce69d6e9a\"\n",
"REPOSITORY = \"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/CellMigrationLab/morphobricks-preprocessing.git\"\n",
"EXTRAS = \"notebook\"\n",
"\n",
Expand Down
2 changes: 1 addition & 1 deletion notebooks/notebook_latest_versions.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Latest version of each notebook, read by LabConstrictor's version check by notebook_name.
# Equal to the notebook's current_version and its RELEASE_TAG without the "v" (README, "Notebook").
"MorphoBricks_Preprocessing": "0.5.2"
"MorphoBricks_Preprocessing": "0.5.3"
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "morphobricks-preprocessing"
version = "0.5.2"
version = "0.5.3"
description = "MorphoBricks preprocessing brick: per-plane intensity histograms and normalization methods for microscopy TIFFs."
readme = "README.md"
license = "MIT"
Expand All @@ -26,7 +26,7 @@ dependencies = [
"scipy>=1.10",
# Per-plane histograms, the CSV table, pooling and normalization, pinned to a release tag;
# what each pin brought is in CHANGELOG.md.
"morphobricks-normalization @ git+https://github.com/CellMigrationLab/morphobricks-normalization.git@v0.5.0",
"morphobricks-normalization @ git+https://github.com/CellMigrationLab/morphobricks-normalization.git@v0.6.0",
]

[project.optional-dependencies]
Expand Down
2 changes: 2 additions & 0 deletions tests/test_notebook_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ def test_notebook_uses_stable_page_api_not_a_new_app_method():
"d023ff91289a2d2204e1ae56f7dee114d1a665e4": "PreprocessingNotebook()",
# v0.5.2: the same call.
"cadbed4bb049995b2385e3b8499c8d247d3754bd": "PreprocessingNotebook()",
# v0.5.3: the same call.
"14f6b1a0e37474d38f3dd54138bbfdcce69d6e9a": "PreprocessingNotebook()",
}
CHECKOUT_CALL = "PreprocessingNotebook()"
"""The call of this checkout's code (every run described, no ``describe`` argument)."""
Expand Down
22 changes: 11 additions & 11 deletions tests/test_notebook_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ def setup_environment(tmp_path, monkeypatch):
drive=tmp_path / "content/drive",
namespace={},
versions={
"morphobricks-preprocessing": "0.5.2",
"morphobricks-normalization": "0.5.0",
"morphobricks-preprocessing": "0.5.3",
"morphobricks-normalization": "0.6.0",
"ipywidgets": "7.7.1",
},
)
Expand All @@ -131,7 +131,7 @@ def install(command, *, check):
assert check is True
state.installs.append(command)
state.available.update({*REQUIRED, ZARR})
state.versions["morphobricks-preprocessing"] = "0.5.2"
state.versions["morphobricks-preprocessing"] = "0.5.3"

monkeypatch.setattr(subprocess, "run", install)

Expand Down Expand Up @@ -163,7 +163,7 @@ def checkout_at(path):
package.mkdir(parents=True)
(package / "__init__.py").write_text("", encoding="utf-8")
(path / "pyproject.toml").write_text(
'[project]\nname = "morphobricks-preprocessing"\nversion = "0.5.2"\n',
'[project]\nname = "morphobricks-preprocessing"\nversion = "0.5.3"\n',
encoding="utf-8",
)
return path
Expand All @@ -179,7 +179,7 @@ def test_setup_reuses_the_matching_installed_release(setup_environment, capsys):
"morphobricks_preprocessing.notebook_ui", # cell 3 shows it
]
out = capsys.readouterr().out
assert "Ready: preprocessing 0.5.2" in out
assert "Ready: preprocessing 0.5.3" in out
assert "ipywidgets 7.7.1" in out


Expand All @@ -193,8 +193,8 @@ def test_setup_installs_missing_package_from_the_exact_commit(setup_environment,
target = command[5]
assert target.startswith("morphobricks-preprocessing[notebook] @ git+")
assert target.endswith("@" + namespace["RELEASE_REF"])
assert namespace["RELEASE_REF"] == "cadbed4bb049995b2385e3b8499c8d247d3754bd"
assert "pinned v0.5.2 candidate" in capsys.readouterr().out
assert namespace["RELEASE_REF"] == "14f6b1a0e37474d38f3dd54138bbfdcce69d6e9a"
assert "pinned v0.5.3 candidate" in capsys.readouterr().out


def test_setup_replaces_a_mismatched_installed_release(setup_environment):
Expand Down Expand Up @@ -285,7 +285,7 @@ def test_an_explicit_development_override_uses_the_checkout_and_says_so(
assert sys.path[0] == str(checkout / "src")
assert f"commit {OTHER_COMMIT}, uncommitted changes" in out
assert "DEVELOPMENT OVERRIDE is on" in out
assert "DEVELOPMENT OVERRIDE on" in out.split("Notebook 0.5.2")[1]
assert "DEVELOPMENT OVERRIDE on" in out.split("Notebook 0.5.3")[1]
record = namespace["SETUP_RECORD"]
assert record["checkout_commit"] == OTHER_COMMIT
assert record["checkout_has_uncommitted_changes"] is True
Expand Down Expand Up @@ -428,7 +428,7 @@ def test_cell_1_installs_without_mounting_drive_or_showing_the_app(
assert state.apps == []
assert namespace["SETUP_RECORD"]["release_ref"] == RELEASE_REF
out = capsys.readouterr().out
assert "Ready: preprocessing 0.5.2; normalization 0.5.0; ipywidgets 7.7.1." in out
assert "Ready: preprocessing 0.5.3; normalization 0.6.0; ipywidgets 7.7.1." in out
assert "Next: run cell 2 (Mount Google Drive), then cell 3 (Run preprocessing)." in out


Expand All @@ -444,8 +444,8 @@ def test_running_cell_1_again_installs_nothing_more(setup_environment, capsys):
assert len(state.installs) == 1
assert "Installing notebook requirements" in first
assert "Installing notebook requirements" not in again
assert "Ready: preprocessing 0.5.2" in again
assert state.namespace["SETUP_RECORD"]["preprocessing_version"] == "0.5.2"
assert "Ready: preprocessing 0.5.3" in again
assert state.namespace["SETUP_RECORD"]["preprocessing_version"] == "0.5.3"
assert record["release_ref"] == state.namespace["SETUP_RECORD"]["release_ref"]


Expand Down
Loading