From f1cf5590e5012ab618540d6cc4cdd4c5dcb98aa4 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Thu, 14 May 2026 06:12:36 +0000 Subject: [PATCH 1/2] chore: prepare release 0.18.0 --- Directory.Build.props | 72 ++++++++++++++++++++++++++++++++++------- RELEASE_NOTES.md | 74 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 11 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 75b3ee2b2..259b0576f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -8,28 +8,78 @@ latest enable true - 0.17.2 - Netclaw v0.17.2 — Session stability fixes, MCP reconnection, and schema packaging + 0.18.0 + Netclaw v0.18.0 — Directory-scoped approvals, sub-agent reloads, vLLM support, channel reliability, and security hardening + +**Features** + +* **Directory-scoped shell command approval patterns** — approve a shell command rooted at a parent directory and it automatically applies to all subdirectories, eliminating redundant prompts. Approval entries now use a `(verb, directory)` model with proper path hierarchy. ([#896](https://github.com/netclaw-dev/netclaw/pull/896)) + +* **`netclaw approvals` CLI** — new CLI subcommand for listing, revoking, and managing shell command approvals with a TUI interface. Closes [#921](https://github.com/netclaw-dev/netclaw/issues/921). ([#927](https://github.com/netclaw-dev/netclaw/pull/927)) + +* **vLLM-aware capability resolver + timings split** — the provider system now detects vLLM endpoints and resolves capabilities accordingly, with separate prompt/decode timing metrics. ([#619](https://github.com/netclaw-dev/netclaw/pull/619), [#986](https://github.com/netclaw-dev/netclaw/pull/986)) + +* **File-based sub-agent hot reload** — file-defined sub-agents under `~/.netclaw/agents/` now reload automatically when modified, and spawned sub-agents inherit parent session context (session_dir, project_dir). Invalid edits fail closed. ([#984](https://github.com/netclaw-dev/netclaw/pull/984)) + +* **Serialization marker interface + test-time verification** — new `ISerializableMessage` marker interface with compile-time and test-time verification to catch unregistered serialization types early. Closes [#961](https://github.com/netclaw-dev/netclaw/issues/961). ([#978](https://github.com/netclaw-dev/netclaw/pull/978)) + +* **SearXNG backend hardening** — improved SearXNG search with custom User-Agent, 429 retry logic, configurable timeouts, and 403 detection. ([#914](https://github.com/netclaw-dev/netclaw/pull/914)) + +* **Container/proxy exposure mode relaxation** — daemon no longer rejects `https` exposure mode when bound to loopback, supporting TLS-termination-at-proxy topologies. ([#864](https://github.com/netclaw-dev/netclaw/pull/864)) **Bug Fixes** -* Fixed binding actor stream crashes on shutdown — SignalR, Slack, and Discord binding actors now drain their Akka.Streams pipeline before stopping, eliminating `AbruptTerminationException` / `StreamDetachedException` crash logs on graceful shutdown and idle timeout. ([#895](https://github.com/netclaw-dev/netclaw/pull/895)) +* **Button approvals route by SessionId** — approval button clicks now route via the deterministic session actor path instead of the per-thread binding's in-memory lookup, fixing silently-dropped approvals when channel adapters passivate. Closes [#979](https://github.com/netclaw-dev/netclaw/issues/979). ([#982](https://github.com/netclaw-dev/netclaw/pull/982)) + +* **Thread history hydration** — bot messages from thread history now hydrate only once per actor lifetime, with media properly accounted for in compaction size calculations. ([#990](https://github.com/netclaw-dev/netclaw/pull/990)) + +* **Passivation race fix** — sessions now use a post-snapshot grace window to close the passivation race condition where messages could be lost during shutdown. ([#985](https://github.com/netclaw-dev/netclaw/pull/985)) + +* **ModelReference modality schema alignment** — config schema now matches scalar binding for `ModelReference.Modality`. ([#988](https://github.com/netclaw-dev/netclaw/pull/988), [#989](https://github.com/netclaw-dev/netclaw/pull/989)) + +* **Reminder execution fixes** — removed spurious 5-minute execution timeout that was killing long-running reminders, and prevented duplicate concurrent execution of the same reminder. ([#970](https://github.com/netclaw-dev/netclaw/pull/970)) + +* **Config watcher atomic-replace handling** — file system watcher now handles atomic-replace writes (Renamed events) from editors like VS Code. Closes [#959](https://github.com/netclaw-dev/netclaw/issues/959). ([#960](https://github.com/netclaw-dev/netclaw/pull/960)) + +* **Thread history backfill** — bot messages are now included in thread history backfill, and hydration happens only at thread root. ([#954](https://github.com/netclaw-dev/netclaw/pull/954), [#958](https://github.com/netclaw-dev/netclaw/pull/958)) + +* **Memory provenance** — adopted-context provenance is now separated from third-party memory policy. ([#952](https://github.com/netclaw-dev/netclaw/pull/952)) + +* **`netclaw doctor` warning suppression** — no longer warns about explicitly-configured Personal posture and tool profile. ([#950](https://github.com/netclaw-dev/netclaw/pull/950)) + +* **Systemd PATH fix** — systemd unit now bakes PATH so the shell tool can resolve the `netclaw` CLI. ([#948](https://github.com/netclaw-dev/netclaw/pull/948)) + +* **Thinking-only retry guard** — three-layer SSE/middleware/actor diagnostics prevent retries on thinking-only responses, plus `RollingFileLogger` Debug-level fix. ([#947](https://github.com/netclaw-dev/netclaw/pull/947)) + +* **Watchdog two-phase timeout** — watchdog now consumes `prompt_progress` keepalives from llama.cpp and uses a two-phase timeout to prevent false-positive watchdog kills. ([#946](https://github.com/netclaw-dev/netclaw/pull/946)) + +* **MCP personal mode defaults** — personal mode now defaults all tools to Auto and the toggle grants all tools properly. ([#942](https://github.com/netclaw-dev/netclaw/pull/942), [#943](https://github.com/netclaw-dev/netclaw/pull/943)) + +* **Approval button label truncation** — button labels are now truncated to fit Slack/Discord character limits. Closes [#931](https://github.com/netclaw-dev/netclaw/issues/931). ([#937](https://github.com/netclaw-dev/netclaw/pull/937)) + +* **DailyStatsActor SQLite DDL cleanup** — removed SQLite DDL from `PreStart`, and wizard poll-timeout now surfaces crash logs. Closes [#925](https://github.com/netclaw-dev/netclaw/issues/925). ([#938](https://github.com/netclaw-dev/netclaw/pull/938)) + +* **Session diagnostics logging** — all session diagnostics now route into a single session log file, with sidecar `IChatClient` calls wrapped in `SessionDiagnosticsContext`. ([#916](https://github.com/netclaw-dev/netclaw/pull/916), [#926](https://github.com/netclaw-dev/netclaw/pull/926)) + +* **TUI context window display** — status bar now uses daemon-reported context window size. ([#906](https://github.com/netclaw-dev/netclaw/pull/906), [#913](https://github.com/netclaw-dev/netclaw/pull/913)) -* Fixed thinking-only LLM responses being classified as empty — the empty response guard now checks `TextReasoningContent` in addition to `TextContent`, so models that emit thinking tokens without text (e.g., Qwen 3) are no longer incorrectly retried with a nudge. ([#895](https://github.com/netclaw-dev/netclaw/pull/895)) +* **Wizard Personal posture fix** — init wizard with Personal posture no longer silently disables all features. ([#907](https://github.com/netclaw-dev/netclaw/pull/907)) -* Fixed dropped tool calls from llama.cpp-compatible providers — `OpenAiCompatibleChatClient` now emits accumulated tool calls when `finish_reason` is `"stop"`, not just `"tool_calls"`. ([#895](https://github.com/netclaw-dev/netclaw/pull/895)) +**Security** -* Fixed `netclaw doctor` schema validation failing on `Search.Backend` — schema now matches wire format (`duckduckgo`, `brave`, `searxng`). ([#894](https://github.com/netclaw-dev/netclaw/pull/894)) +* **Filesystem root tilde expansion** — `~` and `$HOME` in configured filesystem roots are now properly expanded. ([#975](https://github.com/netclaw-dev/netclaw/pull/975)) -* Embedded config schema as assembly resource so `netclaw doctor` works in all distribution formats. ([#894](https://github.com/netclaw-dev/netclaw/pull/894)) +* **Skill scanner NoOp for system skills** — swapped skill content scanner to NoOp to unblock system skills from loading. ([#976](https://github.com/netclaw-dev/netclaw/pull/976), [#977](https://github.com/netclaw-dev/netclaw/pull/977)) -* Added `CursorAdvanced` serialization bindings for Slack and Discord binding actors. Closes [#887](https://github.com/netclaw-dev/netclaw/issues/887). ([#890](https://github.com/netclaw-dev/netclaw/pull/890)) +* **Non-loopback Daemon.Host rejection** — Local exposure mode now rejects non-loopback `Daemon.Host` values. ([#901](https://github.com/netclaw-dev/netclaw/pull/901)) -* MCP servers that become unreachable no longer stay permanently stuck — new `McpReconnectionService` with exponential backoff. ([#884](https://github.com/netclaw-dev/netclaw/pull/884)) +* **Reverted trust-zones** — stayed on the simpler `(verb, directory) ApprovalEntry` model instead of the trust-zone approach. ([#962](https://github.com/netclaw-dev/netclaw/pull/962)) -**Documentation** +**Dependencies** -* Refocused README on end-user content with Docker quickstart, netclaw.dev links, and Discord link. ([#888](https://github.com/netclaw-dev/netclaw/pull/888)) +* Bumped `Microsoft.SourceLink.GitHub` from 10.0.203 to 10.0.300. ([#983](https://github.com/netclaw-dev/netclaw/pull/983)) +* Bumped Microsoft platform/AI packages and added Dependabot groups. ([#980](https://github.com/netclaw-dev/netclaw/pull/980)) +* Bumped `Netclaw.SkillClient` from 0.2.1 to 0.3.0. ([#936](https://github.com/netclaw-dev/netclaw/pull/936)) net10.0 diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 9cbf83907..19bb5b0a3 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,77 @@ +#### 0.18.0 2026-05-14 #### + +Netclaw v0.18.0 — Directory-scoped approvals, sub-agent reloads, vLLM support, channel reliability, and security hardening + +**Features** + +* **Directory-scoped shell command approval patterns** — approve a shell command rooted at a parent directory and it automatically applies to all subdirectories, eliminating redundant prompts. Approval entries now use a `(verb, directory)` model with proper path hierarchy. ([#896](https://github.com/netclaw-dev/netclaw/pull/896)) + +* **`netclaw approvals` CLI** — new CLI subcommand for listing, revoking, and managing shell command approvals with a TUI interface. Closes [#921](https://github.com/netclaw-dev/netclaw/issues/921). ([#927](https://github.com/netclaw-dev/netclaw/pull/927)) + +* **vLLM-aware capability resolver + timings split** — the provider system now detects vLLM endpoints and resolves capabilities accordingly, with separate prompt/decode timing metrics. ([#619](https://github.com/netclaw-dev/netclaw/pull/619), [#986](https://github.com/netclaw-dev/netclaw/pull/986)) + +* **File-based sub-agent hot reload** — file-defined sub-agents under `~/.netclaw/agents/` now reload automatically when modified, and spawned sub-agents inherit parent session context (session_dir, project_dir). Invalid edits fail closed. ([#984](https://github.com/netclaw-dev/netclaw/pull/984)) + +* **Serialization marker interface + test-time verification** — new `ISerializableMessage` marker interface with compile-time and test-time verification to catch unregistered serialization types early. Closes [#961](https://github.com/netclaw-dev/netclaw/issues/961). ([#978](https://github.com/netclaw-dev/netclaw/pull/978)) + +* **SearXNG backend hardening** — improved SearXNG search with custom User-Agent, 429 retry logic, configurable timeouts, and 403 detection. ([#914](https://github.com/netclaw-dev/netclaw/pull/914)) + +* **Container/proxy exposure mode relaxation** — daemon no longer rejects `https` exposure mode when bound to loopback, supporting TLS-termination-at-proxy topologies. ([#864](https://github.com/netclaw-dev/netclaw/pull/864)) + +**Bug Fixes** + +* **Button approvals route by SessionId** — approval button clicks now route via the deterministic session actor path instead of the per-thread binding's in-memory lookup, fixing silently-dropped approvals when channel adapters passivate. Closes [#979](https://github.com/netclaw-dev/netclaw/issues/979). ([#982](https://github.com/netclaw-dev/netclaw/pull/982)) + +* **Thread history hydration** — bot messages from thread history now hydrate only once per actor lifetime, with media properly accounted for in compaction size calculations. ([#990](https://github.com/netclaw-dev/netclaw/pull/990)) + +* **Passivation race fix** — sessions now use a post-snapshot grace window to close the passivation race condition where messages could be lost during shutdown. ([#985](https://github.com/netclaw-dev/netclaw/pull/985)) + +* **ModelReference modality schema alignment** — config schema now matches scalar binding for `ModelReference.Modality`. ([#988](https://github.com/netclaw-dev/netclaw/pull/988), [#989](https://github.com/netclaw-dev/netclaw/pull/989)) + +* **Reminder execution fixes** — removed spurious 5-minute execution timeout that was killing long-running reminders, and prevented duplicate concurrent execution of the same reminder. ([#970](https://github.com/netclaw-dev/netclaw/pull/970)) + +* **Config watcher atomic-replace handling** — file system watcher now handles atomic-replace writes (Renamed events) from editors like VS Code. Closes [#959](https://github.com/netclaw-dev/netclaw/issues/959). ([#960](https://github.com/netclaw-dev/netclaw/pull/960)) + +* **Thread history backfill** — bot messages are now included in thread history backfill, and hydration happens only at thread root. ([#954](https://github.com/netclaw-dev/netclaw/pull/954), [#958](https://github.com/netclaw-dev/netclaw/pull/958)) + +* **Memory provenance** — adopted-context provenance is now separated from third-party memory policy. ([#952](https://github.com/netclaw-dev/netclaw/pull/952)) + +* **`netclaw doctor` warning suppression** — no longer warns about explicitly-configured Personal posture and tool profile. ([#950](https://github.com/netclaw-dev/netclaw/pull/950)) + +* **Systemd PATH fix** — systemd unit now bakes PATH so the shell tool can resolve the `netclaw` CLI. ([#948](https://github.com/netclaw-dev/netclaw/pull/948)) + +* **Thinking-only retry guard** — three-layer SSE/middleware/actor diagnostics prevent retries on thinking-only responses, plus `RollingFileLogger` Debug-level fix. ([#947](https://github.com/netclaw-dev/netclaw/pull/947)) + +* **Watchdog two-phase timeout** — watchdog now consumes `prompt_progress` keepalives from llama.cpp and uses a two-phase timeout to prevent false-positive watchdog kills. ([#946](https://github.com/netclaw-dev/netclaw/pull/946)) + +* **MCP personal mode defaults** — personal mode now defaults all tools to Auto and the toggle grants all tools properly. ([#942](https://github.com/netclaw-dev/netclaw/pull/942), [#943](https://github.com/netclaw-dev/netclaw/pull/943)) + +* **Approval button label truncation** — button labels are now truncated to fit Slack/Discord character limits. Closes [#931](https://github.com/netclaw-dev/netclaw/issues/931). ([#937](https://github.com/netclaw-dev/netclaw/pull/937)) + +* **DailyStatsActor SQLite DDL cleanup** — removed SQLite DDL from `PreStart`, and wizard poll-timeout now surfaces crash logs. Closes [#925](https://github.com/netclaw-dev/netclaw/issues/925). ([#938](https://github.com/netclaw-dev/netclaw/pull/938)) + +* **Session diagnostics logging** — all session diagnostics now route into a single session log file, with sidecar `IChatClient` calls wrapped in `SessionDiagnosticsContext`. ([#916](https://github.com/netclaw-dev/netclaw/pull/916), [#926](https://github.com/netclaw-dev/netclaw/pull/926)) + +* **TUI context window display** — status bar now uses daemon-reported context window size. ([#906](https://github.com/netclaw-dev/netclaw/pull/906), [#913](https://github.com/netclaw-dev/netclaw/pull/913)) + +* **Wizard Personal posture fix** — init wizard with Personal posture no longer silently disables all features. ([#907](https://github.com/netclaw-dev/netclaw/pull/907)) + +**Security** + +* **Filesystem root tilde expansion** — `~` and `$HOME` in configured filesystem roots are now properly expanded. ([#975](https://github.com/netclaw-dev/netclaw/pull/975)) + +* **Skill scanner NoOp for system skills** — swapped skill content scanner to NoOp to unblock system skills from loading. ([#976](https://github.com/netclaw-dev/netclaw/pull/976), [#977](https://github.com/netclaw-dev/netclaw/pull/977)) + +* **Non-loopback Daemon.Host rejection** — Local exposure mode now rejects non-loopback `Daemon.Host` values. ([#901](https://github.com/netclaw-dev/netclaw/pull/901)) + +* **Reverted trust-zones** — stayed on the simpler `(verb, directory) ApprovalEntry` model instead of the trust-zone approach. ([#962](https://github.com/netclaw-dev/netclaw/pull/962)) + +**Dependencies** + +* Bumped `Microsoft.SourceLink.GitHub` from 10.0.203 to 10.0.300. ([#983](https://github.com/netclaw-dev/netclaw/pull/983)) +* Bumped Microsoft platform/AI packages and added Dependabot groups. ([#980](https://github.com/netclaw-dev/netclaw/pull/980)) +* Bumped `Netclaw.SkillClient` from 0.2.1 to 0.3.0. ([#936](https://github.com/netclaw-dev/netclaw/pull/936)) + #### 0.17.2 2026-05-06 #### Netclaw v0.17.2 — Session stability fixes, MCP reconnection, and schema packaging From c23120212b83d00d60dc231ca32201f2a4b04577 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Thu, 14 May 2026 13:11:48 +0000 Subject: [PATCH 2/2] fix(sessions): make SessionLogFile.AppendLine deterministic under Windows share-mode races Concurrent readers (test polling, tail-f, AV scan-on-close) hold the file with FileShare.Read, which forbids any FileAccess.Write open regardless of the writer's own share mask. On Windows this surfaces as SHARING_VIOLATION; the actor silently dropped the line at Debug. AppendLine now retries transient IOException/UnauthorizedAccessException with 10/20/40/80ms backoff and uses the canonical FileShare.ReadWrite|Delete mask so concurrent readers and log-directory cleanup don't fight the writer. SessionLogActor's catch-and-log path is escalated Debug -> Warning, since reaching it now means a real audit-line loss. Test workaround that re-Tell'd "second" each polling iteration is removed - one Tell + poll-for-content is sufficient now. --- .../Sessions/SessionLogActorTests.cs | 11 +----- src/Netclaw.Actors/Protocol/SessionLogFile.cs | 36 +++++++++++++++++-- .../Sessions/SessionLogActor.cs | 9 +++-- 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/Netclaw.Actors.Tests/Sessions/SessionLogActorTests.cs b/src/Netclaw.Actors.Tests/Sessions/SessionLogActorTests.cs index b16a84016..829fcdce7 100644 --- a/src/Netclaw.Actors.Tests/Sessions/SessionLogActorTests.cs +++ b/src/Netclaw.Actors.Tests/Sessions/SessionLogActorTests.cs @@ -110,19 +110,10 @@ await AwaitAssertAsync(async () => await ExpectTerminatedAsync(dispatcher1, cancellationToken: TestContext.Current.CancellationToken); var dispatcher2 = SpawnDispatcher(Sys, basePath, timeProvider); + dispatcher2.Tell(new TextOutput { SessionId = sessionId, Text = "second" }, ActorRefs.NoSender); - // Spin the send inside AwaitAssertAsync so each polling iteration - // re-Tells "second". On Windows the SessionLogActor's AppendLine - // can throw IOException (SHARING_VIOLATION) after the previous - // dispatcher's writer closed but before AV / kernel handle - // cleanup completes — the actor's existing catch-and-Debug-log - // contract drops the message in that case. Re-sending each - // iteration keeps trying until a write lands. Multiple landed - // writes are fine; Assert.Contains is duplicate-tolerant. await AwaitAssertAsync(async () => { - dispatcher2.Tell(new TextOutput { SessionId = sessionId, Text = "second" }, ActorRefs.NoSender); - var logFile = SessionLogFile.GetLogPath(sessionId, basePath); Assert.True(File.Exists(logFile)); Assert.Single(Directory.GetFiles(Path.GetDirectoryName(logFile)!, "*.log", SearchOption.TopDirectoryOnly)); diff --git a/src/Netclaw.Actors/Protocol/SessionLogFile.cs b/src/Netclaw.Actors/Protocol/SessionLogFile.cs index 723303a0e..dc2568874 100644 --- a/src/Netclaw.Actors/Protocol/SessionLogFile.cs +++ b/src/Netclaw.Actors/Protocol/SessionLogFile.cs @@ -19,6 +19,16 @@ public static class SessionLogFile { public const string FileName = "session.log"; + // Bounded retry budget for transient Windows file-sharing conflicts. On NTFS + // a concurrent reader holding the file with FileShare.Read (e.g. File.ReadAllText*, + // tail-f tools, Search Indexer, AV scan-on-close) blocks any FileAccess.Write + // open regardless of the writer's own share mask — share-mode intersection is + // bidirectional and the reader's mask must permit Write. The kernel/AV hand-off + // window is typically sub-10ms; 10/20/40/80ms backoff covers the long tail + // without exceeding an actor's per-message processing budget. + private const int MaxAttempts = 4; + private static readonly int[] BackoffMs = [10, 20, 40, 80]; + public static string GetLogsDirectory(SessionId sessionId, string sessionLogsBasePath) { var sanitized = SessionDirectoryHelper.SanitizeSessionId(sessionId); @@ -33,8 +43,28 @@ public static void AppendLine(SessionId sessionId, string sessionLogsBasePath, s var logPath = GetLogPath(sessionId, sessionLogsBasePath); Directory.CreateDirectory(Path.GetDirectoryName(logPath)!); - using var stream = new FileStream(logPath, FileMode.Append, FileAccess.Write, FileShare.Read); - using var writer = new StreamWriter(stream) { AutoFlush = true }; - writer.WriteLine(line); + for (var attempt = 0; ; attempt++) + { + try + { + // FileShare.ReadWrite | FileShare.Delete is the canonical log-file mask: + // it lets concurrent readers (tail, audit consumers, tests) coexist and + // lets log rotation / Directory.Delete proceed on Windows. The + // single-writer invariant is enforced by SessionLogActor's mailbox, + // not by this share mask. + using var stream = new FileStream( + logPath, FileMode.Append, FileAccess.Write, + FileShare.ReadWrite | FileShare.Delete); + using var writer = new StreamWriter(stream) { AutoFlush = true }; + writer.WriteLine(line); + return; + } + catch (Exception ex) when ( + (ex is IOException || ex is UnauthorizedAccessException) + && attempt < MaxAttempts - 1) + { + Thread.Sleep(BackoffMs[attempt]); + } + } } } diff --git a/src/Netclaw.Actors/Sessions/SessionLogActor.cs b/src/Netclaw.Actors/Sessions/SessionLogActor.cs index d848f8ce6..bae5712f7 100644 --- a/src/Netclaw.Actors/Sessions/SessionLogActor.cs +++ b/src/Netclaw.Actors/Sessions/SessionLogActor.cs @@ -64,7 +64,10 @@ private void OnUserMessage(SendUserMessage msg) } catch (Exception ex) { - _log.Debug(ex, "Failed to write user message log entry for {SessionId}", _sessionId.Value); + // AppendLine retries transient IO failures internally; reaching this + // catch means the audit line was lost. Audit-trail loss is a real + // production fault — log loudly, not at Debug. + _log.Warning(ex, "Dropped user message audit line for {SessionId}", _sessionId.Value); } } @@ -102,7 +105,7 @@ private void OnOutput(SessionOutput output) } catch (Exception ex) { - _log.Debug(ex, "Failed to write session log entry for {SessionId}", _sessionId.Value); + _log.Warning(ex, "Dropped session log audit line for {SessionId}", _sessionId.Value); } } @@ -114,7 +117,7 @@ private void OnDiagnostic(SessionLogDiagnostic diagnostic) } catch (Exception ex) { - _log.Debug(ex, "Failed to write diagnostic log entry for {SessionId}", _sessionId.Value); + _log.Warning(ex, "Dropped diagnostic audit line for {SessionId}", _sessionId.Value); } }