Activity mirror release lifetime

A terminal activity write releases its unwatched mirror on completion, after its final value has been delivered. Releasing on the value can tear down the write's own upstream subscription before its terminal signal arrives. That ordering stays unchanged.

Completion can nevertheless arrive after the writer's service scope has retired. Resolving IMeshNodeStreamCache from that scope in the completion callback throws ObjectDisposedException. This was captured in ActivityLogAppender.Append during a dependent Monolith test run on 9 September 2026. Passing test assertions did not establish that cleanup was correct.

ActivityLogAppender.PrepareMirrorRelease resolves the cache while preparing the write and returns an action accepting the final status. The completion callback invokes that action without resolving services. The cache itself owns the disposed-state and subscriber checks: a disposed cache or a watched mirror is left alone, and a non-terminal activity never triggers release.

The four framework writers use the same preparation step: ActivityLogAppender.Append, BuildProtocolDriver.FinishActivity, CodeNodeType.FailActivity, and ActivityLogLogger.PublishSnapshotLocked. The existing immediate ReleaseMirrorWhenFinal method remains available for callers with a live scope; it is not the delayed-completion API.

ActivityReleaseLifetimeTest captures cleanup from a real hub, retires that hub and its nested service scope, and then invokes the completion. The original callback fails at the disposed Autofac scope; the prepared callback succeeds. TerminalActivityWriterReleaseTest separately proves that terminal writes still release unwatched mirrors and that a Running status releases nothing.

This fixes the late service lookup. It does not establish that all activity producers join their work before teardown, or resolve the separate node-CRUD and concurrent-logon scheduling issues.

Reconnecting…
The server was updated. Reloading the page to pick up the latest version.