Failure scenario
Run odin validate or the legacy plan CLI's run command against syntactically valid YAML/JSON with a wrong structure, for example:
name: bad
steps: not-a-list
or a step with params: [], missing id, nonnumeric timeout, or a top-level YAML scalar.
load_plan() performs mapping/list operations and numeric casts without shape validation, producing uncaught AttributeError, TypeError, KeyError, or ValueError. Both CLI paths call it outside any error handler, so the command prints a Python traceback and exits 1 rather than reporting the invalid fields and returning the documented validation exit code 2. A plan with params: [] can even load successfully and then fail later in parameter resolution.
Sites
src/odin/plan_loader.py:29-33 passes arbitrary decoded YAML/JSON directly to _from_dict().
src/odin/plan_loader.py:40-68 assumes mapping/list/step shapes and coerces values without structured errors.
src/odin/cli.py:57-70 calls load_plan() outside the PlanValidationError handling boundary.
Expected result
Validate the complete decoded plan schema before constructing dataclasses and convert all user-input failures into clear field-specific validation messages with exit code 2 and no traceback.
Failure scenario
Run
odin validateor the legacy plan CLI'sruncommand against syntactically valid YAML/JSON with a wrong structure, for example:or a step with
params: [], missingid, nonnumerictimeout, or a top-level YAML scalar.load_plan()performs mapping/list operations and numeric casts without shape validation, producing uncaughtAttributeError,TypeError,KeyError, orValueError. Both CLI paths call it outside any error handler, so the command prints a Python traceback and exits 1 rather than reporting the invalid fields and returning the documented validation exit code 2. A plan withparams: []can even load successfully and then fail later in parameter resolution.Sites
src/odin/plan_loader.py:29-33passes arbitrary decoded YAML/JSON directly to_from_dict().src/odin/plan_loader.py:40-68assumes mapping/list/step shapes and coerces values without structured errors.src/odin/cli.py:57-70callsload_plan()outside thePlanValidationErrorhandling boundary.Expected result
Validate the complete decoded plan schema before constructing dataclasses and convert all user-input failures into clear field-specific validation messages with exit code 2 and no traceback.