RimDeltaSummaryEnsemble-Test.cpp and RimSummaryCaseMainCollection-Test.cpp each defined an identical createMockCase() in an anonymous namespace. A unity build concatenates the two translation units, which merges the two anonymous namespaces into one and makes the second definition a redefinition, breaking the build with C2084.
Move the factory to RimMockSummaryCase.h, the header both tests already include for the mock case itself, and drop both local copies.
Closing all summary cases while a delta ensemble is present crashed with a use-after-free. RicCloseSummaryCaseFeature::deleteSummaryCases holds a case list across removeCases and deletes it afterwards, while removeCases made the delta ensemble rebuild and destroy derived cases that are themselves part of that list. The previous fix rewrote the caller list with the surviving cases, which stopped the crash but left the hazard in place for any other caller holding a case list across a removal.
Add RimSummaryCaseUpdateBatch, a plain scope object that is ambient for the duration of its scope. Removal now only detaches, and hands the detached cases to the batch. The outermost scope flushes, regenerating the dirty delta ensembles in dependency order first so a chained delta ensemble sees the final state of its source, then destroying the orphans with caf::PdmObjectHandleTools::deleteObjects. A nested batch contributes to the outermost one and never flushes. Orphans are held as guarded pointers, so a case the caller destroyed itself is skipped instead of being destroyed twice. Both contribution points fall back to immediate execution when no batch is active, so call sites that do not open one keep behaving as before.
Open a batch in RimSummaryCaseMainCollection::removeCases and in RicCloseSummaryCaseFeature::deleteSummaryCases, which is the outermost of the two and therefore keeps the detached cases alive across its own deleteObjects call. removeCases no longer rewrites the caller list, so it now takes it by const reference.
Derived cases were pooled through an m_inUse flag on RimDeltaSummaryCase. Every regeneration marked all cases not in use, which also severed their source references and cleared their caches, then handed them back out one by one and deleted whatever was left over. The flag conflated pool bookkeeping with owning the source references, allSummaryCases() hid the not-in-use cases from the rest of the project, and cases taken from the pool were pushed straight into m_cases without connecting nameChanged, so a renamed source case never propagated to the derived case.
Replace the pooling with desiredSourceCasePairs(), a pure computation of the source case pairs the ensemble should have, and rebuildDerivedCases(), which diffs that against the existing derived cases keyed on the source case pointer pair. Matching cases are reused, missing ones are created, and surplus ones are detached and returned to the caller instead of being deleted in place. Keying on the pointer pair makes the rebuild idempotent, also right after project load where the derived cases arrive from XML with their sources already resolved.
Add the protected RimSummaryEnsemble::addCaseWithoutDependencyUpdate(), used both by addCase() and by the rebuild, so a derived case gets nameChanged connected without triggering the dependent-ensemble notification that the rebuild is already performing itself.
Remove setAllCasesNotInUse(), firstCaseNotInUse(), deleteCasesNoInUse(), RimDeltaSummaryCase::setInUse()/isInUse() and the m_inUse field, and add clearSourceCases() for the one thing setInUse(false) was actually needed for. Old project files keep loading, unknown XML keywords are skipped. The activeOnly parameter of allDerivedCases() is gone and RimDeltaSummaryEnsemble no longer overrides allSummaryCases().
Delta ensembles were located by scanning the summary case main collection for objects referring to a given ensemble, and the dependent ensembles were visited by unguarded recursion. A dependency cycle, which is constructible through the UI, made that recursion run forever, and an ensemble used as both source 1 and source 2 was reported twice.
Add dependentDeltaEnsembles(), deltaEnsemblesInUpdateOrder() and wouldCreateDependencyCycle() to RimSummaryEnsembleTools. The traversal uses the PDM back references, deduplicates, is iterative with visited and on-path sets, and returns the delta ensembles in topological order so a delta ensemble is always visited before the delta ensembles using it as a source. Back edges are logged instead of traversed.
Reimplement updateDependentDeltaEnsembles on top of the new traversal and replace RimDeltaSummaryEnsemble::findReferringEnsembles() with dependentDeltaEnsembles() at its four call sites. Back references also find a delta ensemble that is detached from the project tree, which the previous ancestor scan did not.