Skip to content

Stage 2: A_* error classes and RunInputs.section_digests() - #229

Merged
guijacquemet merged 2 commits into
mainfrom
claude/a-error-classes
Nov 8, 2026
Merged

guijacquemet merged 2 commits into
mainfrom
claude/a-error-classes

Conversation

@guijacquemet

@guijacquemet guijacquemet commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

What

  • ChannelNotFoundError.code = "A_SCOPE_INCOMPLETE". channel_index now names the image in its message and ends it with the code. The old text matched PHASE.*DNA, ACTIN, and the new text still does.
  • New MaskNotInstancesError (A_MASK_NOT_INSTANCES) and RgbNotSupportedError (A_RGB_NOT_SUPPORTED). Both are RunInputsError subclasses (a ValueError) in run_inputs_error.py, for the bricks to raise. Preprocessing checks neither.
  • ChannelNotFoundError, MaskNotInstancesError, RgbNotSupportedError, DraftRefusedError and MaskUncheckedError are exported at the package top level and in __all__. The last two were not exported before; I added them so the five codes come as one set.
  • RunInputs.section_digests() returns a copy of manifest.experiment_description["section_digests"] (the conditions, channels, repeats and masks digests). It returns None for a run without a description. It reads the manifest only, so it still works if the published file has since changed.
  • Contract §9.1 (RunInputs semantics) and a new §16 row, "Analysis refusals (Stage 2)", plus the CHANGELOG.

Why

Stage 2 design, PR 2 (section 1.1). Owner answer 2: a channel no image has and a channel some images lack follow one rule, with one code that names the images. The classes live here as shared rules (D86) so no brick keeps its own copy of the code. Digests let a brick key a memo or cache on the section it depends on (for example the repeats digest, design PR 12).

Regression evidence

New tests/test_a_error_classes.py (15 tests, 10 of them parametrized over the five classes):

  • Each class is a RunInputsError with its code, is exported, and carries the code in its message.
  • Each class survives a pickle round trip with its code (a worker process may raise them).
  • RunInputsError.code stays None.
  • channel_index raises A_SCOPE_INCOMPLETE naming the image, by Fov and by path.
  • The digests equal the manifest's and the Spec writer's section_digests. The result is a copy, and None without a description.

On main the whole file fails at collection (the new classes and section_digests do not exist; ChannelNotFoundError.code is None), so 0 of 15 run. With the change, 15 pass. tests/test_run_inputs_semantic.py is unchanged and still passes.

Checks

python tools/check.py: ruff check and format clean, 4603 passed, 4 skipped, code rules clean. The 4 skips are environmental: 3 need a case-sensitive-name file system check (test_cli_log.py:165, test_folder_rules.py:201 and :229) and 1 needs file modes to apply (test_discovery.py:665, running as root).

Needs an owner decision

  • "Some images lack it" is a per-request scope fact: "present in k of n acquisitions" with the unit list. channel_index is per image, so it names that one image. The scope-level message and its check belong to the bricks (design PR 10, 16, 17), unless you want a helper here (the Stage 1 channel_availability(), deferred to Stage 3).
  • The two new classes are RunInputsError subclasses, whose docstring says "run preprocessing again". That is the established pattern (DraftRefusedError, MaskUncheckedError), so I followed it. If a brick-raised refusal should not be a RunInputsError, say so.
  • The top-level export of DraftRefusedError and MaskUncheckedError was my addition.
  • CHANGELOG and contract conflicts with Z projection 5/6: the run side (part of #176) #217, List images without header channel names in a described run #226 and the other Stage 2 PRs are expected. Keep both sides.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm

Stage 2 design PR 2. ChannelNotFoundError carries A_SCOPE_INCOMPLETE (owner
answer 2: one rule for a channel no image has and one some images lack; the
message names the image). MaskNotInstancesError (A_MASK_NOT_INSTANCES) and
RgbNotSupportedError (A_RGB_NOT_SUPPORTED) are shared classes for the bricks
to raise, and all five analysis error classes are exported. section_digests()
returns the manifest's description digests, or None without a description.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
@guijacquemet
guijacquemet merged commit 52878fc into main Nov 8, 2026
7 checks passed
@guijacquemet
guijacquemet deleted the claude/a-error-classes branch November 8, 2026 09:19
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