From 9e30efebadccbf34f9ba57304acea5c5130ece1b Mon Sep 17 00:00:00 2001
From: David Hammer <david.hammer@xfel.eu>
Date: Wed, 26 Jul 2023 17:08:13 +0200
Subject: [PATCH] Use tags for managed keys

---
 docs/extensions.md                           |  6 +--
 src/calng/CalibrationManager.py              | 22 ++++----
 src/calng/base_calcat.py                     | 16 ++----
 src/calng/base_correction.py                 | 46 ++++++++--------
 src/calng/correction_addons/base_addon.py    |  2 +-
 src/calng/correction_addons/peakfinder9.py   | 24 ++++-----
 src/calng/correction_addons/random_frames.py |  4 +-
 src/calng/corrections/AgipdCorrection.py     | 57 +++++++-------------
 src/calng/corrections/DsscCorrection.py      | 29 +++-------
 src/calng/corrections/Epix100Correction.py   | 38 +++++--------
 src/calng/corrections/Gotthard2Correction.py | 25 +++------
 src/calng/corrections/JungfrauCorrection.py  | 35 ++++--------
 src/calng/corrections/LpdCorrection.py       | 39 ++++----------
 src/calng/corrections/LpdminiCorrection.py   | 20 ++-----
 src/calng/corrections/PnccdCorrection.py     | 36 +++----------
 src/tests/test_calcat_utils.py               | 11 +---
 16 files changed, 137 insertions(+), 273 deletions(-)

diff --git a/docs/extensions.md b/docs/extensions.md
index 403f9614..f26d17d2 100644
--- a/docs/extensions.md
+++ b/docs/extensions.md
@@ -33,11 +33,12 @@ When writing a subclass, you will want to override a subset of these stubs:
 - `__init__(self, config)`: initializer similar to that of a karabo device.
     - The `config` passed to the addon will be the contents of the addon's node within the the correction device's configuration.
     - This means you will here get the keys you added under the prefix with `extend_device_schema`.
-- `extend_device_schema(schema, managed_keys, prefix)`: a static method which will be called during the correction device's `expectedParameters` (where the class tells Karabo about the schema of this device type).
+- `extend_device_schema(schema, prefix)`: a static method which will be called during the correction device's `expectedParameters` (where the class tells Karabo about the schema of this device type).
     - This is used to add configurable parameters for the addon in the regular Karabo bound API style.
     - `schema` is the device schema (passed from the `expectedParameters` call.
-    - `managed_keys` is a set of keys which will be given to the manager - you will most likely want to add configurable parameters to allow configuring the addon via the manager (note that the boolean to control whether the addon is active is added and managed automatically).
     - `prefix` will be a substring under which you should put any elements you add to the schema.
+    - You will probably want to add `.tags("managed")` to most parameters to control them via the manager.
+    - Note that the boolean to control whether the addon is active is added and managed automatically.
 	Please use it - this means add elements by something like `FLOAT_ELEMENT(schema).key(f"{prefix}.someParameter")`.
     - Ordinary Karabo schema features apply.
 	Remember to `.commit()`, give default values, and consider `.reconfigurable()`.
@@ -62,7 +63,6 @@ This class for now containts stubs for the methods a kernel will need to provide
     - It is given the part of the device configuration hash under the node belonging to this kernel.
     - This depends on what is added via `extend_device_schema`.
 - `extend_device_schema(schema, prefix)`: see the method of the same name for correction addons.
-  The only difference is that, as this extends the schema of a lone frame selection arbiter, there are no `managed_keys` to update.
 - `consider(self, train_id, sources, num_frames)` is the main function called upon matching to compute a frame mask.
     - The default one provides a mask of all ones (truthy), meaning all frames are included.
 	  Note that the `dtype` of the returned array is `numpy.uint8`.
diff --git a/src/calng/CalibrationManager.py b/src/calng/CalibrationManager.py
index bbad11f8..6a05a276 100644
--- a/src/calng/CalibrationManager.py
+++ b/src/calng/CalibrationManager.py
@@ -9,7 +9,6 @@ from collections import defaultdict
 from collections.abc import Hashable
 from datetime import datetime
 from inspect import ismethod
-from itertools import chain, repeat
 from traceback import format_exc
 from urllib.parse import urlparse
 import json
@@ -788,23 +787,22 @@ class CalibrationManager(DeviceClientBase, Device):
                 f'known or loadable by device server `{corr_server}`')
             return
 
-        # Collect the keys to be managed including the nodes leading up
-        # to them. To do this a new hash is constructed with the managed
-        # key paths, filling in the node gaps in between.
-        managed_keys = set(managed_schema.hash['managedKeys', 'defaultValue'])
-        managed_paths = set(Hash(*chain.from_iterable(
-            zip(managed_keys, repeat(None, len(managed_keys))))).paths())
+        managed_hash = managed_schema.hash
+        # full paths and intermediate node paths
+        managed_paths = set(managed_schema.filterByTags('managed').paths())
+        # only leaf nodes (actual things to set)
+        managed_keys = set(path for path in managed_paths
+                           if not isinstance(managed_hash[path], Hash))
 
         # Reduce the correction schema to the managed paths.
-        managed_hash = managed_schema.hash
         for path in managed_hash.paths():
-            if path not in managed_paths:
-                # Remove unmanaged path.
-                del managed_hash[path]
-            else:
+            if path in managed_paths:
                 # Set owner and remote name (identical for corrections).
                 managed_hash[path, ...].update(
                     __owner='corrections', __remote=path)
+            else:
+                # Remove unmanaged path.
+                del managed_hash[path]
 
         # Retrieve any previous values already on running devices in
         # order to update the defaultValue attribute in the schema just
diff --git a/src/calng/base_calcat.py b/src/calng/base_calcat.py
index 09fce8d3..79e8db7d 100644
--- a/src/calng/base_calcat.py
+++ b/src/calng/base_calcat.py
@@ -239,11 +239,7 @@ class BaseCalcatFriend:
     _constants_need_conditions = None  # subclass should set
 
     @staticmethod
-    def add_schema(
-        schema,
-        managed_keys,
-        detector_type,
-    ):
+    def add_schema(schema, detector_type):
         """Add elements needed by this object to device's schema (expectedSchema)
 
         All elements added to schema go under prefixes which should end with name of
@@ -278,6 +274,7 @@ class BaseCalcatFriend:
         (
             STRING_ELEMENT(schema)
             .key("constantParameters.deviceMappingSnapshotAt")
+            .tags("managed")
             .displayedName("Snapshot timestamp (for device mapping)")
             .description(
                 "CalCat supports querying with a specific snapshot of the database. "
@@ -294,6 +291,7 @@ class BaseCalcatFriend:
 
             STRING_ELEMENT(schema)
             .key("constantParameters.constantVersionEventAt")
+            .tags("managed")
             .displayedName("Event at timestamp (for constant version)")
             .description("TODO")
             .assignmentOptional()
@@ -343,6 +341,7 @@ class BaseCalcatFriend:
 
             UINT32_ELEMENT(schema)
             .key("constantParameters.memoryCells")
+            .tags("managed")
             .displayedName("Memory cells")
             .description(
                 "Number of memory cells / frames per train. Relevant for burst mode."
@@ -368,6 +367,7 @@ class BaseCalcatFriend:
 
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.biasVoltage")
+            .tags("managed")
             .displayedName("Bias voltage")
             .description("Sensor bias voltage")
             .assignmentOptional()
@@ -375,12 +375,6 @@ class BaseCalcatFriend:
             .reconfigurable()
             .commit(),
         )
-        managed_keys.add("constantParameters.deviceMappingSnapshotAt")
-        managed_keys.add("constantParameters.constantVersionEventAt")
-        managed_keys.add("constantParameters.memoryCells")
-        managed_keys.add("constantParameters.pixelsX")
-        managed_keys.add("constantParameters.pixelsY")
-        managed_keys.add("constantParameters.biasVoltage")
 
     def __init__(
         self,
diff --git a/src/calng/base_correction.py b/src/calng/base_correction.py
index edabb39d..94c0d829 100644
--- a/src/calng/base_correction.py
+++ b/src/calng/base_correction.py
@@ -73,20 +73,6 @@ class BaseCorrection(PythonDevice):
     _correction_steps = None  # subclass must set
     _kernel_runner_class = None  # subclass must set (ex.: dssc_gpu.DsscGpuRunner)
     _kernel_runner_init_args = {}  # optional extra args for runner
-    _managed_keys = {
-        "dataFormat.outputAxisOrder",
-        "dataFormat.outputImageDtype",
-        "workarounds.overrideInputAxisOrder",
-        "workarounds.trainFromFutureThreshold",
-        "frameFilter.spec",
-        "frameFilter.type",
-        "loadMostRecentConstants",
-        "outputShmemBufferSize",
-        "preview.index",
-        "preview.selectionMode",
-        "useShmemHandles",
-        "useInfiniband",
-    }  # subclass can extend this, /must/ put it in schema as managedKeys
     _image_data_path = "image.data"  # customize for *some* subclasses
     _cell_table_path = "image.cellId"
     _pulse_table_path = "image.pulseId"
@@ -179,6 +165,7 @@ class BaseCorrection(PythonDevice):
 
             STRING_ELEMENT(expected)
             .key("frameFilter.type")
+            .tags("managed")
             .displayedName("Type")
             .description(
                 "Controls how frameFilter.spec is used. The default value of 'none' "
@@ -195,6 +182,7 @@ class BaseCorrection(PythonDevice):
 
             STRING_ELEMENT(expected)
             .key("frameFilter.spec")
+            .tags("managed")
             .displayedName("Specification")
             .assignmentOptional()
             .defaultValue("")
@@ -214,6 +202,7 @@ class BaseCorrection(PythonDevice):
 
             UINT32_ELEMENT(expected)
             .key("outputShmemBufferSize")
+            .tags("managed")
             .displayedName("Output buffer size limit")
             .unit(Unit.BYTE)
             .metricPrefix(MetricPrefix.GIGA)
@@ -235,6 +224,7 @@ class BaseCorrection(PythonDevice):
 
             BOOL_ELEMENT(expected)
             .key("useShmemHandles")
+            .tags("managed")
             .displayedName("Use shared memory")
             .description(
                 "If enabled, shared memory handles will be used to avoid copying "
@@ -248,6 +238,7 @@ class BaseCorrection(PythonDevice):
 
             BOOL_ELEMENT(expected)
             .key("useInfiniband")
+            .tags("managed")
             .displayedName("Use infiniband")
             .description(
                 "If enabled, device will during initialization try to bind its main "
@@ -275,6 +266,7 @@ class BaseCorrection(PythonDevice):
 
             STRING_ELEMENT(expected)
             .key("dataFormat.outputImageDtype")
+            .tags("managed")
             .displayedName("Output image data dtype")
             .description(
                 "The (numpy) dtype to use for outgoing image data. Input is cast to "
@@ -324,6 +316,7 @@ class BaseCorrection(PythonDevice):
 
             STRING_ELEMENT(expected)
             .key("dataFormat.outputAxisOrder")
+            .tags("managed")
             .displayedName("Output axis order")
             .description(
                 "Axes of main data output can be reordered after correction. Axis "
@@ -380,6 +373,7 @@ class BaseCorrection(PythonDevice):
 
             BOOL_ELEMENT(expected)
             .key("workarounds.overrideInputAxisOrder")
+            .tags("managed")
             .displayedName("Override input axis order")
             .description(
                 "The shape of the image data ndarray as received from the "
@@ -395,6 +389,7 @@ class BaseCorrection(PythonDevice):
 
             UINT64_ELEMENT(expected)
             .key("workarounds.trainFromFutureThreshold")
+            .tags("managed")
             .displayedName("Spurious future train ID threshold")
             .description(
                 "Some detectors occasionally send a train with incorrect and much too "
@@ -412,6 +407,7 @@ class BaseCorrection(PythonDevice):
         (
             SLOT_ELEMENT(expected)
             .key("loadMostRecentConstants")
+            .tags("managed")
             .displayedName("Load most recent constants")
             .description(
                 "Calling this slot will flush all constant buffers and cause the "
@@ -455,6 +451,7 @@ class BaseCorrection(PythonDevice):
 
             INT32_ELEMENT(expected)
             .key("preview.index")
+            .tags("managed")
             .displayedName("Index (or stat) for preview")
             .description(
                 "If this value is ≥ 0, the corresponding index (frame, cell, or pulse) "
@@ -470,6 +467,7 @@ class BaseCorrection(PythonDevice):
 
             STRING_ELEMENT(expected)
             .key("preview.selectionMode")
+            .tags("managed")
             .displayedName("Index selection mode")
             .description(
                 "The value of preview.index can be used in multiple ways, controlled "
@@ -1309,14 +1307,14 @@ if not hasattr(BaseCorrection, "unsafe_get"):
     setattr(BaseCorrection, "unsafe_get", unsafe_get)
 
 
-def add_correction_step_schema(schema, managed_keys, field_flag_constants_mapping):
+def add_correction_step_schema(schema, field_flag_constants_mapping):
     """Using the fields in the provided mapping, will add nodes to schema
 
     field_flag_mapping is assumed to be iterable of pairs where first entry in each
     pair is the name of a correction step as it will appear in device schema (second
     entry - typically an enum field - is ignored). For correction step, a node and some
-    booleans are added to the schema and the toggleable booleans are added to
-    managed_keys. Subclass can customize / add additional keys under node later.
+    booleans are added to the schema (all tagged as managed). Subclass can customize /
+    add additional keys under node later.
 
     This method should be called in expectedParameters of subclass after the same for
     BaseCorrection has been called. Would be nice to include in BaseCorrection instead,
@@ -1345,6 +1343,7 @@ def add_correction_step_schema(schema, managed_keys, field_flag_constants_mappin
 
             BOOL_ELEMENT(schema)
             .key(f"{node_name}.enable")
+            .tags("managed")
             .displayedName("Enable")
             .description(
                 "Controls whether to apply this correction step for main data "
@@ -1357,6 +1356,7 @@ def add_correction_step_schema(schema, managed_keys, field_flag_constants_mappin
 
             BOOL_ELEMENT(schema)
             .key(f"{node_name}.preview")
+            .tags("managed")
             .displayedName("Preview")
             .description(
                 "Whether to apply this correction step for corrected preview "
@@ -1367,14 +1367,13 @@ def add_correction_step_schema(schema, managed_keys, field_flag_constants_mappin
             .reconfigurable()
             .commit(),
         )
-        managed_keys.add(f"{node_name}.enable")
-        managed_keys.add(f"{node_name}.preview")
 
 
-def add_bad_pixel_config_node(schema, managed_keys, prefix="corrections.badPixels"):
+def add_bad_pixel_config_node(schema, prefix="corrections.badPixels"):
     (
         STRING_ELEMENT(schema)
         .key("corrections.badPixels.maskingValue")
+        .tags("managed")
         .displayedName("Bad pixel masking value")
         .description(
             "Any pixels masked by the bad pixel mask will have their value replaced "
@@ -1385,6 +1384,7 @@ def add_bad_pixel_config_node(schema, managed_keys, prefix="corrections.badPixel
         .defaultValue("nan")
         .reconfigurable()
         .commit(),
+
         NODE_ELEMENT(schema)
         .key("corrections.badPixels.subsetToUse")
         .displayedName("Bad pixel flags to use")
@@ -1397,17 +1397,16 @@ def add_bad_pixel_config_node(schema, managed_keys, prefix="corrections.badPixel
         )
         .commit(),
     )
-    managed_keys.add("corrections.badPixels.maskingValue")
     for field in utils.BadPixelValues:
         (
             BOOL_ELEMENT(schema)
             .key(f"corrections.badPixels.subsetToUse.{field.name}")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(True)
             .reconfigurable()
             .commit()
         )
-        managed_keys.add(f"corrections.badPixels.subsetToUse.{field.name}")
 
 
 def add_preview_outputs(schema, channels):
@@ -1442,9 +1441,8 @@ def add_addon_nodes(schema, device_class, prefix="addons"):
             .defaultValue(False)
             .commit(),
         )
-        device_class._managed_keys.add(f"{prefix}.{addon_class._node_name}.enable")
         addon_class.extend_device_schema(
-            schema, device_class._managed_keys, f"{prefix}.{addon_class._node_name}"
+            schema, f"{prefix}.{addon_class._node_name}"
         )
 
 
diff --git a/src/calng/correction_addons/base_addon.py b/src/calng/correction_addons/base_addon.py
index 562606ff..ae03225e 100644
--- a/src/calng/correction_addons/base_addon.py
+++ b/src/calng/correction_addons/base_addon.py
@@ -2,7 +2,7 @@ class BaseCorrectionAddon:
     _node_name = None  # subclass must set (usually name of addon minus "Addon" suffix)
 
     @staticmethod
-    def extend_device_schema(schema, managed_keys, prefix):
+    def extend_device_schema(schema, prefix):
         """Will be given the device schema where everything should be put under a
         prefix. This prefix should be something like 'addons.nodeName', is given to this
         class by the base correction device, and the root node of prefix will already be
diff --git a/src/calng/correction_addons/peakfinder9.py b/src/calng/correction_addons/peakfinder9.py
index 3c25ba30..8285bb97 100644
--- a/src/calng/correction_addons/peakfinder9.py
+++ b/src/calng/correction_addons/peakfinder9.py
@@ -15,10 +15,11 @@ class Peakfinder9Addon(BaseCorrectionAddon):
     _node_name = "peakfinder9"
 
     @staticmethod
-    def extend_device_schema(schema, managed_keys, prefix):
+    def extend_device_schema(schema, prefix):
         (
             UINT32_ELEMENT(schema)
             .key(f"{prefix}.windowRadius")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(2)
             .reconfigurable()
@@ -26,6 +27,7 @@ class Peakfinder9Addon(BaseCorrectionAddon):
 
             UINT32_ELEMENT(schema)
             .key(f"{prefix}.maxPeaks")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(500)
             .reconfigurable()
@@ -33,6 +35,7 @@ class Peakfinder9Addon(BaseCorrectionAddon):
 
             FLOAT_ELEMENT(schema)
             .key(f"{prefix}.minPeakValueOverNeighbors")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(10)
             .reconfigurable()
@@ -40,6 +43,7 @@ class Peakfinder9Addon(BaseCorrectionAddon):
 
             FLOAT_ELEMENT(schema)
             .key(f"{prefix}.minSnrMaxPixel")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(5)
             .reconfigurable()
@@ -47,6 +51,7 @@ class Peakfinder9Addon(BaseCorrectionAddon):
 
             FLOAT_ELEMENT(schema)
             .key(f"{prefix}.minSnrPeakPixels")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(4)
             .reconfigurable()
@@ -54,6 +59,7 @@ class Peakfinder9Addon(BaseCorrectionAddon):
 
             FLOAT_ELEMENT(schema)
             .key(f"{prefix}.minSnrWholePeak")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(6)
             .reconfigurable()
@@ -61,6 +67,7 @@ class Peakfinder9Addon(BaseCorrectionAddon):
 
             FLOAT_ELEMENT(schema)
             .key(f"{prefix}.minSigma")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(5)
             .reconfigurable()
@@ -68,6 +75,7 @@ class Peakfinder9Addon(BaseCorrectionAddon):
 
             UINT32_ELEMENT(schema)
             .key(f"{prefix}.blockX")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(1)
             .reconfigurable()
@@ -75,6 +83,7 @@ class Peakfinder9Addon(BaseCorrectionAddon):
 
             UINT32_ELEMENT(schema)
             .key(f"{prefix}.blockY")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(1)
             .reconfigurable()
@@ -82,23 +91,12 @@ class Peakfinder9Addon(BaseCorrectionAddon):
 
             UINT32_ELEMENT(schema)
             .key(f"{prefix}.blockZ")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(64)
             .reconfigurable()
             .commit(),
         )
-        managed_keys |= {
-            f"{prefix}.{key}"
-            for key in {
-                "windowRadius",
-                "maxPeaks",
-                "minPeakValueOverNeighbors",
-                "minSnrMaxPixel",
-                "minSnrPeakPixels",
-                "minSnrWholePeak",
-                "minSigma",
-            }
-        }
 
     @staticmethod
     def extend_output_schema(schema):
diff --git a/src/calng/correction_addons/random_frames.py b/src/calng/correction_addons/random_frames.py
index e51221e1..64903842 100644
--- a/src/calng/correction_addons/random_frames.py
+++ b/src/calng/correction_addons/random_frames.py
@@ -7,17 +7,17 @@ class RandomFramesAddon(BaseCorrectionAddon):
     _node_name = "randomFrames"
 
     @staticmethod
-    def extend_device_schema(schema, managed_keys, prefix):
+    def extend_device_schema(schema, prefix):
         (
             DOUBLE_ELEMENT(schema)
             .key(f"{prefix}.probability")
+            .tags("managed")
             .unit(Unit.PERCENT)
             .assignmentOptional()
             .defaultValue(50)
             .reconfigurable()
             .commit(),
         )
-        managed_keys.add(f"{prefix}.probability")
 
     @staticmethod
     def extend_output_schema(schema):
diff --git a/src/calng/corrections/AgipdCorrection.py b/src/calng/corrections/AgipdCorrection.py
index 212462f8..041f9f45 100644
--- a/src/calng/corrections/AgipdCorrection.py
+++ b/src/calng/corrections/AgipdCorrection.py
@@ -394,10 +394,8 @@ class AgipdCalcatFriend(base_calcat.BaseCalcatFriend):
         }
 
     @staticmethod
-    def add_schema(schema, managed_keys):
-        super(AgipdCalcatFriend, AgipdCalcatFriend).add_schema(
-            schema, managed_keys, "AGIPD-Type"
-        )
+    def add_schema(schema):
+        super(AgipdCalcatFriend, AgipdCalcatFriend).add_schema(schema, "AGIPD-Type")
 
         (
             OVERWRITE_ELEMENT(schema)
@@ -414,6 +412,7 @@ class AgipdCalcatFriend(base_calcat.BaseCalcatFriend):
         (
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.acquisitionRate")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(1.1)
             .reconfigurable()
@@ -421,6 +420,7 @@ class AgipdCalcatFriend(base_calcat.BaseCalcatFriend):
 
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.gainSetting")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(0)
             .reconfigurable()
@@ -428,6 +428,7 @@ class AgipdCalcatFriend(base_calcat.BaseCalcatFriend):
 
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.photonEnergy")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(9.2)
             .reconfigurable()
@@ -435,6 +436,7 @@ class AgipdCalcatFriend(base_calcat.BaseCalcatFriend):
 
             STRING_ELEMENT(schema)
             .key("constantParameters.gainMode")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue("ADAPTIVE_GAIN")
             .options(",".join(gain_mode.name for gain_mode in GainModes))
@@ -443,16 +445,12 @@ class AgipdCalcatFriend(base_calcat.BaseCalcatFriend):
 
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.integrationTime")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(12)
             .reconfigurable()
             .commit(),
         )
-        managed_keys.add("constantParameters.acquisitionRate")
-        managed_keys.add("constantParameters.gainSetting")
-        managed_keys.add("constantParameters.photonEnergy")
-        managed_keys.add("constantParameters.gainMode")
-        managed_keys.add("constantParameters.integrationTime")
 
         base_calcat.add_status_schema_from_enum(schema, Constants)
 
@@ -524,7 +522,6 @@ class AgipdCorrection(base_correction.BaseCorrection):
     )
     _calcat_friend_class = AgipdCalcatFriend
     _constant_enum_class = Constants
-    _managed_keys = base_correction.BaseCorrection._managed_keys.copy()
     _preview_outputs = [
         "outputRaw",
         "outputCorrected",
@@ -552,21 +549,14 @@ class AgipdCorrection(base_correction.BaseCorrection):
         )
 
         # this is not automatically done by superclass for complicated class reasons
-        base_correction.add_preview_outputs(expected, AgipdCorrection._preview_outputs)
-        base_correction.add_correction_step_schema(
-            expected,
-            AgipdCorrection._managed_keys,
-            AgipdCorrection._correction_steps,
-        )
+        AgipdCalcatFriend.add_schema(expected)
         base_correction.add_addon_nodes(expected, AgipdCorrection)
-        base_correction.add_bad_pixel_config_node(
-            expected, AgipdCorrection._managed_keys
-        )
-        AgipdCalcatFriend.add_schema(expected, AgipdCorrection._managed_keys)
+        base_correction.add_preview_outputs(expected, AgipdCorrection._preview_outputs)
         (
             # support both CPU and GPU kernels
             STRING_ELEMENT(expected)
             .key("kernelType")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(base_kernel_runner.KernelRunnerTypes.GPU.name)
             .options(
@@ -578,7 +568,11 @@ class AgipdCorrection(base_correction.BaseCorrection):
             .reconfigurable()
             .commit(),
         )
-        AgipdCorrection._managed_keys.add("kernelType")
+        base_correction.add_correction_step_schema(
+            expected,
+            AgipdCorrection._correction_steps,
+        )
+        base_correction.add_bad_pixel_config_node(expected)
 
         # turn off the force MG / HG steps by default
         for step in ("forceMgIfBelow", "forceHgIfBelow"):
@@ -593,6 +587,7 @@ class AgipdCorrection(base_correction.BaseCorrection):
         (
             FLOAT_ELEMENT(expected)
             .key("corrections.forceMgIfBelow.hardThreshold")
+            .tags("managed")
             .description(
                 "If enabled, any pixels assigned to low gain stage which would be "
                 "below this threshold after having medium gain offset subtracted will "
@@ -605,6 +600,7 @@ class AgipdCorrection(base_correction.BaseCorrection):
 
             FLOAT_ELEMENT(expected)
             .key("corrections.forceHgIfBelow.hardThreshold")
+            .tags("managed")
             .description(
                 "Like forceMgIfBelow, but potentially reassigning from medium gain to "
                 "high gain based on threshold and pixel value minus low gain offset. "
@@ -618,6 +614,7 @@ class AgipdCorrection(base_correction.BaseCorrection):
 
             BOOL_ELEMENT(expected)
             .key("corrections.relGainPc.overrideMdAdditionalOffset")
+            .tags("managed")
             .displayedName("Override md_additional_offset")
             .description(
                 "Toggling this on will use the value in the next field globally for "
@@ -632,6 +629,7 @@ class AgipdCorrection(base_correction.BaseCorrection):
 
             FLOAT_ELEMENT(expected)
             .key("corrections.relGainPc.mdAdditionalOffset")
+            .tags("managed")
             .displayedName("Value for md_additional_offset (if overriding)")
             .description(
                 "Normally, md_additional_offset (part of relative gain correction) is "
@@ -646,6 +644,7 @@ class AgipdCorrection(base_correction.BaseCorrection):
 
             FLOAT_ELEMENT(expected)
             .key("corrections.gainXray.gGainValue")
+            .tags("managed")
             .displayedName("G_gain_value")
             .description(
                 "Newer X-ray gain correction constants are absolute. The default "
@@ -659,22 +658,6 @@ class AgipdCorrection(base_correction.BaseCorrection):
             .reconfigurable()
             .commit(),
         )
-        AgipdCorrection._managed_keys |= {
-            "corrections.forceMgIfBelow.hardThreshold",
-            "corrections.forceHgIfBelow.hardThreshold",
-            "corrections.relGainPc.overrideMdAdditionalOffset",
-            "corrections.relGainPc.mdAdditionalOffset",
-            "corrections.gainXray.gGainValue",
-        }
-
-        # mandatory: manager needs this in schema
-        (
-            VECTOR_STRING_ELEMENT(expected)
-            .key("managedKeys")
-            .assignmentOptional()
-            .defaultValue(list(AgipdCorrection._managed_keys))
-            .commit()
-        )
 
     @property
     def input_data_shape(self):
diff --git a/src/calng/corrections/DsscCorrection.py b/src/calng/corrections/DsscCorrection.py
index 043a1384..c6de4a4c 100644
--- a/src/calng/corrections/DsscCorrection.py
+++ b/src/calng/corrections/DsscCorrection.py
@@ -159,10 +159,8 @@ class DsscCalcatFriend(base_calcat.BaseCalcatFriend):
         return {Constants.Offset: self.dark_condition}
 
     @staticmethod
-    def add_schema(schema, managed_keys):
-        super(DsscCalcatFriend, DsscCalcatFriend).add_schema(
-            schema, managed_keys, "DSSC-Type"
-        )
+    def add_schema(schema):
+        super(DsscCalcatFriend, DsscCalcatFriend).add_schema(schema, "DSSC-Type")
         (
             OVERWRITE_ELEMENT(schema)
             .key("constantParameters.memoryCells")
@@ -194,7 +192,6 @@ class DsscCorrection(base_correction.BaseCorrection):
     _correction_steps = (("offset", CorrectionFlags.OFFSET, {Constants.Offset}),)
     _calcat_friend_class = DsscCalcatFriend
     _constant_enum_class = Constants
-    _managed_keys = base_correction.BaseCorrection._managed_keys.copy()
     _preview_outputs = ["outputRaw", "outputCorrected"]
 
     @staticmethod
@@ -220,19 +217,14 @@ class DsscCorrection(base_correction.BaseCorrection):
             .setNewDefaultValue("pulse")
             .commit(),
         )
-        base_correction.add_preview_outputs(expected, DsscCorrection._preview_outputs)
-        base_correction.add_correction_step_schema(
-            expected,
-            DsscCorrection._managed_keys,
-            DsscCorrection._correction_steps,
-        )
+        DsscCalcatFriend.add_schema(expected)
         base_correction.add_addon_nodes(expected, DsscCorrection)
-        DsscCalcatFriend.add_schema(expected, DsscCorrection._managed_keys)
-
+        base_correction.add_preview_outputs(expected, DsscCorrection._preview_outputs)
         (
             # support both CPU and GPU kernels
             STRING_ELEMENT(expected)
             .key("kernelType")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(base_kernel_runner.KernelRunnerTypes.GPU.name)
             .options(
@@ -244,14 +236,9 @@ class DsscCorrection(base_correction.BaseCorrection):
             .reconfigurable()
             .commit(),
         )
-        DsscCorrection._managed_keys.add("kernelType")
-
-        (
-            VECTOR_STRING_ELEMENT(expected)
-            .key("managedKeys")
-            .assignmentOptional()
-            .defaultValue(list(DsscCorrection._managed_keys))
-            .commit()
+        base_correction.add_correction_step_schema(
+            expected,
+            DsscCorrection._correction_steps,
         )
 
     @property
diff --git a/src/calng/corrections/Epix100Correction.py b/src/calng/corrections/Epix100Correction.py
index b7f1b127..47c50cb8 100644
--- a/src/calng/corrections/Epix100Correction.py
+++ b/src/calng/corrections/Epix100Correction.py
@@ -52,9 +52,9 @@ class Epix100CalcatFriend(base_calcat.BaseCalcatFriend):
         }
 
     @staticmethod
-    def add_schema(schema, managed_keys):
+    def add_schema(schema):
         super(Epix100CalcatFriend, Epix100CalcatFriend).add_schema(
-            schema, managed_keys, "ePix100-Type"
+            schema, "ePix100-Type"
         )
 
         # set some defaults for common parameters
@@ -83,6 +83,7 @@ class Epix100CalcatFriend(base_calcat.BaseCalcatFriend):
         (
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.integrationTime")
+            .tags("managed")
             .displayedName("Integration Time")
             .assignmentOptional()
             .defaultValue(10)
@@ -91,6 +92,7 @@ class Epix100CalcatFriend(base_calcat.BaseCalcatFriend):
 
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.sensorTemperature")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(264.32933824373004)
             .reconfigurable()
@@ -98,6 +100,7 @@ class Epix100CalcatFriend(base_calcat.BaseCalcatFriend):
 
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.inVacuum")
+            .tags("managed")
             .displayedName("In vacuum")
             .assignmentOptional()
             .defaultValue(0)
@@ -106,6 +109,7 @@ class Epix100CalcatFriend(base_calcat.BaseCalcatFriend):
 
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.sourceEnergy")
+            .tags("managed")
             .displayedName("Source Energy")
             .assignmentOptional()
             .defaultValue(8.04778)
@@ -113,11 +117,6 @@ class Epix100CalcatFriend(base_calcat.BaseCalcatFriend):
             .commit(),
         )
 
-        managed_keys.add("constantParameters.integrationTime")
-        managed_keys.add("constantParameters.inVacuum")
-        managed_keys.add("constantParameters.sourceEnergy")
-        managed_keys.add("constantParameters.sensorTemperature")
-
         base_calcat.add_status_schema_from_enum(schema, Constants)
 
     def dark_condition(self):
@@ -316,7 +315,6 @@ class Epix100Correction(base_correction.BaseCorrection):
     _kernel_runner_class = Epix100CpuRunner
     _calcat_friend_class = Epix100CalcatFriend
     _constant_enum_class = Constants
-    _managed_keys = base_correction.BaseCorrection._managed_keys.copy()
     _preview_outputs = ["outputRaw", "outputCorrected"]
     _cell_table_path = None
     _pulse_table_path = None
@@ -363,12 +361,12 @@ class Epix100Correction(base_correction.BaseCorrection):
         )
         base_correction.add_correction_step_schema(
             expected,
-            Epix100Correction._managed_keys,
             Epix100Correction._correction_steps,
         )
         (
             DOUBLE_ELEMENT(expected)
             .key("corrections.commonMode.noiseSigma")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(5)
             .reconfigurable()
@@ -376,6 +374,7 @@ class Epix100Correction(base_correction.BaseCorrection):
 
             DOUBLE_ELEMENT(expected)
             .key("corrections.commonMode.minFrac")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(0.25)
             .reconfigurable()
@@ -383,6 +382,7 @@ class Epix100Correction(base_correction.BaseCorrection):
 
             BOOL_ELEMENT(expected)
             .key("corrections.commonMode.enableRow")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(True)
             .reconfigurable()
@@ -390,6 +390,7 @@ class Epix100Correction(base_correction.BaseCorrection):
 
             BOOL_ELEMENT(expected)
             .key("corrections.commonMode.enableCol")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(True)
             .reconfigurable()
@@ -397,30 +398,15 @@ class Epix100Correction(base_correction.BaseCorrection):
 
             BOOL_ELEMENT(expected)
             .key("corrections.commonMode.enableBlock")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(True)
             .reconfigurable()
             .commit(),
         )
-        Epix100Correction._managed_keys |= {
-            "corrections.commonMode.noiseSigma",
-            "corrections.commonMode.minFrac",
-            "corrections.commonMode.enableRow",
-            "corrections.commonMode.enableCol",
-            "corrections.commonMode.enableBlock",
-        }
-        Epix100CalcatFriend.add_schema(expected, Epix100Correction._managed_keys)
+        Epix100CalcatFriend.add_schema(expected)
         # TODO: bad pixel node?
 
-        # mandatory: manager needs this in schema
-        (
-            VECTOR_STRING_ELEMENT(expected)
-            .key("managedKeys")
-            .assignmentOptional()
-            .defaultValue(list(Epix100Correction._managed_keys))
-            .commit()
-        )
-
     @property
     def input_data_shape(self):
         # TODO: check
diff --git a/src/calng/corrections/Gotthard2Correction.py b/src/calng/corrections/Gotthard2Correction.py
index 2f305054..cf4e5eb5 100644
--- a/src/calng/corrections/Gotthard2Correction.py
+++ b/src/calng/corrections/Gotthard2Correction.py
@@ -160,9 +160,9 @@ class Gotthard2CalcatFriend(base_calcat.BaseCalcatFriend):
         return res
 
     @staticmethod
-    def add_schema(schema, managed_keys):
+    def add_schema(schema):
         super(Gotthard2CalcatFriend, Gotthard2CalcatFriend).add_schema(
-            schema, managed_keys, "Gotthard2-Type"
+            schema, "Gotthard2-Type"
         )
 
         # set some defaults for common parameters
@@ -191,6 +191,7 @@ class Gotthard2CalcatFriend(base_calcat.BaseCalcatFriend):
         (
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.exposureTime")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(0)
             .reconfigurable()
@@ -198,6 +199,7 @@ class Gotthard2CalcatFriend(base_calcat.BaseCalcatFriend):
 
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.exposurePeriod")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(0)
             .reconfigurable()
@@ -205,6 +207,7 @@ class Gotthard2CalcatFriend(base_calcat.BaseCalcatFriend):
 
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.singlePhoton")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(0)
             .reconfigurable()
@@ -212,6 +215,7 @@ class Gotthard2CalcatFriend(base_calcat.BaseCalcatFriend):
 
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.acquisitionRate")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(1.1)
             .reconfigurable()
@@ -255,7 +259,6 @@ class Gotthard2Correction(base_correction.BaseCorrection):
     _kernel_runner_class = Gotthard2CpuRunner
     _calcat_friend_class = Gotthard2CalcatFriend
     _constant_enum_class = Constants
-    _managed_keys = base_correction.BaseCorrection._managed_keys.copy()
     _image_data_path = "data.adc"
     _cell_table_path = "data.memoryCell"
     _pulse_table_path = None
@@ -311,22 +314,10 @@ class Gotthard2Correction(base_correction.BaseCorrection):
             )
         base_correction.add_correction_step_schema(
             expected,
-            Gotthard2Correction._managed_keys,
             Gotthard2Correction._correction_steps,
         )
-        base_correction.add_bad_pixel_config_node(
-            expected, Gotthard2Correction._managed_keys
-        )
-        Gotthard2CalcatFriend.add_schema(expected, Gotthard2Correction._managed_keys)
-
-        # mandatory: manager needs this in schema
-        (
-            VECTOR_STRING_ELEMENT(expected)
-            .key("managedKeys")
-            .assignmentOptional()
-            .defaultValue(list(Gotthard2Correction._managed_keys))
-            .commit()
-        )
+        base_correction.add_bad_pixel_config_node(expected)
+        Gotthard2CalcatFriend.add_schema(expected)
 
     @property
     def input_data_shape(self):
diff --git a/src/calng/corrections/JungfrauCorrection.py b/src/calng/corrections/JungfrauCorrection.py
index b4d61ab8..8b8e8e9d 100644
--- a/src/calng/corrections/JungfrauCorrection.py
+++ b/src/calng/corrections/JungfrauCorrection.py
@@ -333,9 +333,9 @@ class JungfrauCalcatFriend(base_calcat.BaseCalcatFriend):
         }
 
     @staticmethod
-    def add_schema(schema, managed_keys):
+    def add_schema(schema):
         super(JungfrauCalcatFriend, JungfrauCalcatFriend).add_schema(
-            schema, managed_keys, "jungfrau-Type"
+            schema, "jungfrau-Type"
         )
 
         # set some defaults for common parameters
@@ -365,6 +365,7 @@ class JungfrauCalcatFriend(base_calcat.BaseCalcatFriend):
         (
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.integrationTime")
+            .tags("managed")
             .displayedName("Integration time")
             .description("Integration time in ms")
             .assignmentOptional()
@@ -374,6 +375,7 @@ class JungfrauCalcatFriend(base_calcat.BaseCalcatFriend):
 
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.sensorTemperature")
+            .tags("managed")
             .displayedName("Sensor temperature")
             .description("Sensor temperature in K")
             .assignmentOptional()
@@ -383,6 +385,7 @@ class JungfrauCalcatFriend(base_calcat.BaseCalcatFriend):
 
             STRING_ELEMENT(schema)
             .key("constantParameters.gainMode")
+            .tags("managed")
             .displayedName("Gain mode")
             .description(
                 "Detector may be operating in one of several gain modes. In CalCat, "
@@ -398,6 +401,7 @@ class JungfrauCalcatFriend(base_calcat.BaseCalcatFriend):
 
             STRING_ELEMENT(schema)
             .key("constantParameters.gainSetting")
+            .tags("managed")
             .displayedName("Gain setting")
             .description("See description of gainMode")
             .assignmentOptional()
@@ -406,14 +410,8 @@ class JungfrauCalcatFriend(base_calcat.BaseCalcatFriend):
             .reconfigurable()
             .commit(),
         )
-        managed_keys.add("constantParameters.integrationTime")
-        managed_keys.add("constantParameters.sensorTemperature")
-        managed_keys.add("constantParameters.gainMode")
-        managed_keys.add("constantParameters.gainSetting")
 
-        base_calcat.add_status_schema_from_enum(
-            schema, Constants
-        )
+        base_calcat.add_status_schema_from_enum(schema, Constants)
 
     def dark_condition(self):
         res = base_calcat.OperatingConditions()
@@ -456,7 +454,6 @@ class JungfrauCorrection(base_correction.BaseCorrection):
     _kernel_runner_class = None  # note: set in __init__ based on config
     _calcat_friend_class = JungfrauCalcatFriend
     _constant_enum_class = Constants
-    _managed_keys = base_correction.BaseCorrection._managed_keys.copy()
     _preview_outputs = [
         "outputRaw",
         "outputCorrected",
@@ -504,6 +501,7 @@ class JungfrauCorrection(base_correction.BaseCorrection):
             # support both CPU and GPU kernels
             STRING_ELEMENT(expected)
             .key("kernelType")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(base_kernel_runner.KernelRunnerTypes.GPU.name)
             .options(
@@ -515,14 +513,12 @@ class JungfrauCorrection(base_correction.BaseCorrection):
             .reconfigurable()
             .commit(),
         )
-        JungfrauCorrection._managed_keys.add("kernelType")
 
         base_correction.add_preview_outputs(
             expected, JungfrauCorrection._preview_outputs
         )
         base_correction.add_correction_step_schema(
             expected,
-            JungfrauCorrection._managed_keys,
             JungfrauCorrection._correction_steps,
         )
         (
@@ -536,19 +532,8 @@ class JungfrauCorrection(base_correction.BaseCorrection):
             .setNewDefaultValue(False)
             .commit(),
         )
-        base_correction.add_bad_pixel_config_node(
-            expected, JungfrauCorrection._managed_keys
-        )
-        JungfrauCalcatFriend.add_schema(expected, JungfrauCorrection._managed_keys)
-
-        # mandatory: manager needs this in schema
-        (
-            VECTOR_STRING_ELEMENT(expected)
-            .key("managedKeys")
-            .assignmentOptional()
-            .defaultValue(list(JungfrauCorrection._managed_keys))
-            .commit()
-        )
+        base_correction.add_bad_pixel_config_node(expected)
+        JungfrauCalcatFriend.add_schema(expected)
 
     @property
     def input_data_shape(self):
diff --git a/src/calng/corrections/LpdCorrection.py b/src/calng/corrections/LpdCorrection.py
index 8c571b6e..a5436191 100644
--- a/src/calng/corrections/LpdCorrection.py
+++ b/src/calng/corrections/LpdCorrection.py
@@ -199,13 +199,8 @@ class LpdCalcatFriend(base_calcat.BaseCalcatFriend):
         }
 
     @staticmethod
-    def add_schema(
-        schema,
-        managed_keys,
-    ):
-        super(LpdCalcatFriend, LpdCalcatFriend).add_schema(
-            schema, managed_keys, "LPD-Type"
-        )
+    def add_schema(schema):
+        super(LpdCalcatFriend, LpdCalcatFriend).add_schema(schema, "LPD-Type")
 
         (
             OVERWRITE_ELEMENT(schema)
@@ -232,6 +227,7 @@ class LpdCalcatFriend(base_calcat.BaseCalcatFriend):
         (
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.feedbackCapacitor")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(5)
             .reconfigurable()
@@ -239,6 +235,7 @@ class LpdCalcatFriend(base_calcat.BaseCalcatFriend):
 
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.photonEnergy")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(9.3)
             .reconfigurable()
@@ -246,6 +243,7 @@ class LpdCalcatFriend(base_calcat.BaseCalcatFriend):
 
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.category")
+            .tags("managed")
             .displayedName("Category")
             .assignmentOptional()
             .defaultValue(0)
@@ -254,6 +252,7 @@ class LpdCalcatFriend(base_calcat.BaseCalcatFriend):
 
             BOOL_ELEMENT(schema)
             .key("constantParameters.useMemoryCellOrder")
+            .tags("managed")
             .displayedName("Use memory cell order parameter")
             .assignmentOptional()
             .defaultValue(True)
@@ -262,17 +261,13 @@ class LpdCalcatFriend(base_calcat.BaseCalcatFriend):
 
             STRING_ELEMENT(schema)
             .key("constantParameters.memoryCellOrder")
+            .tags("managed")
             .displayedName("Memory cell order")
             .assignmentOptional()
             .defaultValue("")
             .reconfigurable()
             .commit(),
         )
-        managed_keys.add("constantParameters.useMemoryCellOrder")
-        managed_keys.add("constantParameters.memoryCellOrder")
-        managed_keys.add("constantParameters.feedbackCapacitor")
-        managed_keys.add("constantParameters.photonEnergy")
-        managed_keys.add("constantParameters.category")
 
         base_calcat.add_status_schema_from_enum(schema, Constants)
 
@@ -325,7 +320,6 @@ class LpdCorrection(BaseCorrection):
     _kernel_runner_class = LpdGpuRunner
     _calcat_friend_class = LpdCalcatFriend
     _constant_enum_class = Constants
-    _managed_keys = BaseCorrection._managed_keys.copy()
     _preview_outputs = ["outputRaw", "outputCorrected", "outputGainMap"]
 
     @staticmethod
@@ -359,17 +353,14 @@ class LpdCorrection(BaseCorrection):
         )
 
         add_preview_outputs(expected, LpdCorrection._preview_outputs)
-        add_correction_step_schema(
-            expected,
-            LpdCorrection._managed_keys,
-            LpdCorrection._correction_steps,
-        )
-        LpdCalcatFriend.add_schema(expected, LpdCorrection._managed_keys)
+        add_correction_step_schema(expected, LpdCorrection._correction_steps)
+        LpdCalcatFriend.add_schema(expected)
 
         # additional settings for correction steps
         (
             STRING_ELEMENT(expected)
             .key("corrections.badPixels.maskingValue")
+            .tags("managed")
             .displayedName("Bad pixel masking value")
             .description(
                 "Any pixels masked by the bad pixel mask will have their value "
@@ -381,16 +372,6 @@ class LpdCorrection(BaseCorrection):
             .reconfigurable()
             .commit(),
         )
-        LpdCorrection._managed_keys.add("corrections.badPixels.maskingValue")
-
-        # mandatory: manager needs this in schema
-        (
-            VECTOR_STRING_ELEMENT(expected)
-            .key("managedKeys")
-            .assignmentOptional()
-            .defaultValue(list(LpdCorrection._managed_keys))
-            .commit()
-        )
 
     @property
     def input_data_shape(self):
diff --git a/src/calng/corrections/LpdminiCorrection.py b/src/calng/corrections/LpdminiCorrection.py
index 688b174b..f43650ad 100644
--- a/src/calng/corrections/LpdminiCorrection.py
+++ b/src/calng/corrections/LpdminiCorrection.py
@@ -36,11 +36,8 @@ class LpdminiGpuRunner(LpdCorrection.LpdGpuRunner):
 
 class LpdminiCalcatFriend(LpdCorrection.LpdCalcatFriend):
     @staticmethod
-    def add_schema(
-        schema,
-        managed_keys,
-    ):
-        super(LpdminiCalcatFriend, LpdminiCalcatFriend).add_schema(schema, managed_keys)
+    def add_schema(schema):
+        super(LpdminiCalcatFriend, LpdminiCalcatFriend).add_schema(schema)
         (
             OVERWRITE_ELEMENT(schema)
             .key("constantParameters.biasVoltage")
@@ -50,6 +47,7 @@ class LpdminiCalcatFriend(LpdCorrection.LpdCalcatFriend):
 
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.biasVoltage2")
+            .tags("managed")
             .displayedName("Bias voltage (even)")
             .description("Separate bias voltage used for minis 2, 4, 6, and 8.")
             .assignmentOptional()
@@ -62,7 +60,6 @@ class LpdminiCalcatFriend(LpdCorrection.LpdCalcatFriend):
             .setNewDefaultValue(32)
             .commit(),
         )
-        managed_keys.add("constantParameters.biasVoltage2")
 
     def basic_condition(self):
         res = super().basic_condition()
@@ -77,7 +74,6 @@ class LpdminiCalcatFriend(LpdCorrection.LpdCalcatFriend):
 class LpdminiCorrection(LpdCorrection.LpdCorrection):
     _calcat_friend_class = LpdminiCalcatFriend
     _kernel_runner_class = LpdminiGpuRunner
-    _managed_keys = LpdCorrection.LpdCorrection._managed_keys.copy()
 
     @classmethod
     def expectedParameters(cls, expected):
@@ -92,12 +88,6 @@ class LpdminiCorrection(LpdCorrection.LpdCorrection):
             .setNewDefaultValue(512)
             .commit(),
         )
-        cls._calcat_friend_class.add_schema(expected, cls._managed_keys)
+        cls._calcat_friend_class.add_schema(expected)
         # warning: this is redundant, but needed for now to get managed keys working
-        add_correction_step_schema(expected, cls._managed_keys, cls._correction_steps)
-        (
-            OVERWRITE_ELEMENT(expected)
-            .key("managedKeys")
-            .setNewDefaultValue(list(cls._managed_keys))
-            .commit()
-        )
+        add_correction_step_schema(expected, cls._correction_steps)
diff --git a/src/calng/corrections/PnccdCorrection.py b/src/calng/corrections/PnccdCorrection.py
index b2adb55d..c1a7ae49 100644
--- a/src/calng/corrections/PnccdCorrection.py
+++ b/src/calng/corrections/PnccdCorrection.py
@@ -53,10 +53,8 @@ class PnccdCalcatFriend(base_calcat.BaseCalcatFriend):
         }
 
     @staticmethod
-    def add_schema(schema, managed_keys):
-        super(PnccdCalcatFriend, PnccdCalcatFriend).add_schema(
-            schema, managed_keys, "pnCCD-Type"
-        )
+    def add_schema(schema):
+        super(PnccdCalcatFriend, PnccdCalcatFriend).add_schema(schema, "pnCCD-Type")
 
         # set some defaults for common parameters
         (
@@ -84,6 +82,7 @@ class PnccdCalcatFriend(base_calcat.BaseCalcatFriend):
         (
             BOOL_ELEMENT(schema)
             .key("constantParameters.ignoreBiasVoltage")
+            .tags("managed")
             .displayedName("Ignore bias voltage parameter")
             .assignmentOptional()
             .defaultValue(False)
@@ -92,6 +91,7 @@ class PnccdCalcatFriend(base_calcat.BaseCalcatFriend):
 
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.integrationTime")
+            .tags("managed")
             .displayedName("Integration Time")
             .assignmentOptional()
             .defaultValue(70)
@@ -100,6 +100,7 @@ class PnccdCalcatFriend(base_calcat.BaseCalcatFriend):
 
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.sensorTemperature")
+            .tags("managed")
             .assignmentOptional()
             .defaultValue(233)
             .reconfigurable()
@@ -107,6 +108,7 @@ class PnccdCalcatFriend(base_calcat.BaseCalcatFriend):
 
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.gainSetting")
+            .tags("managed")
             .displayedName("Gain Setting")
             .assignmentOptional()
             .defaultValue(16)
@@ -115,6 +117,7 @@ class PnccdCalcatFriend(base_calcat.BaseCalcatFriend):
 
             DOUBLE_ELEMENT(schema)
             .key("constantParameters.sourceEnergy")
+            .tags("managed")
             .displayedName("Source Energy")
             .assignmentOptional()
             .defaultValue(1.6)
@@ -122,12 +125,6 @@ class PnccdCalcatFriend(base_calcat.BaseCalcatFriend):
             .commit(),
         )
 
-        managed_keys.add("constantParameters.ignoreBiasVoltage")
-        managed_keys.add("constantParameters.integrationTime")
-        managed_keys.add("constantParameters.gainSetting")
-        managed_keys.add("constantParameters.sourceEnergy")
-        managed_keys.add("constantParameters.sensorTemperature")
-
         base_calcat.add_status_schema_from_enum(schema, Constants)
 
     def dark_condition(self):
@@ -323,7 +320,6 @@ class PnccdCorrection(base_correction.BaseCorrection):
     _kernel_runner_class = PnccdCpuRunner
     _calcat_friend_class = PnccdCalcatFriend
     _constant_enum_class = Constants
-    _managed_keys = base_correction.BaseCorrection._managed_keys.copy()
     _preview_outputs = ["outputRaw", "outputCorrected"]
     _cell_table_path = None
     _pulse_table_path = None
@@ -370,7 +366,6 @@ class PnccdCorrection(base_correction.BaseCorrection):
         )
         base_correction.add_correction_step_schema(
             expected,
-            PnccdCorrection._managed_keys,
             PnccdCorrection._correction_steps,
         )
         (
@@ -402,24 +397,9 @@ class PnccdCorrection(base_correction.BaseCorrection):
             .reconfigurable()
             .commit(),
         )
-        PnccdCorrection._managed_keys |= {
-            "corrections.commonMode.noiseSigma",
-            "corrections.commonMode.minFrac",
-            "corrections.commonMode.enableRow",
-            "corrections.commonMode.enableCol",
-        }
-        PnccdCalcatFriend.add_schema(expected, PnccdCorrection._managed_keys)
+        PnccdCalcatFriend.add_schema(expected)
         # TODO: bad pixel node?
 
-        # mandatory: manager needs this in schema
-        (
-            VECTOR_STRING_ELEMENT(expected)
-            .key("managedKeys")
-            .assignmentOptional()
-            .defaultValue(list(PnccdCorrection._managed_keys))
-            .commit()
-        )
-
     @property
     def input_data_shape(self):
         # TODO: check
diff --git a/src/tests/test_calcat_utils.py b/src/tests/test_calcat_utils.py
index e1d83c09..c78fb107 100644
--- a/src/tests/test_calcat_utils.py
+++ b/src/tests/test_calcat_utils.py
@@ -35,13 +35,10 @@ class DummyBaseDevice:
 # TODO: consider testing by attaching to real karabo.bound.PythonDevice
 class DummyAgipdDevice(DummyBaseDevice):
     device_class_schema = Schema()
-    managed_keys = set()
 
     @staticmethod
     def expectedParameters(expected):
-        AgipdCorrection.AgipdCalcatFriend.add_schema(
-            expected, DummyAgipdDevice.managed_keys
-        )
+        AgipdCorrection.AgipdCalcatFriend.add_schema(expected)
 
     def __init__(self, config):
         self.schema = config
@@ -49,7 +46,6 @@ class DummyAgipdDevice(DummyBaseDevice):
             self,
             _test_calcat_secrets_fn,
         )
-        print(self.managed_keys)
 
 
 DummyAgipdDevice.expectedParameters(DummyAgipdDevice.device_class_schema)
@@ -57,13 +53,10 @@ DummyAgipdDevice.expectedParameters(DummyAgipdDevice.device_class_schema)
 
 class DummyDsscDevice(DummyBaseDevice):
     device_class_schema = Schema()
-    managed_keys = set()
 
     @staticmethod
     def expectedParameters(expected):
-        DsscCorrection.DsscCalcatFriend.add_schema(
-            expected, DummyDsscDevice.managed_keys
-        )
+        DsscCorrection.DsscCalcatFriend.add_schema(expected, DummyDsscDevice)
 
     def __init__(self, config):
         # TODO: check config against schema (as Karabo would)
-- 
GitLab