Skip to content

PP-1: Fov.image_type per position, partial-mask helper, pinned brick surface - #263

Merged
guijacquemet merged 1 commit into
mainfrom
claude/pp-1
Nov 10, 2026
Merged

guijacquemet merged 1 commit into
mainfrom
claude/pp-1

Conversation

@guijacquemet

Copy link
Copy Markdown
Contributor

Stage 2 plan row PP-1 (MorphoBricks-Spec ADR 0002 AR8; contract §9.1 and §16), on main 43e3a93 (PP-C + PP-0). Part of v0.5.1. Do not merge before the independent review.

What changes

  1. Fov.image_type, per position. "rgb" or "channels" (a unit with no image_type, experiment-v1 §4.4b), from the published description only, never from a header, never None on a Fov from fovs(). One source: FovIdentity.image_type (fov_identity.declared_image_type), which Fov and the resolver's FovSelection both carry. resolve_request now reads fov.image_type instead of looking the unit up a second time.
    • FovIdentity.image_type / FovSelection.image_type become str. They are "channels" where PP-0 gave None. RESOLVER_VERSION stays 1 because the resolver is not released before v0.5.1 and nothing consumes it yet. New constants: run_inputs_request.CHANNELS and IMAGE_TYPES.
  2. Removed RunInputs.image_type() (breaking). It returned the manifest's run-level type from the command-line review, null on every described run, so it hid per-unit types. The manifest field stays as a record.
  3. Partial-mask helper. make_described_run(..., masks=IDS, mask_images=PATHS) and write_described_tree(..., mask_images=) write masks only for the named images. testing.described_images(description) lists the tree's paths before anything is written. It is explicit only: no random subsets. These raise ValueError before any write:
    • a path outside the tree,
    • an empty subset,
    • a bare string,
    • a subset given without masks.
  4. Memo (measured first). The description was already parsed once per content (description_consume._parsed). The cost was in read_checked, which decoded the whole mask_representation.json on every mask_check_status call. It now reads the bytes each call and reuses the parse for identical bytes (lru_cache). The result is identical, a rewritten file is parsed again, and RunInputs holds no memo and pickles as before.
    • Measured on a 10,000-position v09 run with Nuclei masks: 1,000 mask_check_status calls took 6.6 s before and 0.85 s after, with the same answers and the same resolve_request result.
    • Found and left alone: resolve_request still takes about 3 s there, and mask_check_status over all 10,000 positions about 12 s. Both come from linear scans per file (file_status, _mask_entry), not from the description.
  5. Pinned API surface: tests/test_brick_api_surface.py covers the RunInputs public methods and their signatures, the fields and types of Fov, RunInputs, the resolver records and CleanRun, RESOLVER_VERSION, make_described_run, make_clean_run and described_images.

Tests

  • New Spec module tests/test_spec_fov_image_type.py, added to spec_gate.EXPECTED_MODULES:
    • v28 is rgb on all 32 FOVs; v13 and v01 are channels.
    • v29's RGB unit and channels unit keep their own types through the identity. The runtime refuses that mixed layout (R_HETEROGENEOUS_LAYOUT_NOT_ENABLED), so the Fovs are checked on one run of each type.
    • v09 built by the helper with masks on 2 of 6 gives A_SCOPE_INCOMPLETE "present in 2 of 6" and rep1 partial through resolve_request, and gives a result equal to PP-0's hand-built tree.
    • The helper's refusals.
  • tests/test_checked_status_memo.py: the parse is reused only for the same bytes; a mask step run after open is seen; the pickle is unchanged.
  • PP-0's v09 test now uses the helper; its image_type is None assertions now expect "channels".

Gate

python tools/check.py: ruff check and format clean, code rules exit 0 (baseline not grown), 5069 passed, 4 skipped (filesystem and permission skips only). The Spec modules have 0 skips: tests/test_spec_*.py gives 1722 passed.

Downstream

CellPatcher main (347c61d) calls inputs.image_type() in run_open.py:365, normalization.py:188 and notebook_session.py:160. Its v0.5.1 pin (CP-4) moves to Fov.image_type. No other brick on main reads it: MorphoFeature, MorphoRelate, MorphoObjects, MorphoEmbed, fiberfeatures, MorphoCoverage and normalization were checked.


Generated by Claude Code

- Fov.image_type: the unit's declared "rgb" or "channels" from the published
  description, through FovIdentity (one source for Fov and FovSelection);
  RunInputs.image_type() (run-wide, null on described runs) removed.
- make_described_run(mask_images=...) and testing.described_images: masks for a
  declared subset of the images (Spec v09, 2 of 6).
- read_checked reuses the parse of the same status-file bytes (measured: 1,000
  mask_check_status calls on a 10,000-position run 6.6 s -> 0.85 s, identical).
- tests/test_brick_api_surface.py pins the brick-facing names and types; new Spec
  module tests/test_spec_fov_image_type.py.
- Contract 9.1/16 and CHANGELOG Unreleased (v0.5.1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
@guijacquemet
guijacquemet merged commit 2a05ff6 into main Nov 10, 2026
7 checks passed
@guijacquemet
guijacquemet deleted the claude/pp-1 branch November 10, 2026 11:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants