Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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.21.0"
version: "2.22.0"
---

# Netclaw Operations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,55 @@ When something seems wrong with Netclaw itself:
3. Check daemon logs at `~/.netclaw/logs/daemon-{yyyy-MM-dd}.log`
4. Check session logs at `~/.netclaw/logs/sessions/{sanitized-session-id}/session.log`

Log split:

- Daemon-global diagnostics stay in the daemon log (rolled daily, capped
at 10 MB per file).
- Session-owned diagnostics and session output audit trails append to
`~/.netclaw/logs/sessions/{sanitized-session-id}/session.log` —
one file per session, no rotation today (see netclaw-dev/netclaw#919).
- Session log directories use the sanitized session ID (`/`, `.`, spaces,
etc. replaced with `_`). Sub-agent diagnostics roll up into the parent
session's `session.log`; you will not find a separate file for a
sub-agent run.
Log split — one stream, partitioned locally by session:

- A log line that carries a session id (an actor's `WithContext("SessionId", …)`,
a `{SessionId}` message field, or a `SessionId` logging scope) is written to that
session's `session.log` and **not** to `daemon.log`. The partition is by session
id — nothing is duplicated locally.
- `daemon.log` holds only sessionless, daemon-wide lines: startup/config, session
start/stop, and operational **alerts** (e.g. the `provider.unreachable` /
`provider.failover` alert raised when an inference provider goes down — surfaced
here, and to webhooks, by the notification sink). Note the *per-call* failover/retry
log lines emitted while serving a specific session carry that session's id, so they
partition into its `session.log`; the daemon-wide outage signal is the alert in
`daemon.log`. Rolled daily, capped at 10 MB per file.
- The **full** stream (daemon and session lines alike) is also exported to OTEL/Seq
with the session id as an attribute; do the global slicing/distilling on the OTEL
receiver side.
- Session log directories use the sanitized session ID (`/`, `.`, spaces, etc.
replaced with `_`). Each **sub-agent run** writes to its **own** `session.log`,
keyed by its sub-session id (`{parentId}/subagent/{name}/{runId}`, sanitized) —
so a sub-agent's detail stays out of the parent's log and you review it in that
run's own file. The parent's `session.log` keeps the spawn breadcrumbs (requested
→ spawned → completed/failed) as the pointer to each run. In OTEL the sub-agent
lines still carry the parent `SessionId` (so they group under the parent) plus
`SubSessionId` (so they slice by run). No rotation today
(see netclaw-dev/netclaw#919).

What to expect inside `session.log`:

- A single chronological timeline. One actor (`SessionLogActor`) is the
only writer per file, so audit lines and diagnostic lines interleave in
wall-clock order — useful for reading "what happened, in order" without
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.
- 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.
- The session's **full local slice** of the log stream, in wall-clock order: the
conversation audit (`User:`, `Assistant:`, `Thinking:`, `Tool call:`,
`Tool result:`, `Usage:`, `Turn N completed`) interleaved with every operational
line scoped to that session — the LLM pipeline, retries, provider failover, tool
and sub-agent activity (spawn requested → child spawned → completed/failed, plus
guard rejections), memory, etc. One actor (`SessionLogActor`) is the only writer
per file.
- Because the partition is by session id, you usually do **not** need to grep — open
the one file for the session and read top to bottom. To correlate across sessions
or globally, use Seq/OTLP (every line is there with `SessionId` as a field).
- The session-log writer's own failure lines are the one exception: they go to
`daemon.log`, never routed back into the file that just failed.
- Writes are split by kind. The **conversation audit** (User/Assistant/Tool/Usage
lines) is flushed **immediately**, so a hard process death cannot drop the audit
tail. The higher-volume **diagnostics** are **batched** (flushed on a ~1s cadence),
so a recent diagnostic line may lag by up to a second. Individual lines may be
dropped on transient IO errors (a warning lands in `daemon.log`).

What stays in `daemon.log`: only sessionless lines — daemon startup/config, session
lifecycle, and global errors. Debugging one session → read its `session.log`;
debugging a daemon-wide problem → read `daemon.log`.

| Symptom | Check |
|---------|-------|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1678,6 +1678,11 @@ internal sealed class FakeChatClient : IChatClient
public List<IReadOnlyList<ChatMessage>> ReceivedMessages { get; } = [];
public List<IReadOnlyList<string>> ReceivedToolNames { get; } = [];

/// <summary>The <see cref="ChatOptions"/> object passed on each call, so tests can
/// assert the session actor threads a <c>SessionScopedChatOptions</c> carrier through
/// for per-session log correlation.</summary>
public List<ChatOptions?> ReceivedOptions { get; } = [];

public TimeSpan Delay { get; set; } = TimeSpan.Zero;

/// <summary>
Expand Down Expand Up @@ -1778,6 +1783,7 @@ public async Task<ChatResponse> GetResponseAsync(

var messageList = messages.ToList();
ReceivedMessages.Add(messageList);
ReceivedOptions.Add(options);
ReceivedToolNames.Add(options?.Tools?
.Select(t => t is AIFunction f ? f.Name : t.GetType().Name)
.ToList()
Expand Down
242 changes: 0 additions & 242 deletions src/Netclaw.Actors.Tests/Sessions/SidecarDiagnosticsContextTests.cs

This file was deleted.

Loading
Loading