Skip to content

fix(fleet): replace bounded event-log reads with uncapped streaming - #489

Merged
Jason Robert (jrob5756) merged 2 commits into
mainfrom
fix/485-runs-screen-tail-window
Aug 24, 2026
Merged

fix(fleet): replace bounded event-log reads with uncapped streaming#489
Jason Robert (jrob5756) merged 2 commits into
mainfrom
fix/485-runs-screen-tail-window

Conversation

@jrob5756

Copy link
Copy Markdown
Collaborator

Summary

Replaces three separate bounded event-log read windows (512 KiB tail, 512 KiB head recovery, 8 MiB full-log cap) used by the Fleet Manager's Runs/History/run-detail screens with a single uncapped streamed reader (summary.py::stream_event_log), bounded only by the longest single line.

A long-lived or resumed run could outgrow these caps: a real 9.72 MB / 20,361-line log lost its current step and token/cost totals, and a resumed run's second workflow_started event fell outside the head-recovery window, misreporting workflow topology.

  • stream_event_log supports an optional keep_types prefilter so the Runs screen's ~2s poll can skip uninteresting lines via a cheap regex check before JSON-parsing (measured 12.5 ms vs 65 ms unfiltered on the 9.72 MB log).
  • _scan_events is generation-aware: a resumed run's second root workflow_started resets status/gate/open-steps and overwrites topology, but token/cost totals accumulate across generations.
  • history.py now delegates its line-reading to the shared streamed reader and derives started_at from the latest root workflow_started, so a resumed run's duration fallback measures the current attempt.

Closes#485

Jason Robertand others added 2 commits August 24, 2026 11:54
The Runs/History/run-detail screens read a run's JSONL event log
through three separate bounded windows (a 512 KiB tail, a 512 KiB head
recovery read, and an 8 MiB full-log cap). A long-lived or resumed run
outgrows these: a 9.72 MB / 20,361-line log lost its current step and
token/cost totals, and a resumed run's second workflow_started event
fell outside the head window, misreporting topology.
summary.py::stream_event_log is a single streamed reader bounded only
by the longest line, replacing all three windows. It supports a
keep_types prefilter so the Runs screen's ~2s poll can skip
uninteresting lines via regex before JSON-parsing. history.py now
delegates to the same reader and takes the latest root
workflow_started's timestamp for a resumed run's duration fallback.
Applies PR #489 review findings on top of the uncapped event-log
streaming change:
- `_scan_agent_details` (run-detail screen) now resets `open_steps`,
`gated`, and `started_at_by_name` at every root `workflow_started`,
matching `_scan_events`'s existing resume-boundary reset. Previously a
generation killed mid-step (an open step or unresolved gate with no
closing event) would survive across a resume forever, so the Runs
screen correctly reported "running, nothing open" while run-detail
simultaneously reported a dead step as still "at-gate" with a stale,
ever-growing elapsed clock.
- `derive_step_detail`'s consumption loop is extracted into
`_scan_step_events`, returning its five locals in one tuple assigned
atomically inside the caller's `try`/`except OSError`. Previously a
mid-stream `OSError` left `status`/`output`/`activity` holding whatever
a partial scan had accumulated, which was then returned as if it were
authoritative (e.g. a completed step with real output rendering as
"running, no output" forever). The same pass also resets a step whose
`status` was still "running" across a resume boundary to "pending",
and clears `prompt` alongside `output`/`activity` on a restart.
Also applies several review recommendations:
- `derive_run_detail` now prefilters its scan with
`keep_types=_SUMMARY_EVENT_TYPES` (a verified superset of everything
`_scan_agent_details` branches on), cutting its cost roughly 5x.
- Promotes `history.py`'s `_finite_float` NaN/Infinity guard into
`summary.py` and reuses it for token/cost accumulation in both
`_scan_events` and `_scan_agent_details`, so a `NaN`/`Infinity` value
from a corrupted log entry can no longer crash the Runs/run-detail
poll loops.
- Corrects several docstrings left describing the deleted bounded
tail/head/full-log readers or overstating scan frequency/cost
(`RunDetail`, `stream_event_log`'s `keep_types` Args/Raises,
`derive_step_detail`'s Args/Returns, `step_detail.py`, `dag.py`, and
history.py's resume-boundary paragraph).
- Adds regression tests: run-detail generation reset, mid-stream
`OSError` no longer surfacing a partial scan, a nested sub-workflow
start not being mistaken for a resume boundary (both `summary.py` and
`history.py`), a `_scan_agent_details` prefilter-equivalence test, a
NaN/Infinity token/cost regression test, and a strengthened
totals-accumulate-across-generations assertion.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jrob5756
Jason Robert (jrob5756) merged commit 75eceda into mainAug 24, 2026
13 checks passed
@jrob5756
Jason Robert (jrob5756) deleted the fix/485-runs-screen-tail-window branch August 24, 2026 16:54
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(fleet): Runs screen loses step, tokens, cost and progress on long/resumed runs (bounded tail window misses agent_started)

1 participant

@jrob5756