Severity
Minor — observability gap. Data is not lost (still available in the daemon-side audit log at ~/.netclaw/logs/sessions/{sanitized_id}/session.log), but the per-session log file used by tooling and headless-mode workflows is missing USAGE entries when the session is driven by Termina.
Summary
In TUI (netclaw chat) mode, the per-session log file at ~/.netclaw/logs/signalr-{sessionId}.log does not receive USAGE: in=… cached=… … lines after each LLM turn. In headless -p mode it does.
Impact
- Post-hoc KV cache hit-rate analysis on interactive sessions can't use the per-session signalr log — it stops mid-session whenever the user switches from headless to TUI on the same session ID.
- Tooling and eval scripts that anchor on
^[usage] / USAGE: lines in the per-session log silently get no data from TUI-driven turns.
- Hides regressions in cache-prefix stability when the analyst grabs the "obvious" log file.
Repro
- Start a TUI session:
netclaw chat
- Send 2–3 prompts; let the model respond.
grep "USAGE:" ~/.netclaw/logs/signalr-{sessionId}.log → empty.
- Compare with
netclaw chat -p "…": same file location, USAGE lines are present.
Root cause
src/Netclaw.Cli/HeadlessChannel.cs:268 writes the USAGE: in=… cached=… reasoning=… context_window=… prompt_ms=… predicted_tok_s=… line to the per-session log via Log(log, …).
src/Netclaw.Cli/Tui/ChatPage.cs:443 (the TUI handler for UsageOutput) only updates the live status bar via ViewModel.UsageDisplay.Value = "in=… out=… (…% ctx)". There is no Log(...) call.
Both code paths receive the same UsageOutput DTO from the daemon — the asymmetry is purely client-side persistence.
Suggested fix
Either:
- Mirror the
HeadlessChannel's Log(...) call in ChatPage's UsageOutput handler (write the same line shape to the same per-session log file so existing tooling Just Works), or
- Promote per-session USAGE persistence out of the client into a shared CLI service so both channels write identically without duplication.
Option 1 is the smaller diff; option 2 is the more durable fix.
Notes
The daemon-side audit log (~/.netclaw/logs/sessions/{sanitized_id}/session.log, written by SessionLogActor) does record USAGE in a different format:
[2026-…Z] Usage: in=… out=… cached=… reasoning=… context=…%
So data is recoverable, just not in the format/location that existing scripts expect.
Severity
Minor — observability gap. Data is not lost (still available in the daemon-side audit log at
~/.netclaw/logs/sessions/{sanitized_id}/session.log), but the per-session log file used by tooling and headless-mode workflows is missing USAGE entries when the session is driven by Termina.Summary
In TUI (
netclaw chat) mode, the per-session log file at~/.netclaw/logs/signalr-{sessionId}.logdoes not receiveUSAGE: in=… cached=… …lines after each LLM turn. In headless-pmode it does.Impact
^[usage]/USAGE:lines in the per-session log silently get no data from TUI-driven turns.Repro
netclaw chatgrep "USAGE:" ~/.netclaw/logs/signalr-{sessionId}.log→ empty.netclaw chat -p "…": same file location, USAGE lines are present.Root cause
src/Netclaw.Cli/HeadlessChannel.cs:268writes theUSAGE: in=… cached=… reasoning=… context_window=… prompt_ms=… predicted_tok_s=…line to the per-session log viaLog(log, …).src/Netclaw.Cli/Tui/ChatPage.cs:443(the TUI handler forUsageOutput) only updates the live status bar viaViewModel.UsageDisplay.Value = "in=… out=… (…% ctx)". There is noLog(...)call.Both code paths receive the same
UsageOutputDTO from the daemon — the asymmetry is purely client-side persistence.Suggested fix
Either:
HeadlessChannel'sLog(...)call inChatPage'sUsageOutputhandler (write the same line shape to the same per-session log file so existing tooling Just Works), orOption 1 is the smaller diff; option 2 is the more durable fix.
Notes
The daemon-side audit log (
~/.netclaw/logs/sessions/{sanitized_id}/session.log, written bySessionLogActor) does record USAGE in a different format:So data is recoverable, just not in the format/location that existing scripts expect.