Skip to content

fix: restore local session result exports - #534

Merged
delete-cloud merged 1 commit into
mainfrom
fix/o6n-product-verification
Jun 4, 2026
Merged

delete-cloud merged 1 commit into
mainfrom
fix/o6n-product-verification

Conversation

@delete-cloud

@delete-cloud delete-cloud commented Jun 4, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • add local-path workspace diff/patch support using a temporary Git index, preserving the existing cloud workspace provider path
  • restore /sessions/{id}/result from persisted runtime events when a restarted process has no live tape turn/current turn id
  • cover linked-worktree local sessions and restart-style persisted runtime event replay

Context

Live o6n verification exposed two product gaps:

  1. local-path sessions could execute real work, but /sessions/{id}/workspace/diff returned 400 Workspace export requires cloud session.
  2. after a deployment restart, session metadata and /runs/{run_id}/display-events persisted, but /sessions/{id}/result returned final_answer=null.

Verification

  • uv run pytest tests/ui/test_http_server.py -k "persisted_runtime_events or workspace_diff or workspace_patch or session_result" -v
  • uv run pytest tests/ui/test_http_server_workspace_transfer.py -v
  • uv run ruff check src/coding_agent/server/http_server.py tests/ui/test_http_server.py
  • uv run ruff format src/coding_agent/server/http_server.py tests/ui/test_http_server.py --check

Notes

  • This does not change cloud workspace provider behavior.
  • Local diff/patch uses GIT_INDEX_FILE with a temporary index and removes it after export, so the real workspace index is not modified.

Summary by CodeRabbit

  • New Features

    • Session results now reliably reconstructed from persisted runtime events when needed
    • Workspace diff and patch endpoints improved with local Git-based computation and automatic fallback
  • Tests

    • Added test coverage for result restoration scenarios
    • Added end-to-end tests for local workspace operations

@coderabbitai

coderabbitai Bot commented Jun 4, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@delete-cloud, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 38 minutes and 36 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 07abd05e-9932-4c1f-8555-ad02a91b57c0

📥 Commits

Reviewing files that changed from the base of the PR and between 9a8eab1 and 8e15e3b.

📒 Files selected for processing (2)
  • coding-agent/src/coding_agent/server/http_server.py
  • coding-agent/tests/ui/test_http_server.py
📝 Walkthrough

Walkthrough

The HTTP server adds runtime-event-based result reconstruction for missing tape and prefers local Git computation for workspace diff/patch operations when the session targets a local path, with corresponding contract tests validating both code paths and fallback to remote export.

Changes

Session Result Recovery and Local Workspace Operations

Layer / File(s) Summary
Import updates and test infrastructure
coding-agent/src/coding_agent/server/http_server.py, coding-agent/tests/ui/test_http_server.py
Standard-library (shutil, tempfile) and LocalPathWorkspaceRef imports added; TurnResult import added; FakeRuntimeReplayStore extended with list_agent_runs(session_id) to support runtime replay test interactions.
Runtime event-based result reconstruction
coding-agent/src/coding_agent/server/http_server.py, coding-agent/tests/ui/test_http_server.py
New helpers parse wire.StreamDelta and wire.ToolCallDelta from up to 1000 persisted runtime events to reconstruct final output text and tool calls; get_session_result endpoint now derives turn_result from runtime events when latest_turn is unavailable; new test verifies reconstruction from empty tape using ordered RuntimeEventRecords.
Local Git-based workspace diff and patch
coding-agent/src/coding_agent/server/http_server.py, coding-agent/tests/ui/test_http_server.py
New helpers validate local workspace is a Git repo and run git diff --cached with temporary GIT_INDEX_FILE, parsing name-status (with rename/copy support) and numstat to produce structured WorkspaceDiff or binary WorkspacePatch; both /workspace/diff and /workspace/patch endpoints now prefer local Git computation when LocalPathWorkspaceRef is available, otherwise fall back to export_workspace_archive; new contract tests verify diff/patch output for Git-mutated local repositories.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant GetSessionResult
  participant LatestTurnCheck
  participant RuntimeEventReplay
  participant TurnResultBuilder
  Client->>GetSessionResult: GET /sessions/{id}/result
  GetSessionResult->>LatestTurnCheck: Check if latest_turn exists
  alt latest_turn is None
    LatestTurnCheck->>RuntimeEventReplay: Replay persisted runtime events
    RuntimeEventReplay->>RuntimeEventReplay: Parse StreamDelta, ToolCallDelta
    RuntimeEventReplay->>TurnResultBuilder: Build content_parts, ToolCallRecords
    TurnResultBuilder->>GetSessionResult: Return reconstructed TurnResult
  else latest_turn exists
    LatestTurnCheck->>GetSessionResult: Return existing turn_result
  end
  GetSessionResult->>Client: Return result with final output & verification summary
Loading
sequenceDiagram
  participant Client
  participant WorkspaceDiffEndpoint
  participant LocalGitCheck
  participant LocalGitUtil
  participant CloudExportFallback
  Client->>WorkspaceDiffEndpoint: GET /sessions/{id}/workspace/diff
  WorkspaceDiffEndpoint->>LocalGitCheck: Check session.default_run_target
  alt LocalPathWorkspaceRef available
    LocalGitCheck->>LocalGitUtil: _local_workspace_diff (via asyncio.to_thread)
    LocalGitUtil->>LocalGitUtil: Validate Git repo, run git diff --cached
    LocalGitUtil->>WorkspaceDiffEndpoint: Return WorkspaceDiff
  else No LocalPathWorkspaceRef
    LocalGitCheck->>CloudExportFallback: export_workspace_archive fallback
    CloudExportFallback->>WorkspaceDiffEndpoint: Return remote diff
  end
  WorkspaceDiffEndpoint->>Client: Return WorkspaceDiff or patch JSON
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • delete-cloud/Coding-Agent-Loop#154: Modifies /sessions/{session_id}/result behavior to derive result from persisted tape/runtime events instead of returning empty fields, directly aligning with this PR's runtime-event reconstruction.
  • delete-cloud/Coding-Agent-Loop#147: Adds client/CLI code to download and display workspace diff/patch responses, directly consuming the diff/patch API contract that this PR implements.
  • delete-cloud/Coding-Agent-Loop#146: Implements git-backed workspace/diff and workspace/patch at the provider level with the same shared workspace models, complementing this PR's local-path Git computation in the server endpoints.

Poem

🐰 A rabbit hops through Git and time,
Recovering lost results sublime—
From runtime's stream, we conjure gold,
While local diffs our workspace unfold. 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: restore local session result exports' accurately reflects the main changes: restoring session result exports for local-path sessions and adding local workspace diff/patch support.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/o6n-product-verification

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@delete-cloud
delete-cloud force-pushed the fix/o6n-product-verification branch from 9a8eab1 to 816c209 Compare June 4, 2026 06:22

@coderabbitai coderabbitai 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.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
coding-agent/src/coding_agent/server/http_server.py (1)

6016-6037: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Populate turn_id from the reconstructed runtime run.

When this endpoint rebuilds turn_result from persisted runtime events after a restart, Line 6031 still returns session.current_turn_id. That leaves callers with a recovered final_answer but a null turn_id, so they cannot correlate the result back to /runs/{id}.

🤖 Prompt for 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.

In `@coding-agent/src/coding_agent/server/http_server.py` around lines 6016 -
6037, The response uses session.current_turn_id even when turn_result was
reconstructed from runtime events, leaving turn_id null for recovered runs;
update the SessionResultResponse construction so turn_id is taken from the
reconstructed result when available (e.g., use turn_id = session.current_turn_id
if turn_result is None else turn_result.run_id or turn_result.turn_id depending
on the attribute on the TurnResult object). Modify the block that builds
SessionResultResponse (where turn_result, verification_summary are computed) to
prefer the turn identifier from turn_result (e.g., turn_result.run_id /
turn_result.turn_id) and fall back to session.current_turn_id.
🤖 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 `@coding-agent/src/coding_agent/server/http_server.py`:
- Around line 5739-5747: The current _session_result_from_runtime_events
function swallows KeyError from
session_manager.load_runtime_run/replay_runtime_events and returns None, which
hides storage failures; change this to not collapse missing persisted runtime
data into a null result by removing the KeyError except-return-None behavior (or
re-raising the caught KeyError) so that failures from
session_manager.load_runtime_run and session_manager.replay_runtime_events
propagate (causing a 500) rather than returning None; locate symbols
_session_result_from_runtime_events, _session_result_runtime_run_id,
session_manager.load_runtime_run, and session_manager.replay_runtime_events to
implement this change.
- Around line 5941-5977: The numstat invocation is not rename-aware or
null-separated, causing mismatches with the rename-aware name-status output;
update _local_workspace_diff to call _run_local_workspace_git for numstat with
the same rename and NUL flags as name-status (include "--find-renames" and "-z"
in the args and adjust the description string), and update
_parse_local_git_numstat to handle NUL-separated output (split on "\0" when NUL
mode is used, then parse each record by tab as before) so additions/deletions
are keyed to the same new path names as _parse_local_git_name_status.
- Around line 5807-5857: The current flow runs git with ambient config and
executes "git add -A" which can invoke repo-defined clean filters and external
diff/textconv drivers; change _run_local_workspace_git to always set
GIT_CONFIG_GLOBAL=/dev/null and GIT_CONFIG_SYSTEM=/dev/null (in addition to
GIT_TERMINAL_PROMPT) to reduce global/system influence, and ensure any git diff
invocations (in _local_workspace_diff / _local_workspace_patch) include
--no-ext-diff and --no-textconv when calling git diff --cached; remove/replace
the unsafe git add -A in _prepare_local_git_result_index (do not call
_run_local_workspace_git with ["add","-A"]) and instead populate the temporary
index using a non-filter-invoking plumbing command such as git update-index
--refresh or writing the index via git read-tree/read-cache or git update-index
--index-info so that clean filters are not executed (modify
_prepare_local_git_result_index to use one of these alternatives and pass the
same temporary GIT_INDEX_FILE env).

In `@coding-agent/tests/ui/test_http_server.py`:
- Around line 6279-6306: The subprocess.run calls in
test_workspace_diff_returns_local_path_git_changes (and the sibling test around
lines 6343-6370) call "git" as a bare command, triggering Ruff S607/S603;
resolve by locating the absolute git executable once (e.g., git_path =
shutil.which("git") and assert it's not None) and replace subprocess.run(["git",
...]) invocations with subprocess.run([git_path, ...]) throughout those tests
(also update any other helper calls in the same tests to use git_path) so PATH
resolution is explicit and Ruff warnings are avoided.

---

Outside diff comments:
In `@coding-agent/src/coding_agent/server/http_server.py`:
- Around line 6016-6037: The response uses session.current_turn_id even when
turn_result was reconstructed from runtime events, leaving turn_id null for
recovered runs; update the SessionResultResponse construction so turn_id is
taken from the reconstructed result when available (e.g., use turn_id =
session.current_turn_id if turn_result is None else turn_result.run_id or
turn_result.turn_id depending on the attribute on the TurnResult object). Modify
the block that builds SessionResultResponse (where turn_result,
verification_summary are computed) to prefer the turn identifier from
turn_result (e.g., turn_result.run_id / turn_result.turn_id) and fall back to
session.current_turn_id.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 924a2c49-03db-4edf-98f0-a59cc6cd7cea

📥 Commits

Reviewing files that changed from the base of the PR and between 249c858 and 9a8eab1.

📒 Files selected for processing (2)
  • coding-agent/src/coding_agent/server/http_server.py
  • coding-agent/tests/ui/test_http_server.py

Add local-path workspace diff/patch support through a temporary Git index, preserving the existing cloud workspace provider path. Restore session results from persisted runtime events when a restarted process has no live turn id or tape turn available.

Verification:

- uv run pytest tests/ui/test_http_server.py -k "persisted_runtime_events or workspace_diff or workspace_patch or session_result" -v

- uv run pytest tests/ui/test_http_server_workspace_transfer.py -v

- uv run ruff check src/coding_agent/server/http_server.py tests/ui/test_http_server.py
@delete-cloud
delete-cloud force-pushed the fix/o6n-product-verification branch from 816c209 to 8e15e3b Compare June 4, 2026 06:36
@delete-cloud
delete-cloud merged commit dcab35c into main Jun 4, 2026
1 check passed
@delete-cloud
delete-cloud deleted the fix/o6n-product-verification branch June 4, 2026 06:42
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