Skip to content

fix: honor Last-Event-ID on job log stream reconnects - #377

Merged
getappz merged 2 commits into
masterfrom
fix/jobs-stream-reconnect-loop
Jul 31, 2026
Merged

getappz merged 2 commits into
masterfrom
fix/jobs-stream-reconnect-loop

Conversation

@getappz

@getappz getappz commented Jul 31, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Fixes item Extend agentflare cost with --days and --by-project #14: the job log detail view (jobs.html) streams the full log again from the start on every SSE reconnect instead of just resuming — looks like the log is replaying in a loop.
  • GET /api/jobs/:id/stream now tags each SSE event with the cumulative byte offset as its id:, and honors an incoming Last-Event-ID header as the starting offset. Browsers auto-reconnect a dropped EventSource and resend that header, so a reconnect (idle proxy timeout, tab suspend, brief network blip) now only receives bytes past what was already delivered instead of re-tailing from byte 0.
  • No client-side changes needed — EventSource sends Last-Event-ID natively.

Test plan

  • cargo test -p agentflare --bin agentflare -- dashboard::server::tests::jobs_ — 8/8 pass, including new jobs_stream_endpoint_resumes_from_last_event_id regression test
  • cargo check / cargo clippy clean on src/dashboard/server.rs

Summary by CodeRabbit

  • Bug Fixes
    • Improved live job output streaming so reconnecting clients resume from their last received position.
    • Prevented previously displayed output from being replayed after reconnection.

The /api/jobs/:id/stream SSE handler always started tailing the
stdout file at byte 0 on a new connection. Browsers auto-reconnect a
dropped EventSource and resend the last Last-Event-ID they saw, but
since that header was ignored, any reconnect (idle proxy timeout, tab
suspend, brief network blip) re-streamed the whole log from the
start. The client appends rather than replaces, so this looked like
the full log replaying in a loop.

Now each event carries the cumulative byte offset as its id, and a
reconnecting request's Last-Event-ID seeds the starting offset so it
only receives bytes past what it already saw.
@coderabbitai

coderabbitai Bot commented Jul 31, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 128fa36e-ce5a-429e-bf93-7a22ea0ffedf

📥 Commits

Reviewing files that changed from the base of the PR and between 58e7a94 and f07ac7f.

📒 Files selected for processing (1)
  • src/dashboard/server.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/dashboard/server.rs

📝 Walkthrough

Walkthrough

The job stdout SSE endpoint now supports reconnects with Last-Event-ID. It starts reading at the specified byte offset and assigns updated offsets to stdout events. An integration test verifies that reconnects receive only remaining output.

Changes

Stdout SSE resumption

Layer / File(s) Summary
Endpoint resumption and regression coverage
src/dashboard/server.rs
The handler reads a valid Last-Event-ID, resumes stdout from that byte offset, includes updated offsets in SSE event IDs, and tests reconnect behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • getappz/agentflare#375: Both changes modify the job stdout SSE endpoint, with this PR adding Last-Event-ID byte-offset resumption.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: honoring Last-Event-ID during job log stream reconnects.
Description check ✅ Passed The description explains the change, motivation, implementation, and test results; only the optional reviewer notes section is absent.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/jobs-stream-reconnect-loop

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/dashboard/server.rs`:
- Around line 990-997: The current assertions in the stream resume test are too
loose and allow incorrect behavior to pass. Replace the existing assertions that
check for contains and does-not-contain with stricter validations: add an
assertion that the emitted SSE ID equals content.len() to verify the exact
resume boundary is calculated correctly, and add an assertion that the resumed
stream data starts exactly with "from job" to ensure the handler seeks to the
precise offset rather than one byte early or late.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: acee9f77-e335-424c-98ab-e62f3d893f7e

📥 Commits

Reviewing files that changed from the base of the PR and between 3ecbf95 and 58e7a94.

📒 Files selected for processing (1)
  • src/dashboard/server.rs

Comment thread src/dashboard/server.rs
CodeRabbit review: the prior contains/does-not-contain assertions
would still pass with a missing id or an off-by-one seek. Assert the
emitted SSE id equals the exact byte offset and that resumed data
starts precisely at "from job", not one byte early or late.
@getappz
getappz merged commit 725dfc0 into master Jul 31, 2026
16 checks passed
@getappz
getappz deleted the fix/jobs-stream-reconnect-loop branch July 31, 2026 13:24
getappz added a commit that referenced this pull request Aug 15, 2026
…m update (#496)

* fix(mcp): resolve sequence_ids in comment and honour parent_id on item update

comment(create|list) passed item_id straight to the backend, so a bare or
#-prefixed sequence_id reached the INSERT and came back as a raw
"FOREIGN KEY constraint failed". It was the one id-taking tool that never
called resolve_item_id. Both arms now resolve, via a new
resolve_existing_item_id that also proves the item exists so an unknown
UUID fails as invalid_params naming the id instead of leaking SQLite
constraint text.

item(update) accepted parent_id and silently discarded it — UpdateItem had
no such field and the update path never read req.parent_id, so the response
echoed the unchanged parent with no error. UpdateItem now carries
parent_id: Option<Option<String>> (None leaves it alone, Some(None)
detaches, Some(Some(id)) re-parents) and the MCP layer resolves a
sequence_id or UUID the same way it resolves id.

Because update is the only path that can close a parent cycle, the backend
rejects a self-parent or a parent that is already a descendant, bounded so
data that is already cyclic errors instead of looping forever.

The comment tests move to their own tests/comment_tests.rs to keep
action_tests.rs under the LOC gate.

Closes #375, closes #377.

Agentflare-Agent: claude-code
Agentflare-Branch: task/375-comment-mcp-tool-rejects-numeric-sequenc
Agentflare-Item: 375

* fix(mcp): resolve sequence_id parent_id on item create, not just update

item(create)'s parent_id was passed straight through to the INSERT's FK
column unresolved, so a sequence_id or #-prefixed id (which the schema
docstring already advertises as accepted) surfaced a raw "FOREIGN KEY
constraint failed" instead of the named invalid_params error update's
parent_id already gets. Reuse resolve_existing_item_id the same way
item_update does.

Agentflare-Agent: claude-code
Agentflare-Branch: task/375-comment-mcp-tool-rejects-numeric-sequenc
Agentflare-Item: 375

---------

Co-authored-by: shiva <shiva@gosysinfo.tech>
Sign up for free to 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.

1 participant