From 6e6a52aa5db9009566c772bc3d9f658904f43e31 Mon Sep 17 00:00:00 2001
From: Justin Wilson <BZ2C7N@NAM.corp.gm.com>
Date: Tue, 25 Mar 2025 12:59:49 -0500
Subject: [PATCH 1/2] Improvements to threading and Qt GUI handling

Observers are now run on the threads that they are triggered on.
Implemented qtInvokeOnMainThreadBehavior to handle observers for gui elements and to provide a general purpose mechanism to run logic on the gui thread.
Refactored gui components that are observer-driven to use qtInvokeOnMainThread.
---
 .../pqSMTKAppComponentsAutoStart.cxx          |  51 +++---
 .../appcomponents/pqSMTKAttributePanel.cxx    | 110 ++++++------
 ...pqSMTKCloseWithActiveOperationBehavior.cxx |   4 +
 .../pqSMTKDisplayAttributeOnLoadBehavior.cxx  |  16 +-
 .../pqSMTKGroupComponentsBehavior.cxx         |  16 +-
 .../pqSMTKOperationHintsBehavior.cxx          |  30 ++--
 .../appcomponents/pqSMTKOperationPanel.cxx    |  44 ++---
 .../pqSMTKOperationParameterPanel.cxx         | 161 +++++++++---------
 .../pqSMTKPipelineSelectionBehavior.cxx       | 100 +++++------
 .../operators/smtkAssignColorsView.cxx        |   3 +-
 .../operators/smtkCoordinateTransformView.cxx |   3 +-
 .../smtkDataSetInfoInspectorView.cxx          |   3 +-
 .../operators/smtkEditPropertiesView.cxx      |  16 +-
 .../widgets/pqSMTKTransformWidget.cxx         |  65 +++----
 smtk/extension/qt/CMakeLists.txt              |   3 +
 .../qt/diagram/qtDefaultTaskNode.cxx          |   3 +-
 .../qt/diagram/qtDefaultTaskNode1.cxx         |   3 +-
 smtk/extension/qt/diagram/qtDiagram.cxx       |   5 +-
 smtk/extension/qt/diagram/qtTaskEditor.cxx    |  87 +++++-----
 smtk/extension/qt/diagram/qtTaskNode.cxx      |  19 ++-
 smtk/extension/qt/diagram/qtTaskPath.cxx      |  19 ++-
 .../qt/qtAssociation2ColumnWidget.cxx         |   5 +-
 smtk/extension/qt/qtAttributeView.cxx         |  26 +--
 smtk/extension/qt/qtAvailableOperations.cxx   |   5 +-
 .../extension/qt/qtComponentAttributeView.cxx |  22 +--
 .../extension/qt/qtDescriptivePhraseModel.cxx |   6 +-
 smtk/extension/qt/qtInstancedView.cxx         |  26 +--
 .../qt/qtInvokeOnMainThreadBehavior.cxx       | 122 +++++++++++++
 .../qt/qtInvokeOnMainThreadBehavior.h         | 104 +++++++++++
 smtk/extension/qt/qtOperationTypeModel.cxx    |  24 +--
 smtk/extension/qt/qtReferenceItem.cxx         |  11 +-
 smtk/extension/qt/qtReferenceItemEditor.cxx   |   5 +-
 smtk/extension/qt/qtReferenceTree.cxx         |  11 +-
 smtk/extension/qt/qtResourceBrowser.cxx       |   3 +-
 smtk/extension/qt/qtWorkletModel.cxx          |  30 ++--
 35 files changed, 742 insertions(+), 419 deletions(-)
 create mode 100644 smtk/extension/qt/qtInvokeOnMainThreadBehavior.cxx
 create mode 100644 smtk/extension/qt/qtInvokeOnMainThreadBehavior.h

diff --git a/smtk/extension/paraview/appcomponents/pqSMTKAppComponentsAutoStart.cxx b/smtk/extension/paraview/appcomponents/pqSMTKAppComponentsAutoStart.cxx
index 8c8275e1c9..0bda282ae4 100644
--- a/smtk/extension/paraview/appcomponents/pqSMTKAppComponentsAutoStart.cxx
+++ b/smtk/extension/paraview/appcomponents/pqSMTKAppComponentsAutoStart.cxx
@@ -30,6 +30,7 @@
 #include "smtk/extension/paraview/appcomponents/pqSMTKPythonTrace.h"
 #endif
 
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/extension/qt/qtSMTKUtilities.h"
 
 #include "pqApplicationCore.h"
@@ -95,7 +96,8 @@ void pqSMTKAppComponentsAutoStart::startup()
 
   auto* behavior = pqSMTKBehavior::instance(this);
   auto* renderResourceBehavior = pqSMTKRenderResourceBehavior::instance(this);
-  auto* callObserversOnMainThread = pqSMTKCallObserversOnMainThreadBehavior::instance(this);
+  //auto* callObserversOnMainThread = pqSMTKCallObserversOnMainThreadBehavior::instance(this);
+  auto* invokeOnMainThread = smtk::extension::qtInvokeOnMainThreadBehavior::instance(this);
   auto* pipelineSync = pqSMTKPipelineSelectionBehavior::instance(this);
   auto* displayOnLoad = pqSMTKDisplayAttributeOnLoadBehavior::instance(this);
   auto* registerImportersBehavior = pqSMTKRegisterImportersBehavior::instance(this);
@@ -118,12 +120,21 @@ void pqSMTKAppComponentsAutoStart::startup()
         pqSMTKCloseWithActiveOperationBehavior::instance(this);
       pqCore->registerManager("smtk close with active operation", closeWithActiveOperationBehavior);
     }
-    pqCore->registerManager("call observers on main thread", callObserversOnMainThread);
+    //pqCore->registerManager("call observers on main thread", callObserversOnMainThread);
+    pqCore->registerManager("invoke on main thread", invokeOnMainThread);
     pqCore->registerManager("smtk register importers", registerImportersBehavior);
     pqCore->registerManager("smtk pipeline selection sync", pipelineSync);
     pqCore->registerManager("smtk display attribute on load", displayOnLoad);
     pqCore->registerManager("operation hints", operationHints);
 
+    QObject::connect(
+      behavior,
+      QOverload<pqSMTKWrapper*, pqServer*>::of(&pqSMTKBehavior::addedManagerOnServer),
+      this,
+      [invokeOnMainThread](pqSMTKWrapper* smtkWrapper, pqServer*) {
+        invokeOnMainThread->addObservers(*smtkWrapper->smtkManagersPtr());
+      });
+
     QObject::connect(
       behavior,
       SIGNAL(addedManagerOnServer(pqSMTKWrapper*, pqServer*)),
@@ -174,7 +185,8 @@ void pqSMTKAppComponentsAutoStart::shutdown()
       pqCore->unRegisterManager("smtk save on close resource");
       pqCore->unRegisterManager("smtk close with active operation");
     }
-    pqCore->unRegisterManager("call observers on main thread");
+    //pqCore->unRegisterManager("call observers on main thread");
+    pqCore->unRegisterManager("invoke on main thread");
     pqCore->unRegisterManager("smtk register importers");
     pqCore->unRegisterManager("smtk pipeline selection sync");
   }
@@ -182,25 +194,26 @@ void pqSMTKAppComponentsAutoStart::shutdown()
 
 void pqSMTKAppComponentsAutoStart::observeWrapper(pqSMTKWrapper* wrapper, pqServer* /*server*/)
 {
-  m_p->m_opObserver = wrapper->smtkOperationManager()->observers().insert(
-    [this](
-      const smtk::operation::Operation& op,
-      smtk::operation::EventType event,
-      smtk::operation::Operation::Result const&
-      /*result*/) -> int {
-      (void)op;
-      if (event == smtk::operation::EventType::WILL_OPERATE)
-      {
+  m_p->m_opObserver =
+    smtk::extension::qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
+      [this](
+        const smtk::operation::Operation& op,
+        smtk::operation::EventType event,
+        smtk::operation::Operation::Result const&
+        /*result*/) -> int {
+        (void)op;
+        if (event == smtk::operation::EventType::WILL_OPERATE)
+        {
       // Trace operation in python
 #ifdef SMTK_PYTHON_ENABLED
-        this->m_p->m_pythonTrace.traceOperation(op);
+          this->m_p->m_pythonTrace.traceOperation(op);
 #endif
-      }
-      return 0;
-    },
-    std::numeric_limits<smtk::operation::Observers::Priority>::lowest(),
-    /* invoke observer on current selection */ false,
-    "pqSMTKAppComponentsAutoStart: observe operations and trace in python.");
+        }
+        return 0;
+      },
+      std::numeric_limits<smtk::operation::Observers::Priority>::lowest(),
+      /* invoke observer on current selection */ false,
+      "pqSMTKAppComponentsAutoStart: observe operations and trace in python.");
 }
 
 void pqSMTKAppComponentsAutoStart::unobserveWrapper(
diff --git a/smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.cxx b/smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.cxx
index ad3460849f..7b428401e0 100644
--- a/smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.cxx
+++ b/smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.cxx
@@ -19,6 +19,7 @@
 #include "smtk/extension/paraview/server/vtkSMTKSettings.h"
 
 #include "smtk/extension/qt/qtBaseView.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 
 #include "smtk/io/Logger.h"
 
@@ -315,10 +316,12 @@ void pqSMTKAttributePanel::observeProjectsOnServer(pqSMTKWrapper* mgr, pqServer*
   QPointer<pqSMTKAttributePanel> self(this);
   auto observerKey = projectManager->observers().insert(
     [self](const smtk::project::Project& project, smtk::project::EventType event) {
-      if (self)
-      {
-        self->handleProjectEvent(project, event);
-      }
+      smtk::extension::qtInvokeOnMainThreadBehavior::invokeOnMainThread([&] {
+        if (self)
+        {
+          self->handleProjectEvent(project, event);
+        }
+      });
     },
     0,    // assign a neutral priority
     true, // immediatelyNotify
@@ -367,24 +370,26 @@ void pqSMTKAttributePanel::handleProjectEvent(
         auto& activeTracker = taskManager->active();
         m_activeObserverKey = activeTracker.observers().insert(
           [this, self](smtk::task::Task* oldTask, smtk::task::Task* newTask) {
-            if (!self)
-            {
-              return;
-            }
-            // Stop observing the prior task (if any).
-            m_currentTaskObserverKey.release();
+            smtk::extension::qtInvokeOnMainThreadBehavior::invokeOnMainThread([&]() {
+              if (!self)
+              {
+                return;
+              }
+              // Stop observing the prior task (if any).
+              m_currentTaskObserverKey.release();
 
-            (void)oldTask;
-            if (m_currentTask)
-            {
-              m_currentTask->observers().erase(m_currentTaskObserverKey);
-              self->displayResource(nullptr);
-            }
-            m_currentTask = nullptr;
-            if (newTask)
-            {
-              self->displayTaskAttribute(newTask);
-            }
+              (void)oldTask;
+              if (m_currentTask)
+              {
+                m_currentTask->observers().erase(m_currentTaskObserverKey);
+                self->displayResource(nullptr);
+              }
+              m_currentTask = nullptr;
+              if (newTask)
+              {
+                self->displayTaskAttribute(newTask);
+              }
+            });
           },
           /* priority */ 0,
           /* initialize */ true,
@@ -532,29 +537,30 @@ bool pqSMTKAttributePanel::displayResourceInternal(
   {
     std::weak_ptr<smtk::resource::Manager> weakResourceManager = rsrcMgr;
     QPointer<pqSMTKAttributePanel> self(this);
-    m_observer = rsrcMgr->observers().insert(
-      [this, weakResourceManager, self](
-        const smtk::resource::Resource& attrRsrc, smtk::resource::EventType evnt) {
-        // Does the panel still exist?
-        if (self == nullptr)
-        {
-          return;
-        }
-        auto rsrc = m_rsrc.lock();
-        if (
-          rsrc == nullptr ||
-          (evnt == smtk::resource::EventType::REMOVED && &attrRsrc == rsrc.get()))
-        {
-          // The application is removing the attribute resource we are viewing.
-          // Clear out the panel and unobserve the manager.
-          this->resetPanel(weakResourceManager.lock());
-          this->updateTitle();
-          m_seln = nullptr;
-        }
-      },
-      std::numeric_limits<smtk::resource::Observers::Priority>::lowest(),
-      /* initialize */ false,
-      "pqSMTKAttributePanel: Clear panel if a removed resource is being displayed.");
+    m_observer =
+      smtk::extension::qtInvokeOnMainThreadBehavior::instance()->resourceObservers().insert(
+        [this, weakResourceManager, self](
+          const smtk::resource::Resource& attrRsrc, smtk::resource::EventType evnt) {
+          // Does the panel still exist?
+          if (self == nullptr)
+          {
+            return;
+          }
+          auto rsrc = m_rsrc.lock();
+          if (
+            rsrc == nullptr ||
+            (evnt == smtk::resource::EventType::REMOVED && &attrRsrc == rsrc.get()))
+          {
+            // The application is removing the attribute resource we are viewing.
+            // Clear out the panel and unobserve the manager.
+            this->resetPanel(weakResourceManager.lock());
+            this->updateTitle();
+            m_seln = nullptr;
+          }
+        },
+        std::numeric_limits<smtk::resource::Observers::Priority>::lowest(),
+        /* initialize */ false,
+        "pqSMTKAttributePanel: Clear panel if a removed resource is being displayed.");
   }
   m_rsrc = rsrc;
 
@@ -658,13 +664,15 @@ bool pqSMTKAttributePanel::displayTaskAttribute(smtk::task::Task* task)
       [self](smtk::task::Task& task, smtk::task::State priorState, smtk::task::State currentState) {
         (void)task;
         (void)priorState;
-        if (!self)
-        {
-          return;
-        }
-        self->m_attrUIMgr->setReadOnly(
-          currentState < smtk::task::State::Incomplete ||
-          currentState >= smtk::task::State::Completed);
+        smtk::extension::qtInvokeOnMainThreadBehavior::invokeOnMainThread([&]() {
+          if (!self)
+          {
+            return;
+          }
+          self->m_attrUIMgr->setReadOnly(
+            currentState < smtk::task::State::Incomplete ||
+            currentState >= smtk::task::State::Completed);
+        });
       },
       "AttributePanel current task state-tracking.");
     this->m_attrUIMgr->setReadOnly(
diff --git a/smtk/extension/paraview/appcomponents/pqSMTKCloseWithActiveOperationBehavior.cxx b/smtk/extension/paraview/appcomponents/pqSMTKCloseWithActiveOperationBehavior.cxx
index 62c79ec8b4..c39754ed49 100644
--- a/smtk/extension/paraview/appcomponents/pqSMTKCloseWithActiveOperationBehavior.cxx
+++ b/smtk/extension/paraview/appcomponents/pqSMTKCloseWithActiveOperationBehavior.cxx
@@ -43,8 +43,11 @@
 #include <QCheckBox>
 #include <QCloseEvent>
 
+#include <mutex>
+
 static pqSMTKCloseWithActiveOperationBehavior* g_instance = nullptr;
 
+std::mutex g_numberOfActiveOperationsMutex;
 static std::atomic<int> g_numberOfActiveOperations(0);
 
 pqSMTKCloseWithActiveOperationBehavior::pqSMTKCloseWithActiveOperationBehavior(QObject* parent)
@@ -102,6 +105,7 @@ void pqSMTKCloseWithActiveOperationBehavior::trackActiveOperations(
       const smtk::operation::Operation&,
       smtk::operation::EventType event,
       smtk::operation::Operation::Result) -> int {
+      std::lock_guard<std::mutex> guard(g_numberOfActiveOperationsMutex);
       if (event == smtk::operation::EventType::WILL_OPERATE)
       {
         ++g_numberOfActiveOperations;
diff --git a/smtk/extension/paraview/appcomponents/pqSMTKDisplayAttributeOnLoadBehavior.cxx b/smtk/extension/paraview/appcomponents/pqSMTKDisplayAttributeOnLoadBehavior.cxx
index d8f47ebb6b..3ba4f8dd2c 100644
--- a/smtk/extension/paraview/appcomponents/pqSMTKDisplayAttributeOnLoadBehavior.cxx
+++ b/smtk/extension/paraview/appcomponents/pqSMTKDisplayAttributeOnLoadBehavior.cxx
@@ -17,6 +17,7 @@
 #include "smtk/extension/paraview/appcomponents/pqSMTKWrapper.h"
 
 #include "smtk/extension/paraview/server/vtkSMSMTKWrapperProxy.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 
 #include "smtk/view/Selection.h"
 
@@ -98,13 +99,14 @@ void pqSMTKDisplayAttributeOnLoadBehavior::observeResourcesOnServer(
     return;
   }
 
-  smtk::resource::Observers::Key observerKey = rsrcMgr->observers().insert(
-    [this](const smtk::resource::Resource& rsrc, smtk::resource::EventType event) {
-      this->handleResourceEvent(rsrc, event);
-    },
-    0,    // assign a neutral priority
-    true, // immediatelyNotify
-    "pqSMTKDisplayAttributeOnLoadBehavior: Display new attribute resource in panel.");
+  smtk::resource::Observers::Key observerKey =
+    smtk::extension::qtInvokeOnMainThreadBehavior::instance()->resourceObservers().insert(
+      [this](const smtk::resource::Resource& rsrc, smtk::resource::EventType event) {
+        this->handleResourceEvent(rsrc, event);
+      },
+      0,    // assign a neutral priority
+      true, // immediatelyNotify
+      "pqSMTKDisplayAttributeOnLoadBehavior: Display new attribute resource in panel.");
   m_resourceManagerObservers[rsrcMgr] = std::move(observerKey);
 }
 
diff --git a/smtk/extension/paraview/appcomponents/pqSMTKGroupComponentsBehavior.cxx b/smtk/extension/paraview/appcomponents/pqSMTKGroupComponentsBehavior.cxx
index 319e089ffa..a912e4c1a9 100644
--- a/smtk/extension/paraview/appcomponents/pqSMTKGroupComponentsBehavior.cxx
+++ b/smtk/extension/paraview/appcomponents/pqSMTKGroupComponentsBehavior.cxx
@@ -31,6 +31,7 @@
 #include "smtk/extension/paraview/appcomponents/pqSMTKResource.h"
 #include "smtk/extension/paraview/appcomponents/pqSMTKWrapper.h"
 #include "smtk/extension/paraview/server/vtkSMTKSettings.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/extension/qt/qtOperationView.h"
 #include "smtk/extension/qt/qtUIManager.h"
 #include "smtk/io/Logger.h"
@@ -99,13 +100,14 @@ void pqGroupComponentsReaction::updateSelectionObserver(pqServer* server)
   auto selection = wrapper ? wrapper->smtkSelection() : nullptr;
   if (selection)
   {
-    m_observerKey = selection->observers().insert(
-      [this](const std::string&, std::shared_ptr<smtk::view::Selection> const&) {
-        this->updateEnableState();
-      },
-      std::numeric_limits<smtk::view::SelectionObservers::Priority>::lowest(),
-      /* initialize immediately */ true,
-      m_isGrouping ? "update grouping action" : "update ungrouping action");
+    m_observerKey =
+      smtk::extension::qtInvokeOnMainThreadBehavior::instance()->selectionObservers().insert(
+        [this](const std::string&, std::shared_ptr<smtk::view::Selection> const&) {
+          this->updateEnableState();
+        },
+        std::numeric_limits<smtk::view::SelectionObservers::Priority>::lowest(),
+        /* initialize immediately */ true,
+        m_isGrouping ? "update grouping action" : "update ungrouping action");
   }
   else
   {
diff --git a/smtk/extension/paraview/appcomponents/pqSMTKOperationHintsBehavior.cxx b/smtk/extension/paraview/appcomponents/pqSMTKOperationHintsBehavior.cxx
index b6f9507b23..e4083936de 100644
--- a/smtk/extension/paraview/appcomponents/pqSMTKOperationHintsBehavior.cxx
+++ b/smtk/extension/paraview/appcomponents/pqSMTKOperationHintsBehavior.cxx
@@ -17,6 +17,7 @@
 
 #include "smtk/extension/qt/diagram/qtTaskEditor.h"
 #include "smtk/extension/qt/qtDescriptivePhraseModel.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/extension/qt/qtResourceBrowser.h"
 
 #include "smtk/view/PhraseModel.h"
@@ -394,20 +395,21 @@ void pqSMTKOperationHintsBehavior::observeWrapper(pqSMTKWrapper* wrapper, pqServ
   auto operationManager = wrapper->smtkOperationManager();
   if (operationManager)
   {
-    bool didInsert = m_p->m_opObservers
-                       .emplace(
-                         server,
-                         operationManager->observers().insert(
-                           [this](
-                             const smtk::operation::Operation& op,
-                             smtk::operation::EventType eventType,
-                             smtk::operation::Operation::Result result) -> int {
-                             return this->processHints(op, eventType, result);
-                           },
-                           smtk::view::PhraseModel::operationObserverPriority() - 128,
-                           /*initialize*/ true,
-                           "Process operation hints."))
-                       .second;
+    bool didInsert =
+      m_p->m_opObservers
+        .emplace(
+          server,
+          smtk::extension::qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
+            [this](
+              const smtk::operation::Operation& op,
+              smtk::operation::EventType eventType,
+              smtk::operation::Operation::Result result) -> int {
+              return this->processHints(op, eventType, result);
+            },
+            smtk::view::PhraseModel::operationObserverPriority() - 128,
+            /*initialize*/ true,
+            "Process operation hints."))
+        .second;
     if (!didInsert)
     {
       smtkErrorMacro(
diff --git a/smtk/extension/paraview/appcomponents/pqSMTKOperationPanel.cxx b/smtk/extension/paraview/appcomponents/pqSMTKOperationPanel.cxx
index 1998547798..2d71a10bdd 100644
--- a/smtk/extension/paraview/appcomponents/pqSMTKOperationPanel.cxx
+++ b/smtk/extension/paraview/appcomponents/pqSMTKOperationPanel.cxx
@@ -13,6 +13,7 @@
 #include "smtk/extension/paraview/appcomponents/pqSMTKRenderResourceBehavior.h"
 #include "smtk/extension/paraview/appcomponents/pqSMTKResource.h"
 #include "smtk/extension/paraview/appcomponents/pqSMTKWrapper.h"
+#include <smtk/extension/qt/qtInvokeOnMainThreadBehavior.h>
 
 #include "smtk/operation/Operation.h"
 
@@ -292,29 +293,30 @@ bool pqSMTKOperationPanel::editOperation(smtk::operation::OperationPtr op)
     // If the operation's specification is destroyed, then
     // get rid of the UI.
     std::weak_ptr<smtk::resource::Manager> weakResourceManager = rsrcMgr;
-    m_observer = rsrcMgr->observers().insert(
-      [this, weakResourceManager](
-        const smtk::resource::Resource& attrRsrc, smtk::resource::EventType evnt) {
-        auto rsrc = m_rsrc.lock();
-        if (
-          rsrc == nullptr ||
-          (evnt == smtk::resource::EventType::REMOVED && &attrRsrc == rsrc.get()))
-        {
-          // The application is removing the attribute resource we are viewing.
-          // Clear out the panel and unobserve the manager.
-          if (auto rsrcMgr = weakResourceManager.lock())
-          {
-            rsrcMgr->observers().erase(m_observer);
-          }
-          delete m_attrUIMgr;
-          while (QWidget* w = m_p->OperationEditor->findChild<QWidget*>())
+    m_observer =
+      smtk::extension::qtInvokeOnMainThreadBehavior::instance()->resourceObservers().insert(
+        [this, weakResourceManager](
+          const smtk::resource::Resource& attrRsrc, smtk::resource::EventType evnt) {
+          auto rsrc = m_rsrc.lock();
+          if (
+            rsrc == nullptr ||
+            (evnt == smtk::resource::EventType::REMOVED && &attrRsrc == rsrc.get()))
           {
-            delete w;
+            // The application is removing the attribute resource we are viewing.
+            // Clear out the panel and unobserve the manager.
+            if (auto rsrcMgr = weakResourceManager.lock())
+            {
+              rsrcMgr->observers().erase(m_observer);
+            }
+            delete m_attrUIMgr;
+            while (QWidget* w = m_p->OperationEditor->findChild<QWidget*>())
+            {
+              delete w;
+            }
+            m_attrUIMgr = nullptr;
           }
-          m_attrUIMgr = nullptr;
-        }
-      },
-      "pqSMTKOperationPanel: Clear panel if a removed operation specification is displayed.");
+        },
+        "pqSMTKOperationPanel: Clear panel if a removed operation specification is displayed.");
   }
   return didDisplay;
 }
diff --git a/smtk/extension/paraview/appcomponents/pqSMTKOperationParameterPanel.cxx b/smtk/extension/paraview/appcomponents/pqSMTKOperationParameterPanel.cxx
index 3cfd8b8855..a687924569 100644
--- a/smtk/extension/paraview/appcomponents/pqSMTKOperationParameterPanel.cxx
+++ b/smtk/extension/paraview/appcomponents/pqSMTKOperationParameterPanel.cxx
@@ -15,6 +15,7 @@
 #include "smtk/extension/paraview/appcomponents/pqSMTKResource.h"
 #include "smtk/extension/paraview/appcomponents/pqSMTKWrapper.h"
 
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/extension/qt/qtOperationTypeModel.h"
 #include "smtk/extension/qt/qtOperationView.h"
 
@@ -628,10 +629,12 @@ void pqSMTKOperationParameterPanel::observeProjectsOnServer()
   QPointer<pqSMTKOperationParameterPanel> self(this);
   auto observerKey = projectManager->observers().insert(
     [self](const smtk::project::Project& project, smtk::project::EventType event) {
-      if (self)
-      {
-        self->handleProjectEvent(project, event);
-      }
+      smtk::extension::qtInvokeOnMainThreadBehavior::invokeOnMainThread([&]() {
+        if (self)
+        {
+          self->handleProjectEvent(project, event);
+        }
+      });
     },
     0,    // assign a neutral priority
     true, // immediatelyNotify
@@ -669,17 +672,45 @@ void pqSMTKOperationParameterPanel::handleProjectEvent(
       auto& activeTracker = taskManager->active();
       m_activeObserverKey = activeTracker.observers().insert(
         [this, &activeTracker](smtk::task::Task* oldTask, smtk::task::Task* newTask) {
-          m_taskObserver.release();
-          // First, if oldTask is an operation task, remove its parameter-panel
-          // (if it was configured to display one).
-          smtk::task::SubmitOperationAgent::RunStyle runStyle;
-          auto* prevOp = operationForTask(oldTask);
-          if (prevOp)
-          {
-            auto styles = oldTask->style();
+          smtk::extension::qtInvokeOnMainThreadBehavior::invokeOnMainThread([&]() {
+            m_taskObserver.release();
+            // First, if oldTask is an operation task, remove its parameter-panel
+            // (if it was configured to display one).
+            smtk::task::SubmitOperationAgent::RunStyle runStyle;
+            auto* prevOp = operationForTask(oldTask);
+            if (prevOp)
+            {
+              auto styles = oldTask->style();
+              for (const auto& style : styles)
+              {
+                auto configurations = oldTask->manager()->getStyle(style);
+                if (!configurations.contains("operation-panel"))
+                {
+                  continue;
+                }
+                auto section = configurations.at("operation-panel");
+                bool display = !section.contains("display") || section.at("display").get<bool>();
+                if (!display)
+                {
+                  continue;
+                }
+                this->closeTabForOperation(prevOp->shared_from_this(), true);
+              }
+            }
+
+            // Now that we've "deactivated" any previous operation task, see if we
+            // need to "activate" a new one.
+            auto* nextOp = operationForTask(newTask, &runStyle);
+            if (!nextOp)
+            {
+              return;
+            }
+
+            nlohmann::json panelStyle;
+            auto styles = newTask->style();
             for (const auto& style : styles)
             {
-              auto configurations = oldTask->manager()->getStyle(style);
+              auto configurations = newTask->manager()->getStyle(style);
               if (!configurations.contains("operation-panel"))
               {
                 continue;
@@ -690,62 +721,37 @@ void pqSMTKOperationParameterPanel::handleProjectEvent(
               {
                 continue;
               }
-              this->closeTabForOperation(prevOp->shared_from_this(), true);
-            }
-          }
-
-          // Now that we've "deactivated" any previous operation task, see if we
-          // need to "activate" a new one.
-          auto* nextOp = operationForTask(newTask, &runStyle);
-          if (!nextOp)
-          {
-            return;
-          }
-
-          nlohmann::json panelStyle;
-          auto styles = newTask->style();
-          for (const auto& style : styles)
-          {
-            auto configurations = newTask->manager()->getStyle(style);
-            if (!configurations.contains("operation-panel"))
-            {
-              continue;
-            }
-            auto section = configurations.at("operation-panel");
-            bool display = !section.contains("display") || section.at("display").get<bool>();
-            if (!display)
-            {
-              continue;
-            }
-            panelStyle = section;
-            // At this point, we know we are going to display this operation to the user
-            // Observe the task so when it transitions state we can react (e.g., by
-            // removing the operation tab when the task is completed.)
-            QPointer<pqSMTKOperationParameterPanel> self(this);
-            m_taskObserver = newTask->observers().insert([self, &activeTracker](
-                                                           smtk::task::Task& task,
-                                                           smtk::task::State priorState,
-                                                           smtk::task::State currentState) {
-              // If the panel has been destroyed or the task being
-              // observed is no longer active, ignore this event.
-              if (!self || activeTracker.task() != &task)
+              panelStyle = section;
+              // At this point, we know we are going to display this operation to the user
+              // Observe the task so when it transitions state we can react (e.g., by
+              // removing the operation tab when the task is completed.)
+              QPointer<pqSMTKOperationParameterPanel> self(this);
+              m_taskObserver = newTask->observers().insert([self, &activeTracker](
+                                                             smtk::task::Task& task,
+                                                             smtk::task::State priorState,
+                                                             smtk::task::State currentState) {
+                smtk::extension::qtInvokeOnMainThreadBehavior::invokeOnMainThread([&]() {
+                  // If the panel has been destroyed or the task being
+                  // observed is no longer active, ignore this event.
+                  if (!self || activeTracker.task() != &task)
+                  {
+                    return;
+                  }
+                  self->activeTaskStateChange(task, priorState, currentState);
+                });
+              });
+              // TODO: Fetch proper view-config from the task based on the "view" tag's
+              //       value (one of "anew"/"override"/"modified").
+              if (newTask->state() != smtk::task::State::Completed)
               {
-                return;
-              }
-              self->activeTaskStateChange(task, priorState, currentState);
-            });
-            // TODO: Fetch proper view-config from the task based on the "view" tag's
-            //       value (one of "anew"/"override"/"modified").
-            if (newTask->state() != smtk::task::State::Completed)
-            {
-              // Find or create TabData instance
-              TabData* tabData = this->tabDataForOperation(*nextOp);
-              if (tabData == nullptr)
-              {
-                tabData = this->createTabData(nextOp);
-              }
-              // Get the view configuration
-              smtk::view::ConfigurationPtr view = tabData->m_uiMgr->findOrCreateOperationView();
+                // Find or create TabData instance
+                TabData* tabData = this->tabDataForOperation(*nextOp);
+                if (tabData == nullptr)
+                {
+                  tabData = this->createTabData(nextOp);
+                }
+                // Get the view configuration
+                smtk::view::ConfigurationPtr view = tabData->m_uiMgr->findOrCreateOperationView();
 
 #if 0
               // Process any style:hide-items in the task's style
@@ -755,16 +761,17 @@ void pqSMTKOperationParameterPanel::handleProjectEvent(
               }
 #endif
 
-              this->editExistingOperationParameters(
-                nextOp->shared_from_this(),
-                /* associate selection? */
-                false, // TODO: Determine whether nextOp->associations() is specified.
-                /* allow tab to be closed? */ false,
-                /* show apply button */ runStyle !=
-                  smtk::task::SubmitOperationAgent::RunStyle::OnCompletion,
-                view);
+                this->editExistingOperationParameters(
+                  nextOp->shared_from_this(),
+                  /* associate selection? */
+                  false, // TODO: Determine whether nextOp->associations() is specified.
+                  /* allow tab to be closed? */ false,
+                  /* show apply button */ runStyle !=
+                    smtk::task::SubmitOperationAgent::RunStyle::OnCompletion,
+                  view);
+              }
             }
-          }
+          });
         });
     }
     break;
diff --git a/smtk/extension/paraview/appcomponents/pqSMTKPipelineSelectionBehavior.cxx b/smtk/extension/paraview/appcomponents/pqSMTKPipelineSelectionBehavior.cxx
index f47cae0ed2..cea66dd723 100644
--- a/smtk/extension/paraview/appcomponents/pqSMTKPipelineSelectionBehavior.cxx
+++ b/smtk/extension/paraview/appcomponents/pqSMTKPipelineSelectionBehavior.cxx
@@ -18,6 +18,8 @@
 
 #include "smtk/extension/paraview/server/vtkSMSMTKWrapperProxy.h"
 
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
+
 #include "smtk/view/Selection.h"
 
 #include "smtk/resource/Resource.h"
@@ -114,67 +116,69 @@ void pqSMTKPipelineSelectionBehavior::observeSelectionOnServer(
     return;
   }
 
-  auto observerId = seln->observers().insert(
-    [&](const std::string& source, smtk::view::SelectionPtr selection) {
-      int selnValue = selection->selectionValueFromLabel(m_selectionValue);
-      if (source != "pqSMTKPipelineSelectionBehavior")
-      {
-        smtk::resource::ResourcePtr selectedResource;
-        selection->visitSelection([&](smtk::resource::PersistentObject::Ptr obj, int val) {
-          if ((val & selnValue) == selnValue)
-          {
-            auto rsrc = std::dynamic_pointer_cast<smtk::resource::Resource>(obj);
-            if (rsrc)
-            {
-              selectedResource = rsrc;
-            }
-          }
-        });
-        if (selectedResource)
+  auto observerId =
+    smtk::extension::qtInvokeOnMainThreadBehavior::instance()->selectionObservers().insert(
+      [&](const std::string& source, smtk::view::SelectionPtr selection) {
+        int selnValue = selection->selectionValueFromLabel(m_selectionValue);
+        if (source != "pqSMTKPipelineSelectionBehavior")
         {
-          // Make the reader owning the first selected resource the active PV pipeline source:
-          auto* behavior = pqSMTKBehavior::instance();
-          auto rsrcSrc = behavior->getPVResource(selectedResource);
-          if (rsrcSrc)
-          {
-            pqActiveObjects::instance().setActiveSource(rsrcSrc);
-          }
-          // Also, if the selected resource is an attribute and we are configured to display
-          // selected attributes in the editor panel, do so:
-          if (m_displayAttributeResourcesOnSelection)
-          {
-            auto attrResource =
-              std::dynamic_pointer_cast<smtk::attribute::Resource>(selectedResource);
-            if (attrResource && !attrResource->isPrivate())
+          smtk::resource::ResourcePtr selectedResource;
+          selection->visitSelection([&](smtk::resource::PersistentObject::Ptr obj, int val) {
+            if ((val & selnValue) == selnValue)
             {
-              // Find the attribute panel. For now, only deal with one;
-              // it doesn't make sense to switch multiple panels to display
-              // the same attribute anyway.
-              // pqSMTKDockBase* dock = nullptr;
-              QMainWindow* mainWindow = qobject_cast<QMainWindow*>(pqCoreUtilities::mainWidget());
-              if (!mainWindow)
+              auto rsrc = std::dynamic_pointer_cast<smtk::resource::Resource>(obj);
+              if (rsrc)
               {
-                return;
+                selectedResource = rsrc;
               }
-              Q_FOREACH (
-                pqSMTKAttributeDock* dock, mainWindow->findChildren<pqSMTKAttributeDock*>())
+            }
+          });
+          if (selectedResource)
+          {
+            // Make the reader owning the first selected resource the active PV pipeline source:
+            auto* behavior = pqSMTKBehavior::instance();
+            auto rsrcSrc = behavior->getPVResource(selectedResource);
+            if (rsrcSrc)
+            {
+              pqActiveObjects::instance().setActiveSource(rsrcSrc);
+            }
+            // Also, if the selected resource is an attribute and we are configured to display
+            // selected attributes in the editor panel, do so:
+            if (m_displayAttributeResourcesOnSelection)
+            {
+              auto attrResource =
+                std::dynamic_pointer_cast<smtk::attribute::Resource>(selectedResource);
+              if (attrResource && !attrResource->isPrivate())
               {
-                if (dock)
+                // Find the attribute panel. For now, only deal with one;
+                // it doesn't make sense to switch multiple panels to display
+                // the same attribute anyway.
+                // pqSMTKDockBase* dock = nullptr;
+                QMainWindow* mainWindow = qobject_cast<QMainWindow*>(pqCoreUtilities::mainWidget());
+                if (!mainWindow)
+                {
+                  return;
+                }
+                Q_FOREACH (
+                  pqSMTKAttributeDock* dock, mainWindow->findChildren<pqSMTKAttributeDock*>())
                 {
-                  pqSMTKAttributePanel* panel = qobject_cast<pqSMTKAttributePanel*>(dock->widget());
-                  if (panel)
+                  if (dock)
                   {
-                    panel->displayPipelineSource(rsrcSrc);
-                    break;
+                    pqSMTKAttributePanel* panel =
+                      qobject_cast<pqSMTKAttributePanel*>(dock->widget());
+                    if (panel)
+                    {
+                      panel->displayPipelineSource(rsrcSrc);
+                      break;
+                    }
                   }
                 }
               }
             }
           }
         }
-      }
-    },
-    "pqSMTKPipelineSelectionBehavior: Select ParaView pipeline representing SMTK selection.");
+      },
+      "pqSMTKPipelineSelectionBehavior: Select ParaView pipeline representing SMTK selection.");
   m_selectionObservers[seln] = std::move(observerId);
 }
 
diff --git a/smtk/extension/paraview/operators/smtkAssignColorsView.cxx b/smtk/extension/paraview/operators/smtkAssignColorsView.cxx
index b2c1cbd402..1e442917f7 100644
--- a/smtk/extension/paraview/operators/smtkAssignColorsView.cxx
+++ b/smtk/extension/paraview/operators/smtkAssignColorsView.cxx
@@ -17,6 +17,7 @@
 #include "smtk/attribute/StringItem.h"
 #include "smtk/extension/qt/qtAttribute.h"
 #include "smtk/extension/qt/qtUIManager.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/operation/Manager.h"
 #include "smtk/operation/Observer.h"
 #include "smtk/operation/Operation.h"
@@ -174,7 +175,7 @@ smtkAssignColorsView::smtkAssignColorsView(const smtk::view::Information& info)
   QPointer<smtkAssignColorsView> self(this);
   auto opMgr = this->Internals->CurrentOp->manager();
   this->Internals->ObserverKey =
-    opMgr->observers().insert([self, this](
+    qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert([self, this](
                                 const smtk::operation::Operation& op,
                                 smtk::operation::EventType event,
                                 smtk::operation::Operation::Result result) {
diff --git a/smtk/extension/paraview/operators/smtkCoordinateTransformView.cxx b/smtk/extension/paraview/operators/smtkCoordinateTransformView.cxx
index 38e1b87da8..4f6449fcc3 100644
--- a/smtk/extension/paraview/operators/smtkCoordinateTransformView.cxx
+++ b/smtk/extension/paraview/operators/smtkCoordinateTransformView.cxx
@@ -27,6 +27,7 @@
 #include "smtk/extension/qt/qtAttribute.h"
 #include "smtk/extension/qt/qtAttributeItemInfo.h"
 #include "smtk/extension/qt/qtUIManager.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/operation/Manager.h"
 #include "smtk/operation/Observer.h"
 #include "smtk/resource/Component.h"
@@ -408,7 +409,7 @@ public:
       smtkErrorMacro(smtk::io::Logger::instance(), "No operation manager!");
       return;
     }
-    m_operationObserver = opMgr->observers().insert(
+    m_operationObserver = qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
       [this](
         const smtk::operation::Operation& op,
         smtk::operation::EventType event,
diff --git a/smtk/extension/paraview/operators/smtkDataSetInfoInspectorView.cxx b/smtk/extension/paraview/operators/smtkDataSetInfoInspectorView.cxx
index 33368f0be6..80a5e4df71 100644
--- a/smtk/extension/paraview/operators/smtkDataSetInfoInspectorView.cxx
+++ b/smtk/extension/paraview/operators/smtkDataSetInfoInspectorView.cxx
@@ -21,6 +21,7 @@
 #include "smtk/common/StringUtil.h"
 #include "smtk/extension/qt/qtAttribute.h"
 #include "smtk/extension/qt/qtUIManager.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/operation/Manager.h"
 #include "smtk/view/Configuration.h"
 
@@ -298,7 +299,7 @@ void smtkDataSetInfoInspectorView::requestOperation(const smtk::operation::Opera
       }
       return 0; // 0 = do not cancel operation
     };
-    m_p->m_opObserver = opManager->observers().insert(
+    m_p->m_opObserver = qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
       observer,
       std::numeric_limits<smtk::operation::Observers::Priority>::lowest(),
       /* initialize */ false,
diff --git a/smtk/extension/paraview/operators/smtkEditPropertiesView.cxx b/smtk/extension/paraview/operators/smtkEditPropertiesView.cxx
index 80bc14e068..dac24557df 100644
--- a/smtk/extension/paraview/operators/smtkEditPropertiesView.cxx
+++ b/smtk/extension/paraview/operators/smtkEditPropertiesView.cxx
@@ -21,6 +21,7 @@
 #include "smtk/attribute/StringItem.h"
 #include "smtk/extension/qt/qtAttribute.h"
 #include "smtk/extension/qt/qtAttributeItemInfo.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/extension/qt/qtUIManager.h"
 #include "smtk/operation/Manager.h"
 #include "smtk/resource/Component.h"
@@ -496,13 +497,14 @@ smtkEditPropertiesView::smtkEditPropertiesView(const smtk::view::Information& in
   {
     const auto& managers = uiManager->managers();
     const auto& selection = managers.get<smtk::view::Selection::Ptr>();
-    m_p->m_selectionObserver = selection->observers().insert(
-      [this](const std::string& src, std::shared_ptr<smtk::view::Selection> const& sel) {
-        m_p->selectionModified(src, sel);
-      },
-      std::numeric_limits<smtk::view::SelectionObservers::Priority>::lowest(),
-      /* initialize immediately */ true,
-      "update freeform attribute editor");
+    m_p->m_selectionObserver =
+      qtInvokeOnMainThreadBehavior::instance()->selectionObservers().insert(
+        [this](const std::string& src, std::shared_ptr<smtk::view::Selection> const& sel) {
+          m_p->selectionModified(src, sel);
+        },
+        std::numeric_limits<smtk::view::SelectionObservers::Priority>::lowest(),
+        /* initialize immediately */ true,
+        "update freeform attribute editor");
   }
 }
 
diff --git a/smtk/extension/paraview/widgets/pqSMTKTransformWidget.cxx b/smtk/extension/paraview/widgets/pqSMTKTransformWidget.cxx
index bcc8f398db..7776efee5e 100644
--- a/smtk/extension/paraview/widgets/pqSMTKTransformWidget.cxx
+++ b/smtk/extension/paraview/widgets/pqSMTKTransformWidget.cxx
@@ -17,6 +17,7 @@
 
 #include "smtk/attribute/operators/Signal.h"
 
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/extension/qt/qtOperationView.h"
 
 #include "smtk/geometry/queries/BoundingBox.h"
@@ -59,10 +60,11 @@ template<typename MyClass>
 class CanHideReferenceBounds
 {
   template<typename X>
-  static std::true_type testConstructorWithArgs(decltype(X{ std::declval<vtkSMProxy*>(),
-                                                            std::declval<vtkSMPropertyGroup*>(),
-                                                            std::declval<QWidget*>(),
-                                                            std::declval<bool>() })*);
+  static std::true_type testConstructorWithArgs(
+    decltype(X{ std::declval<vtkSMProxy*>(),
+                std::declval<vtkSMPropertyGroup*>(),
+                std::declval<QWidget*>(),
+                std::declval<bool>() })*);
   template<typename X>
   static std::false_type testConstructorWithArgs(...);
 
@@ -107,34 +109,35 @@ pqSMTKTransformWidget::pqSMTKTransformWidget(
 {
 
   QPointer<pqSMTKTransformWidget> guardedObject(this);
-  m_internal->m_opObserver = info.baseView()->uiManager()->operationManager()->observers().insert(
-    [guardedObject](
-      const smtk::operation::Operation& op,
-      smtk::operation::EventType event,
-      smtk::operation::Operation::Result res) {
-      if (
-        !guardedObject || !guardedObject->item() ||
-        event != smtk::operation::EventType::DID_OPERATE ||
-        op.index() == std::type_index(typeid(smtk::attribute::Signal)).hash_code())
-      {
-        return 0;
-      }
-
-      auto attribute = guardedObject->item()->attribute();
-      if (!attribute)
-      {
+  m_internal->m_opObserver =
+    smtk::extension::qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
+      [guardedObject](
+        const smtk::operation::Operation& op,
+        smtk::operation::EventType event,
+        smtk::operation::Operation::Result res) {
+        if (
+          !guardedObject || !guardedObject->item() ||
+          event != smtk::operation::EventType::DID_OPERATE ||
+          op.index() == std::type_index(typeid(smtk::attribute::Signal)).hash_code())
+        {
+          return 0;
+        }
+
+        auto attribute = guardedObject->item()->attribute();
+        if (!attribute)
+        {
+          return 0;
+        }
+
+        smtk::resource::PersistentObjectPtr object = attribute->associations()->value();
+
+        if (res->findReference("modified")->contains(attribute->associations()->value()))
+        {
+          guardedObject->resetWidget();
+        }
         return 0;
-      }
-
-      smtk::resource::PersistentObjectPtr object = attribute->associations()->value();
-
-      if (res->findReference("modified")->contains(attribute->associations()->value()))
-      {
-        guardedObject->resetWidget();
-      }
-      return 0;
-    },
-    "pqSMTKTransformWidget: Reset widget if the item being transformed is modified.");
+      },
+      "pqSMTKTransformWidget: Reset widget if the item being transformed is modified.");
 
   this->createWidget();
 }
diff --git a/smtk/extension/qt/CMakeLists.txt b/smtk/extension/qt/CMakeLists.txt
index f27150fc62..e23c700807 100644
--- a/smtk/extension/qt/CMakeLists.txt
+++ b/smtk/extension/qt/CMakeLists.txt
@@ -25,6 +25,7 @@ set(QAttrLibSrcs
   qtAssociation2ColumnWidget.cxx
   qtAttributeView.cxx
   qtInstancedView.cxx
+  qtInvokeOnMainThreadBehavior.cxx
   qtComponentAttributeView.cxx
   qtOperationAction.cxx
   qtOperationDialog.cxx
@@ -148,6 +149,7 @@ set(QAttrLibMocHeaders
   qtAssociationView.h
   qtAttributeView.h
   qtInstancedView.h
+  qtInvokeOnMainThreadBehavior.h
   qtComponentAttributeView.h
   qtNotEditableDelegate.h
   qtOperationAction.h
@@ -245,6 +247,7 @@ set(QAttrLibHeaders
   qtDirectoryItem.h
   qtDoubleItem.h
   qtIntItem.h
+  qtInvokeOnMainThreadBehavior.h
   qtResourceBrowserP.h
   qtSMTKUtilities.h
   qtStringItem.h
diff --git a/smtk/extension/qt/diagram/qtDefaultTaskNode.cxx b/smtk/extension/qt/diagram/qtDefaultTaskNode.cxx
index 1f42c2dd93..68a8fd4a39 100644
--- a/smtk/extension/qt/diagram/qtDefaultTaskNode.cxx
+++ b/smtk/extension/qt/diagram/qtDefaultTaskNode.cxx
@@ -15,6 +15,7 @@
 #include "smtk/extension/qt/diagram/qtDiagramViewConfiguration.h"
 #include "smtk/extension/qt/diagram/qtTaskEditor.h"
 #include "smtk/extension/qt/qtBaseView.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 
 #include "smtk/attribute/Attribute.h"
 #include "smtk/attribute/StringItem.h"
@@ -110,7 +111,7 @@ public:
         // so, check that qApp exists before going further.
         if (qApp)
         {
-          this->updateTaskState(prev, next, m_node->isActive());
+          qtInvokeOnMainThreadBehavior::invokeOnMainThread([&]() { this->updateTaskState(prev, next, m_node->isActive()); });
         }
       },
       "DefaultTaskNodeWidget observer");
diff --git a/smtk/extension/qt/diagram/qtDefaultTaskNode1.cxx b/smtk/extension/qt/diagram/qtDefaultTaskNode1.cxx
index 11f2a9a30c..563133315e 100644
--- a/smtk/extension/qt/diagram/qtDefaultTaskNode1.cxx
+++ b/smtk/extension/qt/diagram/qtDefaultTaskNode1.cxx
@@ -15,6 +15,7 @@
 #include "smtk/extension/qt/diagram/qtDiagramViewConfiguration.h"
 #include "smtk/extension/qt/diagram/qtTaskEditor.h"
 #include "smtk/extension/qt/qtBaseView.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 
 #include "smtk/task/Active.h"
 #include "smtk/task/Manager.h"
@@ -122,7 +123,7 @@ public:
         // so, check that qApp exists before going further.
         if (qApp)
         {
-          this->updateTaskState(prev, next, m_node->isActive());
+          qtInvokeOnMainThreadBehavior::invokeOnMainThread([&]() { this->updateTaskState(prev, next, m_node->isActive()); });
         }
       },
       "DefaultTaskNodeWidget1 observer");
diff --git a/smtk/extension/qt/diagram/qtDiagram.cxx b/smtk/extension/qt/diagram/qtDiagram.cxx
index e03dfc602f..12a2cf45e2 100644
--- a/smtk/extension/qt/diagram/qtDiagram.cxx
+++ b/smtk/extension/qt/diagram/qtDiagram.cxx
@@ -26,6 +26,7 @@
 #include "smtk/common/json/jsonUUID.h"
 
 #include "smtk/extension/qt/SVGIconEngine.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/extension/qt/qtManager.h"
 
 // nodes
@@ -396,7 +397,7 @@ public:
 
     if (m_operationManager)
     {
-      m_onKey = m_operationManager->observers().insert(
+      m_onKey = qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
         [this](
           const smtk::operation::Operation& op,
           smtk::operation::EventType event,
@@ -419,7 +420,7 @@ public:
       if (selection)
       {
         QPointer<qtDiagram> selfOrNull(m_self);
-        m_selectionObserver = selection->observers().insert(
+        m_selectionObserver = qtInvokeOnMainThreadBehavior::instance()->selectionObservers().insert(
           [this, selfOrNull](const std::string& changeSource, smtk::view::Selection::Ptr) {
             // Do nothing if our parent qtDiagram object has been destroyed.
             if (!selfOrNull)
diff --git a/smtk/extension/qt/diagram/qtTaskEditor.cxx b/smtk/extension/qt/diagram/qtTaskEditor.cxx
index 22f67971af..36bbef520e 100644
--- a/smtk/extension/qt/diagram/qtTaskEditor.cxx
+++ b/smtk/extension/qt/diagram/qtTaskEditor.cxx
@@ -36,6 +36,8 @@
 #include "smtk/extension/qt/diagram/qtPanMode.h"
 #include "smtk/extension/qt/diagram/qtSelectMode.h"
 
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
+
 #include "smtk/view/Configuration.h"
 #include "smtk/view/json/jsonView.h"
 
@@ -299,8 +301,9 @@ bool qtTaskEditor::updateArcs(
         continue;
       }
       if (
-        depTasks.find(std::static_pointer_cast<smtk::task::Task>(
-          predecessor->task()->shared_from_this())) != depTasks.end())
+        depTasks.find(
+          std::static_pointer_cast<smtk::task::Task>(predecessor->task()->shared_from_this())) !=
+        depTasks.end())
       {
         // This predecessor is still a dependency. No need to remove it.
         continue;
@@ -594,45 +597,47 @@ void qtTaskEditor::updateSceneNodes(
       m_p->m_worklets->setToplevelCatagoryExpression(m_p->m_taskManager->toplevelExpression());
       m_p->m_activeObserverKey = project->taskManager().active().observers().insert(
         [this, self](smtk::task::Task* prev, smtk::task::Task* next) {
-          if (!self)
-          {
-            return;
-          }
-          // std::cout << "Switch active task from " << prev << " to " << next << "\n";
-          // What is the current tail of the task path
-          auto* currentLastTask = m_taskPath->lastTask();
-          auto* prevTaskNode = this->findTaskNode(prev);
-          auto* nextTaskNode = this->findTaskNode(next);
-          if (prevTaskNode)
-          {
-            auto prevState = prev->state();
-            prevTaskNode->updateTaskState(prevState, prevState, false);
-            prevTaskNode->setOutlineStyle(qtBaseTaskNode::OutlineStyle::Normal);
-          }
-          if (nextTaskNode)
-          {
-            auto nextState = next->state();
-            nextTaskNode->updateTaskState(nextState, nextState, true);
-            nextTaskNode->setOutlineStyle(qtBaseTaskNode::OutlineStyle::Active);
-          }
-          this->m_taskPath->setActiveTask(next);
-          // Update visibility if needed
-          auto* newLastTask = m_taskPath->lastTask();
-          this->updateVisibility(currentLastTask, newLastTask);
-          // Update the worklet palette
-          shared_ptr<smtk::task::Task> task;
-          // Is there a new active task?
-          if (next)
-          {
-            task = std::dynamic_pointer_cast<smtk::task::Task>(next->shared_from_this());
-          }
-          // Else what is the last task listed in the task path?
-          else if (m_taskPath->lastTask())
-          {
-            task = std::dynamic_pointer_cast<smtk::task::Task>(
-              m_taskPath->lastTask()->shared_from_this());
-          }
-          m_p->m_worklets->setParentTask(task);
+          qtInvokeOnMainThreadBehavior::invokeOnMainThread([&]() {
+            if (!self)
+            {
+              return;
+            }
+            // std::cout << "Switch active task from " << prev << " to " << next << "\n";
+            // What is the current tail of the task path
+            auto* currentLastTask = m_taskPath->lastTask();
+            auto* prevTaskNode = this->findTaskNode(prev);
+            auto* nextTaskNode = this->findTaskNode(next);
+            if (prevTaskNode)
+            {
+              auto prevState = prev->state();
+              prevTaskNode->updateTaskState(prevState, prevState, false);
+              prevTaskNode->setOutlineStyle(qtBaseTaskNode::OutlineStyle::Normal);
+            }
+            if (nextTaskNode)
+            {
+              auto nextState = next->state();
+              nextTaskNode->updateTaskState(nextState, nextState, true);
+              nextTaskNode->setOutlineStyle(qtBaseTaskNode::OutlineStyle::Active);
+            }
+            this->m_taskPath->setActiveTask(next);
+            // Update visibility if needed
+            auto* newLastTask = m_taskPath->lastTask();
+            this->updateVisibility(currentLastTask, newLastTask);
+            // Update the worklet palette
+            shared_ptr<smtk::task::Task> task;
+            // Is there a new active task?
+            if (next)
+            {
+              task = std::dynamic_pointer_cast<smtk::task::Task>(next->shared_from_this());
+            }
+            // Else what is the last task listed in the task path?
+            else if (m_taskPath->lastTask())
+            {
+              task = std::dynamic_pointer_cast<smtk::task::Task>(
+                m_taskPath->lastTask()->shared_from_this());
+            }
+            m_p->m_worklets->setParentTask(task);
+          });
         },
         0,
         true,
diff --git a/smtk/extension/qt/diagram/qtTaskNode.cxx b/smtk/extension/qt/diagram/qtTaskNode.cxx
index 79b676ef7e..2d9f5dd129 100644
--- a/smtk/extension/qt/diagram/qtTaskNode.cxx
+++ b/smtk/extension/qt/diagram/qtTaskNode.cxx
@@ -15,6 +15,7 @@
 #include "smtk/extension/qt/diagram/qtDiagramViewConfiguration.h"
 #include "smtk/extension/qt/diagram/qtTaskEditor.h"
 #include "smtk/extension/qt/qtBaseView.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 
 #include "smtk/attribute/Attribute.h"
 #include "smtk/attribute/StringItem.h"
@@ -576,14 +577,16 @@ qtTaskNode::qtTaskNode(qtDiagramGenerator* generator, smtk::task::Task* task, QG
 
   m_taskObserver = m_task->observers().insert(
     [this](smtk::task::Task&, smtk::task::State prev, smtk::task::State next) {
-      // Sometimes the application invokes this observer after the GUI
-      // has been shut down. Calling setEnabled on widgets generates a
-      // log message and attempting to construct a QPixmap throws exceptions;
-      // so, check that qApp exists before going further.
-      if (qApp)
-      {
-        this->updateTaskState(prev, next, this->isActive());
-      }
+      qtInvokeOnMainThreadBehavior::invokeOnMainThread([&]() {
+        // Sometimes the application invokes this observer after the GUI
+        // has been shut down. Calling setEnabled on widgets generates a
+        // log message and attempting to construct a QPixmap throws exceptions;
+        // so, check that qApp exists before going further.
+        if (qApp)
+        {
+          this->updateTaskState(prev, next, this->isActive());
+        }
+      });
     },
     "qtTaskNode task-observer");
 
diff --git a/smtk/extension/qt/diagram/qtTaskPath.cxx b/smtk/extension/qt/diagram/qtTaskPath.cxx
index 2a8c08f5dd..632cb319f2 100644
--- a/smtk/extension/qt/diagram/qtTaskPath.cxx
+++ b/smtk/extension/qt/diagram/qtTaskPath.cxx
@@ -12,6 +12,7 @@
 #include "smtk/extension/qt/diagram/qtDiagramScene.h"
 #include "smtk/extension/qt/diagram/qtDiagramViewConfiguration.h"
 #include "smtk/extension/qt/diagram/qtTaskEditor.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 
 #include "smtk/task/State.h"
 #include "smtk/task/Task.h"
@@ -73,14 +74,16 @@ public:
         [tb](smtk::task::Task& t, smtk::task::State prev, smtk::task::State next) {
           (void)t;
           (void)prev;
-          if (!tb)
-          {
-            return; // widget has been deleted
-          }
-          tb->setIcon(renderStatusIcon(
-            next,
-            tb->height() / 3,
-            tb->m_taskPath->editor()->diagram()->diagramScene()->configuration()));
+          smtk::extension::qtInvokeOnMainThreadBehavior::invokeOnMainThread([&]() {
+            if (!tb)
+            {
+              return; // widget has been deleted
+            }
+            tb->setIcon(renderStatusIcon(
+              next,
+              tb->height() / 3,
+              tb->m_taskPath->editor()->diagram()->diagramScene()->configuration()));
+          });
         });
     }
   }
diff --git a/smtk/extension/qt/qtAssociation2ColumnWidget.cxx b/smtk/extension/qt/qtAssociation2ColumnWidget.cxx
index 3f5274bca9..0cd443b240 100644
--- a/smtk/extension/qt/qtAssociation2ColumnWidget.cxx
+++ b/smtk/extension/qt/qtAssociation2ColumnWidget.cxx
@@ -12,6 +12,7 @@
 
 #include "smtk/extension/qt/qtAttribute.h"
 #include "smtk/extension/qt/qtBaseView.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/extension/qt/qtItem.h"
 #include "smtk/extension/qt/qtSMTKUtilities.h"
 #include "smtk/extension/qt/qtTableWidget.h"
@@ -122,7 +123,7 @@ qtAssociation2ColumnWidget::qtAssociation2ColumnWidget(QWidget* _p, qtBaseView*
   QPointer<qtAssociation2ColumnWidget> guardedObject(this);
   if (opManager != nullptr)
   {
-    m_operationObserverKey = opManager->observers().insert(
+    m_operationObserverKey = qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
       [guardedObject](
         const smtk::operation::Operation& oper,
         smtk::operation::EventType event,
@@ -142,7 +143,7 @@ qtAssociation2ColumnWidget::qtAssociation2ColumnWidget(QWidget* _p, qtBaseView*
   auto resManager = m_view->uiManager()->resourceManager();
   if (resManager != nullptr)
   {
-    m_resourceObserverKey = resManager->observers().insert(
+    m_resourceObserverKey = qtInvokeOnMainThreadBehavior::instance()->resourceObservers().insert(
       [guardedObject](const smtk::resource::Resource& resource, smtk::resource::EventType event) {
         if (guardedObject == nullptr)
         {
diff --git a/smtk/extension/qt/qtAttributeView.cxx b/smtk/extension/qt/qtAttributeView.cxx
index 7590d1686a..b5b978ab3b 100644
--- a/smtk/extension/qt/qtAttributeView.cxx
+++ b/smtk/extension/qt/qtAttributeView.cxx
@@ -13,6 +13,7 @@
 #include "smtk/extension/qt/qtAssociation2ColumnWidget.h"
 #include "smtk/extension/qt/qtAttribute.h"
 #include "smtk/extension/qt/qtCheckItemComboBox.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/extension/qt/qtItem.h"
 #include "smtk/extension/qt/qtNotEditableDelegate.h"
 #include "smtk/extension/qt/qtRegexDelegate.h"
@@ -513,18 +514,19 @@ void qtAttributeView::createWidget()
   QPointer<qtAttributeView> guardedObject(this);
   if (opManager != nullptr)
   {
-    m_internals->m_observerKey = opManager->observers().insert(
-      [guardedObject](
-        const smtk::operation::Operation& oper,
-        smtk::operation::EventType event,
-        smtk::operation::Operation::Result result) -> int {
-        if (guardedObject == nullptr)
-        {
-          return 0;
-        }
-        return guardedObject->handleOperationEvent(oper, event, result);
-      },
-      "qtInstancedView: Refresh qtAttributeView when components are modified.");
+    m_internals->m_observerKey =
+      qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
+        [guardedObject](
+          const smtk::operation::Operation& oper,
+          smtk::operation::EventType event,
+          smtk::operation::Operation::Result result) -> int {
+          if (guardedObject == nullptr)
+          {
+            return 0;
+          }
+          return guardedObject->handleOperationEvent(oper, event, result);
+        },
+        "qtInstancedView: Refresh qtAttributeView when components are modified.");
   }
 
   // Initialize any object connected to definitionSelected later
diff --git a/smtk/extension/qt/qtAvailableOperations.cxx b/smtk/extension/qt/qtAvailableOperations.cxx
index e9ecafd623..b93c1a772d 100644
--- a/smtk/extension/qt/qtAvailableOperations.cxx
+++ b/smtk/extension/qt/qtAvailableOperations.cxx
@@ -9,6 +9,7 @@
 //=========================================================================
 #include "smtk/extension/qt/qtAvailableOperations.h"
 
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/operation/MetadataContainer.h"
 #include "smtk/operation/SpecificationOps.h"
 #include "smtk/view/AvailableOperations.h"
@@ -46,7 +47,9 @@ void qtAvailableOperations::setOperationSource(smtk::view::AvailableOperationsPt
   if (m_operationSource)
   {
     m_operationSourceObserverId = m_operationSource->observers().insert(
-      [this](smtk::view::AvailableOperationsPtr /*unused*/) { this->updateList(); },
+      [this](smtk::view::AvailableOperationsPtr /*unused*/) {
+        qtInvokeOnMainThreadBehavior::invokeOnMainThread([&]() { this->updateList(); });
+      },
       0,    // assign a neutral priority
       true, // immediatelyInvoke
       "qtAvailableOperations: Update list of available operations.");
diff --git a/smtk/extension/qt/qtComponentAttributeView.cxx b/smtk/extension/qt/qtComponentAttributeView.cxx
index 01a9c652d9..66a396ffd2 100644
--- a/smtk/extension/qt/qtComponentAttributeView.cxx
+++ b/smtk/extension/qt/qtComponentAttributeView.cxx
@@ -11,6 +11,7 @@
 #include "smtk/extension/qt/qtComponentAttributeView.h"
 
 #include "smtk/extension/qt/qtAttribute.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/extension/qt/qtSMTKUtilities.h"
 #include "smtk/extension/qt/qtTableWidget.h"
 #include "smtk/extension/qt/qtUIManager.h"
@@ -261,16 +262,17 @@ void qtComponentAttributeView::buildUI()
                                      << "failed. Already existed!");
     }
     QPointer<qtComponentAttributeView> guardedObject(this);
-    this->Internals->m_selectionObserverId = sel->observers().insert(
-      [guardedObject](const std::string& selectionSource, smtk::view::SelectionPtr sp) {
-        if (guardedObject != nullptr)
-        {
-          guardedObject->updateSelectedComponent(selectionSource, sp);
-        }
-      },
-      0,
-      true,
-      "qtComponentAttributeView: Change focus on selection.");
+    this->Internals->m_selectionObserverId =
+      qtInvokeOnMainThreadBehavior::instance()->selectionObservers().insert(
+        [guardedObject](const std::string& selectionSource, smtk::view::SelectionPtr sp) {
+          if (guardedObject != nullptr)
+          {
+            guardedObject->updateSelectedComponent(selectionSource, sp);
+          }
+        },
+        0,
+        true,
+        "qtComponentAttributeView: Change focus on selection.");
   }
 }
 
diff --git a/smtk/extension/qt/qtDescriptivePhraseModel.cxx b/smtk/extension/qt/qtDescriptivePhraseModel.cxx
index 7c1f3c54eb..d47eff48c7 100644
--- a/smtk/extension/qt/qtDescriptivePhraseModel.cxx
+++ b/smtk/extension/qt/qtDescriptivePhraseModel.cxx
@@ -10,6 +10,7 @@
 #include "smtk/extension/qt/qtDescriptivePhraseModel.h"
 
 #include "smtk/extension/qt/SVGIconEngine.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/extension/qt/qtTypeDeclarations.h"
 
 #include "smtk/view/DescriptivePhrase.h"
@@ -155,7 +156,10 @@ void qtDescriptivePhraseModel::setPhraseModel(smtk::view::PhraseModelPtr model)
         smtk::view::PhraseModelEvent event,
         const std::vector<int>& src,
         const std::vector<int>& dst,
-        const std::vector<int>& range) { this->updateObserver(phrase, event, src, dst, range); },
+        const std::vector<int>& range) {
+        qtInvokeOnMainThreadBehavior::invokeOnMainThread(
+          [&]() { this->updateObserver(phrase, event, src, dst, range); });
+      },
       0,
       true,
       modelDesc.str());
diff --git a/smtk/extension/qt/qtInstancedView.cxx b/smtk/extension/qt/qtInstancedView.cxx
index 4fb7954f1d..ec9a7d1330 100644
--- a/smtk/extension/qt/qtInstancedView.cxx
+++ b/smtk/extension/qt/qtInstancedView.cxx
@@ -15,6 +15,7 @@
 #include "smtk/attribute/StringItem.h"
 #include "smtk/attribute/operators/Signal.h"
 #include "smtk/extension/qt/qtAttribute.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/extension/qt/qtUIManager.h"
 #include "smtk/operation/Manager.h"
 #include "smtk/operation/Observer.h"
@@ -113,18 +114,19 @@ void qtInstancedView::createWidget()
   QPointer<qtInstancedView> guardedObject(this);
   if (opManager != nullptr)
   {
-    this->Internals->m_observerKey = opManager->observers().insert(
-      [guardedObject](
-        const smtk::operation::Operation& oper,
-        smtk::operation::EventType event,
-        smtk::operation::Operation::Result result) -> int {
-        if (guardedObject == nullptr)
-        {
-          return 0;
-        }
-        return guardedObject->handleOperationEvent(oper, event, result);
-      },
-      "qtInstancedView: Refresh qtInstancedView when components are modified.");
+    this->Internals->m_observerKey =
+      qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
+        [guardedObject](
+          const smtk::operation::Operation& oper,
+          smtk::operation::EventType event,
+          smtk::operation::Operation::Result result) -> int {
+          if (guardedObject == nullptr)
+          {
+            return 0;
+          }
+          return guardedObject->handleOperationEvent(oper, event, result);
+        },
+        "qtInstancedView: Refresh qtInstancedView when components are modified.");
   }
 }
 
diff --git a/smtk/extension/qt/qtInvokeOnMainThreadBehavior.cxx b/smtk/extension/qt/qtInvokeOnMainThreadBehavior.cxx
new file mode 100644
index 0000000000..b5eff4629e
--- /dev/null
+++ b/smtk/extension/qt/qtInvokeOnMainThreadBehavior.cxx
@@ -0,0 +1,122 @@
+//=========================================================================
+//  Copyright (c) Kitware, Inc.
+//  All rights reserved.
+//  See LICENSE.txt for details.
+//
+//  This software is distributed WITHOUT ANY WARRANTY; without even
+//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+//  PURPOSE.  See the above copyright notice for more information.
+//=========================================================================
+
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
+
+#include "smtk/operation/Manager.h"
+#include "smtk/resource/Manager.h"
+#include "smtk/view/Selection.h"
+
+#include <unordered_map>
+
+namespace smtk
+{
+namespace extension
+{
+
+static qtInvokeOnMainThreadBehavior* g_instance = nullptr;
+
+class qtInvokeOnMainThreadBehaviorPrivate : public QObject
+{
+public:
+  explicit qtInvokeOnMainThreadBehaviorPrivate(QObject* parent)
+    : QObject(parent)
+  {
+  }
+
+  smtk::resource::Observers m_resourceObservers;
+  smtk::operation::Observers m_operationObservers;
+  smtk::view::SelectionObservers m_selectionObservers;
+
+  std::unordered_map<smtk::resource::ManagerPtr, smtk::resource::Observers::Key>
+    m_resourceObserverKeys;
+  std::unordered_map<smtk::operation::ManagerPtr, smtk::operation::Observers::Key>
+    m_operationObserverKeys;
+  std::unordered_map<smtk::view::SelectionPtr, smtk::view::SelectionObservers::Key>
+    m_selectionObserverKeys;
+};
+
+qtInvokeOnMainThreadBehavior* qtInvokeOnMainThreadBehavior::instance(QObject* parent)
+{
+  if (!g_instance)
+  {
+    g_instance = new qtInvokeOnMainThreadBehavior(parent);
+  }
+
+  if (g_instance->parent() == nullptr && parent)
+  {
+    g_instance->setParent(parent);
+  }
+
+  return g_instance;
+}
+qtInvokeOnMainThreadBehavior::~qtInvokeOnMainThreadBehavior() {}
+
+smtk::resource::Observers& qtInvokeOnMainThreadBehavior::resourceObservers() const
+{
+  return this->m_private->m_resourceObservers;
+}
+
+smtk::operation::Observers&
+qtInvokeOnMainThreadBehavior::qtInvokeOnMainThreadBehavior::operationObservers() const
+{
+  return this->m_private->m_operationObservers;
+}
+
+smtk::view::SelectionObservers&
+qtInvokeOnMainThreadBehavior::qtInvokeOnMainThreadBehavior::selectionObservers() const
+{
+  return this->m_private->m_selectionObservers;
+}
+
+qtInvokeOnMainThreadBehavior::qtInvokeOnMainThreadBehavior(QObject* parent)
+  : Superclass(parent)
+  , m_private(new qtInvokeOnMainThreadBehaviorPrivate(this))
+{
+}
+
+void qtInvokeOnMainThreadBehavior::addObservers(const smtk::common::Managers& managers) const
+{
+  const auto resourceManager = managers.get<smtk::resource::ManagerPtr>();
+  const auto operationManager = managers.get<smtk::operation::ManagerPtr>();
+  const auto selection = managers.get<smtk::view::SelectionPtr>();
+
+  // Add delegating observers as lowest priority so the GUI is updated last.
+  this->m_private->m_resourceObserverKeys[resourceManager] = resourceManager->observers().insert(
+    [this](const smtk::resource::Resource& rsrc, smtk::resource::EventType event) {
+      invokeOnMainThread(
+        [&]() { this->m_private->m_resourceObservers.callObserversDirectly(rsrc, event); });
+    },
+    INT_MIN,
+    false,
+    "qtInvokeOnMainThreadBehavior resource observer");
+
+  this->m_private->m_operationObserverKeys[operationManager] = operationManager->observers().insert(
+    [&](
+      const smtk::operation::Operation& oper,
+      smtk::operation::EventType event,
+      smtk::operation::Operation::Result result) -> int {
+      return this->m_private->m_operationObservers.callObserversDirectly(oper, event, result);
+    },
+    INT_MIN,
+    false,
+    "qtInvokeOnMainThreadBehavior operation observer");
+
+  this->m_private->m_selectionObserverKeys[selection] = selection->observers().insert(
+    [&](const std::string& str, smtk::view::Selection::Ptr selection) {
+      this->m_private->m_selectionObservers.callObserversDirectly(str, selection);
+    },
+    INT_MIN,
+    false,
+    "qtInvokeOnMainThreadBehavior selection observer");
+}
+
+} // namespace extension
+} // namespace smtk
\ No newline at end of file
diff --git a/smtk/extension/qt/qtInvokeOnMainThreadBehavior.h b/smtk/extension/qt/qtInvokeOnMainThreadBehavior.h
new file mode 100644
index 0000000000..7fef9dc34c
--- /dev/null
+++ b/smtk/extension/qt/qtInvokeOnMainThreadBehavior.h
@@ -0,0 +1,104 @@
+//=========================================================================
+//  Copyright (c) Kitware, Inc.
+//  All rights reserved.
+//  See LICENSE.txt for details.
+//
+//  This software is distributed WITHOUT ANY WARRANTY; without even
+//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+//  PURPOSE.  See the above copyright notice for more information.
+//=========================================================================
+#ifndef smtk_extension_qt_InvokeOnMainThreadBehavior_h
+#define smtk_extension_qt_InvokeOnMainThreadBehavior_h
+
+#include "smtk/extension/qt/Exports.h"
+
+#include <QApplication>
+#include <QObject>
+#include <QThread>
+
+#include <smtk/operation/Observer.h>
+#include <smtk/resource/Observer.h>
+#include <smtk/view/SelectionObserver.h>
+
+namespace smtk
+{
+namespace extension
+{
+
+class qtInvokeOnMainThreadBehaviorPrivate;
+
+class SMTKQTEXT_EXPORT qtInvokeOnMainThreadBehavior : public QObject
+{
+  Q_OBJECT
+    using Superclass = QObject;
+public:
+  static qtInvokeOnMainThreadBehavior* instance(QObject* parent = nullptr);
+  ~qtInvokeOnMainThreadBehavior() override;
+
+  template<typename Func, typename... Args>
+  static auto invokeOnMainThread(Func&& fn, Args&&... args) -> std::enable_if_t<
+    // Non-void returning function specialization.
+    !std::is_void_v<decltype(fn(std::forward<Args>(args)...))>,
+    decltype(fn(
+      std::forward<Args>(args)...))>
+  {
+    using ReturnType = decltype(fn(std::forward<Args>(args)...));
+    ReturnType result;
+
+    if (qApp->thread() != QThread::currentThread())
+    {
+      // Not on main thread. Run the function on the main thread while this one blocks until it
+      // returns.
+      QMetaObject::invokeMethod(
+        qApp,
+        [&]() { result = std::invoke(std::forward<Func>(fn), std::forward<Args>(args)...); },
+        Qt::BlockingQueuedConnection);
+    }
+    else
+    {
+      // Call on this thread directly.
+      result = std::invoke(std::forward<Func>(fn), std::forward<Args>(args)...);
+    }
+
+    return result;
+  }
+
+  template<typename Func, typename... Args>
+  static auto invokeOnMainThread(Func&& fn, Args&&... args)
+    // Void returning function specialization.
+    -> std::enable_if_t<std::is_void_v<decltype(fn(std::forward<Args>(args)...))>, void>
+  {
+    if (qApp->thread() != QThread::currentThread())
+    {
+      // Not on main thread. Run the function on the main thread while this one blocks until it
+      // returns.
+      QMetaObject::invokeMethod(
+        qApp,
+        [&]() { std::invoke(std::forward<Func>(fn), std::forward<Args>(args)...); },
+        Qt::BlockingQueuedConnection);
+    }
+    else
+    {
+      // Call on this thread directly.
+      std::invoke(std::forward<Func>(fn), std::forward<Args>(args)...);
+    }
+  }
+
+  void addObservers(const smtk::common::Managers& managers) const;
+  smtk::resource::Observers& resourceObservers() const;
+  smtk::operation::Observers& operationObservers() const;
+  smtk::view::SelectionObservers& selectionObservers() const;
+
+protected:
+  explicit qtInvokeOnMainThreadBehavior(QObject* parent = nullptr);
+
+private:
+  Q_DISABLE_COPY(qtInvokeOnMainThreadBehavior);
+
+  qtInvokeOnMainThreadBehaviorPrivate *m_private;
+};
+
+}
+}
+
+#endif //smtk_extension_qt_InvokeOnMainThreadBehavior_h
diff --git a/smtk/extension/qt/qtOperationTypeModel.cxx b/smtk/extension/qt/qtOperationTypeModel.cxx
index 657275e6b4..babedd42c8 100644
--- a/smtk/extension/qt/qtOperationTypeModel.cxx
+++ b/smtk/extension/qt/qtOperationTypeModel.cxx
@@ -10,6 +10,7 @@
 #include "smtk/extension/qt/qtOperationTypeModel.h"
 
 #include "smtk/extension/qt/SVGIconEngine.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/extension/qt/qtOperationAction.h"
 #include "smtk/extension/qt/qtTypeDeclarations.h"
 
@@ -147,16 +148,19 @@ qtOperationTypeModel::qtOperationTypeModel(const smtk::view::Information& info,
     {
       m_selectionValue = m_selection->findOrCreateLabeledValue(selectionValueLabel);
     }
-    m_selectionObserverKey = m_selection->observers().insert(
-      [this](const std::string& src, std::shared_ptr<smtk::view::Selection> seln) {
-        if (!m_operations.empty())
-        {
-          this->updateSelectionAssociability(src, seln, 0, this->rowCount(QModelIndex()) - 1);
-        }
-      },
-      std::numeric_limits<smtk::view::SelectionObservers::Priority>::lowest(),
-      /* initialize */ true,
-      "qtOperationTypeModel associability updater");
+    m_selectionObserverKey =
+      smtk::extension::qtInvokeOnMainThreadBehavior::qtInvokeOnMainThreadBehavior::instance()
+        ->selectionObservers()
+        .insert(
+          [this](const std::string& src, std::shared_ptr<smtk::view::Selection> seln) {
+            if (!m_operations.empty())
+            {
+              this->updateSelectionAssociability(src, seln, 0, this->rowCount(QModelIndex()) - 1);
+            }
+          },
+          std::numeric_limits<smtk::view::SelectionObservers::Priority>::lowest(),
+          /* initialize */ true,
+          "qtOperationTypeModel associability updater");
   }
 }
 
diff --git a/smtk/extension/qt/qtReferenceItem.cxx b/smtk/extension/qt/qtReferenceItem.cxx
index ba04cfa72e..e36652a622 100644
--- a/smtk/extension/qt/qtReferenceItem.cxx
+++ b/smtk/extension/qt/qtReferenceItem.cxx
@@ -14,6 +14,7 @@
 #include "smtk/extension/qt/MembershipBadge.h"
 #include "smtk/extension/qt/qtBadgeActionToggle.h"
 #include "smtk/extension/qt/qtBaseAttributeView.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/extension/qt/qtOverlay.h"
 #include "smtk/extension/qt/qtTypeDeclarations.h"
 #include "smtk/extension/qt/qtUIManager.h"
@@ -481,10 +482,12 @@ void qtReferenceItem::updateUI()
         const std::vector<int>& src,
         const std::vector<int>& dst,
         const std::vector<int>& refs) {
-        if (guardedObject)
-        {
-          guardedObject->checkRemovedComponents(phr, evt, src, dst, refs);
-        }
+        qtInvokeOnMainThreadBehavior::invokeOnMainThread([&]() {
+          if (guardedObject)
+          {
+            guardedObject->checkRemovedComponents(phr, evt, src, dst, refs);
+          }
+        });
       },
       "qtReferenceItem: Check for removed components.");
     // we need to know when membership is changed, to update our labels
diff --git a/smtk/extension/qt/qtReferenceItemEditor.cxx b/smtk/extension/qt/qtReferenceItemEditor.cxx
index d5f6a31a77..f911dbd345 100644
--- a/smtk/extension/qt/qtReferenceItemEditor.cxx
+++ b/smtk/extension/qt/qtReferenceItemEditor.cxx
@@ -12,6 +12,7 @@
 
 #include "smtk/extension/qt/qtAttribute.h"
 #include "smtk/extension/qt/qtBaseAttributeView.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/extension/qt/qtItem.h"
 #include "smtk/extension/qt/qtSMTKUtilities.h"
 #include "smtk/extension/qt/qtTableWidget.h"
@@ -137,7 +138,7 @@ qtReferenceItemEditor::qtReferenceItemEditor(const qtAttributeItemInfo& info)
   if (opManager != nullptr)
   {
     QPointer<qtReferenceItemEditor> guardedObject(this);
-    m_operationObserverKey = opManager->observers().insert(
+    m_operationObserverKey = qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
       [guardedObject](
         const smtk::operation::Operation& oper,
         smtk::operation::EventType event,
@@ -160,7 +161,7 @@ qtReferenceItemEditor::qtReferenceItemEditor(const qtAttributeItemInfo& info)
   auto resManager = uiManager->resourceManager();
   if (resManager != nullptr)
   {
-    m_resourceObserverKey = resManager->observers().insert(
+    m_resourceObserverKey = qtInvokeOnMainThreadBehavior::instance()->resourceObservers().insert(
       [this](const smtk::resource::Resource& resource, smtk::resource::EventType event) {
         this->handleResourceEvent(resource, event);
       },
diff --git a/smtk/extension/qt/qtReferenceTree.cxx b/smtk/extension/qt/qtReferenceTree.cxx
index 728347fe88..1efa879e0a 100644
--- a/smtk/extension/qt/qtReferenceTree.cxx
+++ b/smtk/extension/qt/qtReferenceTree.cxx
@@ -14,6 +14,7 @@
 #include "smtk/extension/qt/VisibilityBadge.h"
 #include "smtk/extension/qt/qtBadgeActionToggle.h"
 #include "smtk/extension/qt/qtBaseAttributeView.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/extension/qt/qtOverlay.h"
 #include "smtk/extension/qt/qtTypeDeclarations.h"
 #include "smtk/extension/qt/qtUIManager.h"
@@ -496,10 +497,12 @@ void qtReferenceTree::updateUI()
         const std::vector<int>& src,
         const std::vector<int>& dst,
         const std::vector<int>& refs) {
-        if (guardedObject)
-        {
-          guardedObject->checkRemovedComponents(phr, evt, src, dst, refs);
-        }
+        qtInvokeOnMainThreadBehavior::invokeOnMainThread([&]() {
+          if (guardedObject)
+          {
+            guardedObject->checkRemovedComponents(phr, evt, src, dst, refs);
+          }
+        });
       },
       "qtReferenceTree: Check for removed components.");
     // we need to know when membership is changed, to update our labels
diff --git a/smtk/extension/qt/qtResourceBrowser.cxx b/smtk/extension/qt/qtResourceBrowser.cxx
index 2261af639e..f6d520b62b 100644
--- a/smtk/extension/qt/qtResourceBrowser.cxx
+++ b/smtk/extension/qt/qtResourceBrowser.cxx
@@ -48,6 +48,7 @@
 #include <QPointer>
 #include <QTreeView>
 
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/extension/qt/qtResourceBrowserP.h"
 #include "smtk/extension/qt/qtTypeDeclarations.h"
 
@@ -286,7 +287,7 @@ void qtResourceBrowser::addSource(smtk::common::TypeContainer& managers)
     m_p->m_hoverValue = m_p->m_seln->findOrCreateLabeledValue(m_p->m_hoverLabel);
     QPointer<qtResourceBrowser> self(this);
     m_p->m_seln->registerSelectionSource(m_p->m_selnSource);
-    m_p->m_selnHandle = m_p->m_seln->observers().insert(
+    m_p->m_selnHandle = qtInvokeOnMainThreadBehavior::instance()->selectionObservers().insert(
       [self](const std::string& source, smtk::view::Selection::Ptr seln) {
         if (self)
         {
diff --git a/smtk/extension/qt/qtWorkletModel.cxx b/smtk/extension/qt/qtWorkletModel.cxx
index e89a43bf7d..4c6f1881cb 100644
--- a/smtk/extension/qt/qtWorkletModel.cxx
+++ b/smtk/extension/qt/qtWorkletModel.cxx
@@ -12,6 +12,7 @@
 #include "smtk/io/Logger.h"
 
 #include "smtk/extension/qt/SVGIconEngine.h"
+#include "smtk/extension/qt/qtInvokeOnMainThreadBehavior.h"
 #include "smtk/extension/qt/qtOperationAction.h"
 #include "smtk/extension/qt/qtTypeDeclarations.h"
 
@@ -60,20 +61,21 @@ qtWorkletModel::qtWorkletModel(const smtk::view::Information& info, QObject* par
   // top-level tasks by default.
   m_topLevelExpression.setAllPass();
 
-  m_operationObserverKey = m_operationManager->observers().insert(
-    [this](
-      const smtk::operation::Operation& op,
-      smtk::operation::EventType event,
-      smtk::operation::Operation::Result result) {
-      if (event == smtk::operation::EventType::DID_OPERATE)
-      {
-        this->workletUpdate(op, result);
-      }
-      return 0;
-    },
-    /* priority */ 0,
-    /* initialize */ true,
-    "qtWorkletModel updater");
+  m_operationObserverKey =
+    smtk::extension::qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
+      [this](
+        const smtk::operation::Operation& op,
+        smtk::operation::EventType event,
+        smtk::operation::Operation::Result result) {
+        if (event == smtk::operation::EventType::DID_OPERATE)
+        {
+          this->workletUpdate(op, result);
+        }
+        return 0;
+      },
+      /* priority */ 0,
+      /* initialize */ true,
+      "qtWorkletModel updater");
   m_viewManager = managers->get<smtk::view::Manager::Ptr>();
 
   // Find or create an operation launcher.
-- 
GitLab


From 9096617a66bf3bdc44f708f5db21672f5d8da332 Mon Sep 17 00:00:00 2001
From: Justin Wilson <BZ2C7N@NAM.corp.gm.com>
Date: Wed, 26 Mar 2025 16:56:53 -0500
Subject: [PATCH 2/2] Added smtk::common::Context to Operation API

Implemented smtk::common::Context as a means of passing arbitrary information up the stack.
Operations will add information to the context for child operations to inherit previously
acquired resource locks and for guaranteeing that child operations are run on the same
thread as the parent.
Operation observers now pass the context of the parent that triggered them so the child
operations called from within an observer function can have access to resource lock
information to prevent deadlocking.
---
 smtk/attribute/operators/Associate.cxx        |   2 +-
 smtk/attribute/operators/Associate.h          |   2 +-
 smtk/attribute/operators/Dissociate.cxx       |   2 +-
 smtk/attribute/operators/Dissociate.h         |   2 +-
 smtk/attribute/operators/Export.cxx           |   2 +-
 smtk/attribute/operators/Export.h             |   2 +-
 smtk/attribute/operators/Import.cxx           |   2 +-
 smtk/attribute/operators/Import.h             |   2 +-
 smtk/attribute/operators/Read.cxx             |   2 +-
 smtk/attribute/operators/Read.h               |   2 +-
 smtk/attribute/operators/Signal.cxx           |   2 +-
 smtk/attribute/operators/Signal.h             |   2 +-
 smtk/attribute/operators/Write.cxx            |   2 +-
 smtk/attribute/operators/Write.h              |   2 +-
 smtk/common/CMakeLists.txt                    |   1 +
 smtk/common/Context.h                         | 113 ++++++++++++++++++
 .../delaunay/operators/TessellateFaces.cxx    |   2 +-
 .../delaunay/operators/TessellateFaces.h      |   2 +-
 .../delaunay/operators/TriangulateFaces.cxx   |   2 +-
 .../delaunay/operators/TriangulateFaces.h     |   2 +-
 .../pqSMTKAppComponentsAutoStart.cxx          |   3 +
 ...qSMTKCallObserversOnMainThreadBehavior.cxx |   5 +-
 ...pqSMTKCloseWithActiveOperationBehavior.cxx |   1 +
 .../pqSMTKOperationHintsBehavior.cxx          |   1 +
 .../paraview/appcomponents/pqSMTKResource.cxx |   1 +
 .../paraview/markup/qtOntologyItem.cxx        |   1 +
 .../paraview/mesh/VTKMeshCellSelection.cxx    |   2 +-
 .../paraview/mesh/VTKMeshCellSelection.h      |   2 +-
 .../VTKModelInstancePlacementSelection.cxx    |   2 +-
 .../VTKModelInstancePlacementSelection.h      |   2 +-
 .../operators/smtkAssignColorsView.cxx        |   1 +
 .../operators/smtkCoordinateTransformView.cxx |   1 +
 .../smtkDataSetInfoInspectorView.cxx          |   2 +-
 .../paraview/server/RespondToVTKSelection.cxx |   2 +-
 .../paraview/server/RespondToVTKSelection.h   |   2 +-
 .../widgets/pqSMTKTransformWidget.cxx         |   1 +
 smtk/extension/qt/diagram/qtDiagram.cxx       |   1 +
 .../qt/qtAssociation2ColumnWidget.cxx         |   1 +
 smtk/extension/qt/qtAttributeView.cxx         |   1 +
 smtk/extension/qt/qtInstancedView.cxx         |   1 +
 .../qt/qtInvokeOnMainThreadBehavior.cxx       |  11 +-
 smtk/extension/qt/qtReferenceItemEditor.cxx   |   1 +
 ...tSMTKCallObserversOnMainThreadBehavior.cxx |   5 +-
 smtk/extension/qt/qtWorkletModel.cxx          |   1 +
 .../vtk/operators/DataSetInfoInspector.cxx    |   2 +-
 .../vtk/operators/DataSetInfoInspector.h      |   2 +-
 .../vtk/operators/ExportEdgesToVTK.cxx        |   2 +-
 .../vtk/operators/ExportEdgesToVTK.h          |   2 +-
 .../extension/vtk/operators/ExportFaceset.cxx |   2 +-
 smtk/extension/vtk/operators/ExportFaceset.h  |   2 +-
 .../vtk/operators/ImageInspector.cxx          |   2 +-
 smtk/extension/vtk/operators/ImageInspector.h |   2 +-
 .../extension/vtk/operators/MeshInspector.cxx |   2 +-
 smtk/extension/vtk/operators/MeshInspector.h  |   2 +-
 smtk/graph/operators/CreateArc.cxx            |   2 +-
 smtk/graph/operators/CreateArc.h              |   2 +-
 smtk/graph/operators/CreateArcType.cxx        |   2 +-
 smtk/graph/operators/CreateArcType.h          |   2 +-
 smtk/graph/operators/DeleteArc.cxx            |   2 +-
 smtk/graph/operators/DeleteArc.h              |   2 +-
 smtk/markup/operators/Create.cxx              |   2 +-
 smtk/markup/operators/Create.h                |   2 +-
 smtk/markup/operators/CreateAnalyticShape.cxx |   2 +-
 smtk/markup/operators/CreateAnalyticShape.h   |   2 +-
 smtk/markup/operators/CreateGroup.cxx         |   2 +-
 smtk/markup/operators/CreateGroup.h           |   2 +-
 smtk/markup/operators/Delete.cxx              |   2 +-
 smtk/markup/operators/Delete.h                |   2 +-
 smtk/markup/operators/DumpGraph.cxx           |   2 +-
 smtk/markup/operators/DumpGraph.h             |   2 +-
 smtk/markup/operators/EditComment.cxx         |   2 +-
 smtk/markup/operators/EditComment.h           |   2 +-
 smtk/markup/operators/Import.cxx              |   2 +-
 smtk/markup/operators/Import.h                |   2 +-
 smtk/markup/operators/Read.cxx                |   2 +-
 smtk/markup/operators/Read.h                  |   2 +-
 smtk/markup/operators/SetName.cxx             |   2 +-
 smtk/markup/operators/SetName.h               |   2 +-
 smtk/markup/operators/TagIndividual.cxx       |   2 +-
 smtk/markup/operators/TagIndividual.h         |   2 +-
 smtk/markup/operators/Ungroup.cxx             |   2 +-
 smtk/markup/operators/Ungroup.h               |   2 +-
 smtk/markup/operators/Write.cxx               |   2 +-
 smtk/markup/operators/Write.h                 |   2 +-
 smtk/mesh/operators/DeleteMesh.cxx            |   2 +-
 smtk/mesh/operators/DeleteMesh.h              |   2 +-
 smtk/mesh/operators/ElevateMesh.cxx           |   2 +-
 smtk/mesh/operators/ElevateMesh.h             |   2 +-
 smtk/mesh/operators/Export.cxx                |   2 +-
 smtk/mesh/operators/Export.h                  |   2 +-
 smtk/mesh/operators/ExtractAdjacency.cxx      |   2 +-
 smtk/mesh/operators/ExtractAdjacency.h        |   2 +-
 .../mesh/operators/ExtractByDihedralAngle.cxx |   2 +-
 smtk/mesh/operators/ExtractByDihedralAngle.h  |   2 +-
 smtk/mesh/operators/ExtractSkin.cxx           |   2 +-
 smtk/mesh/operators/ExtractSkin.h             |   2 +-
 smtk/mesh/operators/GenerateHotStartData.cxx  |   2 +-
 smtk/mesh/operators/GenerateHotStartData.h    |   2 +-
 smtk/mesh/operators/Import.cxx                |   2 +-
 smtk/mesh/operators/Import.h                  |   2 +-
 smtk/mesh/operators/InterpolateOntoMesh.cxx   |   2 +-
 smtk/mesh/operators/InterpolateOntoMesh.h     |   2 +-
 smtk/mesh/operators/MergeCoincidentPoints.cxx |   2 +-
 smtk/mesh/operators/MergeCoincidentPoints.h   |   2 +-
 smtk/mesh/operators/PrintMeshInformation.cxx  |   2 +-
 smtk/mesh/operators/PrintMeshInformation.h    |   2 +-
 smtk/mesh/operators/Read.cxx                  |   2 +-
 smtk/mesh/operators/Read.h                    |   2 +-
 smtk/mesh/operators/ReadResource.cxx          |   2 +-
 smtk/mesh/operators/ReadResource.h            |   2 +-
 smtk/mesh/operators/SelectCells.cxx           |   2 +-
 smtk/mesh/operators/SelectCells.h             |   2 +-
 smtk/mesh/operators/SetMeshName.cxx           |   2 +-
 smtk/mesh/operators/SetMeshName.h             |   2 +-
 smtk/mesh/operators/Subtract.cxx              |   2 +-
 smtk/mesh/operators/Subtract.h                |   2 +-
 smtk/mesh/operators/Transform.cxx             |   2 +-
 smtk/mesh/operators/Transform.h               |   2 +-
 smtk/mesh/operators/UndoElevateMesh.cxx       |   2 +-
 smtk/mesh/operators/UndoElevateMesh.h         |   2 +-
 smtk/mesh/operators/Write.cxx                 |   2 +-
 smtk/mesh/operators/Write.h                   |   2 +-
 smtk/mesh/operators/WriteResource.cxx         |   2 +-
 smtk/mesh/operators/WriteResource.h           |   2 +-
 smtk/model/operators/AddAuxiliaryGeometry.cxx |   2 +-
 smtk/model/operators/AddAuxiliaryGeometry.h   |   2 +-
 smtk/model/operators/AddImage.cxx             |   4 +-
 smtk/model/operators/AddImage.h               |   2 +-
 smtk/model/operators/CloseModel.cxx           |   2 +-
 smtk/model/operators/CloseModel.h             |   2 +-
 smtk/model/operators/CreateInstances.cxx      |   2 +-
 smtk/model/operators/CreateInstances.h        |   2 +-
 smtk/model/operators/Delete.cxx               |   2 +-
 smtk/model/operators/Delete.h                 |   2 +-
 smtk/model/operators/DivideInstance.cxx       |   2 +-
 smtk/model/operators/DivideInstance.h         |   2 +-
 smtk/model/operators/EntityGroupOperation.cxx |   2 +-
 smtk/model/operators/EntityGroupOperation.h   |   2 +-
 smtk/model/operators/ExportModelJSON.cxx      |   2 +-
 smtk/model/operators/ExportModelJSON.h        |   2 +-
 .../operators/GroupAuxiliaryGeometry.cxx      |   2 +-
 smtk/model/operators/GroupAuxiliaryGeometry.h |   2 +-
 smtk/model/operators/MergeInstances.cxx       |   2 +-
 smtk/model/operators/MergeInstances.h         |   2 +-
 smtk/model/operators/SetInstancePrototype.cxx |   2 +-
 smtk/model/operators/SetInstancePrototype.h   |   2 +-
 smtk/model/operators/TerrainExtraction.cxx    |   2 +-
 smtk/model/operators/TerrainExtraction.h      |   2 +-
 smtk/operation/CreateResource.cxx             |   2 +-
 smtk/operation/CreateResource.h               |   2 +-
 smtk/operation/Manager.cxx                    |   1 +
 smtk/operation/Observer.h                     |   3 +-
 smtk/operation/Operation.cxx                  |  55 +++++++--
 smtk/operation/Operation.h                    |  45 +++++--
 smtk/operation/XMLOperation.h                 |   2 +-
 smtk/operation/operators/AssignColors.cxx     |   2 +-
 smtk/operation/operators/AssignColors.h       |   2 +-
 .../operators/CoordinateTransform.cxx         |   2 +-
 .../operation/operators/CoordinateTransform.h |   2 +-
 smtk/operation/operators/CopyResources.cxx    |   2 +-
 smtk/operation/operators/CopyResources.h      |   2 +-
 smtk/operation/operators/EditProperties.cxx   |   2 +-
 smtk/operation/operators/EditProperties.h     |   2 +-
 .../operators/ImportPythonOperation.cxx       |   2 +-
 .../operators/ImportPythonOperation.h         |   2 +-
 smtk/operation/operators/ImportResource.cxx   |   2 +-
 smtk/operation/operators/ImportResource.h     |   2 +-
 smtk/operation/operators/MarkModified.cxx     |   2 +-
 smtk/operation/operators/MarkModified.h       |   2 +-
 smtk/operation/operators/ReadResource.cxx     |   2 +-
 smtk/operation/operators/ReadResource.h       |   2 +-
 smtk/operation/operators/RemoveResource.cxx   |   2 +-
 smtk/operation/operators/RemoveResource.h     |   4 +-
 smtk/operation/operators/SetProperty.cxx      |   2 +-
 smtk/operation/operators/SetProperty.h        |   2 +-
 smtk/operation/operators/WriteResource.cxx    |   2 +-
 smtk/operation/operators/WriteResource.h      |   2 +-
 smtk/operation/pybind11/PyOperation.h         |   2 +-
 smtk/operation/pybind11/PybindObserver.h      |   2 +-
 .../testing/cxx/TestAsyncOperation.cxx        |   4 +-
 .../testing/cxx/TestAvailableOperations.cxx   |   4 +-
 smtk/operation/testing/cxx/TestHints.cxx      |   4 +-
 .../testing/cxx/TestMutexedOperation.cxx      |   8 +-
 .../testing/cxx/TestOperationGroup.cxx        |   4 +-
 .../testing/cxx/TestOperationLauncher.cxx     |   4 +-
 .../cxx/TestSafeBlockingInvocation.cxx        |   4 +-
 .../cxx/TestThreadSafeLazyEvaluation.cxx      |   4 +-
 .../operation/testing/cxx/unitNamingGroup.cxx |   4 +-
 smtk/operation/testing/cxx/unitOperation.cxx  |   2 +-
 smtk/project/Manager.cxx                      |   1 +
 smtk/project/operators/Add.cxx                |   2 +-
 smtk/project/operators/Add.h                  |   2 +-
 smtk/project/operators/Create.cxx             |   2 +-
 smtk/project/operators/Create.h               |   2 +-
 smtk/project/operators/Define.cxx             |   2 +-
 smtk/project/operators/Define.h               |   2 +-
 smtk/project/operators/Print.cxx              |   2 +-
 smtk/project/operators/Print.h                |   2 +-
 smtk/project/operators/Read.cxx               |   2 +-
 smtk/project/operators/Read.h                 |   2 +-
 smtk/project/operators/Remove.cxx             |   2 +-
 smtk/project/operators/Remove.h               |   2 +-
 smtk/project/operators/Write.cxx              |   2 +-
 smtk/project/operators/Write.h                |   2 +-
 .../testing/cxx/TestProjectLifeCycle.cxx      |   2 +-
 smtk/resource/GarbageCollector.cxx            |   1 +
 .../testing/cxx/TestGarbageCollector.cxx      |   6 +-
 .../mesh/operators/CreateUniformGrid.cxx      |   2 +-
 .../mesh/operators/CreateUniformGrid.h        |   2 +-
 .../operators/EulerCharacteristicRatio.cxx    |   2 +-
 .../mesh/operators/EulerCharacteristicRatio.h |   2 +-
 smtk/session/mesh/operators/Export.cxx        |   2 +-
 smtk/session/mesh/operators/Export.h          |   2 +-
 smtk/session/mesh/operators/Import.cxx        |   2 +-
 smtk/session/mesh/operators/Import.h          |   2 +-
 smtk/session/mesh/operators/Merge.cxx         |   2 +-
 smtk/session/mesh/operators/Merge.h           |   2 +-
 smtk/session/mesh/operators/Print.cxx         |   2 +-
 smtk/session/mesh/operators/Print.h           |   2 +-
 smtk/session/mesh/operators/Read.cxx          |   2 +-
 smtk/session/mesh/operators/Read.h            |   2 +-
 smtk/session/mesh/operators/Transform.cxx     |   2 +-
 smtk/session/mesh/operators/Transform.h       |   2 +-
 smtk/session/mesh/operators/Write.cxx         |   2 +-
 smtk/session/mesh/operators/Write.h           |   2 +-
 .../oscillator/operators/CreateModel.cxx      |   2 +-
 .../oscillator/operators/CreateModel.h        |   2 +-
 .../oscillator/operators/EditDomain.cxx       |   2 +-
 .../session/oscillator/operators/EditDomain.h |   2 +-
 .../oscillator/operators/EditSource.cxx       |   2 +-
 .../session/oscillator/operators/EditSource.h |   2 +-
 smtk/session/oscillator/operators/Export.cxx  |   2 +-
 smtk/session/oscillator/operators/Export.h    |   2 +-
 smtk/session/oscillator/operators/Read.cxx    |   2 +-
 smtk/session/oscillator/operators/Read.h      |   2 +-
 smtk/session/oscillator/operators/Write.cxx   |   2 +-
 smtk/session/oscillator/operators/Write.h     |   2 +-
 .../polygon/operators/CleanGeometry.cxx       |   2 +-
 .../session/polygon/operators/CleanGeometry.h |   2 +-
 smtk/session/polygon/operators/CreateEdge.cxx |   2 +-
 smtk/session/polygon/operators/CreateEdge.h   |   2 +-
 .../operators/CreateEdgeFromPoints.cxx        |   2 +-
 .../polygon/operators/CreateEdgeFromPoints.h  |   2 +-
 .../operators/CreateEdgeFromVertices.cxx      |   2 +-
 .../operators/CreateEdgeFromVertices.h        |   2 +-
 .../session/polygon/operators/CreateFaces.cxx |   2 +-
 smtk/session/polygon/operators/CreateFaces.h  |   2 +-
 .../session/polygon/operators/CreateModel.cxx |   2 +-
 smtk/session/polygon/operators/CreateModel.h  |   2 +-
 .../polygon/operators/CreateVertices.cxx      |   2 +-
 .../polygon/operators/CreateVertices.h        |   2 +-
 smtk/session/polygon/operators/Delete.cxx     |   2 +-
 smtk/session/polygon/operators/Delete.h       |   2 +-
 .../polygon/operators/DemoteVertex.cxx        |   2 +-
 smtk/session/polygon/operators/DemoteVertex.h |   2 +-
 .../polygon/operators/ExtractContours.cxx     |   2 +-
 .../polygon/operators/ExtractContours.h       |   2 +-
 .../polygon/operators/ForceCreateFace.cxx     |   2 +-
 .../polygon/operators/ForceCreateFace.h       |   2 +-
 smtk/session/polygon/operators/Import.cxx     |   2 +-
 smtk/session/polygon/operators/Import.h       |   2 +-
 smtk/session/polygon/operators/ImportPPG.cxx  |   2 +-
 smtk/session/polygon/operators/ImportPPG.h    |   2 +-
 smtk/session/polygon/operators/LegacyRead.cxx |   2 +-
 smtk/session/polygon/operators/LegacyRead.h   |   2 +-
 smtk/session/polygon/operators/Read.cxx       |   2 +-
 smtk/session/polygon/operators/Read.h         |   2 +-
 smtk/session/polygon/operators/SplitEdge.cxx  |   2 +-
 smtk/session/polygon/operators/SplitEdge.h    |   2 +-
 smtk/session/polygon/operators/TweakEdge.cxx  |   2 +-
 smtk/session/polygon/operators/TweakEdge.h    |   2 +-
 smtk/session/polygon/operators/Write.cxx      |   2 +-
 smtk/session/polygon/operators/Write.h        |   2 +-
 smtk/session/vtk/operators/Export.cxx         |   2 +-
 smtk/session/vtk/operators/Export.h           |   2 +-
 smtk/session/vtk/operators/Import.cxx         |   2 +-
 smtk/session/vtk/operators/Import.h           |   2 +-
 smtk/session/vtk/operators/LegacyRead.cxx     |   2 +-
 smtk/session/vtk/operators/LegacyRead.h       |   2 +-
 smtk/session/vtk/operators/Read.cxx           |   2 +-
 smtk/session/vtk/operators/Read.h             |   2 +-
 smtk/session/vtk/operators/Write.cxx          |   2 +-
 smtk/session/vtk/operators/Write.h            |   2 +-
 smtk/task/Agent.cxx                           |   2 +-
 smtk/task/Agent.h                             |   5 +-
 smtk/task/FillOutAttributes.cxx               |   3 +-
 smtk/task/FillOutAttributes.h                 |   2 +-
 smtk/task/FillOutAttributesAgent.cxx          |   5 +-
 smtk/task/FillOutAttributesAgent.h            |   2 +-
 smtk/task/GatherObjectsAgent.cxx              |  12 +-
 smtk/task/GatherObjectsAgent.h                |   6 +-
 smtk/task/Manager.cxx                         |  28 +++--
 smtk/task/Manager.h                           |   7 +-
 smtk/task/PortForwardingAgent.cxx             |   4 +-
 smtk/task/PortForwardingAgent.h               |   2 +-
 smtk/task/SubmitOperation.cxx                 |   1 +
 smtk/task/SubmitOperationAgent.cxx            |   3 +-
 smtk/task/SubmitOperationAgent.h              |   2 +-
 smtk/task/Task.cxx                            |   6 +-
 smtk/task/Task.h                              |   5 +-
 smtk/task/TrivialProducerAgent.cxx            |   3 +-
 smtk/task/adaptor/ConfigureOperation.cxx      |   1 +
 smtk/task/operators/AddDependency.cxx         |   2 +-
 smtk/task/operators/AddDependency.h           |   2 +-
 smtk/task/operators/ConnectPorts.cxx          |   2 +-
 smtk/task/operators/ConnectPorts.h            |   2 +-
 smtk/task/operators/DisconnectPorts.cxx       |   2 +-
 smtk/task/operators/DisconnectPorts.h         |   2 +-
 smtk/task/operators/EmplaceWorklet.cxx        |   2 +-
 smtk/task/operators/EmplaceWorklet.h          |   2 +-
 smtk/task/operators/RemoveDependency.cxx      |   2 +-
 smtk/task/operators/RemoveDependency.h        |   2 +-
 smtk/task/operators/RenameTask.cxx            |   2 +-
 smtk/task/operators/RenameTask.h              |   2 +-
 smtk/task/pybind11/PybindGatherObjectsAgent.h |  56 +++++----
 .../testing/cxx/TestConfigureOperation.cxx    |   2 +-
 smtk/task/testing/cxx/TestTaskPorts.cxx       |   2 +-
 smtk/view/PhraseModel.cxx                     |  23 ++--
 318 files changed, 617 insertions(+), 392 deletions(-)
 create mode 100644 smtk/common/Context.h

diff --git a/smtk/attribute/operators/Associate.cxx b/smtk/attribute/operators/Associate.cxx
index b1dc3903e8..fa3af6258d 100644
--- a/smtk/attribute/operators/Associate.cxx
+++ b/smtk/attribute/operators/Associate.cxx
@@ -24,7 +24,7 @@ namespace smtk
 namespace attribute
 {
 
-Associate::Result Associate::operateInternal()
+Associate::Result Associate::operateInternal(Context ctx)
 {
   // Access the attribute resource to associate.
   smtk::attribute::Resource::Ptr resource = std::dynamic_pointer_cast<smtk::attribute::Resource>(
diff --git a/smtk/attribute/operators/Associate.h b/smtk/attribute/operators/Associate.h
index 68a6248d69..a8bea2b402 100644
--- a/smtk/attribute/operators/Associate.h
+++ b/smtk/attribute/operators/Associate.h
@@ -31,7 +31,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace attribute
diff --git a/smtk/attribute/operators/Dissociate.cxx b/smtk/attribute/operators/Dissociate.cxx
index 39ad178d46..caaa108ac3 100644
--- a/smtk/attribute/operators/Dissociate.cxx
+++ b/smtk/attribute/operators/Dissociate.cxx
@@ -24,7 +24,7 @@ namespace smtk
 namespace attribute
 {
 
-Dissociate::Result Dissociate::operateInternal()
+Dissociate::Result Dissociate::operateInternal(Context ctx)
 {
   // Access the attribute resource to dissociate.
   smtk::attribute::Resource::Ptr resource = std::dynamic_pointer_cast<smtk::attribute::Resource>(
diff --git a/smtk/attribute/operators/Dissociate.h b/smtk/attribute/operators/Dissociate.h
index 3b4835a88e..44e9eda846 100644
--- a/smtk/attribute/operators/Dissociate.h
+++ b/smtk/attribute/operators/Dissociate.h
@@ -31,7 +31,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace attribute
diff --git a/smtk/attribute/operators/Export.cxx b/smtk/attribute/operators/Export.cxx
index 72454b8c56..6a72327389 100644
--- a/smtk/attribute/operators/Export.cxx
+++ b/smtk/attribute/operators/Export.cxx
@@ -30,7 +30,7 @@ namespace smtk
 namespace attribute
 {
 
-Export::Result Export::operateInternal()
+Export::Result Export::operateInternal(Context ctx)
 {
   // Access the file name.
   std::string outputfile = this->parameters()->findFile("filename")->value();
diff --git a/smtk/attribute/operators/Export.h b/smtk/attribute/operators/Export.h
index 5da70de5dd..2609a3ace2 100644
--- a/smtk/attribute/operators/Export.h
+++ b/smtk/attribute/operators/Export.h
@@ -28,7 +28,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace attribute
diff --git a/smtk/attribute/operators/Import.cxx b/smtk/attribute/operators/Import.cxx
index f02396491f..e9ddf7e834 100644
--- a/smtk/attribute/operators/Import.cxx
+++ b/smtk/attribute/operators/Import.cxx
@@ -37,7 +37,7 @@ namespace smtk
 namespace attribute
 {
 
-Import::Result Import::operateInternal()
+Import::Result Import::operateInternal(Context ctx)
 {
   // Access the file name.
   std::string filename = this->parameters()->findFile("filename")->value();
diff --git a/smtk/attribute/operators/Import.h b/smtk/attribute/operators/Import.h
index 326d14e244..77a3c89877 100644
--- a/smtk/attribute/operators/Import.h
+++ b/smtk/attribute/operators/Import.h
@@ -28,7 +28,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace attribute
diff --git a/smtk/attribute/operators/Read.cxx b/smtk/attribute/operators/Read.cxx
index f3a3c229c9..b826cbdc18 100644
--- a/smtk/attribute/operators/Read.cxx
+++ b/smtk/attribute/operators/Read.cxx
@@ -44,7 +44,7 @@ namespace smtk
 namespace attribute
 {
 
-Read::Result Read::operateInternal()
+Read::Result Read::operateInternal(Context ctx)
 {
   using smtk::common::VersionNumber;
 
diff --git a/smtk/attribute/operators/Read.h b/smtk/attribute/operators/Read.h
index a8df8ca20d..782189cd06 100644
--- a/smtk/attribute/operators/Read.h
+++ b/smtk/attribute/operators/Read.h
@@ -28,7 +28,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
   void markModifiedResources(Result&) override;
 };
diff --git a/smtk/attribute/operators/Signal.cxx b/smtk/attribute/operators/Signal.cxx
index 82329106b1..4dcd1e8d61 100644
--- a/smtk/attribute/operators/Signal.cxx
+++ b/smtk/attribute/operators/Signal.cxx
@@ -30,7 +30,7 @@ namespace smtk
 namespace attribute
 {
 
-Signal::Result Signal::operateInternal()
+Signal::Result Signal::operateInternal(Context ctx)
 {
   auto params = this->parameters();
   auto result = this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED);
diff --git a/smtk/attribute/operators/Signal.h b/smtk/attribute/operators/Signal.h
index 7b33d83238..b83f57f4c1 100644
--- a/smtk/attribute/operators/Signal.h
+++ b/smtk/attribute/operators/Signal.h
@@ -32,7 +32,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   void generateSummary(Operation::Result&) override;
   const char* xmlDescription() const override;
 };
diff --git a/smtk/attribute/operators/Write.cxx b/smtk/attribute/operators/Write.cxx
index 1e554a7960..6979c9d228 100644
--- a/smtk/attribute/operators/Write.cxx
+++ b/smtk/attribute/operators/Write.cxx
@@ -35,7 +35,7 @@ namespace smtk
 namespace attribute
 {
 
-Write::Result Write::operateInternal()
+Write::Result Write::operateInternal(Context ctx)
 {
   auto resourceItem = this->parameters()->associations();
 
diff --git a/smtk/attribute/operators/Write.h b/smtk/attribute/operators/Write.h
index 30ccd4b850..9562b6cd28 100644
--- a/smtk/attribute/operators/Write.h
+++ b/smtk/attribute/operators/Write.h
@@ -28,7 +28,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
   void markModifiedResources(Result&) override;
 };
diff --git a/smtk/common/CMakeLists.txt b/smtk/common/CMakeLists.txt
index 2c5489637c..984eeec85d 100644
--- a/smtk/common/CMakeLists.txt
+++ b/smtk/common/CMakeLists.txt
@@ -31,6 +31,7 @@ set(commonHeaders
   Categories.h
   Color.h
   CompilerInformation.h
+  Context.h
   DateTime.h
   DateTimeZonePair.h
   Deprecation.h
diff --git a/smtk/common/Context.h b/smtk/common/Context.h
new file mode 100644
index 0000000000..9e8b68824d
--- /dev/null
+++ b/smtk/common/Context.h
@@ -0,0 +1,113 @@
+//=========================================================================
+//  Copyright (c) Kitware, Inc.
+//  All rights reserved.
+//  See LICENSE.txt for details.
+//
+//  This software is distributed WITHOUT ANY WARRANTY; without even
+//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+//  PURPOSE.  See the above copyright notice for more information.
+//=========================================================================
+#ifndef smtk_common_Context_h
+#define smtk_common_Context_h
+
+#include "smtk/CoreExports.h"
+
+//#include <optional>
+#include <typeindex>
+#include <unordered_map>
+
+#include <boost/any.hpp>
+
+// TODO: Replace this with std::optional before merging this.
+#include <boost/optional.hpp>
+
+#define DECLARE_CONTEXT_KEY_TYPE(NAME)                                                                     \
+struct NAME                                                                                      \
+{                                                                                                \
+};
+
+namespace smtk
+{
+namespace common
+{
+
+class SMTKCORE_EXPORT Context final
+{
+  using key_type = std::size_t;
+  using value_type = boost::any;
+  using value_map = std::unordered_map<key_type, value_type>;
+
+public:
+  template<typename keyT, typename valueT>
+  boost::optional<valueT> value(const keyT& key) const
+  {
+    const auto keyHash = std::hash<keyT>{}(key);
+    const auto it = this->m_valueEntries.find(keyHash);
+    if (it != this->m_valueEntries.end())
+    {
+      try
+      {
+        return boost::any_cast<valueT>(it->second);
+      }
+      catch (const boost::bad_any_cast&)
+      {
+        return boost::optional<valueT>();
+      }
+    }
+    return boost::optional<valueT>();
+  }
+
+  template<typename keyT, typename valueT>
+  boost::optional<valueT> value() const
+  {
+    const auto keyHash = std::hash<std::type_index>{}(std::type_index(typeid(keyT)));
+    const auto it = this->m_typeEntries.find(keyHash);
+    if (it != this->m_typeEntries.end())
+    {
+      try
+      {
+        return boost::any_cast<valueT>(it->second);
+      }
+      catch (const boost::bad_any_cast&)
+      {
+        return boost::optional<valueT>();
+      }
+    }
+    return boost::optional<valueT>();
+  }
+
+  template<typename keyT, typename valueT>
+  Context withValue(const keyT& key, const valueT& value) const
+  {
+    // Create a copy of the current context.
+    Context result = *this;
+
+    // Add the entry to the copy.
+    result.m_valueEntries[std::hash<keyT>{}(key)] = value;
+
+    // Return the copy.
+    return result;
+  }
+
+  template<typename keyT, typename valueT>
+  Context withValue(const valueT& value) const
+  {
+    // Create a copy of the current context.
+    Context result = *this;
+
+    // Add the entry to the copy using the type id of keyT as key value.
+    result.m_typeEntries[std::hash<std::type_index>{}(std::type_index(typeid(keyT)))] = value;
+
+    // Return the copy.
+    return result;
+  }
+
+private:
+  value_map m_valueEntries;
+  value_map m_typeEntries;
+};
+
+} // namespace common
+} // namespace smtk
+
+#endif // smtk_common_Context_h
\ No newline at end of file
diff --git a/smtk/extension/delaunay/operators/TessellateFaces.cxx b/smtk/extension/delaunay/operators/TessellateFaces.cxx
index 93abc9f0a8..5adfde8b67 100644
--- a/smtk/extension/delaunay/operators/TessellateFaces.cxx
+++ b/smtk/extension/delaunay/operators/TessellateFaces.cxx
@@ -62,7 +62,7 @@ bool TessellateFaces::ableToOperate()
   return smtk::operation::Operation::ableToOperate();
 }
 
-TessellateFaces::Result TessellateFaces::operateInternal()
+TessellateFaces::Result TessellateFaces::operateInternal(Context ctx)
 {
   auto associations = this->parameters()->associations();
   auto faces = associations->as<smtk::model::Faces>([](smtk::resource::PersistentObjectPtr obj) {
diff --git a/smtk/extension/delaunay/operators/TessellateFaces.h b/smtk/extension/delaunay/operators/TessellateFaces.h
index c6f0618cc3..940c7bee7a 100644
--- a/smtk/extension/delaunay/operators/TessellateFaces.h
+++ b/smtk/extension/delaunay/operators/TessellateFaces.h
@@ -35,7 +35,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace delaunay
diff --git a/smtk/extension/delaunay/operators/TriangulateFaces.cxx b/smtk/extension/delaunay/operators/TriangulateFaces.cxx
index d061406e5d..01545b3e69 100644
--- a/smtk/extension/delaunay/operators/TriangulateFaces.cxx
+++ b/smtk/extension/delaunay/operators/TriangulateFaces.cxx
@@ -63,7 +63,7 @@ bool TriangulateFaces::ableToOperate()
   return smtk::operation::Operation::ableToOperate();
 }
 
-TriangulateFaces::Result TriangulateFaces::operateInternal()
+TriangulateFaces::Result TriangulateFaces::operateInternal(Context ctx)
 {
   auto associations = this->parameters()->associations();
   auto faces = associations->as<smtk::model::Faces>([](smtk::resource::PersistentObjectPtr obj) {
diff --git a/smtk/extension/delaunay/operators/TriangulateFaces.h b/smtk/extension/delaunay/operators/TriangulateFaces.h
index b9e964e74e..76c9385685 100644
--- a/smtk/extension/delaunay/operators/TriangulateFaces.h
+++ b/smtk/extension/delaunay/operators/TriangulateFaces.h
@@ -38,7 +38,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace delaunay
diff --git a/smtk/extension/paraview/appcomponents/pqSMTKAppComponentsAutoStart.cxx b/smtk/extension/paraview/appcomponents/pqSMTKAppComponentsAutoStart.cxx
index 0bda282ae4..f52e18f770 100644
--- a/smtk/extension/paraview/appcomponents/pqSMTKAppComponentsAutoStart.cxx
+++ b/smtk/extension/paraview/appcomponents/pqSMTKAppComponentsAutoStart.cxx
@@ -43,6 +43,8 @@
 #include "vtkVersion.h"
 #include "vtksys/SystemTools.hxx"
 
+#include <pqServer.h>
+
 namespace
 {
 class vtkSMTKAppComponentsFactory : public vtkObjectFactory
@@ -197,6 +199,7 @@ void pqSMTKAppComponentsAutoStart::observeWrapper(pqSMTKWrapper* wrapper, pqServ
   m_p->m_opObserver =
     smtk::extension::qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
       [this](
+      smtk::common::Context ctx,
         const smtk::operation::Operation& op,
         smtk::operation::EventType event,
         smtk::operation::Operation::Result const&
diff --git a/smtk/extension/paraview/appcomponents/pqSMTKCallObserversOnMainThreadBehavior.cxx b/smtk/extension/paraview/appcomponents/pqSMTKCallObserversOnMainThreadBehavior.cxx
index 908fc96d37..f3004256bd 100644
--- a/smtk/extension/paraview/appcomponents/pqSMTKCallObserversOnMainThreadBehavior.cxx
+++ b/smtk/extension/paraview/appcomponents/pqSMTKCallObserversOnMainThreadBehavior.cxx
@@ -108,6 +108,7 @@ void pqSMTKCallObserversOnMainThreadBehavior::forceObserversToBeCalledOnMainThre
   // instead of calling its Observer functors directly.
   wrapper->smtkOperationManager()->observers().overrideWith(
     [this, wrapper](
+      smtk::common::Context ctx,
       const smtk::operation::Operation& oper,
       smtk::operation::EventType event,
       smtk::operation::Operation::Result result) -> int {
@@ -126,7 +127,7 @@ void pqSMTKCallObserversOnMainThreadBehavior::forceObserversToBeCalledOnMainThre
       else
       {
         // Directly invoke the observers.
-        wrapper->smtkOperationManager()->observers().callObserversDirectly(oper, event, result);
+        wrapper->smtkOperationManager()->observers().callObserversDirectly(ctx, oper, event, result);
       }
       return 0;
     });
@@ -149,7 +150,7 @@ void pqSMTKCallObserversOnMainThreadBehavior::forceObserversToBeCalledOnMainThre
         {
           att = operation->specification()->findAttribute(resultName.toStdString());
         }
-        operation->manager()->observers().callObserversDirectly(
+        operation->manager()->observers().callObserversDirectly(smtk::common::Context(),
           *operation, static_cast<smtk::operation::EventType>(event), att);
       }
       m_activeOperations.erase(id);
diff --git a/smtk/extension/paraview/appcomponents/pqSMTKCloseWithActiveOperationBehavior.cxx b/smtk/extension/paraview/appcomponents/pqSMTKCloseWithActiveOperationBehavior.cxx
index c39754ed49..9d92309314 100644
--- a/smtk/extension/paraview/appcomponents/pqSMTKCloseWithActiveOperationBehavior.cxx
+++ b/smtk/extension/paraview/appcomponents/pqSMTKCloseWithActiveOperationBehavior.cxx
@@ -102,6 +102,7 @@ void pqSMTKCloseWithActiveOperationBehavior::trackActiveOperations(
 
   m_key = wrapper->smtkOperationManager()->observers().insert(
     [](
+    smtk::common::Context ctx,
       const smtk::operation::Operation&,
       smtk::operation::EventType event,
       smtk::operation::Operation::Result) -> int {
diff --git a/smtk/extension/paraview/appcomponents/pqSMTKOperationHintsBehavior.cxx b/smtk/extension/paraview/appcomponents/pqSMTKOperationHintsBehavior.cxx
index e4083936de..9a1aa875ff 100644
--- a/smtk/extension/paraview/appcomponents/pqSMTKOperationHintsBehavior.cxx
+++ b/smtk/extension/paraview/appcomponents/pqSMTKOperationHintsBehavior.cxx
@@ -401,6 +401,7 @@ void pqSMTKOperationHintsBehavior::observeWrapper(pqSMTKWrapper* wrapper, pqServ
           server,
           smtk::extension::qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
             [this](
+            smtk::common::Context ctx,
               const smtk::operation::Operation& op,
               smtk::operation::EventType eventType,
               smtk::operation::Operation::Result result) -> int {
diff --git a/smtk/extension/paraview/appcomponents/pqSMTKResource.cxx b/smtk/extension/paraview/appcomponents/pqSMTKResource.cxx
index 07d9e6c7a4..cf15d13842 100644
--- a/smtk/extension/paraview/appcomponents/pqSMTKResource.cxx
+++ b/smtk/extension/paraview/appcomponents/pqSMTKResource.cxx
@@ -62,6 +62,7 @@ pqSMTKResource::pqSMTKResource(
   // that has this class instance as its context.
   m_key = rsrcMgr->smtkOperationManager()->observers().insert(
     [&](
+    smtk::common::Context ctx,
       const smtk::operation::Operation& op,
       smtk::operation::EventType event,
       smtk::operation::Operation::Result result) {
diff --git a/smtk/extension/paraview/markup/qtOntologyItem.cxx b/smtk/extension/paraview/markup/qtOntologyItem.cxx
index d2e01afee4..af5906db09 100644
--- a/smtk/extension/paraview/markup/qtOntologyItem.cxx
+++ b/smtk/extension/paraview/markup/qtOntologyItem.cxx
@@ -645,6 +645,7 @@ void qtOntologyItem::updateUI()
   {
     static auto key = opMgr->observers().insert(
       [&](
+      smtk::common::Context ctx,
         const smtk::operation::Operation& op,
         smtk::operation::EventType event,
         smtk::operation::Operation::Result) -> int {
diff --git a/smtk/extension/paraview/mesh/VTKMeshCellSelection.cxx b/smtk/extension/paraview/mesh/VTKMeshCellSelection.cxx
index 4c81a2f54e..5222d58e45 100644
--- a/smtk/extension/paraview/mesh/VTKMeshCellSelection.cxx
+++ b/smtk/extension/paraview/mesh/VTKMeshCellSelection.cxx
@@ -214,7 +214,7 @@ bool VTKMeshCellSelection::transcribeCellIdSelection(Result& result)
   return didModify;
 }
 
-VTKMeshCellSelection::Result VTKMeshCellSelection::operateInternal()
+VTKMeshCellSelection::Result VTKMeshCellSelection::operateInternal(Context ctx)
 {
   auto result = this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED);
   bool worked = this->transcribeCellIdSelection(result);
diff --git a/smtk/extension/paraview/mesh/VTKMeshCellSelection.h b/smtk/extension/paraview/mesh/VTKMeshCellSelection.h
index 4c71a74f61..f000c0183e 100644
--- a/smtk/extension/paraview/mesh/VTKMeshCellSelection.h
+++ b/smtk/extension/paraview/mesh/VTKMeshCellSelection.h
@@ -50,7 +50,7 @@ protected:
   bool transcribeCellIdSelection(Result& result);
 
   /// Simply call transcribeCellIdSelection().
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
 private:
   const char* xmlDescription() const override;
diff --git a/smtk/extension/paraview/model/VTKModelInstancePlacementSelection.cxx b/smtk/extension/paraview/model/VTKModelInstancePlacementSelection.cxx
index 6a28e8c53c..0644c8c70d 100644
--- a/smtk/extension/paraview/model/VTKModelInstancePlacementSelection.cxx
+++ b/smtk/extension/paraview/model/VTKModelInstancePlacementSelection.cxx
@@ -223,7 +223,7 @@ bool VTKModelInstancePlacementSelection::transcribePlacementSelection(Result& re
   return didModify;
 }
 
-VTKModelInstancePlacementSelection::Result VTKModelInstancePlacementSelection::operateInternal()
+VTKModelInstancePlacementSelection::Result VTKModelInstancePlacementSelection::operateInternal(Context ctx)
 {
   auto result = this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED);
   bool worked = this->transcribePlacementSelection(result);
diff --git a/smtk/extension/paraview/model/VTKModelInstancePlacementSelection.h b/smtk/extension/paraview/model/VTKModelInstancePlacementSelection.h
index c0ea0329ca..fd309181c7 100644
--- a/smtk/extension/paraview/model/VTKModelInstancePlacementSelection.h
+++ b/smtk/extension/paraview/model/VTKModelInstancePlacementSelection.h
@@ -71,7 +71,7 @@ protected:
   bool transcribePlacementSelection(Result& result);
 
   /// Simply call transcribeCellIdSelection().
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
 private:
   const char* xmlDescription() const override;
diff --git a/smtk/extension/paraview/operators/smtkAssignColorsView.cxx b/smtk/extension/paraview/operators/smtkAssignColorsView.cxx
index 1e442917f7..6b99887236 100644
--- a/smtk/extension/paraview/operators/smtkAssignColorsView.cxx
+++ b/smtk/extension/paraview/operators/smtkAssignColorsView.cxx
@@ -176,6 +176,7 @@ smtkAssignColorsView::smtkAssignColorsView(const smtk::view::Information& info)
   auto opMgr = this->Internals->CurrentOp->manager();
   this->Internals->ObserverKey =
     qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert([self, this](
+    smtk::common::Context ctx,
                                 const smtk::operation::Operation& op,
                                 smtk::operation::EventType event,
                                 smtk::operation::Operation::Result result) {
diff --git a/smtk/extension/paraview/operators/smtkCoordinateTransformView.cxx b/smtk/extension/paraview/operators/smtkCoordinateTransformView.cxx
index 4f6449fcc3..83399a1b05 100644
--- a/smtk/extension/paraview/operators/smtkCoordinateTransformView.cxx
+++ b/smtk/extension/paraview/operators/smtkCoordinateTransformView.cxx
@@ -411,6 +411,7 @@ public:
     }
     m_operationObserver = qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
       [this](
+      smtk::common::Context ctx,
         const smtk::operation::Operation& op,
         smtk::operation::EventType event,
         smtk::operation::Operation::Result result) {
diff --git a/smtk/extension/paraview/operators/smtkDataSetInfoInspectorView.cxx b/smtk/extension/paraview/operators/smtkDataSetInfoInspectorView.cxx
index 80a5e4df71..02ce333647 100644
--- a/smtk/extension/paraview/operators/smtkDataSetInfoInspectorView.cxx
+++ b/smtk/extension/paraview/operators/smtkDataSetInfoInspectorView.cxx
@@ -287,7 +287,7 @@ void smtkDataSetInfoInspectorView::requestOperation(const smtk::operation::Opera
   auto opManager = op->manager();
   if (!m_p->m_opObserver.assigned() && opManager)
   {
-    smtk::operation::Observer observer = [this](
+    smtk::operation::Observer observer = [this](smtk::common::Context ctx,
                                            const smtk::operation::Operation& op,
                                            smtk::operation::EventType event,
                                            smtk::operation::Operation::Result result) {
diff --git a/smtk/extension/paraview/server/RespondToVTKSelection.cxx b/smtk/extension/paraview/server/RespondToVTKSelection.cxx
index 64e84e2b6b..fbb744a7b8 100644
--- a/smtk/extension/paraview/server/RespondToVTKSelection.cxx
+++ b/smtk/extension/paraview/server/RespondToVTKSelection.cxx
@@ -368,7 +368,7 @@ bool RespondToVTKSelection::transcribeBlockSelection()
   return didModify;
 }
 
-RespondToVTKSelection::Result RespondToVTKSelection::operateInternal()
+RespondToVTKSelection::Result RespondToVTKSelection::operateInternal(Context ctx)
 {
   bool worked = this->transcribeBlockSelection();
   auto result = this->createResult(
diff --git a/smtk/extension/paraview/server/RespondToVTKSelection.h b/smtk/extension/paraview/server/RespondToVTKSelection.h
index 4134bc51f9..980c51caf8 100644
--- a/smtk/extension/paraview/server/RespondToVTKSelection.h
+++ b/smtk/extension/paraview/server/RespondToVTKSelection.h
@@ -144,7 +144,7 @@ protected:
   bool transcribeBlockSelection();
 
   /// By default, only handle block selections by calling transcribeBlockSelection().
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   /// Fail or succeed quietly.
   void generateSummary(Operation::Result&) override {}
 
diff --git a/smtk/extension/paraview/widgets/pqSMTKTransformWidget.cxx b/smtk/extension/paraview/widgets/pqSMTKTransformWidget.cxx
index 7776efee5e..71b45df8ed 100644
--- a/smtk/extension/paraview/widgets/pqSMTKTransformWidget.cxx
+++ b/smtk/extension/paraview/widgets/pqSMTKTransformWidget.cxx
@@ -112,6 +112,7 @@ pqSMTKTransformWidget::pqSMTKTransformWidget(
   m_internal->m_opObserver =
     smtk::extension::qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
       [guardedObject](
+      smtk::common::Context ctx,
         const smtk::operation::Operation& op,
         smtk::operation::EventType event,
         smtk::operation::Operation::Result res) {
diff --git a/smtk/extension/qt/diagram/qtDiagram.cxx b/smtk/extension/qt/diagram/qtDiagram.cxx
index 12a2cf45e2..af389afb4b 100644
--- a/smtk/extension/qt/diagram/qtDiagram.cxx
+++ b/smtk/extension/qt/diagram/qtDiagram.cxx
@@ -399,6 +399,7 @@ public:
     {
       m_onKey = qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
         [this](
+        smtk::common::Context ctx,
           const smtk::operation::Operation& op,
           smtk::operation::EventType event,
           smtk::operation::Operation::Result result) {
diff --git a/smtk/extension/qt/qtAssociation2ColumnWidget.cxx b/smtk/extension/qt/qtAssociation2ColumnWidget.cxx
index 0cd443b240..3b6c9a11fa 100644
--- a/smtk/extension/qt/qtAssociation2ColumnWidget.cxx
+++ b/smtk/extension/qt/qtAssociation2ColumnWidget.cxx
@@ -125,6 +125,7 @@ qtAssociation2ColumnWidget::qtAssociation2ColumnWidget(QWidget* _p, qtBaseView*
   {
     m_operationObserverKey = qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
       [guardedObject](
+      smtk::common::Context ctx,
         const smtk::operation::Operation& oper,
         smtk::operation::EventType event,
         smtk::operation::Operation::Result result) -> int {
diff --git a/smtk/extension/qt/qtAttributeView.cxx b/smtk/extension/qt/qtAttributeView.cxx
index b5b978ab3b..efbbb0b51a 100644
--- a/smtk/extension/qt/qtAttributeView.cxx
+++ b/smtk/extension/qt/qtAttributeView.cxx
@@ -517,6 +517,7 @@ void qtAttributeView::createWidget()
     m_internals->m_observerKey =
       qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
         [guardedObject](
+        smtk::common::Context ctx,
           const smtk::operation::Operation& oper,
           smtk::operation::EventType event,
           smtk::operation::Operation::Result result) -> int {
diff --git a/smtk/extension/qt/qtInstancedView.cxx b/smtk/extension/qt/qtInstancedView.cxx
index ec9a7d1330..6840e3e9bd 100644
--- a/smtk/extension/qt/qtInstancedView.cxx
+++ b/smtk/extension/qt/qtInstancedView.cxx
@@ -117,6 +117,7 @@ void qtInstancedView::createWidget()
     this->Internals->m_observerKey =
       qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
         [guardedObject](
+        smtk::common::Context ctx,
           const smtk::operation::Operation& oper,
           smtk::operation::EventType event,
           smtk::operation::Operation::Result result) -> int {
diff --git a/smtk/extension/qt/qtInvokeOnMainThreadBehavior.cxx b/smtk/extension/qt/qtInvokeOnMainThreadBehavior.cxx
index b5eff4629e..acd03102ee 100644
--- a/smtk/extension/qt/qtInvokeOnMainThreadBehavior.cxx
+++ b/smtk/extension/qt/qtInvokeOnMainThreadBehavior.cxx
@@ -100,10 +100,14 @@ void qtInvokeOnMainThreadBehavior::addObservers(const smtk::common::Managers& ma
 
   this->m_private->m_operationObserverKeys[operationManager] = operationManager->observers().insert(
     [&](
+      smtk::common::Context ctx,
       const smtk::operation::Operation& oper,
       smtk::operation::EventType event,
       smtk::operation::Operation::Result result) -> int {
-      return this->m_private->m_operationObservers.callObserversDirectly(oper, event, result);
+      return invokeOnMainThread([&]() -> int {
+        return this->m_private->m_operationObservers.callObserversDirectly(
+          ctx, oper, event, result);
+      });
     },
     INT_MIN,
     false,
@@ -111,7 +115,8 @@ void qtInvokeOnMainThreadBehavior::addObservers(const smtk::common::Managers& ma
 
   this->m_private->m_selectionObserverKeys[selection] = selection->observers().insert(
     [&](const std::string& str, smtk::view::Selection::Ptr selection) {
-      this->m_private->m_selectionObservers.callObserversDirectly(str, selection);
+      invokeOnMainThread(
+        [&]() { this->m_private->m_selectionObservers.callObserversDirectly(str, selection); });
     },
     INT_MIN,
     false,
@@ -119,4 +124,4 @@ void qtInvokeOnMainThreadBehavior::addObservers(const smtk::common::Managers& ma
 }
 
 } // namespace extension
-} // namespace smtk
\ No newline at end of file
+} // namespace smtk
diff --git a/smtk/extension/qt/qtReferenceItemEditor.cxx b/smtk/extension/qt/qtReferenceItemEditor.cxx
index f911dbd345..c5989290ee 100644
--- a/smtk/extension/qt/qtReferenceItemEditor.cxx
+++ b/smtk/extension/qt/qtReferenceItemEditor.cxx
@@ -140,6 +140,7 @@ qtReferenceItemEditor::qtReferenceItemEditor(const qtAttributeItemInfo& info)
     QPointer<qtReferenceItemEditor> guardedObject(this);
     m_operationObserverKey = qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
       [guardedObject](
+      smtk::common::Context ctx,
         const smtk::operation::Operation& oper,
         smtk::operation::EventType event,
         smtk::operation::Operation::Result result) -> int {
diff --git a/smtk/extension/qt/qtSMTKCallObserversOnMainThreadBehavior.cxx b/smtk/extension/qt/qtSMTKCallObserversOnMainThreadBehavior.cxx
index 57031deea8..e943e0cf94 100644
--- a/smtk/extension/qt/qtSMTKCallObserversOnMainThreadBehavior.cxx
+++ b/smtk/extension/qt/qtSMTKCallObserversOnMainThreadBehavior.cxx
@@ -97,6 +97,7 @@ void qtSMTKCallObserversOnMainThreadBehavior::forceObserversToBeCalledOnMainThre
   {
     operationManager->observers().overrideWith(
       [this, operationManager](
+      smtk::common::Context ctx,
         const smtk::operation::Operation& oper,
         smtk::operation::EventType event,
         smtk::operation::Operation::Result result) -> int {
@@ -115,7 +116,7 @@ void qtSMTKCallObserversOnMainThreadBehavior::forceObserversToBeCalledOnMainThre
         else
         {
           // Directly invoke the observers.
-          operationManager->observers().callObserversDirectly(oper, event, result);
+          operationManager->observers().callObserversDirectly(ctx, oper, event, result);
         }
         return 0;
       });
@@ -138,7 +139,7 @@ void qtSMTKCallObserversOnMainThreadBehavior::forceObserversToBeCalledOnMainThre
           {
             att = operation->specification()->findAttribute(resultName.toStdString());
           }
-          operation->manager()->observers().callObserversDirectly(
+          operation->manager()->observers().callObserversDirectly(smtk::common::Context(),
             *operation, static_cast<smtk::operation::EventType>(event), att);
         }
         m_activeOperations.erase(id);
diff --git a/smtk/extension/qt/qtWorkletModel.cxx b/smtk/extension/qt/qtWorkletModel.cxx
index 4c6f1881cb..469eb69a23 100644
--- a/smtk/extension/qt/qtWorkletModel.cxx
+++ b/smtk/extension/qt/qtWorkletModel.cxx
@@ -64,6 +64,7 @@ qtWorkletModel::qtWorkletModel(const smtk::view::Information& info, QObject* par
   m_operationObserverKey =
     smtk::extension::qtInvokeOnMainThreadBehavior::instance()->operationObservers().insert(
       [this](
+      smtk::common::Context ctx,
         const smtk::operation::Operation& op,
         smtk::operation::EventType event,
         smtk::operation::Operation::Result result) {
diff --git a/smtk/extension/vtk/operators/DataSetInfoInspector.cxx b/smtk/extension/vtk/operators/DataSetInfoInspector.cxx
index fab7c8b887..6a032b213b 100644
--- a/smtk/extension/vtk/operators/DataSetInfoInspector.cxx
+++ b/smtk/extension/vtk/operators/DataSetInfoInspector.cxx
@@ -156,7 +156,7 @@ void prepareResult(DataSetInfoInspector::Result& result, std::set<DataSetInfo>&
   }
 }
 
-DataSetInfoInspector::Result DataSetInfoInspector::operateInternal()
+DataSetInfoInspector::Result DataSetInfoInspector::operateInternal(Context ctx)
 {
   auto result = this->createResult(smtk::operation::Operation::Outcome::FAILED);
 
diff --git a/smtk/extension/vtk/operators/DataSetInfoInspector.h b/smtk/extension/vtk/operators/DataSetInfoInspector.h
index 640c09c676..e0cfdadc61 100644
--- a/smtk/extension/vtk/operators/DataSetInfoInspector.h
+++ b/smtk/extension/vtk/operators/DataSetInfoInspector.h
@@ -31,7 +31,7 @@ public:
   smtkSuperclassMacro(Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
   void generateSummary(Result&) override {}
 };
diff --git a/smtk/extension/vtk/operators/ExportEdgesToVTK.cxx b/smtk/extension/vtk/operators/ExportEdgesToVTK.cxx
index 7a4aa3ecc6..0d7b28b17c 100644
--- a/smtk/extension/vtk/operators/ExportEdgesToVTK.cxx
+++ b/smtk/extension/vtk/operators/ExportEdgesToVTK.cxx
@@ -137,7 +137,7 @@ void insertEdges(
   }
 }
 
-ExportEdgesToVTK::Result ExportEdgesToVTK::operateInternal()
+ExportEdgesToVTK::Result ExportEdgesToVTK::operateInternal(Context ctx)
 {
   smtk::attribute::FileItemPtr filenameItem = this->parameters()->findFile("filename");
 
diff --git a/smtk/extension/vtk/operators/ExportEdgesToVTK.h b/smtk/extension/vtk/operators/ExportEdgesToVTK.h
index 67ef0c2105..f2b189fedd 100644
--- a/smtk/extension/vtk/operators/ExportEdgesToVTK.h
+++ b/smtk/extension/vtk/operators/ExportEdgesToVTK.h
@@ -28,7 +28,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/extension/vtk/operators/ExportFaceset.cxx b/smtk/extension/vtk/operators/ExportFaceset.cxx
index 4697644434..99fec71983 100644
--- a/smtk/extension/vtk/operators/ExportFaceset.cxx
+++ b/smtk/extension/vtk/operators/ExportFaceset.cxx
@@ -199,7 +199,7 @@ std::list<vtkSmartPointer<vtkPolyData>> ExtractFaceset(vtkDataObject* data)
   return retValue;
 }
 
-ExportFaceset::Result ExportFaceset::operateInternal()
+ExportFaceset::Result ExportFaceset::operateInternal(Context ctx)
 {
   smtk::attribute::FileItemPtr filenameItem = this->parameters()->findFile("filename");
   const std::string filename = filenameItem->value();
diff --git a/smtk/extension/vtk/operators/ExportFaceset.h b/smtk/extension/vtk/operators/ExportFaceset.h
index 6589f8ddcb..386872c851 100644
--- a/smtk/extension/vtk/operators/ExportFaceset.h
+++ b/smtk/extension/vtk/operators/ExportFaceset.h
@@ -33,7 +33,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/extension/vtk/operators/ImageInspector.cxx b/smtk/extension/vtk/operators/ImageInspector.cxx
index 1682510963..d04304169e 100644
--- a/smtk/extension/vtk/operators/ImageInspector.cxx
+++ b/smtk/extension/vtk/operators/ImageInspector.cxx
@@ -37,7 +37,7 @@ namespace smtk
 namespace geometry
 {
 
-ImageInspector::Result ImageInspector::operateInternal()
+ImageInspector::Result ImageInspector::operateInternal(Context ctx)
 {
   auto result = this->createResult(smtk::operation::Operation::Outcome::FAILED);
 
diff --git a/smtk/extension/vtk/operators/ImageInspector.h b/smtk/extension/vtk/operators/ImageInspector.h
index 96a6459913..27ce447929 100644
--- a/smtk/extension/vtk/operators/ImageInspector.h
+++ b/smtk/extension/vtk/operators/ImageInspector.h
@@ -33,7 +33,7 @@ public:
   smtkSuperclassMacro(Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
   void generateSummary(Result&) override {}
 };
diff --git a/smtk/extension/vtk/operators/MeshInspector.cxx b/smtk/extension/vtk/operators/MeshInspector.cxx
index 35feaec4fd..91b48018f5 100644
--- a/smtk/extension/vtk/operators/MeshInspector.cxx
+++ b/smtk/extension/vtk/operators/MeshInspector.cxx
@@ -37,7 +37,7 @@ namespace smtk
 namespace geometry
 {
 
-MeshInspector::Result MeshInspector::operateInternal()
+MeshInspector::Result MeshInspector::operateInternal(Context ctx)
 {
   auto result = this->createResult(smtk::operation::Operation::Outcome::FAILED);
 
diff --git a/smtk/extension/vtk/operators/MeshInspector.h b/smtk/extension/vtk/operators/MeshInspector.h
index 821876ee74..7fe77f0e16 100644
--- a/smtk/extension/vtk/operators/MeshInspector.h
+++ b/smtk/extension/vtk/operators/MeshInspector.h
@@ -33,7 +33,7 @@ public:
   smtkSuperclassMacro(Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
   void generateSummary(Result&) override {}
 };
diff --git a/smtk/graph/operators/CreateArc.cxx b/smtk/graph/operators/CreateArc.cxx
index c34ac98176..394f102e03 100644
--- a/smtk/graph/operators/CreateArc.cxx
+++ b/smtk/graph/operators/CreateArc.cxx
@@ -59,7 +59,7 @@ bool CreateArc::ableToOperate()
   return ok;
 }
 
-CreateArc::Result CreateArc::operateInternal()
+CreateArc::Result CreateArc::operateInternal(Context ctx)
 {
   smtk::string::Token arcTypeName;
   smtk::graph::Component::Ptr fromNode;
diff --git a/smtk/graph/operators/CreateArc.h b/smtk/graph/operators/CreateArc.h
index 650d76a027..c5f7aa7660 100644
--- a/smtk/graph/operators/CreateArc.h
+++ b/smtk/graph/operators/CreateArc.h
@@ -43,7 +43,7 @@ public:
 
 protected:
   CreateArc();
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
   /// Used inside ableToOperate and operateInternal to decide on success or failure.
   bool fetchNodesAndCheckArcType(
diff --git a/smtk/graph/operators/CreateArcType.cxx b/smtk/graph/operators/CreateArcType.cxx
index 5e8a2a0cd0..c99b07ce32 100644
--- a/smtk/graph/operators/CreateArcType.cxx
+++ b/smtk/graph/operators/CreateArcType.cxx
@@ -64,7 +64,7 @@ void appendArcTypes(
 
 CreateArcType::CreateArcType() = default;
 
-CreateArcType::Result CreateArcType::operateInternal()
+CreateArcType::Result CreateArcType::operateInternal(Context ctx)
 {
   auto resource = this->parameters()->associations()->valueAs<smtk::graph::ResourceBase>();
   smtk::string::Token arcTypeName = this->parameters()->findString("type name")->value();
diff --git a/smtk/graph/operators/CreateArcType.h b/smtk/graph/operators/CreateArcType.h
index 1a40e0df38..8365557184 100644
--- a/smtk/graph/operators/CreateArcType.h
+++ b/smtk/graph/operators/CreateArcType.h
@@ -63,7 +63,7 @@ public:
 
 protected:
   CreateArcType();
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/graph/operators/DeleteArc.cxx b/smtk/graph/operators/DeleteArc.cxx
index 46c0d5dbfd..f41b489ff9 100644
--- a/smtk/graph/operators/DeleteArc.cxx
+++ b/smtk/graph/operators/DeleteArc.cxx
@@ -231,7 +231,7 @@ bool DeleteArc::fetchArcTypeAndEndpointsItem(
   return ok;
 }
 
-DeleteArc::Result DeleteArc::operateInternal()
+DeleteArc::Result DeleteArc::operateInternal(Context ctx)
 {
   smtk::string::Token arcTypeName;
   smtk::attribute::GroupItem::Ptr endpoints;
diff --git a/smtk/graph/operators/DeleteArc.h b/smtk/graph/operators/DeleteArc.h
index adc47fc273..1f1e8d6c16 100644
--- a/smtk/graph/operators/DeleteArc.h
+++ b/smtk/graph/operators/DeleteArc.h
@@ -83,7 +83,7 @@ public:
 
 protected:
   DeleteArc();
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
   Specification createSpecification() override;
   const char* xmlDescription() const override;
diff --git a/smtk/markup/operators/Create.cxx b/smtk/markup/operators/Create.cxx
index 526c2638a1..90d43dfdcd 100644
--- a/smtk/markup/operators/Create.cxx
+++ b/smtk/markup/operators/Create.cxx
@@ -31,7 +31,7 @@ namespace smtk
 namespace markup
 {
 
-Create::Result Create::operateInternal()
+Create::Result Create::operateInternal(Context ctx)
 {
   // We may be given an optional location:
   auto resource = smtk::markup::Resource::create();
diff --git a/smtk/markup/operators/Create.h b/smtk/markup/operators/Create.h
index 3119d16b74..c76bfd72ec 100644
--- a/smtk/markup/operators/Create.h
+++ b/smtk/markup/operators/Create.h
@@ -30,7 +30,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
   void markModifiedResources(Result&) override;
 };
diff --git a/smtk/markup/operators/CreateAnalyticShape.cxx b/smtk/markup/operators/CreateAnalyticShape.cxx
index 2e4df5a85b..828ff5d6eb 100644
--- a/smtk/markup/operators/CreateAnalyticShape.cxx
+++ b/smtk/markup/operators/CreateAnalyticShape.cxx
@@ -31,7 +31,7 @@ namespace smtk
 namespace markup
 {
 
-CreateAnalyticShape::Result CreateAnalyticShape::operateInternal()
+CreateAnalyticShape::Result CreateAnalyticShape::operateInternal(Context ctx)
 {
   auto params = this->parameters();
   auto resource = params->associations()->valueAs<smtk::markup::Resource>();
diff --git a/smtk/markup/operators/CreateAnalyticShape.h b/smtk/markup/operators/CreateAnalyticShape.h
index 36fe829e7c..e8bdeb3f85 100644
--- a/smtk/markup/operators/CreateAnalyticShape.h
+++ b/smtk/markup/operators/CreateAnalyticShape.h
@@ -30,7 +30,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/markup/operators/CreateGroup.cxx b/smtk/markup/operators/CreateGroup.cxx
index bc74ac971a..fc036b2025 100644
--- a/smtk/markup/operators/CreateGroup.cxx
+++ b/smtk/markup/operators/CreateGroup.cxx
@@ -31,7 +31,7 @@ namespace smtk
 namespace markup
 {
 
-CreateGroup::Result CreateGroup::operateInternal()
+CreateGroup::Result CreateGroup::operateInternal(Context ctx)
 {
   auto params = this->parameters();
   auto assoc = params->associations();
diff --git a/smtk/markup/operators/CreateGroup.h b/smtk/markup/operators/CreateGroup.h
index bda1f5689d..6e09e05425 100644
--- a/smtk/markup/operators/CreateGroup.h
+++ b/smtk/markup/operators/CreateGroup.h
@@ -30,7 +30,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/markup/operators/Delete.cxx b/smtk/markup/operators/Delete.cxx
index d535f29e59..c1e138fde0 100644
--- a/smtk/markup/operators/Delete.cxx
+++ b/smtk/markup/operators/Delete.cxx
@@ -59,7 +59,7 @@ bool Delete::ableToOperate()
   return !haveExternalDeps;
 }
 
-Delete::Result Delete::operateInternal()
+Delete::Result Delete::operateInternal(Context ctx)
 {
   m_result = this->createResult(Delete::Outcome::FAILED);
 
diff --git a/smtk/markup/operators/Delete.h b/smtk/markup/operators/Delete.h
index a350e598df..78ab0ebcc4 100644
--- a/smtk/markup/operators/Delete.h
+++ b/smtk/markup/operators/Delete.h
@@ -71,7 +71,7 @@ public:
 
 protected:
   Delete();
-  Delete::Result operateInternal() override;
+  Delete::Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 
   Result m_result; // TODO: Let subclass define?
diff --git a/smtk/markup/operators/DumpGraph.cxx b/smtk/markup/operators/DumpGraph.cxx
index 679eb5919a..9133111e20 100644
--- a/smtk/markup/operators/DumpGraph.cxx
+++ b/smtk/markup/operators/DumpGraph.cxx
@@ -37,7 +37,7 @@ namespace smtk
 namespace markup
 {
 
-DumpGraph::Result DumpGraph::operateInternal()
+DumpGraph::Result DumpGraph::operateInternal(Context ctx)
 {
   auto result = this->createResult(smtk::operation::Operation::Outcome::FAILED);
   auto created = result->findComponent("created");
diff --git a/smtk/markup/operators/DumpGraph.h b/smtk/markup/operators/DumpGraph.h
index 2e22894d25..f5dbcc360a 100644
--- a/smtk/markup/operators/DumpGraph.h
+++ b/smtk/markup/operators/DumpGraph.h
@@ -29,7 +29,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/markup/operators/EditComment.cxx b/smtk/markup/operators/EditComment.cxx
index afc6957930..6c12ce0124 100644
--- a/smtk/markup/operators/EditComment.cxx
+++ b/smtk/markup/operators/EditComment.cxx
@@ -38,7 +38,7 @@ namespace markup
 
 using namespace smtk::string::literals;
 
-EditComment::Result EditComment::operateInternal()
+EditComment::Result EditComment::operateInternal(Context ctx)
 {
   auto objects = this->parameters()->associations();
   smtk::string::Token mimetype = this->parameters()->findString("mime-type")->value();
diff --git a/smtk/markup/operators/EditComment.h b/smtk/markup/operators/EditComment.h
index 5c74fc0c71..bda7140149 100644
--- a/smtk/markup/operators/EditComment.h
+++ b/smtk/markup/operators/EditComment.h
@@ -43,7 +43,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/markup/operators/Import.cxx b/smtk/markup/operators/Import.cxx
index 5770b9cbe4..32055b2a48 100644
--- a/smtk/markup/operators/Import.cxx
+++ b/smtk/markup/operators/Import.cxx
@@ -350,7 +350,7 @@ public:
 
 } // anonymous namespace
 
-Import::Result Import::operateInternal()
+Import::Result Import::operateInternal(Context ctx)
 {
   using namespace smtk::string::literals;
 
diff --git a/smtk/markup/operators/Import.h b/smtk/markup/operators/Import.h
index a30152b78a..9602d4e08c 100644
--- a/smtk/markup/operators/Import.h
+++ b/smtk/markup/operators/Import.h
@@ -50,7 +50,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   Specification createSpecification() override;
   const char* xmlDescription() const override;
 
diff --git a/smtk/markup/operators/Read.cxx b/smtk/markup/operators/Read.cxx
index 0f01859fb1..da3bcd52ea 100644
--- a/smtk/markup/operators/Read.cxx
+++ b/smtk/markup/operators/Read.cxx
@@ -49,7 +49,7 @@ bool Read::ableToOperate()
   return file.good();
 }
 
-Read::Result Read::operateInternal()
+Read::Result Read::operateInternal(Context ctx)
 {
   auto filename = this->parameters()->findFile("filename")->value();
   std::ifstream file(filename);
diff --git a/smtk/markup/operators/Read.h b/smtk/markup/operators/Read.h
index 59452ab0c9..a5e85dc550 100644
--- a/smtk/markup/operators/Read.h
+++ b/smtk/markup/operators/Read.h
@@ -32,7 +32,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
   void markModifiedResources(Result&) override;
 };
diff --git a/smtk/markup/operators/SetName.cxx b/smtk/markup/operators/SetName.cxx
index 9533bf596c..6a2711dd58 100644
--- a/smtk/markup/operators/SetName.cxx
+++ b/smtk/markup/operators/SetName.cxx
@@ -31,7 +31,7 @@ namespace smtk
 namespace markup
 {
 
-SetName::Result SetName::operateInternal()
+SetName::Result SetName::operateInternal(Context ctx)
 {
   auto object = this->parameters()->associations()->value();
   auto name = this->parameters()->findString("name")->value();
diff --git a/smtk/markup/operators/SetName.h b/smtk/markup/operators/SetName.h
index 7944e7fc3b..5879760220 100644
--- a/smtk/markup/operators/SetName.h
+++ b/smtk/markup/operators/SetName.h
@@ -30,7 +30,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/markup/operators/TagIndividual.cxx b/smtk/markup/operators/TagIndividual.cxx
index 6308d2944e..0b7ff57c21 100644
--- a/smtk/markup/operators/TagIndividual.cxx
+++ b/smtk/markup/operators/TagIndividual.cxx
@@ -38,7 +38,7 @@ namespace smtk
 namespace markup
 {
 
-TagIndividual::Result TagIndividual::operateInternal()
+TagIndividual::Result TagIndividual::operateInternal(Context ctx)
 {
   auto result = this->createResult(smtk::operation::Operation::Outcome::FAILED);
   auto created = result->findComponent("created");
diff --git a/smtk/markup/operators/TagIndividual.h b/smtk/markup/operators/TagIndividual.h
index 3d779904ff..31d2ab4b71 100644
--- a/smtk/markup/operators/TagIndividual.h
+++ b/smtk/markup/operators/TagIndividual.h
@@ -33,7 +33,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
   /// Find or create an OntologyIdentifier with the given \a nodeName,
   /// \a ontologyName, and \a nodeURL.
diff --git a/smtk/markup/operators/Ungroup.cxx b/smtk/markup/operators/Ungroup.cxx
index f0c80ece1c..2cee39266e 100644
--- a/smtk/markup/operators/Ungroup.cxx
+++ b/smtk/markup/operators/Ungroup.cxx
@@ -29,7 +29,7 @@ namespace smtk
 namespace markup
 {
 
-Ungroup::Result Ungroup::operateInternal()
+Ungroup::Result Ungroup::operateInternal(Context ctx)
 {
   Ungroup::Result result;
   auto params = this->parameters();
diff --git a/smtk/markup/operators/Ungroup.h b/smtk/markup/operators/Ungroup.h
index 77692a6869..73542f4ca7 100644
--- a/smtk/markup/operators/Ungroup.h
+++ b/smtk/markup/operators/Ungroup.h
@@ -30,7 +30,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/markup/operators/Write.cxx b/smtk/markup/operators/Write.cxx
index 2921f59e24..7c59ccf1a1 100644
--- a/smtk/markup/operators/Write.cxx
+++ b/smtk/markup/operators/Write.cxx
@@ -67,7 +67,7 @@ bool Write::ableToOperate()
   return true;
 }
 
-Write::Result Write::operateInternal()
+Write::Result Write::operateInternal(Context ctx)
 {
   auto resourceItem = this->parameters()->associations();
 
diff --git a/smtk/markup/operators/Write.h b/smtk/markup/operators/Write.h
index 0f8dd40cf1..5df83961c5 100644
--- a/smtk/markup/operators/Write.h
+++ b/smtk/markup/operators/Write.h
@@ -35,7 +35,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
   void markModifiedResources(Result&) override;
 
diff --git a/smtk/mesh/operators/DeleteMesh.cxx b/smtk/mesh/operators/DeleteMesh.cxx
index 7a6f98adee..dc0d689634 100644
--- a/smtk/mesh/operators/DeleteMesh.cxx
+++ b/smtk/mesh/operators/DeleteMesh.cxx
@@ -30,7 +30,7 @@ namespace mesh
 
 DeleteMesh::DeleteMesh() = default;
 
-smtk::mesh::DeleteMesh::Result DeleteMesh::operateInternal()
+smtk::mesh::DeleteMesh::Result DeleteMesh::operateInternal(Context ctx)
 {
   Result result = this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED);
 
diff --git a/smtk/mesh/operators/DeleteMesh.h b/smtk/mesh/operators/DeleteMesh.h
index a8fcd8055e..cf8e2217cd 100644
--- a/smtk/mesh/operators/DeleteMesh.h
+++ b/smtk/mesh/operators/DeleteMesh.h
@@ -31,7 +31,7 @@ public:
 
 protected:
   DeleteMesh();
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 
   void generateSummary(smtk::operation::Operation::Result&) override;
diff --git a/smtk/mesh/operators/ElevateMesh.cxx b/smtk/mesh/operators/ElevateMesh.cxx
index 7c34ccdb3a..616b0ce2f8 100644
--- a/smtk/mesh/operators/ElevateMesh.cxx
+++ b/smtk/mesh/operators/ElevateMesh.cxx
@@ -128,7 +128,7 @@ bool ElevateMesh::ableToOperate()
   return this->Superclass::ableToOperate();
 }
 
-ElevateMesh::Result ElevateMesh::operateInternal()
+ElevateMesh::Result ElevateMesh::operateInternal(Context ctx)
 {
   // Access the string describing the input data type
   smtk::attribute::StringItem::Ptr inputDataItem = this->parameters()->findString("input data");
diff --git a/smtk/mesh/operators/ElevateMesh.h b/smtk/mesh/operators/ElevateMesh.h
index 5c080c97c5..1464d86c5b 100644
--- a/smtk/mesh/operators/ElevateMesh.h
+++ b/smtk/mesh/operators/ElevateMesh.h
@@ -37,7 +37,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace mesh
diff --git a/smtk/mesh/operators/Export.cxx b/smtk/mesh/operators/Export.cxx
index d00146280e..67feed7be1 100644
--- a/smtk/mesh/operators/Export.cxx
+++ b/smtk/mesh/operators/Export.cxx
@@ -62,7 +62,7 @@ bool Export::ableToOperate()
   return this->Superclass::ableToOperate();
 }
 
-Export::Result Export::operateInternal()
+Export::Result Export::operateInternal(Context ctx)
 {
   std::string outputfile = this->parameters()->findFile("filename")->value();
 
diff --git a/smtk/mesh/operators/Export.h b/smtk/mesh/operators/Export.h
index ef8b51d2e2..534b59be3c 100644
--- a/smtk/mesh/operators/Export.h
+++ b/smtk/mesh/operators/Export.h
@@ -30,7 +30,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   Specification createSpecification() override;
   const char* xmlDescription() const override;
 };
diff --git a/smtk/mesh/operators/ExtractAdjacency.cxx b/smtk/mesh/operators/ExtractAdjacency.cxx
index 2e23db7833..4946b440a1 100644
--- a/smtk/mesh/operators/ExtractAdjacency.cxx
+++ b/smtk/mesh/operators/ExtractAdjacency.cxx
@@ -29,7 +29,7 @@ namespace smtk
 namespace mesh
 {
 
-smtk::mesh::ExtractAdjacency::Result ExtractAdjacency::operateInternal()
+smtk::mesh::ExtractAdjacency::Result ExtractAdjacency::operateInternal(Context ctx)
 {
   // Access the meshset
   smtk::attribute::ReferenceItem::Ptr meshItem = this->parameters()->associations();
diff --git a/smtk/mesh/operators/ExtractAdjacency.h b/smtk/mesh/operators/ExtractAdjacency.h
index 8c236beed0..4e9ad1652a 100644
--- a/smtk/mesh/operators/ExtractAdjacency.h
+++ b/smtk/mesh/operators/ExtractAdjacency.h
@@ -30,7 +30,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace mesh
diff --git a/smtk/mesh/operators/ExtractByDihedralAngle.cxx b/smtk/mesh/operators/ExtractByDihedralAngle.cxx
index 3087f5a7b5..fec00cb24f 100644
--- a/smtk/mesh/operators/ExtractByDihedralAngle.cxx
+++ b/smtk/mesh/operators/ExtractByDihedralAngle.cxx
@@ -147,7 +147,7 @@ bool ExtractByDihedralAngle::ableToOperate()
   return meshset.types().cellTypes() == triangles;
 }
 
-smtk::mesh::ExtractByDihedralAngle::Result ExtractByDihedralAngle::operateInternal()
+smtk::mesh::ExtractByDihedralAngle::Result ExtractByDihedralAngle::operateInternal(Context ctx)
 {
   smtk::attribute::ReferenceItem::Ptr meshItem = this->parameters()->associations();
   smtk::mesh::Component::Ptr meshComponent = meshItem->valueAs<smtk::mesh::Component>();
diff --git a/smtk/mesh/operators/ExtractByDihedralAngle.h b/smtk/mesh/operators/ExtractByDihedralAngle.h
index b18369b68a..be77d4fe59 100644
--- a/smtk/mesh/operators/ExtractByDihedralAngle.h
+++ b/smtk/mesh/operators/ExtractByDihedralAngle.h
@@ -31,7 +31,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/mesh/operators/ExtractSkin.cxx b/smtk/mesh/operators/ExtractSkin.cxx
index 5a82c46801..d38cb06b11 100644
--- a/smtk/mesh/operators/ExtractSkin.cxx
+++ b/smtk/mesh/operators/ExtractSkin.cxx
@@ -29,7 +29,7 @@ namespace smtk
 namespace mesh
 {
 
-smtk::mesh::ExtractSkin::Result ExtractSkin::operateInternal()
+smtk::mesh::ExtractSkin::Result ExtractSkin::operateInternal(Context ctx)
 {
   // Access the meshset
   smtk::attribute::ReferenceItem::Ptr meshItem = this->parameters()->associations();
diff --git a/smtk/mesh/operators/ExtractSkin.h b/smtk/mesh/operators/ExtractSkin.h
index 5b701251c7..b61ed83639 100644
--- a/smtk/mesh/operators/ExtractSkin.h
+++ b/smtk/mesh/operators/ExtractSkin.h
@@ -28,7 +28,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace mesh
diff --git a/smtk/mesh/operators/GenerateHotStartData.cxx b/smtk/mesh/operators/GenerateHotStartData.cxx
index 1a922fad36..b098da8d96 100644
--- a/smtk/mesh/operators/GenerateHotStartData.cxx
+++ b/smtk/mesh/operators/GenerateHotStartData.cxx
@@ -178,7 +178,7 @@ bool GenerateHotStartData::ableToOperate()
   return this->Superclass::ableToOperate();
 }
 
-GenerateHotStartData::Result GenerateHotStartData::operateInternal()
+GenerateHotStartData::Result GenerateHotStartData::operateInternal(Context ctx)
 {
   // Access the mesh to elevate
   smtk::attribute::ReferenceItem::Ptr meshItem = this->parameters()->associations();
diff --git a/smtk/mesh/operators/GenerateHotStartData.h b/smtk/mesh/operators/GenerateHotStartData.h
index 600f5af806..a39e347206 100644
--- a/smtk/mesh/operators/GenerateHotStartData.h
+++ b/smtk/mesh/operators/GenerateHotStartData.h
@@ -38,7 +38,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace mesh
diff --git a/smtk/mesh/operators/Import.cxx b/smtk/mesh/operators/Import.cxx
index c70371e7e0..939b3cdb74 100644
--- a/smtk/mesh/operators/Import.cxx
+++ b/smtk/mesh/operators/Import.cxx
@@ -53,7 +53,7 @@ namespace smtk
 namespace mesh
 {
 
-Import::Result Import::operateInternal()
+Import::Result Import::operateInternal(Context ctx)
 {
   // Get the import file name
   smtk::attribute::FileItem::Ptr filePathItem = this->parameters()->findFile("filename");
diff --git a/smtk/mesh/operators/Import.h b/smtk/mesh/operators/Import.h
index ddd00f1f17..9d2c207d0d 100644
--- a/smtk/mesh/operators/Import.h
+++ b/smtk/mesh/operators/Import.h
@@ -32,7 +32,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   Specification createSpecification() override;
   const char* xmlDescription() const override;
 };
diff --git a/smtk/mesh/operators/InterpolateOntoMesh.cxx b/smtk/mesh/operators/InterpolateOntoMesh.cxx
index 7c48c88a3b..f95b1babb1 100644
--- a/smtk/mesh/operators/InterpolateOntoMesh.cxx
+++ b/smtk/mesh/operators/InterpolateOntoMesh.cxx
@@ -134,7 +134,7 @@ bool InterpolateOntoMesh::ableToOperate()
   return this->Superclass::ableToOperate();
 }
 
-InterpolateOntoMesh::Result InterpolateOntoMesh::operateInternal()
+InterpolateOntoMesh::Result InterpolateOntoMesh::operateInternal(Context ctx)
 {
   // Access the string describing the input data type
   smtk::attribute::StringItem::Ptr inputDataItem = this->parameters()->findString("input data");
diff --git a/smtk/mesh/operators/InterpolateOntoMesh.h b/smtk/mesh/operators/InterpolateOntoMesh.h
index 9c9424c41c..4ba0a16d7c 100644
--- a/smtk/mesh/operators/InterpolateOntoMesh.h
+++ b/smtk/mesh/operators/InterpolateOntoMesh.h
@@ -30,7 +30,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace mesh
diff --git a/smtk/mesh/operators/MergeCoincidentPoints.cxx b/smtk/mesh/operators/MergeCoincidentPoints.cxx
index edcc8d97bd..d6d40afb65 100644
--- a/smtk/mesh/operators/MergeCoincidentPoints.cxx
+++ b/smtk/mesh/operators/MergeCoincidentPoints.cxx
@@ -26,7 +26,7 @@ namespace smtk
 namespace mesh
 {
 
-smtk::mesh::MergeCoincidentPoints::Result MergeCoincidentPoints::operateInternal()
+smtk::mesh::MergeCoincidentPoints::Result MergeCoincidentPoints::operateInternal(Context ctx)
 {
   // Access the tolerance.
   double tolerance = this->parameters()->findDouble("tolerance")->value();
diff --git a/smtk/mesh/operators/MergeCoincidentPoints.h b/smtk/mesh/operators/MergeCoincidentPoints.h
index 8187ac4e4b..f3f8e2e637 100644
--- a/smtk/mesh/operators/MergeCoincidentPoints.h
+++ b/smtk/mesh/operators/MergeCoincidentPoints.h
@@ -28,7 +28,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/mesh/operators/PrintMeshInformation.cxx b/smtk/mesh/operators/PrintMeshInformation.cxx
index 466918b983..097a52abe5 100644
--- a/smtk/mesh/operators/PrintMeshInformation.cxx
+++ b/smtk/mesh/operators/PrintMeshInformation.cxx
@@ -26,7 +26,7 @@ namespace smtk
 namespace mesh
 {
 
-smtk::mesh::PrintMeshInformation::Result PrintMeshInformation::operateInternal()
+smtk::mesh::PrintMeshInformation::Result PrintMeshInformation::operateInternal(Context ctx)
 {
   smtk::attribute::ReferenceItem::Ptr meshItem = this->parameters()->associations();
 
diff --git a/smtk/mesh/operators/PrintMeshInformation.h b/smtk/mesh/operators/PrintMeshInformation.h
index 92dd509052..46d03cfac2 100644
--- a/smtk/mesh/operators/PrintMeshInformation.h
+++ b/smtk/mesh/operators/PrintMeshInformation.h
@@ -28,7 +28,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/mesh/operators/Read.cxx b/smtk/mesh/operators/Read.cxx
index 4f5a27965b..64dcb84672 100644
--- a/smtk/mesh/operators/Read.cxx
+++ b/smtk/mesh/operators/Read.cxx
@@ -63,7 +63,7 @@ namespace smtk
 namespace mesh
 {
 
-Read::Result Read::operateInternal()
+Read::Result Read::operateInternal(Context ctx)
 {
   // Get the read file name
   smtk::attribute::FileItem::Ptr filePathItem = this->parameters()->findFile("filename");
diff --git a/smtk/mesh/operators/Read.h b/smtk/mesh/operators/Read.h
index 3bb09a1197..76f3d7db74 100644
--- a/smtk/mesh/operators/Read.h
+++ b/smtk/mesh/operators/Read.h
@@ -32,7 +32,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   Specification createSpecification() override;
   const char* xmlDescription() const override;
   void markModifiedResources(Result&) override;
diff --git a/smtk/mesh/operators/ReadResource.cxx b/smtk/mesh/operators/ReadResource.cxx
index 412499ce88..00a1fbb7de 100644
--- a/smtk/mesh/operators/ReadResource.cxx
+++ b/smtk/mesh/operators/ReadResource.cxx
@@ -42,7 +42,7 @@ namespace smtk
 namespace mesh
 {
 
-ReadResource::Result ReadResource::operateInternal()
+ReadResource::Result ReadResource::operateInternal(Context ctx)
 {
   std::string filename = this->parameters()->findFile("filename")->value();
 
diff --git a/smtk/mesh/operators/ReadResource.h b/smtk/mesh/operators/ReadResource.h
index df6dce0ec1..cb68c80aeb 100644
--- a/smtk/mesh/operators/ReadResource.h
+++ b/smtk/mesh/operators/ReadResource.h
@@ -28,7 +28,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
   void markModifiedResources(Result&) override;
 };
diff --git a/smtk/mesh/operators/SelectCells.cxx b/smtk/mesh/operators/SelectCells.cxx
index c958162506..256a5f5704 100644
--- a/smtk/mesh/operators/SelectCells.cxx
+++ b/smtk/mesh/operators/SelectCells.cxx
@@ -33,7 +33,7 @@ namespace smtk
 namespace mesh
 {
 
-SelectCells::Result SelectCells::operateInternal()
+SelectCells::Result SelectCells::operateInternal(Context ctx)
 {
   // Access the mesh resource.
   smtk::attribute::ReferenceItem::Ptr resourceItem = this->parameters()->associations();
diff --git a/smtk/mesh/operators/SelectCells.h b/smtk/mesh/operators/SelectCells.h
index 92f44a4142..7463a98f38 100644
--- a/smtk/mesh/operators/SelectCells.h
+++ b/smtk/mesh/operators/SelectCells.h
@@ -30,7 +30,7 @@ public:
   void generateSummary(Result&) override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace mesh
diff --git a/smtk/mesh/operators/SetMeshName.cxx b/smtk/mesh/operators/SetMeshName.cxx
index deeb1a0e21..3979fbfe65 100644
--- a/smtk/mesh/operators/SetMeshName.cxx
+++ b/smtk/mesh/operators/SetMeshName.cxx
@@ -25,7 +25,7 @@ namespace smtk
 namespace mesh
 {
 
-smtk::mesh::SetMeshName::Result SetMeshName::operateInternal()
+smtk::mesh::SetMeshName::Result SetMeshName::operateInternal(Context ctx)
 {
   // Access the name to use when renaming the meshset
   smtk::attribute::StringItem::Ptr nameItem = this->parameters()->findString("name");
diff --git a/smtk/mesh/operators/SetMeshName.h b/smtk/mesh/operators/SetMeshName.h
index e0d7b5469b..60d5fd216a 100644
--- a/smtk/mesh/operators/SetMeshName.h
+++ b/smtk/mesh/operators/SetMeshName.h
@@ -28,7 +28,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/mesh/operators/Subtract.cxx b/smtk/mesh/operators/Subtract.cxx
index 157c11c17f..d20c6719ff 100644
--- a/smtk/mesh/operators/Subtract.cxx
+++ b/smtk/mesh/operators/Subtract.cxx
@@ -27,7 +27,7 @@ namespace smtk
 namespace mesh
 {
 
-smtk::mesh::Subtract::Result Subtract::operateInternal()
+smtk::mesh::Subtract::Result Subtract::operateInternal(Context ctx)
 {
   // Access the minuend and its resource
   smtk::mesh::MeshSet minuend =
diff --git a/smtk/mesh/operators/Subtract.h b/smtk/mesh/operators/Subtract.h
index 82983cdb45..b052dd35bc 100644
--- a/smtk/mesh/operators/Subtract.h
+++ b/smtk/mesh/operators/Subtract.h
@@ -28,7 +28,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/mesh/operators/Transform.cxx b/smtk/mesh/operators/Transform.cxx
index cc9cedf5bf..32d39adf21 100644
--- a/smtk/mesh/operators/Transform.cxx
+++ b/smtk/mesh/operators/Transform.cxx
@@ -36,7 +36,7 @@ namespace smtk
 namespace mesh
 {
 
-smtk::mesh::Transform::Result Transform::operateInternal()
+smtk::mesh::Transform::Result Transform::operateInternal(Context ctx)
 {
   smtk::attribute::ReferenceItem::Ptr meshItem = this->parameters()->associations();
 
diff --git a/smtk/mesh/operators/Transform.h b/smtk/mesh/operators/Transform.h
index bf1f0511fd..c8551bf4a4 100644
--- a/smtk/mesh/operators/Transform.h
+++ b/smtk/mesh/operators/Transform.h
@@ -28,7 +28,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/mesh/operators/UndoElevateMesh.cxx b/smtk/mesh/operators/UndoElevateMesh.cxx
index ffa01e295b..11636cfbe1 100644
--- a/smtk/mesh/operators/UndoElevateMesh.cxx
+++ b/smtk/mesh/operators/UndoElevateMesh.cxx
@@ -56,7 +56,7 @@ bool UndoElevateMesh::ableToOperate()
   return true;
 }
 
-UndoElevateMesh::Result UndoElevateMesh::operateInternal()
+UndoElevateMesh::Result UndoElevateMesh::operateInternal(Context ctx)
 {
   // Access the mesh
   smtk::attribute::ReferenceItem::Ptr meshItem = this->parameters()->associations();
diff --git a/smtk/mesh/operators/UndoElevateMesh.h b/smtk/mesh/operators/UndoElevateMesh.h
index 9f6875b3aa..8f9a05eef0 100644
--- a/smtk/mesh/operators/UndoElevateMesh.h
+++ b/smtk/mesh/operators/UndoElevateMesh.h
@@ -34,7 +34,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace mesh
diff --git a/smtk/mesh/operators/Write.cxx b/smtk/mesh/operators/Write.cxx
index 991b418536..210c527ee6 100644
--- a/smtk/mesh/operators/Write.cxx
+++ b/smtk/mesh/operators/Write.cxx
@@ -62,7 +62,7 @@ bool Write::ableToOperate()
   return this->Superclass::ableToOperate();
 }
 
-Write::Result Write::operateInternal()
+Write::Result Write::operateInternal(Context ctx)
 {
   std::string outputfile = this->parameters()->findFile("filename")->value();
 
diff --git a/smtk/mesh/operators/Write.h b/smtk/mesh/operators/Write.h
index 9345e6c94e..6eefcc39ae 100644
--- a/smtk/mesh/operators/Write.h
+++ b/smtk/mesh/operators/Write.h
@@ -30,7 +30,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   Specification createSpecification() override;
   const char* xmlDescription() const override;
   void markModifiedResources(Result&) override;
diff --git a/smtk/mesh/operators/WriteResource.cxx b/smtk/mesh/operators/WriteResource.cxx
index 80bc351b4b..3d0ca873e5 100644
--- a/smtk/mesh/operators/WriteResource.cxx
+++ b/smtk/mesh/operators/WriteResource.cxx
@@ -59,7 +59,7 @@ bool WriteResource::ableToOperate()
   return true;
 }
 
-WriteResource::Result WriteResource::operateInternal()
+WriteResource::Result WriteResource::operateInternal(Context ctx)
 {
   auto resourceItem = this->parameters()->associations();
 
diff --git a/smtk/mesh/operators/WriteResource.h b/smtk/mesh/operators/WriteResource.h
index 4410128b00..2a9e95100e 100644
--- a/smtk/mesh/operators/WriteResource.h
+++ b/smtk/mesh/operators/WriteResource.h
@@ -30,7 +30,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
   void markModifiedResources(Result&) override;
 };
diff --git a/smtk/model/operators/AddAuxiliaryGeometry.cxx b/smtk/model/operators/AddAuxiliaryGeometry.cxx
index 3d5507431a..ec6d3b169e 100644
--- a/smtk/model/operators/AddAuxiliaryGeometry.cxx
+++ b/smtk/model/operators/AddAuxiliaryGeometry.cxx
@@ -42,7 +42,7 @@ namespace smtk
 namespace model
 {
 
-AddAuxiliaryGeometry::Result AddAuxiliaryGeometry::operateInternal()
+AddAuxiliaryGeometry::Result AddAuxiliaryGeometry::operateInternal(Context ctx)
 {
   auto associations = this->parameters()->associations();
   auto entities = associations->as<EntityRefArray>(
diff --git a/smtk/model/operators/AddAuxiliaryGeometry.h b/smtk/model/operators/AddAuxiliaryGeometry.h
index cdc1a3dbd9..aed281af4a 100644
--- a/smtk/model/operators/AddAuxiliaryGeometry.h
+++ b/smtk/model/operators/AddAuxiliaryGeometry.h
@@ -26,7 +26,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/model/operators/AddImage.cxx b/smtk/model/operators/AddImage.cxx
index a284ebcb88..e011b5f825 100644
--- a/smtk/model/operators/AddImage.cxx
+++ b/smtk/model/operators/AddImage.cxx
@@ -20,9 +20,9 @@ namespace smtk
 namespace model
 {
 
-AddImage::Result AddImage::operateInternal()
+AddImage::Result AddImage::operateInternal(Context ctx)
 {
-  Result res = this->AddAuxiliaryGeometry::operateInternal();
+  Result res = this->AddAuxiliaryGeometry::operateInternal(ctx);
   smtk::attribute::ComponentItem::Ptr created = res->findComponent("created");
   for (size_t i = 0; i < created->numberOfValues(); ++i)
   {
diff --git a/smtk/model/operators/AddImage.h b/smtk/model/operators/AddImage.h
index 61dc6ee01d..b327fd2bcf 100644
--- a/smtk/model/operators/AddImage.h
+++ b/smtk/model/operators/AddImage.h
@@ -26,7 +26,7 @@ public:
   smtkSuperclassMacro(AddAuxiliaryGeometry);
 
 private:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/model/operators/CloseModel.cxx b/smtk/model/operators/CloseModel.cxx
index 22d635d020..9e1894c090 100644
--- a/smtk/model/operators/CloseModel.cxx
+++ b/smtk/model/operators/CloseModel.cxx
@@ -43,7 +43,7 @@ bool CloseModel::ableToOperate()
   return modelItem && modelItem->numberOfValues() > 0;
 }
 
-CloseModel::Result CloseModel::operateInternal()
+CloseModel::Result CloseModel::operateInternal(Context ctx)
 {
   // ableToOperate should have verified that model(s) are set
   smtk::attribute::ReferenceItem::Ptr modelItem = this->parameters()->associations();
diff --git a/smtk/model/operators/CloseModel.h b/smtk/model/operators/CloseModel.h
index ad087d5792..8dc5c353e1 100644
--- a/smtk/model/operators/CloseModel.h
+++ b/smtk/model/operators/CloseModel.h
@@ -28,7 +28,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/model/operators/CreateInstances.cxx b/smtk/model/operators/CreateInstances.cxx
index 7c0b12f36a..0fa034b75b 100644
--- a/smtk/model/operators/CreateInstances.cxx
+++ b/smtk/model/operators/CreateInstances.cxx
@@ -106,7 +106,7 @@ void CreateInstances::addSnappingConstraints(Instance& instance, const EntityRef
   }
 }
 
-CreateInstances::Result CreateInstances::operateInternal()
+CreateInstances::Result CreateInstances::operateInternal(Context ctx)
 {
   auto associations = this->parameters()->associations();
   auto prototypes = associations->as<EntityRefArray>([](PersistentObjectPtr obj) {
diff --git a/smtk/model/operators/CreateInstances.h b/smtk/model/operators/CreateInstances.h
index c4c20d6e86..872ec0296c 100644
--- a/smtk/model/operators/CreateInstances.h
+++ b/smtk/model/operators/CreateInstances.h
@@ -26,7 +26,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 
   void addTabularRule(Instance& instance, const EntityRef& prototype);
diff --git a/smtk/model/operators/Delete.cxx b/smtk/model/operators/Delete.cxx
index caf977a8a7..4179997b94 100644
--- a/smtk/model/operators/Delete.cxx
+++ b/smtk/model/operators/Delete.cxx
@@ -310,7 +310,7 @@ void Delete::addBoundaryCells(
   }
 }
 
-Delete::Result Delete::operateInternal()
+Delete::Result Delete::operateInternal(Context ctx)
 {
   smtk::attribute::VoidItem::Ptr deleteHigherDimen =
     this->parameters()->findVoid("delete higher-dimensional neighbors");
diff --git a/smtk/model/operators/Delete.h b/smtk/model/operators/Delete.h
index 8e3421e1aa..3e84465073 100644
--- a/smtk/model/operators/Delete.h
+++ b/smtk/model/operators/Delete.h
@@ -36,7 +36,7 @@ public:
   smtkSuperclassMacro(Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 
   template<typename U, typename V, typename W, typename X>
diff --git a/smtk/model/operators/DivideInstance.cxx b/smtk/model/operators/DivideInstance.cxx
index 432f4bd013..ceb8b26d49 100644
--- a/smtk/model/operators/DivideInstance.cxx
+++ b/smtk/model/operators/DivideInstance.cxx
@@ -44,7 +44,7 @@ bool DivideInstance::ableToOperate()
   return parent.isValid();
 }
 
-DivideInstance::Result DivideInstance::operateInternal()
+DivideInstance::Result DivideInstance::operateInternal(Context ctx)
 {
   auto associations = this->parameters()->associations();
   Instance instance = this->parentOfClones(associations);
diff --git a/smtk/model/operators/DivideInstance.h b/smtk/model/operators/DivideInstance.h
index dfe61d1464..b0a949cadd 100644
--- a/smtk/model/operators/DivideInstance.h
+++ b/smtk/model/operators/DivideInstance.h
@@ -28,7 +28,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 
   /**\brief Return the parent instance of clones in the provided \a item.
diff --git a/smtk/model/operators/EntityGroupOperation.cxx b/smtk/model/operators/EntityGroupOperation.cxx
index 53c237f89f..572d849d51 100644
--- a/smtk/model/operators/EntityGroupOperation.cxx
+++ b/smtk/model/operators/EntityGroupOperation.cxx
@@ -58,7 +58,7 @@ bool EntityGroupOperation::ableToOperate()
   return ableToOperate;
 }
 
-EntityGroupOperation::Result EntityGroupOperation::operateInternal()
+EntityGroupOperation::Result EntityGroupOperation::operateInternal(Context ctx)
 {
   // pre processing the data
   auto modelItem = this->parameters()->associations();
diff --git a/smtk/model/operators/EntityGroupOperation.h b/smtk/model/operators/EntityGroupOperation.h
index 37b40d1f4b..1cdb1a6b78 100644
--- a/smtk/model/operators/EntityGroupOperation.h
+++ b/smtk/model/operators/EntityGroupOperation.h
@@ -37,7 +37,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/model/operators/ExportModelJSON.cxx b/smtk/model/operators/ExportModelJSON.cxx
index 6488007044..b1060bfdcf 100644
--- a/smtk/model/operators/ExportModelJSON.cxx
+++ b/smtk/model/operators/ExportModelJSON.cxx
@@ -31,7 +31,7 @@ namespace smtk
 namespace model
 {
 
-ExportModelJSON::Result ExportModelJSON::operateInternal()
+ExportModelJSON::Result ExportModelJSON::operateInternal(Context ctx)
 {
   smtk::attribute::FileItemPtr filenameItem = this->parameters()->findFile("filename");
   smtk::attribute::IntItemPtr flagsItem = this->parameters()->findInt("flags");
diff --git a/smtk/model/operators/ExportModelJSON.h b/smtk/model/operators/ExportModelJSON.h
index 83ad724d58..52e4856ff7 100644
--- a/smtk/model/operators/ExportModelJSON.h
+++ b/smtk/model/operators/ExportModelJSON.h
@@ -26,7 +26,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/model/operators/GroupAuxiliaryGeometry.cxx b/smtk/model/operators/GroupAuxiliaryGeometry.cxx
index 5158d83a98..b63ece0980 100644
--- a/smtk/model/operators/GroupAuxiliaryGeometry.cxx
+++ b/smtk/model/operators/GroupAuxiliaryGeometry.cxx
@@ -32,7 +32,7 @@ namespace smtk
 namespace model
 {
 
-GroupAuxiliaryGeometry::Result GroupAuxiliaryGeometry::operateInternal()
+GroupAuxiliaryGeometry::Result GroupAuxiliaryGeometry::operateInternal(Context ctx)
 {
   auto associations = this->parameters()->associations();
   auto entities =
diff --git a/smtk/model/operators/GroupAuxiliaryGeometry.h b/smtk/model/operators/GroupAuxiliaryGeometry.h
index c1bca582f0..9fe86ec679 100644
--- a/smtk/model/operators/GroupAuxiliaryGeometry.h
+++ b/smtk/model/operators/GroupAuxiliaryGeometry.h
@@ -26,7 +26,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/model/operators/MergeInstances.cxx b/smtk/model/operators/MergeInstances.cxx
index ab52af5c9c..471524ce96 100644
--- a/smtk/model/operators/MergeInstances.cxx
+++ b/smtk/model/operators/MergeInstances.cxx
@@ -58,7 +58,7 @@ bool MergeInstances::ableToOperate()
   });
 }
 
-MergeInstances::Result MergeInstances::operateInternal()
+MergeInstances::Result MergeInstances::operateInternal(Context ctx)
 {
   auto associations = this->parameters()->associations();
   auto instances = associations->as<Instances>([](smtk::resource::PersistentObjectPtr obj) {
diff --git a/smtk/model/operators/MergeInstances.h b/smtk/model/operators/MergeInstances.h
index 5772ff5775..ab99a508cb 100644
--- a/smtk/model/operators/MergeInstances.h
+++ b/smtk/model/operators/MergeInstances.h
@@ -28,7 +28,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/model/operators/SetInstancePrototype.cxx b/smtk/model/operators/SetInstancePrototype.cxx
index 36c217bc53..b08a184bed 100644
--- a/smtk/model/operators/SetInstancePrototype.cxx
+++ b/smtk/model/operators/SetInstancePrototype.cxx
@@ -29,7 +29,7 @@ namespace smtk
 namespace model
 {
 
-SetInstancePrototype::Result SetInstancePrototype::operateInternal()
+SetInstancePrototype::Result SetInstancePrototype::operateInternal(Context ctx)
 {
   auto associations = this->parameters()->associations();
   auto entities = associations->as<Instances>([](smtk::resource::PersistentObjectPtr obj) {
diff --git a/smtk/model/operators/SetInstancePrototype.h b/smtk/model/operators/SetInstancePrototype.h
index d13c76c59a..a2cfba70c7 100644
--- a/smtk/model/operators/SetInstancePrototype.h
+++ b/smtk/model/operators/SetInstancePrototype.h
@@ -26,7 +26,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/model/operators/TerrainExtraction.cxx b/smtk/model/operators/TerrainExtraction.cxx
index 09a34fe7b2..21ac3425df 100644
--- a/smtk/model/operators/TerrainExtraction.cxx
+++ b/smtk/model/operators/TerrainExtraction.cxx
@@ -25,7 +25,7 @@ namespace smtk
 namespace model
 {
 
-TerrainExtraction::Result TerrainExtraction::operateInternal()
+TerrainExtraction::Result TerrainExtraction::operateInternal(Context ctx)
 {
   auto associations = this->parameters()->associations();
 
diff --git a/smtk/model/operators/TerrainExtraction.h b/smtk/model/operators/TerrainExtraction.h
index a7c924f11a..3481f9a3ea 100644
--- a/smtk/model/operators/TerrainExtraction.h
+++ b/smtk/model/operators/TerrainExtraction.h
@@ -26,7 +26,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/operation/CreateResource.cxx b/smtk/operation/CreateResource.cxx
index fffb1e80cd..498b7a2ad9 100644
--- a/smtk/operation/CreateResource.cxx
+++ b/smtk/operation/CreateResource.cxx
@@ -29,7 +29,7 @@ namespace operation
 
 CreateResource::CreateResource() {}
 
-smtk::operation::Operation::Result CreateResource::operateInternal()
+smtk::operation::Operation::Result CreateResource::operateInternal(Context ctx)
 {
   auto params = this->parameters();
   auto typeItem = params->findString("type");
diff --git a/smtk/operation/CreateResource.h b/smtk/operation/CreateResource.h
index 18a009797d..cc72295b50 100644
--- a/smtk/operation/CreateResource.h
+++ b/smtk/operation/CreateResource.h
@@ -28,7 +28,7 @@ public:
 protected:
   CreateResource();
 
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
   const char* xmlDescription() const override;
   void generateSummary(Result&) override;
diff --git a/smtk/operation/Manager.cxx b/smtk/operation/Manager.cxx
index 5393d999cf..5ad8c2ca22 100644
--- a/smtk/operation/Manager.cxx
+++ b/smtk/operation/Manager.cxx
@@ -190,6 +190,7 @@ bool Manager::registerResourceManager(smtk::resource::ManagerPtr& resourceManage
   // Define an observer that adds all created resources to the resource manager.
   m_resourceObserver = this->observers().insert(
     [&, weakRMPtr](
+    smtk::common::Context ctx,
       const smtk::operation::Operation& /*unused*/,
       smtk::operation::EventType event,
       smtk::operation::Operation::Result result) {
diff --git a/smtk/operation/Observer.h b/smtk/operation/Observer.h
index b508e57ed2..777e663bbd 100644
--- a/smtk/operation/Observer.h
+++ b/smtk/operation/Observer.h
@@ -10,6 +10,7 @@
 #ifndef smtk_operation_Observer_h
 #define smtk_operation_Observer_h
 
+#include "smtk/common/Context.h"
 #include "smtk/common/Observers.h"
 #include "smtk/operation/Operation.h"
 
@@ -39,7 +40,7 @@ enum class EventType
   * Observers must return an integer. If the event type is WILL_OPERATE and
   * the integer is non-zero, the operation will be canceled.
   */
-typedef std::function<int(const Operation&, EventType, Operation::Result)> Observer;
+typedef std::function<int(smtk::common::Context ctx, const Operation&, EventType, Operation::Result)> Observer;
 
 /// Operation observers are a specialization of the common SMTK observer pattern.
 typedef smtk::common::Observers<Observer> Observers;
diff --git a/smtk/operation/Operation.cxx b/smtk/operation/Operation.cxx
index 041cc1261c..0d7138843d 100644
--- a/smtk/operation/Operation.cxx
+++ b/smtk/operation/Operation.cxx
@@ -37,6 +37,10 @@
 #include <sstream>
 #include <thread>
 
+DECLARE_CONTEXT_KEY_TYPE(parentOperationKey)
+DECLARE_CONTEXT_KEY_TYPE(resultsKey)
+DECLARE_CONTEXT_KEY_TYPE(threadKey)
+
 namespace
 {
 // We construct unique parameter and result names in a thread-safe way, rather
@@ -130,15 +134,31 @@ ResourceAccessMap Operation::identifyLocksRequired()
   return extractResourcesAndLockTypes(this->parameters());
 }
 
-Operation::Result Operation::operate()
+Operation::Result Operation::operate(const Context& ctx)
 {
   // Call operate that will invoke observers.
-  return this->operate(BaseKey(
-    nullptr, ObserverOption::InvokeObservers, LockOption::LockAll, ParametersOption::Validate));
+  return this->operate(
+    ctx,
+    BaseKey(
+      nullptr, ObserverOption::InvokeObservers, LockOption::LockAll, ParametersOption::Validate));
 }
 
-Operation::Result Operation::operate(const BaseKey& key)
+Operation::Result Operation::operate(Context ctx, const BaseKey& key)
 {
+  // Check that the current thread matches the initial parent.
+  if (auto thread = ctx.value<threadKey, std::thread::id>())
+  {
+    if (std::this_thread::get_id() != *thread)
+    {
+      throw std::logic_error("context belongs to operation in a different thread");
+    }
+  }
+  else
+  {
+    // Store the current thread id into the context.
+    ctx = ctx.withValue<threadKey>(std::this_thread::get_id());
+  }
+
   // Gather all requested resources and their lock types.
   const auto resourcesAndLockTypes = this->identifyLocksRequired();
 
@@ -174,6 +194,13 @@ Operation::Result Operation::operate(const BaseKey& key)
         this->m_lockedResources = key.m_parent->lockedResources();
       }
 
+		if (auto parentKey = ctx.value<parentOperationKey, BaseKey>())
+        {
+		  // Inherit resource locks from the context.
+          auto& lockedResources = parentKey->m_parent->lockedResources();
+          this->m_lockedResources.insert(lockedResources.begin(), lockedResources.end());
+        }
+
       // Populate queue for pending resource locks.
       std::queue<ResourceAccessMap::value_type> pending;
       for (const auto& resourceAndLockType : resourcesAndLockTypes)
@@ -266,13 +293,13 @@ Operation::Result Operation::operate(const BaseKey& key)
 
       if (pending.empty())
       {
-        // All of the locks were acquired successfully. Continue with the
+        // All the locks were acquired successfully. Continue with the
         // operation logic.
         break;
       }
       else
       {
-        // All of the resource locks could not be acquired. Release the locks on any
+        // All the resource locks could not be acquired. Release the locks on any
         // resource whose lock was successfully acquired so that other threads
         // have a chance to attempt the locks if they are waiting.
         this->unlockResources(lockedByThis);
@@ -310,7 +337,7 @@ Operation::Result Operation::operate(const BaseKey& key)
   {
     if (
       key.m_observerOption == ObserverOption::InvokeObservers && manager &&
-      manager->observers()(*this, EventType::WILL_OPERATE, nullptr))
+      manager->observers()(ctx, *this, EventType::WILL_OPERATE, nullptr))
     {
       outcome = Outcome::CANCELED;
       result = this->createResult(outcome);
@@ -324,8 +351,13 @@ Operation::Result Operation::operate(const BaseKey& key)
       smtk::attribute::IntItem::Ptr debugItem = this->parameters()->findInt("debug level");
       m_debugLevel = ((debugItem && debugItem->isEnabled()) ? debugItem->value() : 0);
 
+      // Store a key in the context to establish this operation as the parent for
+      // all future operations on the current control path.
+      ctx = ctx.withValue<parentOperationKey>(this->childKey());
+
       // Perform the derived operation.
-      result = this->operateInternal();
+      result = this->operateInternal(ctx);
+
       // Post-process the result if the operation was successful.
       outcome = static_cast<Outcome>(result->findInt("outcome")->value());
       if (outcome == Outcome::SUCCEEDED)
@@ -361,7 +393,7 @@ Operation::Result Operation::operate(const BaseKey& key)
   // Execute post-operation observation
   if (key.m_observerOption == ObserverOption::InvokeObservers && observePostOperation && manager)
   {
-    manager->observers()(*this, EventType::DID_OPERATE, result);
+    manager->observers()(ctx, *this, EventType::DID_OPERATE, result);
   }
 
   // Un-manage any resources marked for removal before releasing locks.
@@ -380,6 +412,11 @@ Operation::Result Operation::operate(const BaseKey& key)
   return result;
 }
 
+Operation::Result Operation::operate(const BaseKey& key)
+{
+  return this->operate(Context(), key);
+}
+
 void Operation::unlockResources(const ResourceAccessMap& resources)
 {
   for (const auto& resourceAndLockType : resources)
diff --git a/smtk/operation/Operation.h b/smtk/operation/Operation.h
index 35df8ee437..e819da76b9 100644
--- a/smtk/operation/Operation.h
+++ b/smtk/operation/Operation.h
@@ -14,6 +14,7 @@
 
 #include "smtk/PublicPointerDefs.h"
 #include "smtk/SharedFromThis.h"
+#include "smtk/common/Context.h"
 #include "smtk/common/Deprecation.h"
 
 #include <functional>
@@ -56,7 +57,7 @@ using ResourceAccessMap = std::map<
 /// createSpecification(). There should be exactly one attribute definition that
 /// derives from "operation" and one definition that derives from "result". The
 /// creation, destruction and manipulation of SMTK resources and components
-/// occurs in the method operateInternal(). Operations are designed to work in
+/// occurs in the method operateInternal(Context ctx). Operations are designed to work in
 /// conjunction with an smtk::operation::Manager, but can also be used as
 /// standalone functors.
 class SMTKCORE_EXPORT Operation : smtkEnableSharedPtr(Operation)
@@ -65,6 +66,7 @@ class SMTKCORE_EXPORT Operation : smtkEnableSharedPtr(Operation)
 
 public:
   smtkTypeMacroBase(smtk::operation::Operation);
+  using Context = smtk::common::Context;
 
   // A hash value uniquely representing the operation.
   typedef std::size_t Index;
@@ -100,7 +102,10 @@ public:
   // cannot be set. It is virtual so that derived operations can assign their
   // own index (as is necessary for python operations that would otherwise all
   // resolve to the same index).
-  virtual Index index() const { return std::type_index(typeid(*this)).hash_code(); }
+  virtual Index index() const
+  {
+    return std::type_index(typeid(*this)).hash_code();
+  }
 
   /// Update the operation's specification and operations to be consistent.
   ///
@@ -134,8 +139,8 @@ public:
   virtual bool ableToOperate();
 
   /// Execute the operation, log its outcome and return its results. This method
-  /// calls operateInternal() and handles additional bookkeeping.
-  Result operate();
+  /// calls operateInternal(Context ctx) and handles additional bookkeeping.
+  Result operate(const Context& ctx = Context());
 
   /// Execute the operation in an asynchronous environment. This method does not
   /// return until the operation is complete, but because it does not return a
@@ -162,7 +167,7 @@ public:
   /// resource; anyone holding the shared pointer to the result will keep the
   /// attribute in memory but will experience inconsistent behavior since its items
   /// are removed as part of releasing it from control by the attribute::Resource.
-  virtual bool releaseResult(Result& result);
+  virtual bool releaseResult(Result & result);
 
   /// Retrieve the operation's logger. By default, we use the singleton logger.
   /// Derived classes can reimplement this method if an alternative logging
@@ -186,17 +191,29 @@ public:
   Result createResult(Outcome);
 
   /// Operations that are managed have a non-null pointer to their manager.
-  ManagerPtr manager() const { return m_manager.lock(); }
+  ManagerPtr manager() const
+  {
+    return m_manager.lock();
+  }
 
   /// restore operation parameters from the trace of a previously run operation.
   bool restoreTrace(const std::string& trace);
 
   /// Operations may be passed application state in the form of a Managers type-container.
-  void setManagers(const std::shared_ptr<smtk::common::Managers>& m) { m_managers = m; }
-  std::shared_ptr<smtk::common::Managers> managers() const { return m_managers; }
+  void setManagers(const std::shared_ptr<smtk::common::Managers>& m)
+  {
+    m_managers = m;
+  }
+  std::shared_ptr<smtk::common::Managers> managers() const
+  {
+    return m_managers;
+  }
 
   /// Is this type of operation safe to launch in a thread?
-  virtual bool threadSafe() const { return true; }
+  virtual bool threadSafe() const
+  {
+    return true;
+  }
 
   /// retrieve the resource manager, if available.
   smtk::resource::ManagerPtr resourceManager();
@@ -237,10 +254,13 @@ protected:
   virtual ResourceAccessMap identifyLocksRequired();
 
   /// Returns the set of resources that are currently locked by this operation.
-  const ResourceAccessMap& lockedResources() const { return this->m_lockedResources; }
+  const ResourceAccessMap& lockedResources() const
+  {
+    return this->m_lockedResources;
+  }
 
   /// Perform the actual operation and construct the result.
-  virtual Result operateInternal() = 0;
+  virtual Result operateInternal(Context context) = 0;
 
   // Apply post-processing to the result object. This method should not modify
   // the modeling kernel but may change string/float/int properties stored on
@@ -301,7 +321,7 @@ protected:
   };
 
   /// Return a key that will allow subclasses of Operation to run other
-  /// nested operations (from within `operateInternal()`).
+  /// nested operations (from within `operateInternal(Context ctx)`).
   ///
   /// The options passed to this method control how the nested operation
   /// is validated before running and whether to invoke observers on
@@ -313,6 +333,7 @@ protected:
 
 public:
   /// Run an operation given a key returned by Operation::childKey().
+  Result operate(Context ctx, const BaseKey& key);
   Result operate(const BaseKey& key);
 
 private:
diff --git a/smtk/operation/XMLOperation.h b/smtk/operation/XMLOperation.h
index d84e18957f..0c3dc87d51 100644
--- a/smtk/operation/XMLOperation.h
+++ b/smtk/operation/XMLOperation.h
@@ -32,7 +32,7 @@ protected:
   XMLOperation();
 
   // Perform the actual operation and construct the result.
-  Result operateInternal() override = 0;
+  Result operateInternal(Context ctx) override = 0;
 
   // Construct the operation's specification from the class's XML description.
   Specification createSpecification() override;
diff --git a/smtk/operation/operators/AssignColors.cxx b/smtk/operation/operators/AssignColors.cxx
index cd06582a7a..f65ce34515 100644
--- a/smtk/operation/operators/AssignColors.cxx
+++ b/smtk/operation/operators/AssignColors.cxx
@@ -38,7 +38,7 @@ namespace smtk
 namespace operation
 {
 
-AssignColors::Result AssignColors::operateInternal()
+AssignColors::Result AssignColors::operateInternal(Context ctx)
 {
   auto associations = this->parameters()->associations();
   auto entities = associations->as<std::set<PersistentObjectPtr>>();
diff --git a/smtk/operation/operators/AssignColors.h b/smtk/operation/operators/AssignColors.h
index ebe88e0b83..dcf5a79bdc 100644
--- a/smtk/operation/operators/AssignColors.h
+++ b/smtk/operation/operators/AssignColors.h
@@ -26,7 +26,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/operation/operators/CoordinateTransform.cxx b/smtk/operation/operators/CoordinateTransform.cxx
index d0aae08ee4..c3eca4d9d8 100644
--- a/smtk/operation/operators/CoordinateTransform.cxx
+++ b/smtk/operation/operators/CoordinateTransform.cxx
@@ -93,7 +93,7 @@ bool CoordinateTransform::removeTransform(
   return didRemove;
 }
 
-CoordinateTransform::Result CoordinateTransform::operateInternal()
+CoordinateTransform::Result CoordinateTransform::operateInternal(Context ctx)
 {
   smtk::attribute::ItemPtr removeItem = this->parameters()->find("remove");
   bool removeProperty = removeItem->isEnabled();
diff --git a/smtk/operation/operators/CoordinateTransform.h b/smtk/operation/operators/CoordinateTransform.h
index 1ffb64e5fa..e0c107549b 100644
--- a/smtk/operation/operators/CoordinateTransform.h
+++ b/smtk/operation/operators/CoordinateTransform.h
@@ -64,7 +64,7 @@ protected:
     const std::shared_ptr<smtk::attribute::ReferenceItem>& associations,
     Result& result);
 
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/operation/operators/CopyResources.cxx b/smtk/operation/operators/CopyResources.cxx
index 588808a828..b771714f80 100644
--- a/smtk/operation/operators/CopyResources.cxx
+++ b/smtk/operation/operators/CopyResources.cxx
@@ -70,7 +70,7 @@ bool CopyResources::ableToOperate()
   return true;
 }
 
-smtk::operation::Operation::Result CopyResources::operateInternal()
+smtk::operation::Operation::Result CopyResources::operateInternal(Context ctx)
 {
   smtk::resource::CopyOptions options(this->log());
   auto sources = this->parameters()->associations();
diff --git a/smtk/operation/operators/CopyResources.h b/smtk/operation/operators/CopyResources.h
index 79df37c70a..a0b0914071 100644
--- a/smtk/operation/operators/CopyResources.h
+++ b/smtk/operation/operators/CopyResources.h
@@ -30,7 +30,7 @@ public:
 protected:
   CopyResources();
 
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
   const char* xmlDescription() const override;
 };
diff --git a/smtk/operation/operators/EditProperties.cxx b/smtk/operation/operators/EditProperties.cxx
index cd06e6095b..23cbcb3695 100644
--- a/smtk/operation/operators/EditProperties.cxx
+++ b/smtk/operation/operators/EditProperties.cxx
@@ -154,7 +154,7 @@ void EditPropertiesValue(
   }
 }
 
-EditProperties::Result EditProperties::operateInternal()
+EditProperties::Result EditProperties::operateInternal(Context ctx)
 {
   smtk::attribute::StringItemPtr nameItem = this->parameters()->findString("name");
   smtk::attribute::IntItemPtr typeItem = this->parameters()->findInt("type");
diff --git a/smtk/operation/operators/EditProperties.h b/smtk/operation/operators/EditProperties.h
index 66e2c938d2..04f9e1904f 100644
--- a/smtk/operation/operators/EditProperties.h
+++ b/smtk/operation/operators/EditProperties.h
@@ -34,7 +34,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/operation/operators/ImportPythonOperation.cxx b/smtk/operation/operators/ImportPythonOperation.cxx
index 2d5b6a8602..66809af50b 100644
--- a/smtk/operation/operators/ImportPythonOperation.cxx
+++ b/smtk/operation/operators/ImportPythonOperation.cxx
@@ -119,7 +119,7 @@ bool ImportPythonOperation::importOperation(
   return manager.registerOperation(Metadata(typeName, index, specification, create));
 }
 
-ImportPythonOperation::Result ImportPythonOperation::operateInternal()
+ImportPythonOperation::Result ImportPythonOperation::operateInternal(Context ctx)
 {
   // Access the python operation's file name
   smtk::attribute::FileItemPtr fileItem = this->parameters()->findFile("filename");
diff --git a/smtk/operation/operators/ImportPythonOperation.h b/smtk/operation/operators/ImportPythonOperation.h
index f530563ea3..694ac3d73c 100644
--- a/smtk/operation/operators/ImportPythonOperation.h
+++ b/smtk/operation/operators/ImportPythonOperation.h
@@ -42,7 +42,7 @@ public:
     const std::string& opName);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   void generateSummary(Result&) override;
   Specification createSpecification() override;
 };
diff --git a/smtk/operation/operators/ImportResource.cxx b/smtk/operation/operators/ImportResource.cxx
index cffe92d0ef..5c3288d488 100644
--- a/smtk/operation/operators/ImportResource.cxx
+++ b/smtk/operation/operators/ImportResource.cxx
@@ -67,7 +67,7 @@ bool ImportResource::ableToOperate()
   return true;
 }
 
-ImportResource::Result ImportResource::operateInternal()
+ImportResource::Result ImportResource::operateInternal(Context ctx)
 {
   auto manager = m_manager.lock();
 
diff --git a/smtk/operation/operators/ImportResource.h b/smtk/operation/operators/ImportResource.h
index 2fe5c6ba24..c88a087d69 100644
--- a/smtk/operation/operators/ImportResource.h
+++ b/smtk/operation/operators/ImportResource.h
@@ -32,7 +32,7 @@ public:
 protected:
   ImportResource();
 
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
   Specification createSpecification() override;
   void generateSummary(Result&) override;
diff --git a/smtk/operation/operators/MarkModified.cxx b/smtk/operation/operators/MarkModified.cxx
index 3795e9813d..48277aac87 100644
--- a/smtk/operation/operators/MarkModified.cxx
+++ b/smtk/operation/operators/MarkModified.cxx
@@ -26,7 +26,7 @@ namespace smtk
 namespace operation
 {
 
-MarkModified::Result MarkModified::operateInternal()
+MarkModified::Result MarkModified::operateInternal(Context ctx)
 {
   auto params = this->parameters();
   auto result = this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED);
diff --git a/smtk/operation/operators/MarkModified.h b/smtk/operation/operators/MarkModified.h
index 0d6e7e11da..3e656419c1 100644
--- a/smtk/operation/operators/MarkModified.h
+++ b/smtk/operation/operators/MarkModified.h
@@ -31,7 +31,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   void generateSummary(Operation::Result&) override;
   const char* xmlDescription() const override;
 };
diff --git a/smtk/operation/operators/ReadResource.cxx b/smtk/operation/operators/ReadResource.cxx
index 62cf820a70..0fd6a7e060 100644
--- a/smtk/operation/operators/ReadResource.cxx
+++ b/smtk/operation/operators/ReadResource.cxx
@@ -56,7 +56,7 @@ bool ReadResource::ableToOperate()
   return true;
 }
 
-ReadResource::Result ReadResource::operateInternal()
+ReadResource::Result ReadResource::operateInternal(Context ctx)
 {
   auto manager = m_manager.lock();
 
diff --git a/smtk/operation/operators/ReadResource.h b/smtk/operation/operators/ReadResource.h
index cd410a5086..a62352d953 100644
--- a/smtk/operation/operators/ReadResource.h
+++ b/smtk/operation/operators/ReadResource.h
@@ -30,7 +30,7 @@ public:
 protected:
   ReadResource();
 
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
   const char* xmlDescription() const override;
   void markModifiedResources(Result&) override;
diff --git a/smtk/operation/operators/RemoveResource.cxx b/smtk/operation/operators/RemoveResource.cxx
index eff87cd57a..9c7e8bb710 100644
--- a/smtk/operation/operators/RemoveResource.cxx
+++ b/smtk/operation/operators/RemoveResource.cxx
@@ -46,7 +46,7 @@ bool RemoveResource::ableToOperate()
   return true;
 }
 
-RemoveResource::Result RemoveResource::operateInternal()
+RemoveResource::Result RemoveResource::operateInternal(Context ctx)
 {
   // Access the resource manager (provided by the operation manager that created
   // this operation
diff --git a/smtk/operation/operators/RemoveResource.h b/smtk/operation/operators/RemoveResource.h
index 0d068664a5..dcb43a8b45 100644
--- a/smtk/operation/operators/RemoveResource.h
+++ b/smtk/operation/operators/RemoveResource.h
@@ -37,10 +37,10 @@ protected:
 
   bool ableToOperate() override;
 
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
   /// Do not report success as the base class prints specific console
-  /// messages after operateInternal() completes.
+  /// messages after operateInternal(Context ctx) completes.
   void generateSummary(Result&) override {}
 
   const char* xmlDescription() const override;
diff --git a/smtk/operation/operators/SetProperty.cxx b/smtk/operation/operators/SetProperty.cxx
index 10fb15c059..ded7a246e4 100644
--- a/smtk/operation/operators/SetProperty.cxx
+++ b/smtk/operation/operators/SetProperty.cxx
@@ -64,7 +64,7 @@ void SetPropertyValue(
   }
 }
 
-SetProperty::Result SetProperty::operateInternal()
+SetProperty::Result SetProperty::operateInternal(Context ctx)
 {
   smtk::attribute::StringItemPtr nameItem = this->parameters()->findString("name");
   smtk::attribute::StringItemPtr stringItem = this->parameters()->findString("string value");
diff --git a/smtk/operation/operators/SetProperty.h b/smtk/operation/operators/SetProperty.h
index ffe3ab0dad..f4f929fa53 100644
--- a/smtk/operation/operators/SetProperty.h
+++ b/smtk/operation/operators/SetProperty.h
@@ -28,7 +28,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace operation
diff --git a/smtk/operation/operators/WriteResource.cxx b/smtk/operation/operators/WriteResource.cxx
index 2d38d77190..d1adc51c79 100644
--- a/smtk/operation/operators/WriteResource.cxx
+++ b/smtk/operation/operators/WriteResource.cxx
@@ -68,7 +68,7 @@ bool WriteResource::ableToOperate()
   return true;
 }
 
-smtk::operation::Operation::Result WriteResource::operateInternal()
+smtk::operation::Operation::Result WriteResource::operateInternal(Context ctx)
 {
   auto manager = m_manager.lock();
 
diff --git a/smtk/operation/operators/WriteResource.h b/smtk/operation/operators/WriteResource.h
index e4bacfff34..dcba668c8f 100644
--- a/smtk/operation/operators/WriteResource.h
+++ b/smtk/operation/operators/WriteResource.h
@@ -30,7 +30,7 @@ public:
 protected:
   WriteResource();
 
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
   const char* xmlDescription() const override;
   void markModifiedResources(Result&) override;
diff --git a/smtk/operation/pybind11/PyOperation.h b/smtk/operation/pybind11/PyOperation.h
index 87d9c038f7..33ed2e9c7a 100644
--- a/smtk/operation/pybind11/PyOperation.h
+++ b/smtk/operation/pybind11/PyOperation.h
@@ -166,7 +166,7 @@ public:
     PYBIND11_OVERLOAD(smtk::io::Logger&, Operation, log, );
   }
 
-  Result operateInternal() override
+  Result operateInternal(Context ctx) override
     {
       Result result = nullptr;
       PyOperation::runOnMainThread([&]()
diff --git a/smtk/operation/pybind11/PybindObserver.h b/smtk/operation/pybind11/PybindObserver.h
index f58768114a..615c5a4c06 100644
--- a/smtk/operation/pybind11/PybindObserver.h
+++ b/smtk/operation/pybind11/PybindObserver.h
@@ -32,7 +32,7 @@ inline py::class_< smtk::operation::Observers > pybind11_init_smtk_operation_Obs
   py::class_< smtk::operation::Observers > instance(m, "Observers");
   instance
     .def(py::init<>())
-    .def("__call__", [](smtk::operation::Observers& observers, smtk::operation::Operation& op, ::smtk::operation::EventType eventType, ::smtk::operation::Operation::Result result) { return observers(op, eventType, result); })
+    .def("__call__", [](smtk::operation::Observers& observers, smtk::operation::Operation& op, ::smtk::operation::EventType eventType, ::smtk::operation::Operation::Result result) { return observers(smtk::common::Context(), op, eventType, result); })
     .def("__len__", &smtk::operation::Observers::size)
     .def("erase", (std::size_t (smtk::operation::Observers::*)(smtk::operation::Observers::Key&)) &smtk::operation::Observers::erase)
     .def("insert", (smtk::operation::Observers::Key (smtk::operation::Observers::*)(smtk::operation::Observer, std::string)) &smtk::operation::Observers::insert, pybind11::keep_alive<1, 2>())
diff --git a/smtk/operation/testing/cxx/TestAsyncOperation.cxx b/smtk/operation/testing/cxx/TestAsyncOperation.cxx
index 7dec9262f4..a73bbbe20a 100644
--- a/smtk/operation/testing/cxx/TestAsyncOperation.cxx
+++ b/smtk/operation/testing/cxx/TestAsyncOperation.cxx
@@ -39,12 +39,12 @@ public:
   MyOperation() = default;
   ~MyOperation() override = default;
 
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
   const char* xmlDescription() const override;
 };
 
-MyOperation::Result MyOperation::operateInternal()
+MyOperation::Result MyOperation::operateInternal(Context ctx)
 {
   int sleep = this->parameters()->findAs<smtk::attribute::IntItem>("sleep")->value();
   std::this_thread::sleep_for(std::chrono::seconds(sleep));
diff --git a/smtk/operation/testing/cxx/TestAvailableOperations.cxx b/smtk/operation/testing/cxx/TestAvailableOperations.cxx
index 0f22c63fe3..dc5f4865cd 100644
--- a/smtk/operation/testing/cxx/TestAvailableOperations.cxx
+++ b/smtk/operation/testing/cxx/TestAvailableOperations.cxx
@@ -122,7 +122,7 @@ public:
   OperationA() = default;
   ~OperationA() override = default;
 
-  Result operateInternal() override { return this->createResult(Outcome::SUCCEEDED); }
+  Result operateInternal(Context ctx) override { return this->createResult(Outcome::SUCCEEDED); }
 
   const char* xmlDescription() const override;
 };
@@ -175,7 +175,7 @@ public:
   OperationB() = default;
   ~OperationB() override = default;
 
-  Result operateInternal() override { return this->createResult(Outcome::SUCCEEDED); }
+  Result operateInternal(Context ctx) override { return this->createResult(Outcome::SUCCEEDED); }
 
   const char* xmlDescription() const override;
 };
diff --git a/smtk/operation/testing/cxx/TestHints.cxx b/smtk/operation/testing/cxx/TestHints.cxx
index 38ac649433..6ceff86ba6 100644
--- a/smtk/operation/testing/cxx/TestHints.cxx
+++ b/smtk/operation/testing/cxx/TestHints.cxx
@@ -48,14 +48,14 @@ public:
   MyOperation() = default;
   ~MyOperation() override = default;
 
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
   void generateSummary(Result&) override {}
 
   const char* xmlDescription() const override;
 };
 
-MyOperation::Result MyOperation::operateInternal()
+MyOperation::Result MyOperation::operateInternal(Context ctx)
 {
   using namespace smtk::operation;
   // Do our "work"
diff --git a/smtk/operation/testing/cxx/TestMutexedOperation.cxx b/smtk/operation/testing/cxx/TestMutexedOperation.cxx
index 9ac5660797..3e9410459f 100644
--- a/smtk/operation/testing/cxx/TestMutexedOperation.cxx
+++ b/smtk/operation/testing/cxx/TestMutexedOperation.cxx
@@ -99,7 +99,7 @@ public:
 
   smtk::io::Logger& log() const override { return m_logger; }
 
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
   Specification createSpecification() override;
 
@@ -107,7 +107,7 @@ private:
   mutable smtk::io::Logger m_logger;
 };
 
-ReadOperation::Result ReadOperation::operateInternal()
+ReadOperation::Result ReadOperation::operateInternal(Context ctx)
 {
   bool semaphoreValue =
     (this->parameters()->findAs<smtk::attribute::IntItem>("semaphore")->value() != 0);
@@ -199,7 +199,7 @@ public:
 
   smtk::io::Logger& log() const override { return m_logger; }
 
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
   Specification createSpecification() override;
 
@@ -207,7 +207,7 @@ private:
   mutable smtk::io::Logger m_logger;
 };
 
-WriteOperation::Result WriteOperation::operateInternal()
+WriteOperation::Result WriteOperation::operateInternal(Context ctx)
 {
   if (semaphore)
   {
diff --git a/smtk/operation/testing/cxx/TestOperationGroup.cxx b/smtk/operation/testing/cxx/TestOperationGroup.cxx
index 9761041c33..48b20fc096 100644
--- a/smtk/operation/testing/cxx/TestOperationGroup.cxx
+++ b/smtk/operation/testing/cxx/TestOperationGroup.cxx
@@ -41,12 +41,12 @@ public:
   OperationA() = default;
   ~OperationA() override = default;
 
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
   Specification createSpecification() override;
 };
 
-OperationA::Result OperationA::operateInternal()
+OperationA::Result OperationA::operateInternal(Context ctx)
 {
   return this->createResult(Outcome::SUCCEEDED);
 }
diff --git a/smtk/operation/testing/cxx/TestOperationLauncher.cxx b/smtk/operation/testing/cxx/TestOperationLauncher.cxx
index 25a9ef7349..b70f6520b4 100644
--- a/smtk/operation/testing/cxx/TestOperationLauncher.cxx
+++ b/smtk/operation/testing/cxx/TestOperationLauncher.cxx
@@ -41,12 +41,12 @@ public:
   MyOperation() = default;
   ~MyOperation() override = default;
 
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
   const char* xmlDescription() const override;
 };
 
-MyOperation::Result MyOperation::operateInternal()
+MyOperation::Result MyOperation::operateInternal(Context ctx)
 {
   int sleep = this->parameters()->findAs<smtk::attribute::IntItem>("sleep")->value();
   std::this_thread::sleep_for(std::chrono::seconds(sleep));
diff --git a/smtk/operation/testing/cxx/TestSafeBlockingInvocation.cxx b/smtk/operation/testing/cxx/TestSafeBlockingInvocation.cxx
index 836cb305ca..353715fb4c 100644
--- a/smtk/operation/testing/cxx/TestSafeBlockingInvocation.cxx
+++ b/smtk/operation/testing/cxx/TestSafeBlockingInvocation.cxx
@@ -40,12 +40,12 @@ public:
   MyOperation() = default;
   ~MyOperation() override = default;
 
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
   const char* xmlDescription() const override;
 };
 
-MyOperation::Result MyOperation::operateInternal()
+MyOperation::Result MyOperation::operateInternal(Context ctx)
 {
   int sleep = this->parameters()->findAs<smtk::attribute::IntItem>("sleep")->value();
   std::this_thread::sleep_for(std::chrono::seconds(sleep));
diff --git a/smtk/operation/testing/cxx/TestThreadSafeLazyEvaluation.cxx b/smtk/operation/testing/cxx/TestThreadSafeLazyEvaluation.cxx
index 8fdebcd1f9..9c47fef559 100644
--- a/smtk/operation/testing/cxx/TestThreadSafeLazyEvaluation.cxx
+++ b/smtk/operation/testing/cxx/TestThreadSafeLazyEvaluation.cxx
@@ -40,12 +40,12 @@ public:
   MyOperation() = default;
   ~MyOperation() override = default;
 
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
   const char* xmlDescription() const override;
 };
 
-MyOperation::Result MyOperation::operateInternal()
+MyOperation::Result MyOperation::operateInternal(Context ctx)
 {
   int sleep = this->parameters()->findAs<smtk::attribute::IntItem>("sleep")->value();
   std::this_thread::sleep_for(std::chrono::seconds(sleep));
diff --git a/smtk/operation/testing/cxx/unitNamingGroup.cxx b/smtk/operation/testing/cxx/unitNamingGroup.cxx
index 0d4513b8a7..3d7cda9ac7 100644
--- a/smtk/operation/testing/cxx/unitNamingGroup.cxx
+++ b/smtk/operation/testing/cxx/unitNamingGroup.cxx
@@ -107,7 +107,7 @@ public:
   SetNameOp() = default;
   ~SetNameOp() override = default;
 
-  Result operateInternal() override { return this->createResult(Outcome::SUCCEEDED); }
+  Result operateInternal(Context ctx) override { return this->createResult(Outcome::SUCCEEDED); }
 
   const char* xmlDescription() const override { return setNameOp_xml.c_str(); }
 };
@@ -153,7 +153,7 @@ public:
   NonCompliantOp() = default;
   ~NonCompliantOp() override = default;
 
-  Result operateInternal() override { return this->createResult(Outcome::SUCCEEDED); }
+  Result operateInternal(Context ctx) override { return this->createResult(Outcome::SUCCEEDED); }
 
   const char* xmlDescription() const override { return nonCompliantOp_xml.c_str(); }
 };
diff --git a/smtk/operation/testing/cxx/unitOperation.cxx b/smtk/operation/testing/cxx/unitOperation.cxx
index bcbb8d162f..170d1b43e1 100644
--- a/smtk/operation/testing/cxx/unitOperation.cxx
+++ b/smtk/operation/testing/cxx/unitOperation.cxx
@@ -42,7 +42,7 @@ public:
 
   bool ableToOperate() override { return m_outcome != Outcome::UNABLE_TO_OPERATE; }
 
-  Result operateInternal() override { return this->createResult(m_outcome); }
+  Result operateInternal(Context ctx) override { return this->createResult(m_outcome); }
 
   const char* xmlDescription() const override;
 
diff --git a/smtk/project/Manager.cxx b/smtk/project/Manager.cxx
index 10b3317c97..768f819882 100644
--- a/smtk/project/Manager.cxx
+++ b/smtk/project/Manager.cxx
@@ -101,6 +101,7 @@ Manager::Manager(
 
   m_operationManagerObserver = operationManager->observers().insert(
     [this](
+    smtk::common::Context ctx,
       const smtk::operation::Operation&,
       smtk::operation::EventType event,
       smtk::operation::Operation::Result result) -> int {
diff --git a/smtk/project/operators/Add.cxx b/smtk/project/operators/Add.cxx
index 935ab0286c..d829a43970 100644
--- a/smtk/project/operators/Add.cxx
+++ b/smtk/project/operators/Add.cxx
@@ -69,7 +69,7 @@ bool Add::configure(
   return true;
 }
 
-Add::Result Add::operateInternal()
+Add::Result Add::operateInternal(Context ctx)
 {
   smtk::attribute::ReferenceItem::Ptr projectItem = this->parameters()->associations();
   smtk::project::ProjectPtr project = projectItem->valueAs<smtk::project::Project>();
diff --git a/smtk/project/operators/Add.h b/smtk/project/operators/Add.h
index a9c5822795..f5a3f48a4b 100644
--- a/smtk/project/operators/Add.h
+++ b/smtk/project/operators/Add.h
@@ -34,7 +34,7 @@ public:
   bool configure(const smtk::attribute::AttributePtr&, const smtk::attribute::ItemPtr&) override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace project
diff --git a/smtk/project/operators/Create.cxx b/smtk/project/operators/Create.cxx
index ad8422adc8..b54caef7ee 100644
--- a/smtk/project/operators/Create.cxx
+++ b/smtk/project/operators/Create.cxx
@@ -74,7 +74,7 @@ namespace smtk
 namespace project
 {
 
-Create::Result Create::operateInternal()
+Create::Result Create::operateInternal(Context ctx)
 {
   std::string typeName;
   {
diff --git a/smtk/project/operators/Create.h b/smtk/project/operators/Create.h
index b1101c86c9..9214a351f0 100644
--- a/smtk/project/operators/Create.h
+++ b/smtk/project/operators/Create.h
@@ -31,7 +31,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   Specification createSpecification() override;
   const char* xmlDescription() const override;
 };
diff --git a/smtk/project/operators/Define.cxx b/smtk/project/operators/Define.cxx
index aa12b79497..6ce4dc6c83 100644
--- a/smtk/project/operators/Define.cxx
+++ b/smtk/project/operators/Define.cxx
@@ -87,7 +87,7 @@ namespace smtk
 namespace project
 {
 
-Define::Result Define::operateInternal()
+Define::Result Define::operateInternal(Context ctx)
 {
   std::string name;
   {
diff --git a/smtk/project/operators/Define.h b/smtk/project/operators/Define.h
index b679ba5de8..0c4180f100 100644
--- a/smtk/project/operators/Define.h
+++ b/smtk/project/operators/Define.h
@@ -32,7 +32,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   Specification createSpecification() override;
   const char* xmlDescription() const override;
 };
diff --git a/smtk/project/operators/Print.cxx b/smtk/project/operators/Print.cxx
index 3c69788fc8..ed63fd9c75 100644
--- a/smtk/project/operators/Print.cxx
+++ b/smtk/project/operators/Print.cxx
@@ -25,7 +25,7 @@ namespace smtk
 namespace project
 {
 
-Print::Result Print::operateInternal()
+Print::Result Print::operateInternal(Context ctx)
 {
   // Access the project to write.
   smtk::attribute::ReferenceItem::Ptr projectItem = this->parameters()->associations();
diff --git a/smtk/project/operators/Print.h b/smtk/project/operators/Print.h
index 2a282407cd..2ce6bfd6cc 100644
--- a/smtk/project/operators/Print.h
+++ b/smtk/project/operators/Print.h
@@ -30,7 +30,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace project
diff --git a/smtk/project/operators/Read.cxx b/smtk/project/operators/Read.cxx
index 0e66cc3136..f1ddbf89cd 100644
--- a/smtk/project/operators/Read.cxx
+++ b/smtk/project/operators/Read.cxx
@@ -71,7 +71,7 @@ void Read::markModifiedResources(Read::Result& result)
   }
 }
 
-Read::Result Read::operateInternal()
+Read::Result Read::operateInternal(Context ctx)
 {
   std::string filename = this->parameters()->findFile("filename")->value();
 
diff --git a/smtk/project/operators/Read.h b/smtk/project/operators/Read.h
index 830b9b0cf0..49bc9ab219 100644
--- a/smtk/project/operators/Read.h
+++ b/smtk/project/operators/Read.h
@@ -32,7 +32,7 @@ public:
 
 protected:
   void markModifiedResources(Result&) override;
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/project/operators/Remove.cxx b/smtk/project/operators/Remove.cxx
index 6e0cec99bd..8ad136a547 100644
--- a/smtk/project/operators/Remove.cxx
+++ b/smtk/project/operators/Remove.cxx
@@ -33,7 +33,7 @@ namespace smtk
 namespace project
 {
 
-Remove::Result Remove::operateInternal()
+Remove::Result Remove::operateInternal(Context ctx)
 {
   smtk::attribute::ReferenceItem::Ptr projectItem = this->parameters()->associations();
   smtk::project::ProjectPtr project = projectItem->valueAs<smtk::project::Project>();
diff --git a/smtk/project/operators/Remove.h b/smtk/project/operators/Remove.h
index b06edd7ed6..cb658f82d9 100644
--- a/smtk/project/operators/Remove.h
+++ b/smtk/project/operators/Remove.h
@@ -28,7 +28,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace project
diff --git a/smtk/project/operators/Write.cxx b/smtk/project/operators/Write.cxx
index 960e67a5c8..7f4eabb13d 100644
--- a/smtk/project/operators/Write.cxx
+++ b/smtk/project/operators/Write.cxx
@@ -49,7 +49,7 @@ namespace smtk
 namespace project
 {
 
-Write::Result Write::operateInternal()
+Write::Result Write::operateInternal(Context ctx)
 {
   // Access the project to write.
   smtk::attribute::ReferenceItem::Ptr projectItem = this->parameters()->associations();
diff --git a/smtk/project/operators/Write.h b/smtk/project/operators/Write.h
index 5a5c85f51f..0f36880d24 100644
--- a/smtk/project/operators/Write.h
+++ b/smtk/project/operators/Write.h
@@ -33,7 +33,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/project/testing/cxx/TestProjectLifeCycle.cxx b/smtk/project/testing/cxx/TestProjectLifeCycle.cxx
index 9376096fad..7f6b5b1282 100644
--- a/smtk/project/testing/cxx/TestProjectLifeCycle.cxx
+++ b/smtk/project/testing/cxx/TestProjectLifeCycle.cxx
@@ -60,7 +60,7 @@ public:
     return smtk::operation::XMLOperation::ableToOperate();
   }
 
-  Result operateInternal() override
+  Result operateInternal(Context ctx) override
   {
     auto project = m_projManager->create(PROJECT_TYPE);
     if (this->managers())
diff --git a/smtk/resource/GarbageCollector.cxx b/smtk/resource/GarbageCollector.cxx
index 3f40042e48..ebb4ab81aa 100644
--- a/smtk/resource/GarbageCollector.cxx
+++ b/smtk/resource/GarbageCollector.cxx
@@ -71,6 +71,7 @@ bool GarbageCollector::add(const smtk::operation::OperationPtr& deleter)
   {
     Key key = manager->observers().insert(
       [this](
+      smtk::common::Context ctx,
         const smtk::operation::Operation& op,
         smtk::operation::EventType event,
         smtk::operation::Operation::Result result) -> int {
diff --git a/smtk/resource/testing/cxx/TestGarbageCollector.cxx b/smtk/resource/testing/cxx/TestGarbageCollector.cxx
index 4859d24152..e38a351ef6 100644
--- a/smtk/resource/testing/cxx/TestGarbageCollector.cxx
+++ b/smtk/resource/testing/cxx/TestGarbageCollector.cxx
@@ -127,7 +127,7 @@ public:
 protected:
   DeleterA() = default;
 
-  Result operateInternal() override
+  Result operateInternal(Context ctx) override
   {
     bool ok = true;
     auto associations = this->parameters()->associations();
@@ -214,12 +214,12 @@ public:
   TestOperation() = default;
   ~TestOperation() override = default;
 
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
 
   const char* xmlDescription() const override;
 };
 
-TestOperation::Result TestOperation::operateInternal()
+TestOperation::Result TestOperation::operateInternal(Context ctx)
 {
   auto assoc = this->parameters()->associations();
   if (!assoc->isSet(0))
diff --git a/smtk/session/mesh/operators/CreateUniformGrid.cxx b/smtk/session/mesh/operators/CreateUniformGrid.cxx
index 6f7cfb3b7f..3028264c60 100644
--- a/smtk/session/mesh/operators/CreateUniformGrid.cxx
+++ b/smtk/session/mesh/operators/CreateUniformGrid.cxx
@@ -34,7 +34,7 @@ namespace session
 namespace mesh
 {
 
-CreateUniformGrid::Result CreateUniformGrid::operateInternal()
+CreateUniformGrid::Result CreateUniformGrid::operateInternal(Context ctx)
 {
   // Access the string describing the dimension. The dimension is a string so we
   // can use optional children to assign the lengths of the other parameters.
diff --git a/smtk/session/mesh/operators/CreateUniformGrid.h b/smtk/session/mesh/operators/CreateUniformGrid.h
index c3c9bd00db..c4bbac856b 100644
--- a/smtk/session/mesh/operators/CreateUniformGrid.h
+++ b/smtk/session/mesh/operators/CreateUniformGrid.h
@@ -31,7 +31,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace mesh
diff --git a/smtk/session/mesh/operators/EulerCharacteristicRatio.cxx b/smtk/session/mesh/operators/EulerCharacteristicRatio.cxx
index 0683c6e9b8..0b387fb2f7 100644
--- a/smtk/session/mesh/operators/EulerCharacteristicRatio.cxx
+++ b/smtk/session/mesh/operators/EulerCharacteristicRatio.cxx
@@ -34,7 +34,7 @@ namespace session
 namespace mesh
 {
 
-EulerCharacteristicRatio::Result EulerCharacteristicRatio::operateInternal()
+EulerCharacteristicRatio::Result EulerCharacteristicRatio::operateInternal(Context ctx)
 {
   // Access the associated model.
   smtk::model::Model model = this->parameters()->associatedModelEntities<smtk::model::Models>()[0];
diff --git a/smtk/session/mesh/operators/EulerCharacteristicRatio.h b/smtk/session/mesh/operators/EulerCharacteristicRatio.h
index ba2885e5d2..4641efdcf2 100644
--- a/smtk/session/mesh/operators/EulerCharacteristicRatio.h
+++ b/smtk/session/mesh/operators/EulerCharacteristicRatio.h
@@ -32,7 +32,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/session/mesh/operators/Export.cxx b/smtk/session/mesh/operators/Export.cxx
index 5ee15ec3af..ee32391b1d 100644
--- a/smtk/session/mesh/operators/Export.cxx
+++ b/smtk/session/mesh/operators/Export.cxx
@@ -47,7 +47,7 @@ void breakMaterialsByAssociation(const smtk::mesh::ResourcePtr& c)
   }
 }
 
-smtk::session::mesh::Export::Result Export::operateInternal()
+smtk::session::mesh::Export::Result Export::operateInternal(Context ctx)
 {
   smtk::attribute::FileItem::Ptr filePathItem = this->parameters()->findFile("filename");
 
diff --git a/smtk/session/mesh/operators/Export.h b/smtk/session/mesh/operators/Export.h
index 43b92a0bf2..342eb93913 100644
--- a/smtk/session/mesh/operators/Export.h
+++ b/smtk/session/mesh/operators/Export.h
@@ -29,7 +29,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/session/mesh/operators/Import.cxx b/smtk/session/mesh/operators/Import.cxx
index 9088e16079..3f48bd43fc 100644
--- a/smtk/session/mesh/operators/Import.cxx
+++ b/smtk/session/mesh/operators/Import.cxx
@@ -49,7 +49,7 @@ namespace session
 namespace mesh
 {
 
-Import::Result Import::operateInternal()
+Import::Result Import::operateInternal(Context ctx)
 {
   // Get the read file name
   smtk::attribute::FileItem::Ptr filePathItem = this->parameters()->findFile("filename");
diff --git a/smtk/session/mesh/operators/Import.h b/smtk/session/mesh/operators/Import.h
index e3fc261a0d..5d48d0764c 100644
--- a/smtk/session/mesh/operators/Import.h
+++ b/smtk/session/mesh/operators/Import.h
@@ -32,7 +32,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   Specification createSpecification() override;
   const char* xmlDescription() const override;
 
diff --git a/smtk/session/mesh/operators/Merge.cxx b/smtk/session/mesh/operators/Merge.cxx
index f22e89639d..fafeecb017 100644
--- a/smtk/session/mesh/operators/Merge.cxx
+++ b/smtk/session/mesh/operators/Merge.cxx
@@ -91,7 +91,7 @@ bool Merge::ableToOperate()
   return true;
 }
 
-Merge::Result Merge::operateInternal()
+Merge::Result Merge::operateInternal(Context ctx)
 {
   // Access the associated model entities
   auto associations = this->parameters()->associations();
diff --git a/smtk/session/mesh/operators/Merge.h b/smtk/session/mesh/operators/Merge.h
index 9db0238eca..ac9e9dd997 100644
--- a/smtk/session/mesh/operators/Merge.h
+++ b/smtk/session/mesh/operators/Merge.h
@@ -34,7 +34,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace mesh
diff --git a/smtk/session/mesh/operators/Print.cxx b/smtk/session/mesh/operators/Print.cxx
index 4e844b59cf..f58503678c 100644
--- a/smtk/session/mesh/operators/Print.cxx
+++ b/smtk/session/mesh/operators/Print.cxx
@@ -37,7 +37,7 @@ namespace session
 namespace mesh
 {
 
-Print::Result Print::operateInternal()
+Print::Result Print::operateInternal(Context ctx)
 {
   // Access the associated model entities
   auto associations = this->parameters()->associations();
diff --git a/smtk/session/mesh/operators/Print.h b/smtk/session/mesh/operators/Print.h
index 327107f634..133c6157cb 100644
--- a/smtk/session/mesh/operators/Print.h
+++ b/smtk/session/mesh/operators/Print.h
@@ -31,7 +31,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace mesh
diff --git a/smtk/session/mesh/operators/Read.cxx b/smtk/session/mesh/operators/Read.cxx
index bd41d7d9eb..dbe2f77c5e 100644
--- a/smtk/session/mesh/operators/Read.cxx
+++ b/smtk/session/mesh/operators/Read.cxx
@@ -40,7 +40,7 @@ namespace session
 namespace mesh
 {
 
-Read::Result Read::operateInternal()
+Read::Result Read::operateInternal(Context ctx)
 {
   std::string filename = this->parameters()->findFile("filename")->value();
 
diff --git a/smtk/session/mesh/operators/Read.h b/smtk/session/mesh/operators/Read.h
index 13ae634391..2931b6be4b 100644
--- a/smtk/session/mesh/operators/Read.h
+++ b/smtk/session/mesh/operators/Read.h
@@ -40,7 +40,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
   void markModifiedResources(Result&) override;
 };
diff --git a/smtk/session/mesh/operators/Transform.cxx b/smtk/session/mesh/operators/Transform.cxx
index 1efca253a2..1998994214 100644
--- a/smtk/session/mesh/operators/Transform.cxx
+++ b/smtk/session/mesh/operators/Transform.cxx
@@ -37,7 +37,7 @@ namespace session
 namespace mesh
 {
 
-Transform::Result Transform::operateInternal()
+Transform::Result Transform::operateInternal(Context ctx)
 {
   // Access the associated model.
   smtk::model::Model model = this->parameters()->associatedModelEntities<smtk::model::Models>()[0];
diff --git a/smtk/session/mesh/operators/Transform.h b/smtk/session/mesh/operators/Transform.h
index bfc2684bac..879ef3bcdc 100644
--- a/smtk/session/mesh/operators/Transform.h
+++ b/smtk/session/mesh/operators/Transform.h
@@ -31,7 +31,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace mesh
diff --git a/smtk/session/mesh/operators/Write.cxx b/smtk/session/mesh/operators/Write.cxx
index a13d2d3074..7b731d9c11 100644
--- a/smtk/session/mesh/operators/Write.cxx
+++ b/smtk/session/mesh/operators/Write.cxx
@@ -61,7 +61,7 @@ bool Write::ableToOperate()
   return true;
 }
 
-Write::Result Write::operateInternal()
+Write::Result Write::operateInternal(Context ctx)
 {
   auto resourceItem = this->parameters()->associations();
 
diff --git a/smtk/session/mesh/operators/Write.h b/smtk/session/mesh/operators/Write.h
index cc255ccb52..88e5e782b3 100644
--- a/smtk/session/mesh/operators/Write.h
+++ b/smtk/session/mesh/operators/Write.h
@@ -34,7 +34,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
   void markModifiedResources(Result&) override;
 };
diff --git a/smtk/session/oscillator/operators/CreateModel.cxx b/smtk/session/oscillator/operators/CreateModel.cxx
index 627889c7cc..b87e9c342c 100644
--- a/smtk/session/oscillator/operators/CreateModel.cxx
+++ b/smtk/session/oscillator/operators/CreateModel.cxx
@@ -34,7 +34,7 @@ namespace session
 namespace oscillator
 {
 
-CreateModel::Result CreateModel::operateInternal()
+CreateModel::Result CreateModel::operateInternal(Context ctx)
 {
   Result result;
 
diff --git a/smtk/session/oscillator/operators/CreateModel.h b/smtk/session/oscillator/operators/CreateModel.h
index 71f4b0949a..25011deb29 100644
--- a/smtk/session/oscillator/operators/CreateModel.h
+++ b/smtk/session/oscillator/operators/CreateModel.h
@@ -43,7 +43,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/session/oscillator/operators/EditDomain.cxx b/smtk/session/oscillator/operators/EditDomain.cxx
index ec01fa6994..fdee2d4ae5 100644
--- a/smtk/session/oscillator/operators/EditDomain.cxx
+++ b/smtk/session/oscillator/operators/EditDomain.cxx
@@ -41,7 +41,7 @@ namespace session
 namespace oscillator
 {
 
-EditDomain::Result EditDomain::operateInternal()
+EditDomain::Result EditDomain::operateInternal(Context ctx)
 {
   // Access the string describing the dimension. The dimension is a string so we
   // can use optional children to assign the lengths of the other parameters.
diff --git a/smtk/session/oscillator/operators/EditDomain.h b/smtk/session/oscillator/operators/EditDomain.h
index 1d892c23df..4cc77cae9c 100644
--- a/smtk/session/oscillator/operators/EditDomain.h
+++ b/smtk/session/oscillator/operators/EditDomain.h
@@ -32,7 +32,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 } // namespace oscillator
diff --git a/smtk/session/oscillator/operators/EditSource.cxx b/smtk/session/oscillator/operators/EditSource.cxx
index 5b4f69e29c..f8a09cc2f8 100644
--- a/smtk/session/oscillator/operators/EditSource.cxx
+++ b/smtk/session/oscillator/operators/EditSource.cxx
@@ -103,7 +103,7 @@ bool EditSource::configure(
   return true;
 }
 
-EditSource::Result EditSource::operateInternal()
+EditSource::Result EditSource::operateInternal(Context ctx)
 {
   // Access the origin, size and discretization parameters
   smtk::attribute::DoubleItemPtr centerItem =
diff --git a/smtk/session/oscillator/operators/EditSource.h b/smtk/session/oscillator/operators/EditSource.h
index e931b4c17e..b753c6b4ae 100644
--- a/smtk/session/oscillator/operators/EditSource.h
+++ b/smtk/session/oscillator/operators/EditSource.h
@@ -42,7 +42,7 @@ public:
     const smtk::attribute::ItemPtr& changedItem) override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 
   void assignName(smtk::model::Model& model, smtk::model::AuxiliaryGeometry& source);
diff --git a/smtk/session/oscillator/operators/Export.cxx b/smtk/session/oscillator/operators/Export.cxx
index 92b1a405d1..4eeeec5c97 100644
--- a/smtk/session/oscillator/operators/Export.cxx
+++ b/smtk/session/oscillator/operators/Export.cxx
@@ -46,7 +46,7 @@ namespace session
 namespace oscillator
 {
 
-Export::Result Export::operateInternal()
+Export::Result Export::operateInternal(Context ctx)
 {
   smtk::attribute::FileItem::Ptr filenameItem = this->parameters()->findFile("filename");
   std::string filename = filenameItem->value();
diff --git a/smtk/session/oscillator/operators/Export.h b/smtk/session/oscillator/operators/Export.h
index b460f2f206..448c86bdf8 100644
--- a/smtk/session/oscillator/operators/Export.h
+++ b/smtk/session/oscillator/operators/Export.h
@@ -32,7 +32,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/session/oscillator/operators/Read.cxx b/smtk/session/oscillator/operators/Read.cxx
index d791112792..1e78809427 100644
--- a/smtk/session/oscillator/operators/Read.cxx
+++ b/smtk/session/oscillator/operators/Read.cxx
@@ -40,7 +40,7 @@ namespace session
 namespace oscillator
 {
 
-Read::Result Read::operateInternal()
+Read::Result Read::operateInternal(Context ctx)
 {
   std::string filename = this->parameters()->findFile("filename")->value();
 
diff --git a/smtk/session/oscillator/operators/Read.h b/smtk/session/oscillator/operators/Read.h
index 6d0dd23e98..2f4b96109b 100644
--- a/smtk/session/oscillator/operators/Read.h
+++ b/smtk/session/oscillator/operators/Read.h
@@ -32,7 +32,7 @@ public:
   smtkSuperclassMacro(smtk::operation::XMLOperation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
   void markModifiedResources(Result&) override;
 };
diff --git a/smtk/session/oscillator/operators/Write.cxx b/smtk/session/oscillator/operators/Write.cxx
index 1dc5b2e3b1..561ad92ae0 100644
--- a/smtk/session/oscillator/operators/Write.cxx
+++ b/smtk/session/oscillator/operators/Write.cxx
@@ -49,7 +49,7 @@ bool Write::ableToOperate()
   return true;
 }
 
-Write::Result Write::operateInternal()
+Write::Result Write::operateInternal(Context ctx)
 {
   auto resourceItem = this->parameters()->associations();
 
diff --git a/smtk/session/oscillator/operators/Write.h b/smtk/session/oscillator/operators/Write.h
index 5301439fab..3b92ca67ec 100644
--- a/smtk/session/oscillator/operators/Write.h
+++ b/smtk/session/oscillator/operators/Write.h
@@ -35,7 +35,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
   void markModifiedResources(Result&) override;
 };
diff --git a/smtk/session/polygon/operators/CleanGeometry.cxx b/smtk/session/polygon/operators/CleanGeometry.cxx
index 03843247e5..381827f5de 100644
--- a/smtk/session/polygon/operators/CleanGeometry.cxx
+++ b/smtk/session/polygon/operators/CleanGeometry.cxx
@@ -654,7 +654,7 @@ bool CleanGeometry::deleteIfDuplicates(T& edgePair, U& modified, U& expunged)
   *              + This must fix any vertex uses/vertices referred to by edges,
   *                delete old vertices, and mark edges/uses modified.
   */
-CleanGeometry::Result CleanGeometry::operateInternal()
+CleanGeometry::Result CleanGeometry::operateInternal(Context ctx)
 {
   auto cleanItems = this->parameters()->associations();
   auto inputs = cleanItems->as<smtk::model::CellSet>([](smtk::resource::PersistentObjectPtr obj) {
diff --git a/smtk/session/polygon/operators/CleanGeometry.h b/smtk/session/polygon/operators/CleanGeometry.h
index 1c21336a24..b28101eb22 100644
--- a/smtk/session/polygon/operators/CleanGeometry.h
+++ b/smtk/session/polygon/operators/CleanGeometry.h
@@ -32,7 +32,7 @@ public:
   smtkSuperclassMacro(Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 
   template<typename T, typename U, typename V, typename W, typename X>
diff --git a/smtk/session/polygon/operators/CreateEdge.cxx b/smtk/session/polygon/operators/CreateEdge.cxx
index 7642450046..ec146b98c0 100644
--- a/smtk/session/polygon/operators/CreateEdge.cxx
+++ b/smtk/session/polygon/operators/CreateEdge.cxx
@@ -55,7 +55,7 @@ void printSegment(internal::pmodel::Ptr storage, const std::string& msg, T& seg)
 }
 */
 
-CreateEdge::Result CreateEdge::operateInternal()
+CreateEdge::Result CreateEdge::operateInternal(Context ctx)
 {
   // Discover how the user wants to specify scaling.
   smtk::attribute::IntItem::Ptr constructionMethodItem =
diff --git a/smtk/session/polygon/operators/CreateEdge.h b/smtk/session/polygon/operators/CreateEdge.h
index ab43b39450..9d0bc65779 100644
--- a/smtk/session/polygon/operators/CreateEdge.h
+++ b/smtk/session/polygon/operators/CreateEdge.h
@@ -32,7 +32,7 @@ public:
   smtkSuperclassMacro(Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/session/polygon/operators/CreateEdgeFromPoints.cxx b/smtk/session/polygon/operators/CreateEdgeFromPoints.cxx
index 9dc57a41fd..0d0840a9d2 100644
--- a/smtk/session/polygon/operators/CreateEdgeFromPoints.cxx
+++ b/smtk/session/polygon/operators/CreateEdgeFromPoints.cxx
@@ -40,7 +40,7 @@ namespace polygon
 
 typedef std::vector<std::pair<size_t, internal::Segment>> SegmentSplitsT;
 
-CreateEdgeFromPoints::Result CreateEdgeFromPoints::operateInternal()
+CreateEdgeFromPoints::Result CreateEdgeFromPoints::operateInternal(Context ctx)
 {
   auto modelItem = this->parameters()->associations();
 
diff --git a/smtk/session/polygon/operators/CreateEdgeFromPoints.h b/smtk/session/polygon/operators/CreateEdgeFromPoints.h
index e15ff3677a..b260183c73 100644
--- a/smtk/session/polygon/operators/CreateEdgeFromPoints.h
+++ b/smtk/session/polygon/operators/CreateEdgeFromPoints.h
@@ -39,7 +39,7 @@ public:
   Result process(std::vector<double>& pnts, int numCoordsPerPoint, smtk::model::Model& parentModel);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/session/polygon/operators/CreateEdgeFromVertices.cxx b/smtk/session/polygon/operators/CreateEdgeFromVertices.cxx
index a50b0a1cf3..6aa83f7359 100644
--- a/smtk/session/polygon/operators/CreateEdgeFromVertices.cxx
+++ b/smtk/session/polygon/operators/CreateEdgeFromVertices.cxx
@@ -36,7 +36,7 @@ namespace polygon
 
 typedef std::vector<std::pair<size_t, internal::Segment>> SegmentSplitsT;
 
-CreateEdgeFromVertices::Result CreateEdgeFromVertices::operateInternal()
+CreateEdgeFromVertices::Result CreateEdgeFromVertices::operateInternal(Context ctx)
 {
   auto modelItem = this->parameters()->associations();
   auto ment = modelItem->valueAs<smtk::model::Entity>();
diff --git a/smtk/session/polygon/operators/CreateEdgeFromVertices.h b/smtk/session/polygon/operators/CreateEdgeFromVertices.h
index 31e65bdc18..f1f8c66c57 100644
--- a/smtk/session/polygon/operators/CreateEdgeFromVertices.h
+++ b/smtk/session/polygon/operators/CreateEdgeFromVertices.h
@@ -32,7 +32,7 @@ public:
   smtkSuperclassMacro(Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/session/polygon/operators/CreateFaces.cxx b/smtk/session/polygon/operators/CreateFaces.cxx
index 54de7d9b0c..d4c725ba46 100644
--- a/smtk/session/polygon/operators/CreateFaces.cxx
+++ b/smtk/session/polygon/operators/CreateFaces.cxx
@@ -138,7 +138,7 @@ bool CreateFaces::populateEdgeMap()
   return true;
 }
 
-CreateFaces::Result CreateFaces::operateInternal()
+CreateFaces::Result CreateFaces::operateInternal(Context ctx)
 {
   auto modelItem = this->parameters()->associations();
   smtk::model::Model model = modelItem->valueAs<smtk::model::Entity>();
diff --git a/smtk/session/polygon/operators/CreateFaces.h b/smtk/session/polygon/operators/CreateFaces.h
index b91aff8c5a..342e79b57e 100644
--- a/smtk/session/polygon/operators/CreateFaces.h
+++ b/smtk/session/polygon/operators/CreateFaces.h
@@ -73,7 +73,7 @@ protected:
   friend class Neighborhood;
 
   virtual bool populateEdgeMap();
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 
   void evaluateLoop(RegionId faceNumber, OrientedEdges& loop, std::set<RegionId>& borders);
diff --git a/smtk/session/polygon/operators/CreateModel.cxx b/smtk/session/polygon/operators/CreateModel.cxx
index 5fae02e4bd..ec24b8cd20 100644
--- a/smtk/session/polygon/operators/CreateModel.cxx
+++ b/smtk/session/polygon/operators/CreateModel.cxx
@@ -31,7 +31,7 @@ namespace session
 namespace polygon
 {
 
-CreateModel::Result CreateModel::operateInternal()
+CreateModel::Result CreateModel::operateInternal(Context ctx)
 {
   // Discover how the user wants to specify scaling.
   smtk::attribute::IntItem::Ptr constructionMethodItem =
diff --git a/smtk/session/polygon/operators/CreateModel.h b/smtk/session/polygon/operators/CreateModel.h
index 28273306cb..483461fc46 100644
--- a/smtk/session/polygon/operators/CreateModel.h
+++ b/smtk/session/polygon/operators/CreateModel.h
@@ -42,7 +42,7 @@ public:
   smtkSuperclassMacro(Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/session/polygon/operators/CreateVertices.cxx b/smtk/session/polygon/operators/CreateVertices.cxx
index bf86f79b8f..944019368f 100644
--- a/smtk/session/polygon/operators/CreateVertices.cxx
+++ b/smtk/session/polygon/operators/CreateVertices.cxx
@@ -34,7 +34,7 @@ namespace session
 namespace polygon
 {
 
-CreateVertices::Result CreateVertices::operateInternal()
+CreateVertices::Result CreateVertices::operateInternal(Context ctx)
 {
   smtk::attribute::GroupItem::Ptr pointsInfo;
   smtk::attribute::IntItem::Ptr coordinatesItem = this->parameters()->findInt("point dimension");
diff --git a/smtk/session/polygon/operators/CreateVertices.h b/smtk/session/polygon/operators/CreateVertices.h
index cac38896e0..e99ef77420 100644
--- a/smtk/session/polygon/operators/CreateVertices.h
+++ b/smtk/session/polygon/operators/CreateVertices.h
@@ -31,7 +31,7 @@ public:
   smtkSuperclassMacro(Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/session/polygon/operators/Delete.cxx b/smtk/session/polygon/operators/Delete.cxx
index b0bed98ad3..d5520d7643 100644
--- a/smtk/session/polygon/operators/Delete.cxx
+++ b/smtk/session/polygon/operators/Delete.cxx
@@ -311,7 +311,7 @@ void Delete::addBoundaryCells(
   }
 }
 
-Delete::Result Delete::operateInternal()
+Delete::Result Delete::operateInternal(Context ctx)
 {
   smtk::attribute::VoidItem::Ptr deleteHigherDimen =
     this->parameters()->findVoid("delete higher-dimensional neighbors");
diff --git a/smtk/session/polygon/operators/Delete.h b/smtk/session/polygon/operators/Delete.h
index 6040ee69a0..64d2d1e0e6 100644
--- a/smtk/session/polygon/operators/Delete.h
+++ b/smtk/session/polygon/operators/Delete.h
@@ -44,7 +44,7 @@ public:
   smtkSuperclassMacro(Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 
   template<typename U, typename V, typename W, typename X>
diff --git a/smtk/session/polygon/operators/DemoteVertex.cxx b/smtk/session/polygon/operators/DemoteVertex.cxx
index 38521e209b..4175cf729d 100644
--- a/smtk/session/polygon/operators/DemoteVertex.cxx
+++ b/smtk/session/polygon/operators/DemoteVertex.cxx
@@ -35,7 +35,7 @@ namespace session
 namespace polygon
 {
 
-DemoteVertex::Result DemoteVertex::operateInternal()
+DemoteVertex::Result DemoteVertex::operateInternal(Context ctx)
 {
   auto vertItem = this->parameters()->associations();
   smtk::model::Vertex vertexToDemote(vertItem->valueAs<smtk::model::Entity>());
diff --git a/smtk/session/polygon/operators/DemoteVertex.h b/smtk/session/polygon/operators/DemoteVertex.h
index 7129301f48..8ea4f1636a 100644
--- a/smtk/session/polygon/operators/DemoteVertex.h
+++ b/smtk/session/polygon/operators/DemoteVertex.h
@@ -32,7 +32,7 @@ public:
   smtkSuperclassMacro(Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/session/polygon/operators/ExtractContours.cxx b/smtk/session/polygon/operators/ExtractContours.cxx
index 0cd8d3446f..09855f8a2e 100644
--- a/smtk/session/polygon/operators/ExtractContours.cxx
+++ b/smtk/session/polygon/operators/ExtractContours.cxx
@@ -101,7 +101,7 @@ int internal_createEdge(
   return static_cast<int>(createdEds.size());
 }
 
-ExtractContours::Result ExtractContours::operateInternal()
+ExtractContours::Result ExtractContours::operateInternal(Context ctx)
 {
   // ableToOperate should have verified that aux is valid
   smtk::model::AuxiliaryGeometry aux =
diff --git a/smtk/session/polygon/operators/ExtractContours.h b/smtk/session/polygon/operators/ExtractContours.h
index bd1c99706d..db1acb4de3 100644
--- a/smtk/session/polygon/operators/ExtractContours.h
+++ b/smtk/session/polygon/operators/ExtractContours.h
@@ -39,7 +39,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/session/polygon/operators/ForceCreateFace.cxx b/smtk/session/polygon/operators/ForceCreateFace.cxx
index 1ffd474ec5..93e741fa38 100644
--- a/smtk/session/polygon/operators/ForceCreateFace.cxx
+++ b/smtk/session/polygon/operators/ForceCreateFace.cxx
@@ -78,7 +78,7 @@ bool ForceCreateFace::ableToOperate()
 }
 
 /// Create one or more polygonal faces without sanity checks.
-smtk::operation::Operation::Result ForceCreateFace::operateInternal()
+smtk::operation::Operation::Result ForceCreateFace::operateInternal(Context ctx)
 {
   int method = this->parameters()->findInt("construction method")->discreteIndex();
 
diff --git a/smtk/session/polygon/operators/ForceCreateFace.h b/smtk/session/polygon/operators/ForceCreateFace.h
index 881c9abd46..0a50689c69 100644
--- a/smtk/session/polygon/operators/ForceCreateFace.h
+++ b/smtk/session/polygon/operators/ForceCreateFace.h
@@ -46,7 +46,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/session/polygon/operators/Import.cxx b/smtk/session/polygon/operators/Import.cxx
index 029c2ee936..0670b4824e 100644
--- a/smtk/session/polygon/operators/Import.cxx
+++ b/smtk/session/polygon/operators/Import.cxx
@@ -375,7 +375,7 @@ bool Import::ableToOperate()
   return (ext == ".vtp" || ext == ".vtk");
 }
 
-Import::Result Import::operateInternal()
+Import::Result Import::operateInternal(Context ctx)
 {
   Result result;
   std::string filename = this->parameters()->findFile("filename")->value();
diff --git a/smtk/session/polygon/operators/Import.h b/smtk/session/polygon/operators/Import.h
index 5eaf9fc178..2ad7e7f6c8 100644
--- a/smtk/session/polygon/operators/Import.h
+++ b/smtk/session/polygon/operators/Import.h
@@ -49,7 +49,7 @@ public:
 
 protected:
   Import();
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
   int taggedPolyData2PolygonModelEntities(
     smtk::session::polygon::Resource::Ptr& resource,
diff --git a/smtk/session/polygon/operators/ImportPPG.cxx b/smtk/session/polygon/operators/ImportPPG.cxx
index 2477fe1256..8a66a1e9b3 100644
--- a/smtk/session/polygon/operators/ImportPPG.cxx
+++ b/smtk/session/polygon/operators/ImportPPG.cxx
@@ -633,7 +633,7 @@ ImportPPG::~ImportPPG()
   delete m_internal;
 }
 
-smtk::operation::Operation::Result ImportPPG::operateInternal()
+smtk::operation::Operation::Result ImportPPG::operateInternal(Context ctx)
 {
   m_internal->clear();
 
diff --git a/smtk/session/polygon/operators/ImportPPG.h b/smtk/session/polygon/operators/ImportPPG.h
index 3bfdb388c4..26f16e569e 100644
--- a/smtk/session/polygon/operators/ImportPPG.h
+++ b/smtk/session/polygon/operators/ImportPPG.h
@@ -48,7 +48,7 @@ public:
 
 protected:
   ImportPPG();
-  smtk::operation::Operation::Result operateInternal() override;
+  smtk::operation::Operation::Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 
 private:
diff --git a/smtk/session/polygon/operators/LegacyRead.cxx b/smtk/session/polygon/operators/LegacyRead.cxx
index 05fdfd2a4a..20755625a9 100644
--- a/smtk/session/polygon/operators/LegacyRead.cxx
+++ b/smtk/session/polygon/operators/LegacyRead.cxx
@@ -31,7 +31,7 @@ namespace session
 namespace polygon
 {
 
-LegacyRead::Result LegacyRead::operateInternal()
+LegacyRead::Result LegacyRead::operateInternal(Context ctx)
 {
   std::string filename = this->parameters()->findFile("filename")->value();
 
diff --git a/smtk/session/polygon/operators/LegacyRead.h b/smtk/session/polygon/operators/LegacyRead.h
index 330927b3ce..3f5ae72776 100644
--- a/smtk/session/polygon/operators/LegacyRead.h
+++ b/smtk/session/polygon/operators/LegacyRead.h
@@ -39,7 +39,7 @@ public:
   smtkSuperclassMacro(Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/session/polygon/operators/Read.cxx b/smtk/session/polygon/operators/Read.cxx
index ac414eb0b4..41e9813edf 100644
--- a/smtk/session/polygon/operators/Read.cxx
+++ b/smtk/session/polygon/operators/Read.cxx
@@ -31,7 +31,7 @@ namespace session
 namespace polygon
 {
 
-Read::Result Read::operateInternal()
+Read::Result Read::operateInternal(Context ctx)
 {
   std::string filename = this->parameters()->findFile("filename")->value();
 
diff --git a/smtk/session/polygon/operators/Read.h b/smtk/session/polygon/operators/Read.h
index 2fe8d65941..2b2dee707e 100644
--- a/smtk/session/polygon/operators/Read.h
+++ b/smtk/session/polygon/operators/Read.h
@@ -32,7 +32,7 @@ public:
   smtkSuperclassMacro(Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   void markModifiedResources(Result& res) override;
   const char* xmlDescription() const override;
 };
diff --git a/smtk/session/polygon/operators/SplitEdge.cxx b/smtk/session/polygon/operators/SplitEdge.cxx
index ae93b7c763..e1f0fac3e4 100644
--- a/smtk/session/polygon/operators/SplitEdge.cxx
+++ b/smtk/session/polygon/operators/SplitEdge.cxx
@@ -34,7 +34,7 @@ namespace session
 namespace polygon
 {
 
-SplitEdge::Result SplitEdge::operateInternal()
+SplitEdge::Result SplitEdge::operateInternal(Context ctx)
 {
   smtk::attribute::IntItem::Ptr pointIdItem = this->parameters()->findInt("point id");
   smtk::attribute::DoubleItem::Ptr pointItem = this->parameters()->findDouble("point");
diff --git a/smtk/session/polygon/operators/SplitEdge.h b/smtk/session/polygon/operators/SplitEdge.h
index ddee4dd46a..6817d050a0 100644
--- a/smtk/session/polygon/operators/SplitEdge.h
+++ b/smtk/session/polygon/operators/SplitEdge.h
@@ -32,7 +32,7 @@ public:
   smtkSuperclassMacro(Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/session/polygon/operators/TweakEdge.cxx b/smtk/session/polygon/operators/TweakEdge.cxx
index 8008a37ed7..7a8b4dea6e 100644
--- a/smtk/session/polygon/operators/TweakEdge.cxx
+++ b/smtk/session/polygon/operators/TweakEdge.cxx
@@ -35,7 +35,7 @@ namespace session
 namespace polygon
 {
 
-TweakEdge::Result TweakEdge::operateInternal()
+TweakEdge::Result TweakEdge::operateInternal(Context ctx)
 {
   auto edgeItem = this->parameters()->associations();
   auto pointsItem = this->parameters()->findDouble("points");
diff --git a/smtk/session/polygon/operators/TweakEdge.h b/smtk/session/polygon/operators/TweakEdge.h
index b56c15a777..7253c49f80 100644
--- a/smtk/session/polygon/operators/TweakEdge.h
+++ b/smtk/session/polygon/operators/TweakEdge.h
@@ -30,7 +30,7 @@ public:
   smtkSuperclassMacro(Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/session/polygon/operators/Write.cxx b/smtk/session/polygon/operators/Write.cxx
index 23d2ab9b07..8e7183fc93 100644
--- a/smtk/session/polygon/operators/Write.cxx
+++ b/smtk/session/polygon/operators/Write.cxx
@@ -44,7 +44,7 @@ bool Write::ableToOperate()
   return true;
 }
 
-Write::Result Write::operateInternal()
+Write::Result Write::operateInternal(Context ctx)
 {
   auto resourceItem = this->parameters()->associations();
 
diff --git a/smtk/session/polygon/operators/Write.h b/smtk/session/polygon/operators/Write.h
index 001673da7a..f3a0786faf 100644
--- a/smtk/session/polygon/operators/Write.h
+++ b/smtk/session/polygon/operators/Write.h
@@ -34,7 +34,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/session/vtk/operators/Export.cxx b/smtk/session/vtk/operators/Export.cxx
index de129824c0..08c962b361 100644
--- a/smtk/session/vtk/operators/Export.cxx
+++ b/smtk/session/vtk/operators/Export.cxx
@@ -62,7 +62,7 @@ namespace session
 namespace vtk
 {
 
-Export::Result Export::operateInternal()
+Export::Result Export::operateInternal(Context ctx)
 {
   smtk::attribute::FileItem::Ptr filenameItem = this->parameters()->findFile("filename");
   smtk::attribute::StringItem::Ptr filetypeItem = this->parameters()->findString("filetype");
diff --git a/smtk/session/vtk/operators/Export.h b/smtk/session/vtk/operators/Export.h
index c94c86f2e7..7d4f835948 100644
--- a/smtk/session/vtk/operators/Export.h
+++ b/smtk/session/vtk/operators/Export.h
@@ -27,7 +27,7 @@ public:
   smtkSharedFromThisMacro(smtk::operation::Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   virtual Result exportExodus();
   virtual Result exportSLAC();
   virtual Result exportLabelMap();
diff --git a/smtk/session/vtk/operators/Import.cxx b/smtk/session/vtk/operators/Import.cxx
index 1e279be865..64b21f91a5 100644
--- a/smtk/session/vtk/operators/Import.cxx
+++ b/smtk/session/vtk/operators/Import.cxx
@@ -67,7 +67,7 @@ namespace session
 namespace vtk
 {
 
-Import::Result Import::operateInternal()
+Import::Result Import::operateInternal(Context ctx)
 {
   smtk::attribute::FileItem::Ptr filenameItem = this->parameters()->findFile("filename");
   smtk::attribute::StringItem::Ptr filetypeItem = this->parameters()->findString("filetype");
diff --git a/smtk/session/vtk/operators/Import.h b/smtk/session/vtk/operators/Import.h
index 55730acffd..2962031a4a 100644
--- a/smtk/session/vtk/operators/Import.h
+++ b/smtk/session/vtk/operators/Import.h
@@ -35,7 +35,7 @@ public:
   smtkSuperclassMacro(Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   virtual Result importExodus(const smtk::session::vtk::Resource::Ptr&);
   virtual Result importSLAC(const smtk::session::vtk::Resource::Ptr&);
   virtual Result importLabelMap(const smtk::session::vtk::Resource::Ptr&);
diff --git a/smtk/session/vtk/operators/LegacyRead.cxx b/smtk/session/vtk/operators/LegacyRead.cxx
index bfbc1bef98..44db835d08 100644
--- a/smtk/session/vtk/operators/LegacyRead.cxx
+++ b/smtk/session/vtk/operators/LegacyRead.cxx
@@ -35,7 +35,7 @@ namespace session
 namespace vtk
 {
 
-LegacyRead::Result LegacyRead::operateInternal()
+LegacyRead::Result LegacyRead::operateInternal(Context ctx)
 {
   std::string filename = this->parameters()->findFile("filename")->value();
 
diff --git a/smtk/session/vtk/operators/LegacyRead.h b/smtk/session/vtk/operators/LegacyRead.h
index 46202b97bd..6fc78866f8 100644
--- a/smtk/session/vtk/operators/LegacyRead.h
+++ b/smtk/session/vtk/operators/LegacyRead.h
@@ -39,7 +39,7 @@ public:
   smtkSuperclassMacro(Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/session/vtk/operators/Read.cxx b/smtk/session/vtk/operators/Read.cxx
index 62475622cd..0db2c0c023 100644
--- a/smtk/session/vtk/operators/Read.cxx
+++ b/smtk/session/vtk/operators/Read.cxx
@@ -35,7 +35,7 @@ namespace session
 namespace vtk
 {
 
-Read::Result Read::operateInternal()
+Read::Result Read::operateInternal(Context ctx)
 {
   std::string filename = this->parameters()->findFile("filename")->value();
 
diff --git a/smtk/session/vtk/operators/Read.h b/smtk/session/vtk/operators/Read.h
index 2662427357..4d429a213b 100644
--- a/smtk/session/vtk/operators/Read.h
+++ b/smtk/session/vtk/operators/Read.h
@@ -32,7 +32,7 @@ public:
   smtkSuperclassMacro(Operation);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
   void markModifiedResources(Result&) override;
 };
diff --git a/smtk/session/vtk/operators/Write.cxx b/smtk/session/vtk/operators/Write.cxx
index e736f50b7b..650516a9b0 100644
--- a/smtk/session/vtk/operators/Write.cxx
+++ b/smtk/session/vtk/operators/Write.cxx
@@ -83,7 +83,7 @@ bool Write::ableToOperate()
   return true;
 }
 
-Write::Result Write::operateInternal()
+Write::Result Write::operateInternal(Context ctx)
 {
   auto resourceItem = this->parameters()->associations();
 
diff --git a/smtk/session/vtk/operators/Write.h b/smtk/session/vtk/operators/Write.h
index 1b67f762e2..bfc79b4782 100644
--- a/smtk/session/vtk/operators/Write.h
+++ b/smtk/session/vtk/operators/Write.h
@@ -34,7 +34,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
   void markModifiedResources(Result&) override;
 };
diff --git a/smtk/task/Agent.cxx b/smtk/task/Agent.cxx
index af7753795e..2e08d79ced 100644
--- a/smtk/task/Agent.cxx
+++ b/smtk/task/Agent.cxx
@@ -47,7 +47,7 @@ Agent::Configuration Agent::configuration() const
   return config;
 }
 
-void Agent::portDataUpdated(const Port*) {}
+void Agent::portDataUpdated(Context ctx, const Port*) {}
 
 bool Agent::getViewData(smtk::common::TypeContainer& configuration) const
 {
diff --git a/smtk/task/Agent.h b/smtk/task/Agent.h
index bffb6fb45b..d64e241c85 100644
--- a/smtk/task/Agent.h
+++ b/smtk/task/Agent.h
@@ -13,6 +13,7 @@
 #include "smtk/CoreExports.h"
 #include "smtk/SharedFromThis.h"
 #include "smtk/SystemConfig.h"
+#include "smtk/common/Context.h"
 #include "smtk/common/Deprecation.h"
 #include "smtk/common/Managers.h"
 #include "smtk/common/Observers.h"
@@ -48,6 +49,8 @@ class Task;
 class SMTKCORE_EXPORT Agent
 {
 public:
+  using Context = smtk::common::Context;
+
   /// The set of values that an agent's acceptChildCategories method can return.
   enum class CategoryEvaluation
   {
@@ -85,7 +88,7 @@ public:
   /// This will be called when an input \a port connection is modified upstream
   /// of this agent, so \a port should always be an input port of this agent's
   /// parent task.
-  virtual void portDataUpdated(const Port* port);
+  virtual void portDataUpdated(Context ctx, const Port* port);
 
   ///\brief Return the agent's parent task
   Task* parent() const { return m_parent; }
diff --git a/smtk/task/FillOutAttributes.cxx b/smtk/task/FillOutAttributes.cxx
index 3979561dfa..366b641c7a 100644
--- a/smtk/task/FillOutAttributes.cxx
+++ b/smtk/task/FillOutAttributes.cxx
@@ -115,6 +115,7 @@ void FillOutAttributes::configure(const Configuration& config)
     {
       m_observer = operationManager->observers().insert(
         [this](
+        smtk::common::Context ctx,
           const smtk::operation::Operation& op,
           smtk::operation::EventType event,
           smtk::operation::Operation::Result result) { return this->update(op, event, result); },
@@ -188,7 +189,7 @@ std::shared_ptr<PortData> FillOutAttributes::portData(const Port* port) const
   return data;
 }
 
-void FillOutAttributes::portDataUpdated(const Port* port)
+void FillOutAttributes::portDataUpdated(Context ctx, const Port* port)
 {
   if (m_ports.empty() || m_ports["in"] != port)
   {
diff --git a/smtk/task/FillOutAttributes.h b/smtk/task/FillOutAttributes.h
index 87fbbb9795..c548239620 100644
--- a/smtk/task/FillOutAttributes.h
+++ b/smtk/task/FillOutAttributes.h
@@ -115,7 +115,7 @@ public:
   std::shared_ptr<PortData> portData(const Port* port) const override;
 
   /// Update the task based on new port data.
-  void portDataUpdated(const Port* port) override;
+  void portDataUpdated(Context ctx, const Port* port) override;
 
   /// Provide the attribute resource(s) that the user should edit.
   bool getViewData(smtk::common::TypeContainer& configuration) const override;
diff --git a/smtk/task/FillOutAttributesAgent.cxx b/smtk/task/FillOutAttributesAgent.cxx
index 5d3aa9fe0f..0667e7fa88 100644
--- a/smtk/task/FillOutAttributesAgent.cxx
+++ b/smtk/task/FillOutAttributesAgent.cxx
@@ -103,6 +103,7 @@ void FillOutAttributesAgent::configure(const Configuration& config)
     {
       m_observer = operationManager->observers().insert(
         [this](
+        smtk::common::Context ctx,
           const smtk::operation::Operation& op,
           smtk::operation::EventType event,
           smtk::operation::Operation::Result result) { return this->update(op, event, result); },
@@ -201,7 +202,7 @@ std::shared_ptr<PortData> FillOutAttributesAgent::portData(const Port* port) con
   return data;
 }
 
-void FillOutAttributesAgent::portDataUpdated(const Port* port)
+void FillOutAttributesAgent::portDataUpdated(Context ctx, const Port* port)
 {
   // Only respond if the port is our input.
   if (!port || m_inputPort != port)
@@ -256,7 +257,7 @@ void FillOutAttributesAgent::portDataUpdated(const Port* port)
     if (m_outputPort)
     {
       // Propagate changes downstream.
-      m_parent->portDataUpdated(m_outputPort);
+      m_parent->portDataUpdated(ctx, m_outputPort);
     }
   }
 }
diff --git a/smtk/task/FillOutAttributesAgent.h b/smtk/task/FillOutAttributesAgent.h
index 7f936ce0e9..e47615ef7e 100644
--- a/smtk/task/FillOutAttributesAgent.h
+++ b/smtk/task/FillOutAttributesAgent.h
@@ -74,7 +74,7 @@ public:
   std::shared_ptr<PortData> portData(const Port* port) const override;
 
   ///\brief Tell the agent that the data on \a port has been updated.
-  void portDataUpdated(const Port* port) override;
+  void portDataUpdated(Context ctx, const Port* port) override;
 
   ///\brief Insert an attribute resource pointer into \a configuration.
   bool getViewData(smtk::common::TypeContainer& configuration) const override;
diff --git a/smtk/task/GatherObjectsAgent.cxx b/smtk/task/GatherObjectsAgent.cxx
index 2f350cb1d3..9cc6c9dae5 100644
--- a/smtk/task/GatherObjectsAgent.cxx
+++ b/smtk/task/GatherObjectsAgent.cxx
@@ -125,7 +125,7 @@ std::shared_ptr<PortData> GatherObjectsAgent::portData(const Port* port) const
 bool GatherObjectsAgent::addObjectInRole(
   smtk::resource::PersistentObject* object,
   smtk::string::Token role,
-  bool signal)
+  bool signal, Context ctx)
 {
   if (!object || !role.valid())
   {
@@ -162,7 +162,7 @@ bool GatherObjectsAgent::addObjectInRole(
     auto it = m_parent->ports().find(m_outputPortName);
     if (it != m_parent->ports().end())
     {
-      m_parent->portDataUpdated(it->second);
+      m_parent->portDataUpdated(ctx, it->second);
     }
   }
   return true;
@@ -171,7 +171,7 @@ bool GatherObjectsAgent::addObjectInRole(
 bool GatherObjectsAgent::removeObjectFromRole(
   smtk::resource::PersistentObject* object,
   smtk::string::Token role,
-  bool signal)
+  bool signal, Context ctx)
 {
   if (!object || !role.valid())
   {
@@ -214,7 +214,7 @@ bool GatherObjectsAgent::removeObjectFromRole(
           auto it = m_parent->ports().find(m_outputPortName);
           if (it != m_parent->ports().end())
           {
-            m_parent->portDataUpdated(it->second);
+            m_parent->portDataUpdated(ctx, it->second);
           }
         }
         return true;
@@ -224,7 +224,7 @@ bool GatherObjectsAgent::removeObjectFromRole(
   return false;
 }
 
-bool GatherObjectsAgent::clearOutputPort(bool signal)
+bool GatherObjectsAgent::clearOutputPort(bool signal, Context ctx)
 {
   bool wasNotEmpty = !m_objects.empty();
   m_objects.clear();
@@ -233,7 +233,7 @@ bool GatherObjectsAgent::clearOutputPort(bool signal)
     auto it = m_parent->ports().find(m_outputPortName);
     if (it != m_parent->ports().end())
     {
-      m_parent->portDataUpdated(it->second);
+      m_parent->portDataUpdated(ctx, it->second);
     }
   }
   return wasNotEmpty;
diff --git a/smtk/task/GatherObjectsAgent.h b/smtk/task/GatherObjectsAgent.h
index 284b0c1ce2..0683025da2 100644
--- a/smtk/task/GatherObjectsAgent.h
+++ b/smtk/task/GatherObjectsAgent.h
@@ -63,7 +63,7 @@ public:
   bool addObjectInRole(
     smtk::resource::PersistentObject* object,
     smtk::string::Token role,
-    bool signal = false);
+    bool signal = false, Context ctx = Context());
 
   ///\brief Remove a persistent \a object from this port's output for the given \a role.
   ///
@@ -73,13 +73,13 @@ public:
   bool removeObjectFromRole(
     smtk::resource::PersistentObject* object,
     smtk::string::Token role,
-    bool signal = false);
+    bool signal = false, Context ctx = Context());
 
   ///\brief Clear the data to be reported on the output port.
   ///
   /// If the port was modified and \a signal is true,
   /// then Task::portDataUpdated() is invoked for this agent's port.
-  bool clearOutputPort(bool signal = false);
+  bool clearOutputPort(bool signal = false, Context ctx = Context());
 
   /// Return this agent's output port (or null).
   Port* outputPort() const;
diff --git a/smtk/task/Manager.cxx b/smtk/task/Manager.cxx
index 8d973e38c5..48b36a911e 100644
--- a/smtk/task/Manager.cxx
+++ b/smtk/task/Manager.cxx
@@ -123,10 +123,11 @@ void Manager::setManagers(const smtk::common::Managers::Ptr& managers)
     {
       m_taskEventObserver = opMgr->observers().insert(
         [this](
+          smtk::common::Context ctx,
           const smtk::operation::Operation& op,
           smtk::operation::EventType event,
           smtk::operation::Operation::Result result) {
-          return this->handleOperation(op, event, result);
+          return this->handleOperation(ctx, op, event, result);
         },
         Manager::operationObserverPriority(),
         false,
@@ -150,6 +151,7 @@ smtk::resource::Resource* Manager::resource() const
 }
 
 int Manager::handleOperation(
+  smtk::common::Context ctx,
   const smtk::operation::Operation& op,
   smtk::operation::EventType event,
   smtk::operation::Operation::Result result)
@@ -186,7 +188,7 @@ int Manager::handleOperation(
         // connected to this port that have multiple
         // inputs need to be told to reconfigure (without the
         // data from this port).
-        this->removePortFromDownstream(port);
+        this->removePortFromDownstream(ctx, port);
       }
       // Now that we've traversed connections, clear them.
       // This is defensive programming; we don't want stale pointers
@@ -195,7 +197,7 @@ int Manager::handleOperation(
     }
     else
     {
-      this->handlePortDataReferences(comp.get());
+      this->handlePortDataReferences(ctx, comp.get());
     }
   }
   if (expungedResources)
@@ -203,7 +205,7 @@ int Manager::handleOperation(
     // See if any removed resources were used as port inputs.
     for (const auto& resource : *expungedResources)
     {
-      this->handlePortDataReferences(resource.get());
+      this->handlePortDataReferences(ctx, resource.get());
     }
   }
   // TODO: Handle workflows
@@ -211,7 +213,7 @@ int Manager::handleOperation(
   {
     if (auto port = std::dynamic_pointer_cast<smtk::task::Port>(comp))
     {
-      this->handleCreatedOrModifiedPort(port);
+      this->handleCreatedOrModifiedPort(ctx, port);
     }
     else if (auto task = std::dynamic_pointer_cast<smtk::task::Task>(comp))
     {
@@ -227,7 +229,7 @@ int Manager::handleOperation(
   {
     if (auto port = std::dynamic_pointer_cast<smtk::task::Port>(comp))
     {
-      this->handleCreatedOrModifiedPort(port);
+      this->handleCreatedOrModifiedPort(ctx, port);
     }
     else if (auto task = std::dynamic_pointer_cast<smtk::task::Task>(comp))
     {
@@ -265,7 +267,7 @@ int Manager::handleOperation(
   return 0;
 }
 
-bool Manager::handlePortDataReferences(smtk::resource::PersistentObject* obj)
+bool Manager::handlePortDataReferences(smtk::common::Context ctx, smtk::resource::PersistentObject* obj)
 {
   // Any non-Port object may be an input to a Port.
   // Look in the m_portData map to see if this is the case.
@@ -282,7 +284,7 @@ bool Manager::handlePortDataReferences(smtk::resource::PersistentObject* obj)
         auto* downstreamTask = downstreamPort->parent();
         if (downstreamTask)
         {
-          downstreamTask->portDataUpdated(downstreamPort);
+          downstreamTask->portDataUpdated(ctx, downstreamPort);
         }
       }
     }
@@ -310,7 +312,7 @@ bool Manager::removeUpstreamMentions(const std::shared_ptr<smtk::task::Port>& po
   return didRemove;
 }
 
-bool Manager::removePortFromDownstream(const std::shared_ptr<smtk::task::Port>& port)
+bool Manager::removePortFromDownstream(smtk::common::Context ctx, const std::shared_ptr<smtk::task::Port>& port)
 {
   bool didRemove = false;
   if (port->direction() != Port::Direction::Out)
@@ -330,7 +332,7 @@ bool Manager::removePortFromDownstream(const std::shared_ptr<smtk::task::Port>&
           auto* downstreamTask = downstreamPort->parent();
           if (downstreamTask)
           {
-            downstreamTask->portDataUpdated(downstreamPort);
+            downstreamTask->portDataUpdated(ctx, downstreamPort);
           }
         }
       }
@@ -341,7 +343,7 @@ bool Manager::removePortFromDownstream(const std::shared_ptr<smtk::task::Port>&
   return didRemove;
 }
 
-void Manager::handleCreatedOrModifiedPort(const std::shared_ptr<smtk::task::Port>& port)
+void Manager::handleCreatedOrModifiedPort(smtk::common::Context ctx, const std::shared_ptr<smtk::task::Port>& port)
 {
   if (port->direction() == Port::Direction::Out)
   {
@@ -354,7 +356,7 @@ void Manager::handleCreatedOrModifiedPort(const std::shared_ptr<smtk::task::Port
         auto* downstreamTask = downstreamPort->parent();
         if (downstreamTask)
         {
-          downstreamTask->portDataUpdated(downstreamPort);
+          downstreamTask->portDataUpdated(ctx, downstreamPort);
         }
       }
     }
@@ -377,7 +379,7 @@ void Manager::handleCreatedOrModifiedPort(const std::shared_ptr<smtk::task::Port
         // result.
         if (m_portData[conn].insert(port.get()).second)
         {
-          port->parent()->portDataUpdated(port.get());
+          port->parent()->portDataUpdated(ctx, port.get());
         }
       }
     }
diff --git a/smtk/task/Manager.h b/smtk/task/Manager.h
index 9dd682795e..43d7fa859b 100644
--- a/smtk/task/Manager.h
+++ b/smtk/task/Manager.h
@@ -172,23 +172,24 @@ public:
 private:
   /// A method invoked when the \a m_manager's operation manager runs an operation.
   int handleOperation(
+    smtk::common::Context ctx,
     const smtk::operation::Operation& op,
     smtk::operation::EventType event,
     smtk::operation::Operation::Result result);
 
   /// If a port directly references \a obj as a connection, remove \a obj
   /// and potentially notify the consuming port's task.
-  bool handlePortDataReferences(smtk::resource::PersistentObject* obj);
+  bool handlePortDataReferences(smtk::common::Context ctx, smtk::resource::PersistentObject* obj);
   /// Remove an input \a port from upstream output ports.
   bool removeUpstreamMentions(const std::shared_ptr<smtk::task::Port>& port);
   /// Remove an output \a port from all its downstream input ports.
   ///
   /// This may invoke the downstream-task's portDataUpdated() method
   /// (if there are any remaining inputs).
-  bool removePortFromDownstream(const std::shared_ptr<smtk::task::Port>& port);
+  bool removePortFromDownstream(smtk::common::Context ctx, const std::shared_ptr<smtk::task::Port>& port);
 
   /// Invoke methods on upstream/downstream objects when \a port is created/modified.
-  void handleCreatedOrModifiedPort(const std::shared_ptr<smtk::task::Port>& port);
+  void handleCreatedOrModifiedPort(smtk::common::Context ctx, const std::shared_ptr<smtk::task::Port>& port);
 
   TaskInstances m_taskInstances;
   AdaptorInstances m_adaptorInstances;
diff --git a/smtk/task/PortForwardingAgent.cxx b/smtk/task/PortForwardingAgent.cxx
index a09316a1d1..b6d253de6e 100644
--- a/smtk/task/PortForwardingAgent.cxx
+++ b/smtk/task/PortForwardingAgent.cxx
@@ -242,7 +242,7 @@ std::shared_ptr<PortData> PortForwardingAgent::portData(const Port* port) const
   return result;
 }
 
-void PortForwardingAgent::portDataUpdated(const Port* port)
+void PortForwardingAgent::portDataUpdated(Context ctx, const Port* port)
 {
   // Collect a list of output ports whose data needs to be updated
   // because \a port is attached from upstream via this agent.
@@ -257,7 +257,7 @@ void PortForwardingAgent::portDataUpdated(const Port* port)
   // Notify connections on downstream ports once (for this input \a port):
   for (const auto& downstreamPort : downstreamPorts)
   {
-    this->parent()->portDataUpdated(downstreamPort);
+    this->parent()->portDataUpdated(ctx, downstreamPort);
   }
 }
 
diff --git a/smtk/task/PortForwardingAgent.h b/smtk/task/PortForwardingAgent.h
index b403d6b607..ce25fd3e39 100644
--- a/smtk/task/PortForwardingAgent.h
+++ b/smtk/task/PortForwardingAgent.h
@@ -88,7 +88,7 @@ public:
   /// This assumes that \a port is an input port of the task and
   /// forces portDataUpdated() to be called on each downstream
   /// of \a port.
-  void portDataUpdated(const Port* port) override;
+  void portDataUpdated(Context ctx, const Port* port) override;
 
 protected:
   std::vector<Forward> m_forwards;
diff --git a/smtk/task/SubmitOperation.cxx b/smtk/task/SubmitOperation.cxx
index 950a811031..22b40547de 100644
--- a/smtk/task/SubmitOperation.cxx
+++ b/smtk/task/SubmitOperation.cxx
@@ -219,6 +219,7 @@ void SubmitOperation::configure(const Configuration& config)
     {
       m_observer = operationManager->observers().insert(
         [this](
+        smtk::common::Context ctx,
           const smtk::operation::Operation& op,
           smtk::operation::EventType event,
           smtk::operation::Operation::Result result) { return this->update(op, event, result); },
diff --git a/smtk/task/SubmitOperationAgent.cxx b/smtk/task/SubmitOperationAgent.cxx
index 97c6c5869e..b280011b31 100644
--- a/smtk/task/SubmitOperationAgent.cxx
+++ b/smtk/task/SubmitOperationAgent.cxx
@@ -304,6 +304,7 @@ void SubmitOperationAgent::configure(const Configuration& config)
       }
       m_observer = operationManager->observers().insert(
         [this](
+        smtk::common::Context ctx,
           const smtk::operation::Operation& op,
           smtk::operation::EventType event,
           smtk::operation::Operation::Result result) { return this->update(op, event, result); },
@@ -507,7 +508,7 @@ std::shared_ptr<PortData> SubmitOperationAgent::portData(const Port* port) const
   return data;
 }
 
-void SubmitOperationAgent::portDataUpdated(const Port* port)
+void SubmitOperationAgent::portDataUpdated(Context ctx, const Port* port)
 {
   if (!port)
   {
diff --git a/smtk/task/SubmitOperationAgent.h b/smtk/task/SubmitOperationAgent.h
index afd87e50f6..5694dc2ae4 100644
--- a/smtk/task/SubmitOperationAgent.h
+++ b/smtk/task/SubmitOperationAgent.h
@@ -264,7 +264,7 @@ public:
   std::shared_ptr<PortData> portData(const Port* port) const override;
 
   ///\brief  Tell the agent that the data on \a port has been updated.
-  void portDataUpdated(const Port* port) override;
+  void portDataUpdated(Context ctx, const Port* port) override;
 
   /// Return the operation this task requires users to configure and submit.
   smtk::operation::Operation* operation() const { return m_operation.get(); }
diff --git a/smtk/task/Task.cxx b/smtk/task/Task.cxx
index 531e3783fd..70ab95a2d0 100644
--- a/smtk/task/Task.cxx
+++ b/smtk/task/Task.cxx
@@ -398,7 +398,7 @@ std::shared_ptr<PortData> Task::portData(const Port* port) const
   return this->outputPortData(port);
 }
 
-void Task::portDataUpdated(const Port* port)
+void Task::portDataUpdated(Context ctx, const Port* port)
 {
   if (!port)
   {
@@ -410,7 +410,7 @@ void Task::portDataUpdated(const Port* port)
   {
     for (const auto& agent : m_agents)
     {
-      agent->portDataUpdated(port);
+      agent->portDataUpdated(ctx, port);
     }
   }
   else // port->direction() == Port::Direction::Out
@@ -421,7 +421,7 @@ void Task::portDataUpdated(const Port* port)
       {
         if (auto* connParent = connPort->parent())
         {
-          connParent->portDataUpdated(connPort);
+          connParent->portDataUpdated(ctx, connPort);
         }
       }
     }
diff --git a/smtk/task/Task.h b/smtk/task/Task.h
index 8d45c3acd7..934c829d34 100644
--- a/smtk/task/Task.h
+++ b/smtk/task/Task.h
@@ -16,6 +16,7 @@
 #include "smtk/CoreExports.h"
 #include "smtk/SharedFromThis.h"
 #include "smtk/SystemConfig.h"
+#include "smtk/common/Context.h"
 #include "smtk/common/Deprecation.h"
 #include "smtk/common/Managers.h"
 #include "smtk/common/Observers.h"
@@ -74,6 +75,8 @@ public:
   smtkSuperclassMacro(smtk::resource::Component);
   smtkCreateMacro(smtk::resource::PersistentObject);
 
+  using Context = smtk::common::Context;
+
   /// A task's state changes may be observed.
   using Observer = std::function<void(Task&, State, State)>;
   /// The collection of all observers of this task instance.
@@ -200,7 +203,7 @@ public:
   /// Because this function will be called on the user-interface thread,
   /// it is acceptable to take actions affecting the user interface,
   /// including changing the state of this task.
-  virtual void portDataUpdated(const Port* port);
+  virtual void portDataUpdated(Context ctx, const Port* port);
 
   /// Set/get style classes for the task.
   /// A style class specifies how applications should present the task
diff --git a/smtk/task/TrivialProducerAgent.cxx b/smtk/task/TrivialProducerAgent.cxx
index 8cba737887..6c60221926 100644
--- a/smtk/task/TrivialProducerAgent.cxx
+++ b/smtk/task/TrivialProducerAgent.cxx
@@ -76,7 +76,8 @@ void TrivialProducerAgent::configure(const Configuration& config)
   }
   if (addedData && m_outputPort)
   {
-    this->portDataUpdated(m_outputPort);
+    // TODO: This call probably shouldn't be here. Context cannot be passed.
+    this->portDataUpdated(Context(), m_outputPort);
   }
 }
 
diff --git a/smtk/task/adaptor/ConfigureOperation.cxx b/smtk/task/adaptor/ConfigureOperation.cxx
index 8620211c54..d5ecc27db3 100644
--- a/smtk/task/adaptor/ConfigureOperation.cxx
+++ b/smtk/task/adaptor/ConfigureOperation.cxx
@@ -317,6 +317,7 @@ bool ConfigureOperation::setupAttributeObserver()
   auto opManager = managers->get<smtk::operation::Manager::Ptr>();
   m_attributeObserver = opManager->observers().insert(
     [this](
+    smtk::common::Context ctx,
       const smtk::operation::Operation& op,
       smtk::operation::EventType eventType,
       smtk::operation::Operation::Result result) -> int {
diff --git a/smtk/task/operators/AddDependency.cxx b/smtk/task/operators/AddDependency.cxx
index dfddfcf671..7c32122bf5 100644
--- a/smtk/task/operators/AddDependency.cxx
+++ b/smtk/task/operators/AddDependency.cxx
@@ -59,7 +59,7 @@ bool AddDependency::ableToOperate()
   return this->Superclass::ableToOperate();
 }
 
-AddDependency::Result AddDependency::operateInternal()
+AddDependency::Result AddDependency::operateInternal(Context ctx)
 {
   auto fromTask = this->parameters()->associations()->valueAs<smtk::task::Task>();
   auto toTask = this->parameters()->findComponent("to")->valueAs<smtk::task::Task>();
diff --git a/smtk/task/operators/AddDependency.h b/smtk/task/operators/AddDependency.h
index bcb2e9af7f..f1228ec837 100644
--- a/smtk/task/operators/AddDependency.h
+++ b/smtk/task/operators/AddDependency.h
@@ -37,7 +37,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/task/operators/ConnectPorts.cxx b/smtk/task/operators/ConnectPorts.cxx
index e4830010a3..fa60691f89 100644
--- a/smtk/task/operators/ConnectPorts.cxx
+++ b/smtk/task/operators/ConnectPorts.cxx
@@ -122,7 +122,7 @@ bool ConnectPorts::ableToOperate()
   return this->Superclass::ableToOperate();
 }
 
-ConnectPorts::Result ConnectPorts::operateInternal()
+ConnectPorts::Result ConnectPorts::operateInternal(Context ctx)
 {
   auto fromPorts = this->parameters()->associations();
   auto toPorts = this->parameters()->findComponent("to");
diff --git a/smtk/task/operators/ConnectPorts.h b/smtk/task/operators/ConnectPorts.h
index 72d03ac10a..ee0ff7f846 100644
--- a/smtk/task/operators/ConnectPorts.h
+++ b/smtk/task/operators/ConnectPorts.h
@@ -40,7 +40,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/task/operators/DisconnectPorts.cxx b/smtk/task/operators/DisconnectPorts.cxx
index 378c443b33..f00f5685dc 100644
--- a/smtk/task/operators/DisconnectPorts.cxx
+++ b/smtk/task/operators/DisconnectPorts.cxx
@@ -79,7 +79,7 @@ bool DisconnectPorts::ableToOperate()
   return this->Superclass::ableToOperate();
 }
 
-DisconnectPorts::Result DisconnectPorts::operateInternal()
+DisconnectPorts::Result DisconnectPorts::operateInternal(Context ctx)
 {
   Result result = this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED);
   auto modified = result->findComponent("modified");
diff --git a/smtk/task/operators/DisconnectPorts.h b/smtk/task/operators/DisconnectPorts.h
index ef9958bcd9..639b0f7c45 100644
--- a/smtk/task/operators/DisconnectPorts.h
+++ b/smtk/task/operators/DisconnectPorts.h
@@ -40,7 +40,7 @@ public:
   bool ableToOperate() override;
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/task/operators/EmplaceWorklet.cxx b/smtk/task/operators/EmplaceWorklet.cxx
index 7bc21cddae..58572fefdc 100644
--- a/smtk/task/operators/EmplaceWorklet.cxx
+++ b/smtk/task/operators/EmplaceWorklet.cxx
@@ -49,7 +49,7 @@ namespace smtk
 namespace task
 {
 
-EmplaceWorklet::Result EmplaceWorklet::operateInternal()
+EmplaceWorklet::Result EmplaceWorklet::operateInternal(Context ctx)
 {
   auto worklet = this->parameters()->associations()->valueAs<smtk::task::Worklet>();
   auto project = worklet ? dynamic_pointer_cast<smtk::project::Project>(worklet->resource())
diff --git a/smtk/task/operators/EmplaceWorklet.h b/smtk/task/operators/EmplaceWorklet.h
index f1f3971a48..3b1227e633 100644
--- a/smtk/task/operators/EmplaceWorklet.h
+++ b/smtk/task/operators/EmplaceWorklet.h
@@ -32,7 +32,7 @@ public:
   smtkCreateMacro(EmplaceWorklet);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/task/operators/RemoveDependency.cxx b/smtk/task/operators/RemoveDependency.cxx
index 81b780934a..15bbca8820 100644
--- a/smtk/task/operators/RemoveDependency.cxx
+++ b/smtk/task/operators/RemoveDependency.cxx
@@ -44,7 +44,7 @@ namespace smtk
 namespace task
 {
 
-RemoveDependency::Result RemoveDependency::operateInternal()
+RemoveDependency::Result RemoveDependency::operateInternal(Context ctx)
 {
   // First, accumulate task pairs while validating that all dependencies exist.
   auto endpoints = this->parameters()->findGroup("task endpoints");
diff --git a/smtk/task/operators/RemoveDependency.h b/smtk/task/operators/RemoveDependency.h
index 960d8f69f0..9d593f7c24 100644
--- a/smtk/task/operators/RemoveDependency.h
+++ b/smtk/task/operators/RemoveDependency.h
@@ -32,7 +32,7 @@ public:
   smtkCreateMacro(RemoveDependency);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/task/operators/RenameTask.cxx b/smtk/task/operators/RenameTask.cxx
index 54ca98c27a..d49c3ceda7 100644
--- a/smtk/task/operators/RenameTask.cxx
+++ b/smtk/task/operators/RenameTask.cxx
@@ -44,7 +44,7 @@ namespace smtk
 namespace task
 {
 
-RenameTask::Result RenameTask::operateInternal()
+RenameTask::Result RenameTask::operateInternal(Context ctx)
 {
   auto task = this->parameters()->associations()->valueAs<smtk::task::Task>();
   auto project = task ? dynamic_pointer_cast<smtk::project::Project>(task->resource())
diff --git a/smtk/task/operators/RenameTask.h b/smtk/task/operators/RenameTask.h
index 5c30ad6e34..99f228ae02 100644
--- a/smtk/task/operators/RenameTask.h
+++ b/smtk/task/operators/RenameTask.h
@@ -36,7 +36,7 @@ public:
   void setTask(const Task::Ptr& task);
 
 protected:
-  Result operateInternal() override;
+  Result operateInternal(Context ctx) override;
   const char* xmlDescription() const override;
 };
 
diff --git a/smtk/task/pybind11/PybindGatherObjectsAgent.h b/smtk/task/pybind11/PybindGatherObjectsAgent.h
index 4e64296f55..f690f92d01 100644
--- a/smtk/task/pybind11/PybindGatherObjectsAgent.h
+++ b/smtk/task/pybind11/PybindGatherObjectsAgent.h
@@ -20,37 +20,45 @@
 
 namespace py = pybind11;
 
-inline py::class_< smtk::task::GatherObjectsAgent, smtk::task::Agent > pybind11_init_smtk_task_GatherObjectsAgent(py::module &m)
+inline py::class_<smtk::task::GatherObjectsAgent, smtk::task::Agent>
+pybind11_init_smtk_task_GatherObjectsAgent(py::module& m)
 {
-  py::class_< smtk::task::GatherObjectsAgent, smtk::task::Agent > instance(m, "GatherObjectsAgent");
-  instance
-    .def("typeName", &smtk::task::GatherObjectsAgent::typeName)
-    .def("addObjectInRole", [](
+  py::class_<smtk::task::GatherObjectsAgent, smtk::task::Agent> instance(m, "GatherObjectsAgent");
+  instance.def("typeName", &smtk::task::GatherObjectsAgent::typeName)
+    .def(
+      "addObjectInRole",
+      [](
         smtk::task::GatherObjectsAgent& self,
         const smtk::resource::PersistentObject::Ptr& object,
         smtk::string::Token role,
-        bool signal)
-      {
-        return self.addObjectInRole(object.get(), role, signal);
-      }, py::arg("object"), py::arg("role"), py::arg("signal") = false)
-    .def("removeObjectFromRole", [](
+        bool signal) { return self.addObjectInRole(object.get(), role, signal); },
+      py::arg("object"),
+      py::arg("role"),
+      py::arg("signal") = false)
+    .def(
+      "removeObjectFromRole",
+      [](
         smtk::task::GatherObjectsAgent& self,
         const smtk::resource::PersistentObject::Ptr& object,
         smtk::string::Token role,
-        bool signal)
-      {
-        return self.removeObjectFromRole(object.get(), role, signal);
-      }, py::arg("object"), py::arg("role"), py::arg("signal") = false)
-    .def("clearOutputPort", &smtk::task::GatherObjectsAgent::clearOutputPort, py::arg("signal") = false)
-    .def("outputPort", [](smtk::task::GatherObjectsAgent& self)
-      {
-        return self.outputPort()->shared_from_this();
-      })
-    .def("data", [](smtk::task::GatherObjectsAgent& self)
-      {
-        return self.portData(self.outputPort());
-      }, py::return_value_policy::reference_internal)
-    ;
+        bool signal) { return self.removeObjectFromRole(object.get(), role, signal); },
+      py::arg("object"),
+      py::arg("role"),
+      py::arg("signal") = false)
+    //.def("clearOutputPort", &smtk::task::GatherObjectsAgent::clearOutputPort, py::arg("signal") = false, smtk::common::Context())
+    .def(
+      "clearOutputPort",
+      [](smtk::task::GatherObjectsAgent& self, bool signal) {
+        return self.clearOutputPort(signal);
+      },
+      py::arg("signal") = false)
+    .def(
+      "outputPort",
+      [](smtk::task::GatherObjectsAgent& self) { return self.outputPort()->shared_from_this(); })
+    .def(
+      "data",
+      [](smtk::task::GatherObjectsAgent& self) { return self.portData(self.outputPort()); },
+      py::return_value_policy::reference_internal);
   return instance;
 }
 
diff --git a/smtk/task/testing/cxx/TestConfigureOperation.cxx b/smtk/task/testing/cxx/TestConfigureOperation.cxx
index 44e7734985..428c64cd63 100644
--- a/smtk/task/testing/cxx/TestConfigureOperation.cxx
+++ b/smtk/task/testing/cxx/TestConfigureOperation.cxx
@@ -170,7 +170,7 @@ public:
     return spec;
   }
 
-  Result operateInternal() override
+  Result operateInternal(Context ctx) override
   {
     std::cerr << "Running SimpleOperation!\n";
     return this->createResult(m_outcome);
diff --git a/smtk/task/testing/cxx/TestTaskPorts.cxx b/smtk/task/testing/cxx/TestTaskPorts.cxx
index 327385da40..9828ac38d0 100644
--- a/smtk/task/testing/cxx/TestTaskPorts.cxx
+++ b/smtk/task/testing/cxx/TestTaskPorts.cxx
@@ -247,7 +247,7 @@ int TestTaskPorts(int, char*[])
   attInPort->connections().insert(attrib.get());
   // Signal that connections have been modified.
   // This should cause a change in state.
-  fillOutAttributes->portDataUpdated(attInPort);
+  fillOutAttributes->portDataUpdated(smtk::common::Context(), attInPort);
 
   printTaskStates(taskManager);
   test(
diff --git a/smtk/view/PhraseModel.cxx b/smtk/view/PhraseModel.cxx
index 83f503cb86..f5ae12b9d9 100644
--- a/smtk/view/PhraseModel.cxx
+++ b/smtk/view/PhraseModel.cxx
@@ -242,16 +242,19 @@ bool PhraseModel::addSource(const smtk::common::TypeContainer& managers)
                                 false, // observeImmediately
                                 description.str() + "Update phrases when resources change.")
                             : smtk::resource::Observers::Key();
-  auto operHandle = operMgr
-    ? operMgr->observers().insert(
-        [this](const Operation& op, operation::EventType event, const Operation::Result& res) {
-          this->handleOperationEvent(op, event, res);
-          return 0;
-        },
-        PhraseModel::operationObserverPriority(),
-        /*initialize*/ true,
-        description.str() + "Update phrases based on operation results.")
-    : smtk::operation::Observers::Key();
+  auto operHandle = operMgr ? operMgr->observers().insert(
+                                [this](
+                                  smtk::common::Context ctx,
+                                  const Operation& op,
+                                  operation::EventType event,
+                                  const Operation::Result& res) {
+                                  this->handleOperationEvent(op, event, res);
+                                  return 0;
+                                },
+                                PhraseModel::operationObserverPriority(),
+                                /*initialize*/ true,
+                                description.str() + "Update phrases based on operation results.")
+                            : smtk::operation::Observers::Key();
   auto selnHandle = seln ? seln->observers().insert(
                              [this](const std::string& src, smtk::view::SelectionPtr seln) {
                                this->handleSelectionEvent(src, seln);
-- 
GitLab