diff --git a/extensions/chaeo/batch_jobs/20231028_Porto_PA.py b/extensions/chaeo/batch_jobs/int_test_20231028_Porto_PA.py
similarity index 58%
rename from extensions/chaeo/batch_jobs/20231028_Porto_PA.py
rename to extensions/chaeo/batch_jobs/int_test_20231028_Porto_PA.py
index 7e02c856ed24e7cb9e727c12f02dcb3687cc8e22..c6d4284248c6da3f34364fd3d9bb08f018937eeb 100644
--- a/extensions/chaeo/batch_jobs/20231028_Porto_PA.py
+++ b/extensions/chaeo/batch_jobs/int_test_20231028_Porto_PA.py
@@ -2,7 +2,8 @@ from pathlib import Path
 
 from model_server.util import autonumber_new_directory, get_matching_files, loop_workflow
 from extensions.chaeo.ecotaxa import write_ecotaxa_tsv_chunked_subdirectories
-from extensions.chaeo.workflows import export_patches_from_multichannel_zstack
+from extensions.chaeo.params import ZMaskExportParams
+from extensions.chaeo.workflows import infer_object_map_from_zstack
 from extensions.ilastik.models import IlastikPixelClassifierModel
 
 
@@ -17,6 +18,23 @@ if __name__ == '__main__':
 
     px_ilp = Path('c:/Users/rhodes/projects/proj0011-plankton-seg/exp0017/pxAF405_dim8bit.ilp').__str__()
 
+    export_params = {
+        'pixel_probabilities': True,
+        # 'patches_3d': {},
+        'patches_2d_for_training': {
+            # 'draw_bounding_box': False,
+        },
+        'patches_2d_for_annotation': {
+            'draw_bounding_box': True,
+            'rgb_overlay_channels': (1, None, None),
+            'rgb_overlay_weights': (0.2, 1.0, 1.0),
+        },
+        'annotated_z_stack': {
+            'draw_label': True
+        }
+    }
+    ZMaskExportParams(**export_params)
+
     params = {
         'pxmap_threshold': 0.25,
         'pxmap_foreground_channel': 0,
@@ -26,17 +44,19 @@ if __name__ == '__main__':
         'zmask_type': 'boxes',
         'zmask_filters': {'area': (1e3, 1e8)},
         'zmask_expand_box_by': (128, 3),
-        'export_pixel_probabilities': True,
-        'export_2d_patches_for_training': True,
-        'draw_bounding_box_on_2d_patch': True,
-        'export_2d_patches_for_annotation': True,
-        'export_3d_patches': False,
-        'export_annotated_zstack': True,
-        'export_patch_masks': True,
         'zmask_clip': 0.01,
-        'rgb_overlay_channels': (1, None, None),
-        'rgb_overlay_weights': (0.2, 1.0, 1.0),
-        'draw_label_on_zstack': True,
+        # 'export_pixel_probabilities': True,
+        # 'export_2d_patches_for_training': True,
+        # 'draw_bounding_box_on_2d_patch': True,
+        # 'export_2d_patches_for_annotation': True,
+        # 'export_3d_patches': False,
+        # 'export_annotated_zstack': True,
+        # 'export_patch_masks': True,
+
+        # 'rgb_overlay_channels': (1, None, None),
+        # 'rgb_overlay_weights': (0.2, 1.0, 1.0),
+        # 'draw_label_on_zstack': True,
+        'exports': ZMaskExportParams(**export_params),
     }
 
     input_files = get_matching_files(where_czi, 'czi', coord_filter={})
@@ -44,7 +64,7 @@ if __name__ == '__main__':
     loop_workflow(
         input_files,
         where_output,
-        export_patches_from_multichannel_zstack,
+        infer_object_map_from_zstack,
         [IlastikPixelClassifierModel(params={'project_file': Path(px_ilp)})],
         params,
         catch_and_continue=False,
diff --git a/extensions/chaeo/params.py b/extensions/chaeo/params.py
index 02a500efcc615328ab82727c9e33d6c8773c8806..1d03cef549d348f015152b5f542999670d8710c6 100644
--- a/extensions/chaeo/params.py
+++ b/extensions/chaeo/params.py
@@ -8,7 +8,7 @@ class PatchParams(BaseModel):
     draw_mask: bool = False
     rescale_clip: float = 0.001
     focus_metric: str = 'max_sobel'
-    rgb_overlay_channels: List[int] = (None, None, None),
+    rgb_overlay_channels: List[Union[int, None]] = (None, None, None),
     rgb_overlay_weights: List[float] = (1.0, 1.0, 1.0)
 
 class AnnotatedZStackParams(BaseModel):
diff --git a/extensions/chaeo/workflows.py b/extensions/chaeo/workflows.py
index 4938375657a519c66d4a472c4d9a13c3969d7d71..7dfe5b8415167d680913c161d2e11fe71f886504 100644
--- a/extensions/chaeo/workflows.py
+++ b/extensions/chaeo/workflows.py
@@ -317,6 +317,7 @@ def infer_object_map_from_zstack(
         **kwargs
     )
 
+    # TODO: make this a method of ZMaskObjectTable class
     # extract masks
     patch_masks_acc = get_patch_masks_from_zmask_meta(
         stack,
@@ -324,6 +325,7 @@ def infer_object_map_from_zstack(
         **kwargs
     )
 
+    # TODO: add ZMaskObjectTable method to apply object classification results as new DataFrame column
     # send patches and mask stacks to object classifier
     result_acc, _ = object_classifier.infer(patches_acc, patch_masks_acc)
 
@@ -341,6 +343,7 @@ def infer_object_map_from_zstack(
         output_map[labels_map == object_id] = object_class
         meta.append({'object_id': ii, 'object_class': object_id})
 
+    # TODO: add ZMaskObjectTable method to export object map
     output_path = Path(output_folder_path) / ('obj_classes_' + (fstem + '.tif'))
     write_accessor_data_to_file(
         output_path,
diff --git a/extensions/chaeo/zmask.py b/extensions/chaeo/zmask.py
index 24ff80ef63a2c9ec2a243d5b6bc0f69aceacdd33..04bfeb41c640976be34ddf8e10ed27a0610d6fd4 100644
--- a/extensions/chaeo/zmask.py
+++ b/extensions/chaeo/zmask.py
@@ -30,7 +30,8 @@ class ZMaskObjectTable(object):
             filters=filters,
             mask_type=mask_type,
             expand_box_by=expand_box_by
-        )
+        )  # currently, some methods can add columns to self.df
+
         self.acc_raw = acc_raw
         self.count = len(self.zmask_meta)