Skip to content

relayburn-sdk: collapse parse_claude_session onto run_incremental (#346)#417

Merged
willwashburn merged 2 commits into
mainfrom
claude/submit-pr-open-issue-OKISn
May 11, 2026
Merged

relayburn-sdk: collapse parse_claude_session onto run_incremental (#346)#417
willwashburn merged 2 commits into
mainfrom
claude/submit-pr-open-issue-OKISn

Conversation

@willwashburn

Copy link
Copy Markdown
Member

Summary

Last remaining cleanup from #346. PR #371 already landed items 2–6 plus
the codex/opencode half of item 1, but explicitly deferred the claude
side because the two parsers disagreed on trailing in-progress turns:

  • ParseState (non-incremental) emits assistants without a
    stop_reason.
  • run_incremental skips them and backs end_offset up so the next
    resume call re-reads them.

This PR collapses the two by threading an emit_in_progress: bool flag
through run_incremental:

  • false → existing incremental semantics (skip + back up end_offset).
  • true → use cursor_offset for end_offset and emit every working
    record, matching the prior ParseState::finish contract.

parse_claude_session_with_counter now builds a
ParseIncrementalOptions { start_offset: Some(0), .. }, calls
run_incremental(.., true), and converts via a new
From<ParseIncrementalResult> for ParseResult. The 333-line
ParseState struct + impl, the non-incremental record_root and
collect_explicit_claude_relationships helpers, and
derive_file_session_id are deleted. Net -374 LoC in
reader/claude.rs.

Test plan

  • cargo build -p relayburn-sdk clean
  • cargo test --workspace (all 781 tests pass, including the 53
    claude reader tests covering full-parse output, incremental
    output, and the resume cursor)
  • cargo fmt --check on crates/relayburn-sdk/src/reader/claude.rs
    clean

Closes #346.

https://claude.ai/code/session_01FvHFirtBvP9m4jWjVcB6v7


Generated by Claude Code

Last remaining cleanup from #346: the non-incremental claude parser kept
its own `ParseState` machinery in parallel with `run_incremental`, both
reading the same JSONL shape but along separate codepaths. PR #371
deferred this because the two paths disagreed on trailing in-progress
turns (incremental skips them; non-incremental emits them).

This commit adds an `emit_in_progress` flag to `run_incremental`:

- false (incremental callers): keep the existing "back up end_offset to
  the earliest in-progress message and skip those turns" behavior, so
  the next resume call re-reads them.
- true (full-parse caller): use `cursor_offset` for end_offset and emit
  every working record. This matches the prior `ParseState::finish`
  output, including assistants without a `stop_reason`.

`parse_claude_session_with_counter` now builds a
`ParseIncrementalOptions { start_offset: Some(0), .. }` and calls
`run_incremental(.., true)`, then converts via a new
`From<ParseIncrementalResult> for ParseResult`. The ParseState struct,
its impl, the `record_root` / `collect_explicit_claude_relationships`
non-incremental helpers, and `derive_file_session_id` are gone — net
-374 LoC in claude.rs.

The full workspace test suite (claude reader + 600+ other tests) passes
unchanged, validating that the single-shot output matches what
`ParseState` produced.
@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: dd777b47-3452-4924-958d-ca890e6ebac7

📥 Commits

Reviewing files that changed from the base of the PR and between ad3ea7b and c1409cd.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • crates/relayburn-sdk/src/reader/claude.rs
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md

📝 Walkthrough

Walkthrough

This PR consolidates duplicate parsing logic by refactoring parse_claude_session_with_counter to delegate to the incremental parser run_incremental with emit_in_progress=true, eliminating a redundant ParseState codepath. A From<ParseIncrementalResult> for ParseResult conversion wires the result types. Claude system tool/status field-key matching is expanded for robustness. Minor refactors preserve behavior throughout.

Changes

Parse Session Unification

Layer / File(s) Summary
Type Conversion
crates/relayburn-sdk/src/reader/claude.rs
Adds From<ParseIncrementalResult> for ParseResult trait implementation wiring turns, content, events, relationships, tool_result_events, user_turns, and evidence fields.
Sync Path Delegation
crates/relayburn-sdk/src/reader/claude.rs
parse_claude_session_with_counter now builds ParseIncrementalOptions (start_offset=0, last_user_text=None) and delegates to run_incremental(..., emit_in_progress=true), then converts result via From trait.
Incremental Parser Signature and Behavior
crates/relayburn-sdk/src/reader/claude.rs
run_incremental accepts new emit_in_progress parameter. When true, trailing partial JSON lines are processed so end_offset keeps cursor_offset and emits all seen content; when false, it backs up to the earliest incomplete boundary and skips emitting working records without stop_reason. Incremental entrypoint updated to call run_incremental(..., emit_in_progress=false).
Parser Field-Key Expansion
crates/relayburn-sdk/src/reader/claude.rs
build_claude_system_tool_result_event expands tool_use_id key set; claude_system_event_status expands status-field key variants (status, state, result, terminal variants) for normalized lookup.
Minor Refactors and Formatting
crates/relayburn-sdk/src/reader/claude.rs
UTF-8 trimming in prescan_nodes, relationship continuation/evidence accessor refactors, resume-marker token extraction, collect_errored_tool_use_ids formatting, and other call-formatting/line-break cleanups—logic preserved.
Tests / Regression
crates/relayburn-sdk/src/reader/claude.rs
Adds unit test asserting parse_claude_session emits final turn when JSONL ends with a complete JSON object lacking a trailing \n.
Changelog Documentation
CHANGELOG.md
Documents parse_claude_session delegation to incremental parser at offset 0, noting removal of duplicate ParseState codepath while keeping observable behavior unchanged.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • AgentWorkforce/burn#293: Both PRs modify the Claude parser implementation and parsing entrypoints (parse_claude_session / parse_claude_session_incremental and related types).
  • AgentWorkforce/burn#372: Related changes to run_incremental/prescan and JSONL trailing/partial-line handling that overlap with this PR's stream behavior adjustments.
  • AgentWorkforce/burn#352: Related refactor that consolidates alias-aware string_field helpers and influences Claude parsing/accessor handling.

Poem

🐰 I hopped through bytes and trimmed the fray,
One parser now leads the parsing play.
Incremental whispers, sync nods along,
JSON lines sing the same old song.
Cleaner paths — a carrot-fueled refactored day.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: collapsing parse_claude_session onto run_incremental, which is the primary objective of the PR.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the motivation, implementation approach, and testing performed.
Linked Issues check ✅ Passed The PR addresses objective 1 from #346 by collapsing non-incremental parse_claude_session to delegate to run_incremental; other objectives (2-6) remain unaddressed in this PR.
Out of Scope Changes check ✅ Passed All code changes relate to objective 1 from #346 (collapsing the claude parser). Minor refactors like UTF-8 trimming, accessor reformatting, and test updates are within scope.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 claude/submit-pr-open-issue-OKISn

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

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

⚠️ 1 issue in files not directly in the diff

⚠️ Single-shot parse silently drops final line if file lacks trailing newline (crates/relayburn-sdk/src/reader/claude.rs:2171-2173)

The old ParseState codepath used BufReader::read_line, which returns the final line at EOF even without a trailing \n. The new delegation to run_incremental uses read_until(b'\n') with an explicit guard at crates/relayburn-sdk/src/reader/claude.rs:2171 that breaks when line_buf.last() != Some(&b'\n'). For the incremental path (emit_in_progress = false) this is correct — the next call re-reads from cursor_offset. But for the full single-shot parse (emit_in_progress = true), there is no subsequent call, so a syntactically complete JSON line that happens to lack a trailing newline (e.g. file truncated mid-write, or writer hadn't flushed the \n yet) is permanently and silently lost. The CHANGELOG states "Behavior is unchanged" but this is a semantic difference from the prior implementation.

View 3 additional findings in Devin Review.

Open in Devin Review

Devin Review flagged that switching `parse_claude_session_with_counter` to
delegate to `run_incremental` regressed one edge case: the prior
`ParseState` path used `BufReader::read_line`, which surfaces the final
line at EOF even without a trailing `\n`. `run_incremental`'s
`read_until(b'\n')` loop guards against partial trailing lines because
the incremental caller resumes at `cursor_offset` next tick — but a
single-shot full parse has no next tick, so a syntactically complete
final JSON line lacking `\n` (mid-write truncation, unflushed writer)
was silently dropped.

Fix: gate the partial-line `break` on `!emit_in_progress`. When the
single-shot caller asks us to emit in-progress records, treat a
non-newline-terminated tail as a complete line and bump `cursor_offset`
past its body so the per-record offset filters still admit it.

Regression test `full_parse_emits_final_line_without_trailing_newline`
writes simple-turn.jsonl with the trailing `\n` stripped and asserts the
turn still lands.
@willwashburn willwashburn merged commit 6a6263a into main May 11, 2026
11 checks passed
@willwashburn willwashburn deleted the claude/submit-pr-open-issue-OKISn branch May 11, 2026 18:02
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.

Rust refactor: reader code cleanup (parser dup, helper consolidation, dead let_ blocks)

2 participants