diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py
index c5560d3d..89dc766a 100644
--- a/core/lls_core/cmds/__main__.py
+++ b/core/lls_core/cmds/__main__.py
@@ -36,20 +36,17 @@ class CliDeskewDirection(StrEnum):
     "input_image": ["input_image"],
     "angle": ["angle"],
     "skew": ["skew"],
-    "pixel_sizes": ["physical_pixel_sizes"],
-    "rois": ["crop", "roi_list"],
-    "roi_indices": ["crop", "roi_subset"],
-    "z_start": ["crop", "z_range", 0],
-    "z_end": ["crop", "z_range", 1],
+    "physical_pixel_sizes": ["physical_pixel_sizes"],
+    "roi_list": ["crop", "roi_list"],
+    "roi_subset": ["crop", "roi_subset"],
+    "z_range": ["crop", "z_range"],
     "decon_processing": ["deconvolution", "decon_processing"],
     "psf": ["deconvolution", "psf"],
-    "psf_num_iter": ["deconvolution", "psf_num_iter"],
+    "decon_num_iter": ["deconvolution", "decon_num_iter"],
     "background": ["deconvolution", "background"],
     "workflow": ["workflow"],
-    "time_start": ["time_range", 0],
-    "time_end": ["time_range", 1],
-    "channel_start": ["channel_range", 0],
-    "channel_end": ["channel_range", 1],
+    "time_range": ["time_range"],
+    "channel_range": ["channel_range"],
     "save_dir": ["save_dir"],
     "save_name": ["save_name"],
     "save_type": ["save_type"],
@@ -144,35 +141,30 @@ def process(
     input_image: Path = Argument(None, help="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi", show_default=False),
     skew: CliDeskewDirection = field_from_model(DeskewParams, "skew"),# DeskewParams.make_typer_field("skew"),
     angle: float = field_from_model(DeskewParams, "angle") ,
-    pixel_sizes: Tuple[float, float, float] = field_from_model(DeskewParams, "physical_pixel_sizes", extra_description="This takes three arguments, corresponding to the Z, Y and X pixel dimensions respectively", default=(
+    physical_pixel_sizes: Tuple[float, float, float] = field_from_model(DeskewParams, "physical_pixel_sizes", extra_description="This takes three arguments, corresponding to the Z, Y and X pixel dimensions respectively", default=(
         DefinedPixelSizes.get_default("Z"),
         DefinedPixelSizes.get_default("Y"),
         DefinedPixelSizes.get_default("X")
     )),
 
-    rois: List[Path] = field_from_model(CropParams, "roi_list", description="A list of paths pointing to regions of interest to crop to, in ImageJ format."), #Option([], help="A list of paths pointing to regions of interest to crop to, in ImageJ format."),
-    roi_indices: List[int] = field_from_model(CropParams, "roi_subset"),
-    # Ideally this and other range values would be defined as Tuples, but these seem to be broken: https://github.com/tiangolo/typer/discussions/667
-    z_start: Optional[int] = Option(0, help="The index of the first Z slice to use. All prior Z slices will be discarded.", show_default=False),
-    z_end: Optional[int] = Option(None, help="The index of the last Z slice to use. The selected index and all subsequent Z slices will be discarded. Defaults to the last z index of the image.", show_default=False),
-
+    roi_list: List[Path] = field_from_model(CropParams, "roi_list"), 
+    roi_subset: List[int] = field_from_model(CropParams, "roi_subset"),
+    z_range: Optional[Tuple[int,int]] = field_from_model(CropParams, "z_range", show_default=False),
+    
     enable_deconvolution: bool = Option(False, "--deconvolution/--disable-deconvolution", rich_help_panel="Deconvolution"),
     decon_processing: DeconvolutionChoice = field_from_model(DeconvolutionParams, "decon_processing", rich_help_panel="Deconvolution"),
-    psf: List[Path] = field_from_model(DeconvolutionParams, "psf", description="One or more paths pointing to point spread functions to use for deconvolution. Each file should in a standard image format (.czi, .tiff etc), containing a 3D image array. This option can be used multiple times to provide multiple PSF files.", rich_help_panel="Deconvolution"),
-    psf_num_iter: int = field_from_model(DeconvolutionParams, "psf_num_iter", rich_help_panel="Deconvolution"),
+    psf: List[Path] = field_from_model(DeconvolutionParams, "psf", rich_help_panel="Deconvolution"),
+    decon_num_iter: int = field_from_model(DeconvolutionParams, "decon_num_iter", rich_help_panel="Deconvolution"),
     background: str = field_from_model(DeconvolutionParams, "background", rich_help_panel="Deconvolution"),
 
-    time_start: Optional[int] = Option(0, help="Index of the first time slice to use (inclusive). Defaults to the first time index of the image.", rich_help_panel="Output"),
-    time_end: Optional[int] = Option(None, help="Index of the first time slice to use (exclusive). Defaults to the last time index of the image.", show_default=False, rich_help_panel="Output"),
-
-    channel_start: Optional[int] = Option(0, help="Index of the first channel slice to use (inclusive). Defaults to the first channel index of the image.", rich_help_panel="Output"),
-    channel_end: Optional[int] = Option(None, help="Index of the first channel slice to use (exclusive). Defaults to the last channel index of the image.", show_default=False, rich_help_panel="Output"),
-    
+    time_range: Optional[Tuple[int,int]] = field_from_model(OutputParams, "time_range", rich_help_panel="Output"),
+    channel_range: Optional[Tuple[int,int]] = field_from_model(OutputParams,"channel_range", rich_help_panel="Output"),
+        
     save_dir: Path = field_from_model(OutputParams, "save_dir", rich_help_panel="Output"),
     save_name: Optional[str] = field_from_model(OutputParams, "save_name", rich_help_panel="Output"),
     save_type: SaveFileType = field_from_model(OutputParams, "save_type", rich_help_panel="Output"),
 
-    workflow: Optional[Path] = Option(None, help="Path to a Napari Workflow file, in YAML format. If provided, the configured desekewing processing will be added to the chosen workflow.", show_default=False),
+    workflow: Optional[Path] = field_from_model(LatticeData, "workflow", show_default=False),
     json_config: Optional[Path] = Option(None, show_default=False, help="Path to a JSON file from which parameters will be read."),
     yaml_config: Optional[Path] = Option(None, show_default=False, help="Path to a YAML file from which parameters will be read."),
 
diff --git a/core/lls_core/cropping.py b/core/lls_core/cropping.py
index 2f0c35a9..f57f2894 100644
--- a/core/lls_core/cropping.py
+++ b/core/lls_core/cropping.py
@@ -43,7 +43,7 @@ def read_imagej_roi(roi_path: PathLike) -> List[Roi]:
     if roi_path.suffix == ".zip":
         ij_roi = read_roi_zip(roi_path)
     elif roi_path.suffix == ".roi":
-        ij_roi = read_roi_file(roi_path)
+        ij_roi = read_roi_file(str(roi_path))
     else:
         raise Exception("ImageJ ROI file needs to be a zip/roi file")
 
diff --git a/core/lls_core/models/deconvolution.py b/core/lls_core/models/deconvolution.py
index c628afe8..6bfc32ab 100644
--- a/core/lls_core/models/deconvolution.py
+++ b/core/lls_core/models/deconvolution.py
@@ -22,7 +22,7 @@ class DeconvolutionParams(FieldAccessModel):
         default=[],
         description="List of Point Spread Functions to use for deconvolution. Each of which should be a 3D array. Each PSF can also be provided as a `str` path, in which case they will be loaded from disk as images."
     )
-    psf_num_iter: NonNegativeInt = Field(
+    decon_num_iter: NonNegativeInt = Field(
         default=10,
         description="Number of iterations to perform in deconvolution"
     )
diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py
index d432443f..de741b47 100644
--- a/core/lls_core/models/deskew.py
+++ b/core/lls_core/models/deskew.py
@@ -191,7 +191,7 @@ def read_image(cls, values: dict):
 
         # If the image was convertible to AICSImage, we should use the metadata from there
         if aics:
-            values["input_image"] = aics.xarray_dask_data
+            values["input_image"] = aics.xarray_dask_data 
             # Take pixel sizes from the image metadata, but only if they're defined
             # and only if we don't already have them
             if all(size is not None for size in aics.physical_pixel_sizes) and values.get("physical_pixel_sizes") is None:
diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py
index f1322d84..d7e2c98b 100644
--- a/core/lls_core/models/lattice_data.py
+++ b/core/lls_core/models/lattice_data.py
@@ -61,6 +61,7 @@ def read_image(cls, values: dict):
         from lls_core.types import is_pathlike
         from pathlib import Path
         input_image = values.get("input_image")
+        logger.info(f"Processing File {input_image}") # this is handy for debugging
         if is_pathlike(input_image):
             if values.get("save_name") is None:
                 values["save_name"] = Path(values["input_image"]).stem
@@ -76,6 +77,21 @@ def read_image(cls, values: dict):
         # Use the Deskew version of this validator, to do the actual image loading
         return super().read_image(values)
 
+    @validator("input_image", pre=True, always=True)
+    def incomplete_final_frame(cls, v: DataArray) -> Any:
+        """
+        Check final frame, if acquisition is stopped halfway through it causes failures
+        This validator will remove a bad final frame
+        """
+        final_frame = v.isel(T=-1,C=-1, drop=True)
+        try:
+            final_frame.compute()
+        except ValueError:
+            logger.warning("Final frame is borked. Acquisition probably stopped prematurely. Removing final frame.")
+            v = v.drop_isel(T=-1)
+        return v
+        
+
     @validator("workflow", pre=True)
     def parse_workflow(cls, v: Any):
         # Load the workflow from disk if it was provided as a path
@@ -336,24 +352,6 @@ def generate_workflows(
             # The user can use any of these arguments as inputs to their tasks
             yield lattice_slice.copy_with_data(user_workflow)
 
-    def check_incomplete_acquisition(self, volume: ArrayLike, time_point: int, channel: int):
-        """
-        Checks for a slice with incomplete data, caused by incomplete acquisition
-        """
-        import numpy as np
-        if not isinstance(volume, DaskArray):
-            return volume
-        orig_shape = volume.shape
-        raw_vol = volume.compute()
-        if raw_vol.shape != orig_shape:
-            logger.warn(f"Time {time_point}, channel {channel} is incomplete. Actual shape {orig_shape}, got {raw_vol.shape}")
-            z_diff, y_diff, x_diff = np.subtract(orig_shape, raw_vol.shape)
-            logger.info(f"Padding with{z_diff,y_diff,x_diff}")
-            raw_vol = np.pad(raw_vol, ((0, z_diff), (0, y_diff), (0, x_diff)))
-            if raw_vol.shape != orig_shape:
-                raise Exception(f"Shape of last timepoint still doesn't match. Got {raw_vol.shape}")
-            return raw_vol
-
     @property
     def deskewed_volume(self) -> DaskArray:
         from dask.array import zeros
@@ -372,7 +370,7 @@ def _process_crop(self) -> Iterable[ImageSlice]:
             deconv_args: dict[Any, Any] = {}
             if self.deconvolution is not None:
                 deconv_args = dict(
-                    num_iter = self.deconvolution.psf_num_iter,
+                    num_iter = self.deconvolution.decon_num_iter,
                     psf = self.deconvolution.psf[slice.channel].to_numpy(),
                     decon_processing=self.deconvolution.decon_processing
                 )
@@ -417,13 +415,13 @@ def _process_non_crop(self) -> Iterable[ImageSlice]:
                         dxdata=self.dx,
                         dzpsf=self.dz,
                         dxpsf=self.dx,
-                        num_iter=self.deconvolution.psf_num_iter
+                        num_iter=self.deconvolution.decon_num_iter
                     )
                 else:
                     data = skimage_decon(
                         vol_zyx=data,
                         psf=self.deconvolution.psf[slice.channel].to_numpy(),
-                        num_iter=self.deconvolution.psf_num_iter,
+                        num_iter=self.deconvolution.decon_num_iter,
                         clip=False,
                         filter_epsilon=0,
                         boundary='nearest'
diff --git a/core/tests/test_arg_parser.py b/core/tests/test_arg_parser.py
index 26bb1e20..1505be06 100644
--- a/core/tests/test_arg_parser.py
+++ b/core/tests/test_arg_parser.py
@@ -10,9 +10,9 @@ def test_voxel_parsing():
     parser = command.make_parser(ctx)
     args, _, _ = parser.parse_args(args=[
             "process",
-            "input",
+            "input-image",
             "--save-name", "output",
             "--save-type", "tiff",
-            "--pixel-sizes", "1", "1", "1"
+            "--physical-pixel-sizes", "1", "1", "1"
     ])
-    assert args["pixel_sizes"] == ("1", "1", "1")
+    assert args["physical_pixel_sizes"] == ("1", "1", "1")
diff --git a/core/tests/test_cli.py b/core/tests/test_cli.py
index 7d96b33f..38867aba 100644
--- a/core/tests/test_cli.py
+++ b/core/tests/test_cli.py
@@ -62,7 +62,7 @@ def assert_h5(output_dir: Path):
         [
             [["--save-type", "h5"], assert_h5],
             [["--save-type", "tiff"], assert_tiff],
-            [["--save-type", "tiff", "--time-start", "0", "--time-end", "1"], assert_tiff],
+            [["--save-type", "tiff", "--time-range", "0", "1"], assert_tiff],
         ]
 )
 def test_batch_deskew(flags: List[str], check_fn: Callable[[Path], None]):
diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py
index 2360c69f..c46e1b59 100644
--- a/plugin/napari_lattice/fields.py
+++ b/plugin/napari_lattice/fields.py
@@ -375,7 +375,7 @@ class DeconvolutionFields(NapariFieldGroup):
         tooltip="PSFs must be in the same order as the image channels",
         layout="vertical"
     )
-    psf_num_iter = field(int, label = "Number of Iterations")
+    decon_num_iter = field(int, label = "Number of Iterations")
     background = field(ComboBox).with_choices(
         [it.value for it in BackgroundSource]
     ).with_options(label="Background")
@@ -397,7 +397,7 @@ def _enable_custom_background(self, background: str) -> bool:
         fields = [
             decon_processing,
             psf,
-            psf_num_iter,
+            decon_num_iter,
             background
         ]
     )
@@ -418,7 +418,7 @@ def _make_model(self) -> Optional[DeconvolutionParams]:
             background=background,
             # Filter out unset PSFs
             psf=[psf for psf in self.psf.value if psf.is_file()],
-            psf_num_iter=self.psf_num_iter.value
+            decon_num_iter=self.decon_num_iter.value
         )
 
 @magicclass