diff --git a/Directory.Packages.props b/Directory.Packages.props
index 6f4c89e3b..24137c54f 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -98,9 +98,9 @@
-
-
-
+
+
+
diff --git a/NuGet.Config b/NuGet.Config
index ec08ff52f..a154be3de 100644
--- a/NuGet.Config
+++ b/NuGet.Config
@@ -6,7 +6,6 @@
-
@@ -23,9 +22,6 @@
-
-
-
diff --git a/src/modules/Elsa.Bpmn.Interchange/Elsa.Bpmn.Interchange.csproj b/src/modules/Elsa.Bpmn.Interchange/Elsa.Bpmn.Interchange.csproj
index c5e681073..30716a633 100644
--- a/src/modules/Elsa.Bpmn.Interchange/Elsa.Bpmn.Interchange.csproj
+++ b/src/modules/Elsa.Bpmn.Interchange/Elsa.Bpmn.Interchange.csproj
@@ -5,8 +5,6 @@
Provides BPMN XML interchange support by wiring the Bpmn.Interchange library into Elsa.
elsa extension module bpmn interchange
-
- false
diff --git a/src/modules/Elsa.Bpmn/Elsa.Bpmn.csproj b/src/modules/Elsa.Bpmn/Elsa.Bpmn.csproj
index 79d5dcd3a..d2d8c22f7 100644
--- a/src/modules/Elsa.Bpmn/Elsa.Bpmn.csproj
+++ b/src/modules/Elsa.Bpmn/Elsa.Bpmn.csproj
@@ -5,8 +5,6 @@
Provides BPMN execution support by wiring the Bpmn.Model and Bpmn.Semantics libraries into Elsa.
elsa extension module bpmn
-
- false
diff --git a/test/integration/Elsa.Bpmn.IntegrationTests/Scenarios/HostPort/BpmnCompensationTests.cs b/test/integration/Elsa.Bpmn.IntegrationTests/Scenarios/HostPort/BpmnCompensationTests.cs
index 5945eca35..0f631adba 100644
--- a/test/integration/Elsa.Bpmn.IntegrationTests/Scenarios/HostPort/BpmnCompensationTests.cs
+++ b/test/integration/Elsa.Bpmn.IntegrationTests/Scenarios/HostPort/BpmnCompensationTests.cs
@@ -88,23 +88,34 @@ public class BpmnCompensationTests(ITestOutputHelper testOutputHelper)
// Act: the other branch cancels the transaction, which stops the replay's coordinating token.
await _host.FinishWorkAsync("fraudCheck");
- // Assert: the released entries are registered again, so the cancellation's own replay claims them -- the head
- // handler starting a second time is both the first half of the release being real *and* the symptom of
- // valence-works/bpmn#13: BpmnInterpreter.CancelTransaction drops the live work it is abandoning without ever
- // producing a PendingTeardown/CancelWorkSubtree command for it, so the host's original ledger record and
- // bookmark for the releaseSeat slot survive untouched alongside the replay's freshly started one. The scope
- // now holds two live records for the same (BindingRef, IterationId) slot -- pinned below so this fails the
- // moment the library fix lands, at which point both counts become 1. Filed from here as #7959, closed as
- // routed upstream; the applier is deliberately unpatched so this stays visible rather than worked around.
+ // Assert: the released entries are registered again, so the cancellation's own replay claims them, and the head
+ // handler starts a second time -- the first half of the release being real.
//
- // The flip may not be mechanical. Upstream is choosing between tearing down only the compensation handler it
- // re-starts and tearing down every abandoned token; the second also cancels transaction branches still in
- // flight, which can move other scenarios in this suite. Check which shipped when the Bpmn.Semantics pin moves.
+ // The second start is a *replacement*, not a duplicate, and that is what valence-works/bpmn#13 fixed in
+ // Bpmn.Semantics 0.2.0. CancelTransaction used to abandon the live work it was superseding without issuing a
+ // CancelWorkSubtree for it, so the host's original ledger record and bookmark for the releaseSeat slot
+ // survived alongside the replay's freshly started one -- two live records for one (BindingRef, IterationId)
+ // slot, which BpmnHostSnapshot documents as a host obligation never to allow, and which the interpreter's own
+ // BuildLiveWorkHandles then collapses last-wins. Upstream took the wide fix: every token a cancelled
+ // transaction abandons now gets a real teardown, not just the compensation handler being re-started.
+ //
+ // So all three of these are the fix, and each fails differently if it regresses: the handler runs twice
+ // (release), the first run is torn down (teardown), and the slot is left holding exactly one record
+ // (the invariant).
Assert.Equal(2, _host.Log.Occurrences("executed:releaseSeat"));
+ Assert.Contains("cancelled:releaseSeat", _host.Log.Entries);
var releaseSeatBindingRef = BpmnTestProcesses.BindingRef("releaseSeat");
var releaseSeatLiveRecords = _host.LiveWorkOf("sub").Count(record => record.BindingRef == releaseSeatBindingRef);
- Assert.Equal(2, releaseSeatLiveRecords);
+ Assert.Equal(1, releaseSeatLiveRecords);
+
+ // And the invariant holds at the moment that matters, not just once the dust settles. 0.2.0 states the
+ // at-most-one-live-unit-per-slot rule as holding after a command batch is applied in full and in order,
+ // because an interrupting path may emit the replacement StartWork ahead of the CancelWorkSubtree it
+ // supersedes. This snapshot is taken inside the replacement's own execution -- overwritten on each start, so
+ // it is the second one -- and a host that applied the batch out of order, or keyed its ledger by slot rather
+ // than by handle, would be holding two records here even though the count above settles at one.
+ Assert.Equal([releaseSeatBindingRef], _host.Log.Snapshot("liveWork@releaseSeat"));
// And the entry that was claimed but never ran is reached once that head handler finishes: the other half.
var result = await _host.FinishWorkAsync("releaseSeat");