Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b38ce13
refactor(logging): route session logs off the log event, delete the A…
Aaronontheweb Jun 25, 2026
2bd56ea
test(logging): end-to-end proof actor _log routes to session.log via …
Aaronontheweb Jun 25, 2026
b801c2c
Merge branch 'dev' into refactor/unify-session-logging
Aaronontheweb Jun 25, 2026
c9f02e5
refactor(logging): publish session logs explicitly instead of routing…
Aaronontheweb Jun 26, 2026
27e0008
Merge branch 'dev' into refactor/unify-session-logging
Aaronontheweb Jun 26, 2026
9551067
fix(logging): publish routed-skill sub-agent spawn lifecycle to sessi…
Aaronontheweb Jun 26, 2026
748b2bb
feat(logging): correlate LLM chat-client diagnostics to sessions via …
Aaronontheweb Jun 26, 2026
7dace13
refactor(logging): address code-review findings on session-log correl…
Aaronontheweb Jun 26, 2026
dad8fb4
refactor(logging): centralize the "SessionId"/"SubSessionId" log-attr…
Aaronontheweb Jun 26, 2026
fc10a1a
refactor(logging): drop RetryingChatClient's redundant SessionId scope
Aaronontheweb Jun 26, 2026
3409ed7
Merge branch 'dev' into refactor/unify-session-logging
Aaronontheweb Jun 26, 2026
96408ab
refactor(logging): collapse sub-agent spawn breadcrumbs to one fan-ou…
Aaronontheweb Jun 26, 2026
33a708a
feat(logging): partition the log stream by session instead of explici…
Aaronontheweb Jun 29, 2026
38460e3
test(logging): end-to-end partition proof through the real Akka->MEL …
Aaronontheweb Jun 29, 2026
d95ba18
fix(logging): harden the session-partition sink (code-review follow-up)
Aaronontheweb Jun 30, 2026
fb629a3
docs(logging): document the dormant non-streaming retry session-scope…
Aaronontheweb Jun 30, 2026
70d8bf4
refactor(logging): drop the startup buffer + lock for a single volati…
Aaronontheweb Jun 30, 2026
138afba
Merge remote-tracking branch 'origin/refactor/unify-session-logging' …
Aaronontheweb Jun 30, 2026
e0e5776
Merge branch 'dev' into refactor/unify-session-logging
Aaronontheweb Jun 30, 2026
f49a87b
fix(logging): breadcrumb emitters take SubAgentRunId after the merged…
Aaronontheweb Jun 30, 2026
a9b1ea0
feat(logging): give each sub-agent run its own session.log, keyed by …
Aaronontheweb Jun 30, 2026
841dc7c
fix(logging): restore session correlation for sidecar LLM paths (code…
Aaronontheweb Jun 30, 2026
cfcff8f
feat(logging): flush the audit transcript immediately, keep diagnosti…
Aaronontheweb Jun 30, 2026
bebffde
refactor(logging): batch daemon.log flushes + consolidate the /subage…
Aaronontheweb Jun 30, 2026
3ddd796
fix(logging): guard PostStop dispose, fix audit-durability doc, scope…
Aaronontheweb Jun 30, 2026
6395863
fix(logging): keep daemon-infra session-named lines in daemon.log; ca…
Aaronontheweb Jun 30, 2026
8da21e6
Merge branch 'dev' into refactor/unify-session-logging
Aaronontheweb Jun 30, 2026
28c400a
test(logging): address code-quality bot findings (Path.Join, drain-lo…
Aaronontheweb Jul 1, 2026
44f31d6
Merge branch 'dev' into refactor/unify-session-logging
Aaronontheweb Jul 1, 2026
3ec711e
Merge branch 'dev' into refactor/unify-session-logging
Aaronontheweb Jul 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion feeds/skills/.system/files/netclaw-operations/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: netclaw-operations
description: "REQUIRED when the user asks about scheduling, reminders, cron jobs, timers, background jobs, diagnostics, troubleshooting, MCP tools, daemon health, identity updates, or Netclaw capabilities and self-maintenance."
metadata:
author: netclaw
version: "2.18.0"
version: "2.19.0"
---

# Netclaw Operations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,19 @@ What to expect inside `session.log`:
cross-referencing two files.
- Two line shapes: session output audit lines (`User:`, `Assistant:`,
`Thinking:`, `Tool call:`, `Tool result:`, `Usage:`, `Turn N completed`,
etc.) and `Diagnostic:` lines from MEL providers (LLM client, HTTP,
retry middleware) emitted under a session diagnostics scope.
etc.) and `Diagnostic:` lines. A diagnostic line reaches `session.log`
when its log event carries the session id; actor logs are tagged with it
automatically (`SessionId`), so session- and sub-agent-actor lifecycle
lines (startup, guards, timeouts, cancellation, completion) appear here —
routed by the tag on the line, not by any ambient context.
- Best-effort observability. Individual lines may be dropped on transient
IO errors and logged at Debug level in the daemon log; this is not a
transactional audit trail. Cross-check the daemon log for warnings
if a critical line appears missing.
- Sidecar paths (compaction, title generation, sub-agents, memory
distillation) currently bypass the session diagnostics scope, so their
internal diagnostics may not appear in `session.log` even though their
output audit lines do. Tracked in netclaw-dev/netclaw#920.
- Not everything is session-tagged: the LLM-client / HTTP / retry / failover
decorators log their internal diagnostics without a session id, so those
land in `daemon.log` only. For deep LLM-call internals, read `daemon.log`
(filter by the `SessionId` attribute where the line carries one).

| Symptom | Check |
|---------|-------|
Expand Down
242 changes: 0 additions & 242 deletions src/Netclaw.Actors.Tests/Sessions/SidecarDiagnosticsContextTests.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
// -----------------------------------------------------------------------
// <copyright file="SessionDiagnosticsContextTests.cs" company="Petabridge, LLC">
// <copyright file="SubAgentSessionScopeTests.cs" company="Petabridge, LLC">
// Copyright (C) 2026 - 2026 Petabridge, LLC <https://petabridge.com>
// </copyright>
// -----------------------------------------------------------------------
using Netclaw.Configuration;
using Netclaw.Actors.SubAgents;
using Xunit;

namespace Netclaw.Configuration.Tests;
namespace Netclaw.Actors.Tests.SubAgents;

public sealed class SessionDiagnosticsContextTests
public sealed class SubAgentSessionScopeTests
{
[Theory]
[InlineData("ch/thr/subagent/skill", "ch/thr")]
[InlineData("ch/thr", "ch/thr")]
[InlineData("ch/thr/subagent/skill/inner", "ch/thr")]
public void NormalizeSessionId_collapses_subagent_marker_to_parent_id(string input, string expected)
{
Assert.Equal(expected, SessionDiagnosticsContext.NormalizeSessionId(input));
Assert.Equal(expected, SubAgentSessionScope.NormalizeSessionId(input));
}

[Theory]
Expand All @@ -25,6 +25,6 @@ public void NormalizeSessionId_collapses_subagent_marker_to_parent_id(string inp
[InlineData(" ")]
public void NormalizeSessionId_returns_null_for_blank(string? input)
{
Assert.Null(SessionDiagnosticsContext.NormalizeSessionId(input));
Assert.Null(SubAgentSessionScope.NormalizeSessionId(input));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@
namespace Netclaw.Actors.Tests.SubAgents;

/// <summary>
/// Regression coverage for sub-agent spawn observability. A sub-agent's own actor
/// logs go through Akka's async logger bridge, where the diagnostics AsyncLocal is
/// gone, so they never reach the per-session <c>session.log</c>. The spawn lifecycle
/// is instead recorded by parent-side breadcrumbs that must run while the parent
/// session scope is active — otherwise a refused or failed spawn is invisible in the
/// session transcript. These tests assert the scope is active at log time, which is
/// exactly the condition <c>RollingFileLoggerProvider</c> uses to route a line to
/// <c>session.log</c>.
/// Regression coverage for sub-agent spawn observability. The spawn lifecycle is
/// recorded by parent-side breadcrumbs that carry the session id as a structured log
/// field — exactly what <c>RollingFileLoggerProvider</c> reads off the log event to
/// route a line to the per-session <c>session.log</c>. These tests assert each
/// breadcrumb carries the session id; otherwise a refused or failed spawn would be
/// invisible in the session transcript.
/// </summary>
public sealed class SubAgentSpawnObservabilityTests : IDisposable
{
Expand Down Expand Up @@ -113,6 +111,17 @@ public void Log<TState>(
TState state,
Exception? exception,
Func<TState, Exception?, string> formatter)
=> Entries.Add((logLevel, formatter(state, exception), SessionDiagnosticsContext.SessionId));
=> Entries.Add((logLevel, formatter(state, exception), ExtractSessionId(state)));

// Mirror RollingFileLoggerProvider: read the session id off the structured log
// state (the {SessionId} field the breadcrumbs carry), not an ambient context.
private static string? ExtractSessionId<TState>(TState state)
{
if (state is IEnumerable<KeyValuePair<string, object?>> fields)
foreach (var field in fields)
if (field.Key == "SessionId" && field.Value is { } value)
return value.ToString();
Comment thread
github-code-quality[bot] marked this conversation as resolved.
Fixed
return null;
}
}
}
1 change: 0 additions & 1 deletion src/Netclaw.Actors/Memory/MemoryCurationActor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,6 @@ private async Task<CurationDecision> EvaluateSingleAsync(
try
{
using var cts = new CancellationTokenSource(LlmTimeout);
using var diagnosticsScope = SessionDiagnosticsContext.Push(sessionId.Value);

var messages = new List<ChatMessage>
{
Expand Down
11 changes: 5 additions & 6 deletions src/Netclaw.Actors/Protocol/SessionLogDiagnostic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@
// </copyright>
// -----------------------------------------------------------------------
using Akka.Actor;
using Netclaw.Configuration;

namespace Netclaw.Actors.Protocol;

/// <summary>
/// Pre-formatted diagnostic line carried from the MEL logger provider
/// to the <c>SessionLogDispatcher</c>. The provider snapshots
/// <c>SessionDiagnosticsContext.SessionId</c> at log-emit time and embeds
/// it here so the dispatcher routes the line by message field, not by
/// any ambient context that would not flow across actor mailboxes.
/// Pre-formatted diagnostic line carried from the MEL logger provider to the
/// <c>SessionLogDispatcher</c>. The provider reads the session id off each log
/// event's structured state and embeds it here so the dispatcher routes the line
/// by message field, not by any ambient context that would not flow across actor
/// mailboxes.
/// </summary>
public sealed record SessionLogDiagnostic(SessionId SessionId, string Line)
: IWithSessionId, INoSerializationVerificationNeeded;
1 change: 0 additions & 1 deletion src/Netclaw.Actors/Sessions/LlmSessionActor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1737,7 +1737,6 @@ internal static async Task InvokeMemoryExtractionCoreAsync(
try
{
using var cts = new CancellationTokenSource(timeout);
using var diagnosticsScope = SessionDiagnosticsContext.Push(sessionId.Value);
var extractionMessages = new List<AiChatMessage>
{
new(Microsoft.Extensions.AI.ChatRole.System,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ public static async Task ExecuteAsync(
{
using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
cts.CancelAfter(sidecarTimeout);
using var diagnosticsScope = SessionDiagnosticsContext.Push(sessionId.Value);
var observerMessages = new List<AiChatMessage>
{
new(Microsoft.Extensions.AI.ChatRole.System,
Expand Down
Loading
Loading