3559ea67ef
Two unbounded per-session leaks that never released after a workflow ended: - SessionOrchestrator.artifactContentCache (full file contents, keyed "<sessionId>:<path>") grew for the process lifetime. Added evictArtifactContentCache(sessionId) — session-prefix key removal, rehydrate-safe — called on both completeWorkflow and failWorkflow. cancellations was already evicted on both terminal paths. - NarrationSubscriber leaked a Channel + worker coroutine + lanes map entry per session forever. closeLane() now closes the lane channel on WorkflowCompleted/WorkflowFailed (draining the terminal narration first); the worker self-removes from lanes when its for-loop exits on close. Deferred: SqliteEventStore.subscriptions eviction — removing the SharedFlow mid-life would strand LiveArtifactRepository's downstream collector (a suspended-coroutine leak worse than the tiny empty-SharedFlow entry). Green: :core:kernel compile+detekt, :apps:server *NarrationSubscriber* (8).