feat(cli): add dotbot logs to view activity and the last crash (#657) - #663
Conversation
There was a problem hiding this comment.
Pull request overview
Adds post-run diagnostics to dotbot by introducing a dotbot logs command for human-readable tailing/following of .bot/.control/activity.jsonl, plus a persisted crash summary (.bot/.control/last-crash.json) written on fatal runtime exits and surfaced via dotbot runtime-status.
Changes:
- Add
dotbot logsCLI command with--tail,--follow, and--lastmodes. - Persist a best-effort crash summary on runtime crash via
Write-DotbotCrashSummary, and surface it fromdotbot runtime-statuswhen the runtime isn’t running. - Add Layer 2 coverage for the new CLI behavior and wire it into
tests/Run-Tests.ps1.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/Test-LogsCLI.ps1 | New Layer 2 tests covering dotbot logs tail/last and runtime-status crash surfacing. |
| tests/Run-Tests.ps1 | Includes the new Logs CLI test in Layer 2 execution and exit-code aggregation. |
| src/runtime/Scripts/Invoke-DotbotProcess.ps1 | Crash trap now writes a persisted crash summary on unexpected termination. |
| src/runtime/Modules/Dotbot.Process/Dotbot.Process.psm1 | Adds Write-DotbotCrashSummary implementation to generate .control/last-crash.json. |
| src/runtime/Modules/Dotbot.Process/Dotbot.Process.psd1 | Exports Write-DotbotCrashSummary. |
| src/cli/runtime-status.ps1 | Surfaces last crash summary when runtime.json is missing or PID is stale. |
| src/cli/logs.ps1 | New dotbot logs command implementation (tail/follow activity.jsonl; show last-crash.json). |
| README.md | Documents the new dotbot logs command. |
| bin/dotbot.ps1 | Adds logs to help output and CLI dispatch. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Accepted follow-up scope is tracked in #670: caught fatal failures writing last-crash.json, true latest-run filtering, and incremental/performance-safe follow mode. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
54e516d to
08b4c6c
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 9 changed files in this pull request and generated no new comments.
Suppressed comments (1)
tests/Test-LogsCLI.ps1:102
dotbot logs --followis a user-facing mode described in the PR/README, but this test suite doesn't exercise it. Adding at least a smoke test (startlogs.ps1 -Follow -PollIntervalMs 100in a backgroundpwsh, append a new JSONL line toactivity.jsonl, assert the appended message appears, then terminate the child) would help prevent regressions in the follow loop/cursor-reset logic.
# -------------------------------------------------------------------
# dotbot logs — tail (mixed-shape rendering)
# -------------------------------------------------------------------
$r = Invoke-Cli -Script $logsScript -WorkDir $projA -CliArgs @('-Tail', '10')
Assert-Equal -Name "logs tail exits 0" -Expected 0 -Actual $r.Code -Message $r.Output
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 10 changed files in this pull request and generated no new comments.
Suppressed comments (3)
src/runtime/Modules/Dotbot.Process/Dotbot.Process.psm1:965
Write-DotbotCrashSummaryrelies on-BotRootfor all path resolution, but the parameter isn't mandatory/validated. If a caller forgets to pass it, the function will silently do nothing (the outer try/catch swallows the resulting errors), which defeats the purpose of persisting crash diagnostics.
Make BotRoot mandatory and non-empty so misuse is caught at the call site (or at least produces a deterministic parameter-binding error in non-trap contexts).
[string]$BotRoot
src/runtime/Modules/Dotbot.Process/Dotbot.Process.psm1:980
Write-DotbotCrashSummaryreads the entire per-process activity log into memory (ReadToEnd) just to take the last$MaxEventslines. If a process has a large.activity.jsonl, this can significantly slow down the crash trap or even OOM, reducing the likelihood that the crash summary is written when it's needed most.
Consider reading only the last chunk of the file (best-effort) before splitting into lines, then taking the last $MaxEvents entries. This keeps runtime/memory bounded while still capturing recent events.
$stream = [System.IO.FileStream]::new($activityPath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
try {
$reader = [System.IO.StreamReader]::new($stream, [System.Text.Encoding]::UTF8)
try { $text = $reader.ReadToEnd() } finally { $reader.Dispose() }
} finally { $stream.Dispose() }
src/cli/logs.ps1:235
- In
--followmode the loop re-reads and re-splits the entireactivity.jsonlon every poll (Read-ActivityLine), which makes CPU/memory usage grow with log size (O(file_size) per poll). For long-running sessions or verbose logs, this can become a noticeable performance/operational issue.
A more scalable approach is to track a byte offset (or line position) and only read newly appended data each iteration, similar to how workflow-run.ps1 streams new process activity events.
while (-not $script:DotbotLogsStopRequested) {
Start-Sleep -Milliseconds $PollIntervalMs
try {
$lines = Read-ActivityLine -Path $activityPath
} catch {
continue
Linked issue
Closes #657
Summary of changes
Adds a
dotbot logsCLI command so operators can inspect a run after itsterminal window has closed.
dotbot logs— tail the last N events (default 50) of.bot/.control/activity.jsonl, human-readable. Tolerates the mixed-shapelog: logging events carry
message, typed state-change events carryfrom/to/reason.--tail N— override the number of events shown.--follow— tail the log live (Ctrl+C stops).--last— print the crash summary from the last fatal exit..bot/.control/last-crash.json(exit reason, last task, last 20 activity events) via a new
Write-DotbotCrashSummaryhelper in theDotbot.Processmodule.dotbot runtime-statussurfaces the last crash summary when the runtimeis not running (missing
runtime.jsonor a stale PID).Kept scoped to #657:
last-crash.jsonis written only from the existingcrash trap (not from foreground CLI exits).
Screenshots / recordings
Testing notes
tests/Test-LogsCLI.ps1(Layer 2, 30 assertions): tail rendering ofboth event shapes,
--taillimiting,--last, runtime-status surfacing,CLI dispatch wiring, empty-state / no-
.bothandling, and theWrite-DotbotCrashSummarywrite path (read back viadotbot logs --last).ProcessDispatch 44/44, ProcessRegistry 20/20; Layer 3 MockClaude 19/19.
(One unrelated Components failure is pre-existing MAX_PATH worktree noise
on Windows.)
To try it: from an initialized project,
dotbot logs,dotbot logs --tail 5,dotbot logs --follow, anddotbot logs --last.Checklist