Skip to content

Refuse multi-class predictions in the postprocess CLI - #428

Merged
gbeane merged 1 commit into
mainfrom
fix/postprocessing-cli-multiclass
Aug 7, 2026
Merged

Refuse multi-class predictions in the postprocess CLI#428
gbeane merged 1 commit into
mainfrom
fix/postprocessing-cli-multiclass

Conversation

@gbeane

@gbeane gbeane commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

jabs-cli postprocess cannot process a multi-class prediction file, and fails in the worst way: an unhandled ValueError about array shapes, after leaving a partially written output file behind.

Found while reviewing #427, where the justification for a change cited the GUI's binary-only gate (MultiClassClassifyStrategy.postprocess_identity returns None). The CLI has no equivalent gate.

What happens today

$ jabs-cli postprocess --config cfg.json --behavior multiclass --output out.h5 preds.h5
ValueError: probabilities shape (2, 50, 3) does not match expected shape (2, 50)

run_apply_postprocessing loads the predictions (including class_names), runs every stage over the multi-class vectors, then rebuilds BehaviorPrediction without passing class_names through. The dataclass validator infers the binary probability shape from a None class_names and rejects the 3-D array. The construction is not inside a try, so the user gets a raw traceback whose message says nothing about multi-class files.

Two consequences beyond the traceback:

  • out.h5 is created before the failure (the source file is copied up front), so the run leaves an output file with no postprocessed data in it.
  • The stages had already run on the multi-class vectors by then. They classify each frame as behavior or not behavior (states == ClassLabels.BEHAVIOR), so those results were meaningless — only the crash kept them off disk.

Change

  • Refuse multi-class input, checked in the existing pre-flight validation block alongside the "behavior not found in file" check, so it fails before the output file is created:

    Error: Post-processing supports binary predictions only, but the following
    behavior(s) hold multi-class predictions: 'multiclass'.
    

    A new _multiclass_behaviors() helper identifies them by the presence of a class_names dataset, mirroring how _list_behaviors() inspects the file. Only the behaviors named in the config are checked, so a binary behavior still processes normally from a file that also contains multi-class predictions.

  • Pass class_names through when rebuilding BehaviorPrediction. It is None for every binary file, so this is a no-op today, but it closes a latent hazard: the HDF5 adapter deletes an existing class_names dataset when the field is None, which would have silently stripped that metadata had the validator not raised first.

Refusing rather than supporting is deliberate: making the stages meaningful for multi-class predictions is a feature (they would need to know which class is the behavior, or operate per class), not a bug fix. This matches the GUI, which declines the same work.

Verification

Before and after, same input, through the real CLI:

exit message output file
before 1 ValueError: probabilities shape (2, 50, 3) does not match expected shape (2, 50) (traceback) created, unusable
after 1 Error: Post-processing supports binary predictions only... not created

Four regression tests: the refusal and its message, the file being left untouched, a multi-class behavior named alongside binary ones aborting before any output, and a binary behavior still processing from a file that also holds multi-class predictions (asserting the untouched group keeps its class names).

ruff check, ruff format, pytest (1010 passed) and pytest packages/jabs-behavior/tests (101 passed) are clean.

@gbeane
gbeane requested a lite review from Copilot August 7, 2026 17:28
@gbeane gbeane self-assigned this Aug 7, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the jabs-cli postprocess pipeline by explicitly refusing multi-class prediction inputs, preventing confusing shape-validation crashes and avoiding creation of partially written output files. It aligns CLI behavior with the GUI’s existing “binary-only” postprocessing gate.

Changes:

  • Add a pre-flight validation (_multiclass_behaviors) to detect multi-class behaviors (via class_names) and fail early with a clear ClickException.
  • Preserve class_names when rebuilding and saving BehaviorPrediction to avoid metadata being dropped during writes.
  • Add regression tests covering refusal behavior, output-file non-creation, mixed binary+multiclass files, and preservation of multi-class metadata for untouched behaviors.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/jabs/scripts/cli/postprocessing.py Adds early multi-class refusal and ensures class_names is carried through when rewriting predictions.
tests/scripts/test_apply_postprocessing.py Adds regression tests for multi-class refusal, file safety (no partial outputs), and mixed-content prediction files.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@gbeane
gbeane merged commit f155a6e into main Aug 7, 2026
6 checks passed
@gbeane
gbeane deleted the fix/postprocessing-cli-multiclass branch August 7, 2026 17:34
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