Skip to content

RunInputs.resolve_request: the shared request resolver (contract v1, v0.5.1) - #256

Merged
guijacquemet merged 3 commits into
mainfrom
claude/resolve-request
Nov 10, 2026
Merged

guijacquemet merged 3 commits into
mainfrom
claude/resolve-request

Conversation

@guijacquemet

@guijacquemet guijacquemet commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Target v0.5.1 (the first change after PP-C, merged in main cf69fd5). Implements MorphoBricks-Spec ADR 0002 (AR1-AR11, merged in Spec main 63e567e; checked against 9a03d89). Design: MorphoFeature claude/restart-doc, docs/restart/designs/resolver-design-2026-11-10.md; Stage 2 plan row PP-0.

Summary (by module)

  • run_inputs_request.py (pure): the resolver contract's types and rules. RESOLVER_VERSION = 1; Request(channel_ids, masks, planes, capabilities, device), MaskRequest(mask_id, role, binary_mask_policy), Capabilities(rgb_support, volume_support); ResolvedRequest(selections, refusals, coverage, provenance) with FovSelection, MaskSelection, PlaneSelection, Refusal, RepeatCoverage, Provenance; VolumeNotSupportedError (A_VOLUME_NOT_SUPPORTED); the pure rules (scope_refusal "present in k of n acquisitions", coverage full / partial / unavailable, plane_selections, the mask, RGB and volume refusals, check_request).
  • run_inputs_resolve.py (io): the ResolvingInputs mixin, a base of RunInputs: resolve_request(request) reads the manifest, the published description (SHA-checked) and mask_representation.json once, classifies, and returns the immutable result. A refused request has no selections; every applicable refusal is reported together. It reads no pixel, runs no kernel, writes nothing, converts no mask (a recorded binary_mask_policy is returned as MaskSelection.conversion for the route's shared labeller, CellPatcher's), checks only the device's spelling, and imports no brick, CellPatcher or provider module.
  • fov_identity.py: FovIdentity.image_type (the unit's declared type) for the RGB check. Fov is unchanged (PP-1 adds Fov.image_type).
  • run_inputs.py: one import, one base class. __init__.py: the new names exported. pyproject.toml: the two layer declarations. docs/contract.md §9.1 and §16, CHANGELOG.md Unreleased (v0.5.1) "PP-0".

After PP-C (this update)

  • Merged main cf69fd5 (no textual conflicts). The CHANGELOG entry had landed in the 0.5.0 section; it is now its own "PP-0" section of Unreleased (v0.5.1).
  • Trimmed (AR10): the resolver's undescribed-run branch (description() is None → plain RunInputsError) and its test are removed, with the or () / str() fallbacks for undescribed Fovs. Every run the resolver sees is described: RunInputs.open refuses one without morphobricks.toml with A_DESCRIPTION_REQUIRED (tested in the resolver suite).

Adversarial review against AR1-AR11 (findings and fixes)

  1. Provenance defaulted silently (experiment_description or {}, sha256 → "", digests → ()): now read strictly from the manifest's record, which every version-4 manifest carries.
  2. A request with a list was recorded mutable. Request(["DNA"]) was resolved and recorded as given in Provenance.request and in every FovSelection.channel_ids, so the "immutable" result could change after the fact and was unhashable; Request("DNA") was read as the channels D, N, A; a truthy non-bool capability (rgb_support=1, "no") granted support. All are now ValueErrors (malformed request, not a refusal), with tests.
  3. The import test missed transitive imports and skimage. The AST scan covers only the two modules' own imports, and the sys.modules check sliced off skimage with scipy. A new test imports the resolver in a fresh interpreter and bars every brick, CellPatcher, provider, torch and skimage module (scipy is preprocessing's own dependency).
  4. Gaps closed with tests: A_MASK_UNCHECKED for 1 of 4 files (only "4 of 4" was tested); a description changed since the run is refused, not resolved; device spellings CUDA and cuda:0 refused.
  5. Checked and sound, no change: classification only (AR1, AR7); no writes, no pixel read (monkeypatched asarray, read_plane, read_mask); refusals all reported in contract order with "k of n" from the run's images (masks: of the recorded files); per-FOV channel_indices by canonical ID (v13 plus a reordered-channel test; a real mixed-layout run cannot be written yet, preprocessing processes one layout per experiment); mask policy returned, not applied (AR5); volume_support refusing by default, a one-slice volume equal to the plane selection (AR3/AR8); device spelling checked and recorded, availability left to the consumer (AR9); RESOLVER_VERSION = 1; provenance = description SHA-256 + section digests + request.
  6. Noted, not changed (PP-C's behaviour): a morphobricks.toml deleted after RunInputs.open gives description()'s plain RunInputsError ("is gone: the run recorded a description"), not A_DESCRIPTION_REQUIRED.

Tests

python tools/check.py on a fresh clone (Python 3.13, Linux, .[dev,notebook,zarr]): ruff check . clean; ruff format --check . 411 files already formatted; code rules against the baseline: pass; pytest: 5015 passed, 4 skipped (all environment skips: three "this file system tells names apart by case", one "running with permissions that ignore file modes"; none in a Spec module). Spec modules (tests/test_spec_*.py): 1708 passed, 0 skipped; test_spec_resolve_request.py 43 tests (registered in spec_gate.EXPECTED_MODULES).

Fixes nothing yet (#200 and fiberfeatures #195 are fixed by the consuming PRs); prepares P24, P27, P28, SI1.

https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc


Generated by Claude Code

One interpreter of a described run for every analysis route (MorphoBricks architecture
decision of 10 November 2026, Spec ADR 0002). A Request (canonical channel IDs; masks by ID
with a role and the explicit binary_mask_policy; plane or volume; the consumer's Capabilities;
the explicit device) resolves to an immutable, picklable ResolvedRequest: one FovSelection per
position with the C index of every requested channel in that image, the declared masks, the
planes to read and the canonical IDs beside the unchanged key; every applicable refusal
(A_SCOPE_INCOMPLETE "present in k of n", A_MASK_UNCHECKED "k of n files", A_MASK_NOT_INSTANCES,
A_RGB_NOT_SUPPORTED, and the contract's A_VOLUME_NOT_SUPPORTED, refusing Z-volumes unless
volume_support is declared); the coverage of every biological repeat (full / partial /
unavailable); the provenance (resolver version, the manifest's description SHA-256 and section
digests, the request). A refused request selects nothing.

The resolver reads no pixel, runs no kernel, writes nothing, converts no mask (the recorded
policy is returned for the consumer's shared labeller) and imports no brick, CellPatcher or
provider module. FovIdentity gains the unit's image_type; Fov is unchanged (PP-1). Runs without
a description are refused by it and otherwise unchanged.

Tests: tests/test_spec_resolve_request.py (32, Spec fixtures v01 v08 v09 v13 v28 v33 through
make_described_run; a Spec module under the no-skip gate). Contract section 9.1 and 16,
CHANGELOG. New modules declared pure (run_inputs_request) and io (run_inputs_resolve).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
No textual conflicts; the CHANGELOG entry is moved to Unreleased (v0.5.1) in the next commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…docs

- resolve_request: the undescribed-run branch is removed (RunInputs.open
  already refuses with A_DESCRIPTION_REQUIRED); the `or ()` / str() fallbacks
  for hand-built Fovs go; the provenance reads the manifest's description
  record strictly instead of defaulting to an empty SHA-256 and no digests.
- check_request: channels/masks must be tuples (of str IDs, of MaskRequest)
  and capabilities bools; a list left the recorded request and the result
  mutable and unhashable, a str was read one ID per character, a truthy
  non-bool silently granted rgb/volume support. ValueError, not a refusal.
- Tests: the undescribed-run test is removed; added A_DESCRIPTION_REQUIRED
  at open, A_MASK_UNCHECKED "1 of 4 files", a changed description refused,
  device spellings CUDA / cuda:0, the shape checks, and a fresh-interpreter
  import scan (the old sys.modules check skipped skimage by a slice).
- CHANGELOG: the entry moves from 0.5.0 to Unreleased (v0.5.1) as PP-0;
  contract §9.1 and §16 updated (no undescribed path; the resolver finds
  A_MASK_NOT_INSTANCES and A_RGB_NOT_SUPPORTED).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
@guijacquemet guijacquemet changed the title DRAFT (v0.5.1): RunInputs.resolve_request, the shared request resolver (contract v1) RunInputs.resolve_request: the shared request resolver (contract v1, v0.5.1) Nov 10, 2026
@guijacquemet
guijacquemet marked this pull request as ready for review November 10, 2026 09:47
@guijacquemet
guijacquemet merged commit 43e3a93 into main Nov 10, 2026
7 checks passed
@guijacquemet
guijacquemet deleted the claude/resolve-request branch November 10, 2026 10:05
@guijacquemet guijacquemet mentioned this pull request Nov 10, 2026
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