Skip to content

fix(app): derive review changes from agent edits - #819

Merged
Astro-Han merged 22 commits into
devfrom
pawwork/issue-704-turn-change-aggregate
May 21, 2026
Merged

fix(app): derive review changes from agent edits#819
Astro-Han merged 22 commits into
devfrom
pawwork/issue-704-turn-change-aggregate

Conversation

@Astro-Han

@Astro-Han Astro-Han commented May 21, 2026

Copy link
Copy Markdown
Owner

Summary

Self-check

Allowed legacy/PR-C-owned matches remain:

  • SessionSummary.summarize: compatibility no-op in packages/opencode/src/session/summary.ts.
  • session_diff: legacy storage/session read path in packages/opencode/src/session/session.ts and packages/opencode/src/storage/storage.ts for PR C cleanup.
  • summary.diffs: storage import/migration compatibility only.

Expected new symbols are present:

  • TurnChangeUncapturedTable: schema + turn-change service callers.
  • TurnChangeInvalidated: event definition, turn-change/revert publishers, share watcher.
  • aggregateTurnUnion / aggregateSessionFromTurns: service, summary/server/share callers.
  • isLikelyWriteCommand: bash heuristic + bash tool integration.

Verification

  • bun test test/session/turn-change.test.ts test/session/turn-change-aggregate.test.ts test/tool/bash-write-heuristic.test.ts test/session/revert.test.ts test/session/session-artifacts.test.ts test/share/share-next.test.ts test/session/export.test.ts --timeout 30000 — pass, 144 tests.
  • bun test --preload ./happydom.ts src/context/global-sync/session-cache.test.ts src/context/global-sync/event-reducer.test.ts src/context/global-sync/bootstrap.test.ts src/pages/session/use-session-timeline-data.test.ts src/pages/session/use-session-review-panel.test.tsx src/pages/session/use-session-review-state.test.ts — pass, 72 tests.
  • bun test src/components/session-turn-turn-changes.test.ts && bun run typecheck in packages/ui — pass.
  • bun run typecheck at repo root — pass.
  • git diff --check — pass.
  • bun run test:e2e -- e2e/session/session-review.spec.ts — pass with 3 existing/legacy review-comment cases skipped after Last Turn rendering moved to the timeline surface.

Visual / smoke

  • Visual check: targeted session-review.spec.ts walked the changed Last Turn / review surface in Chromium and verified aggregate rows/actions. The planned session-turn-change snap target does not exist in this repo; no new snap target was added in this PR.
  • Share/cloud relay smoke: external SaaS relay was not exercised locally. Deferred risk: receiver compatibility for session_aggregate should be accepted by review or mitigated by dual-publish / coordinated SaaS schema update before merge if required.

Residual risk

  • Bash write detection is best-effort and does not claim full filesystem auditing.
  • Active revert aggregate cutoff remains message-level; UI uses session.revert.summary while an active revert exists.
  • PR C should remove legacy computeDiff, Session.Event.Diff, session_diff, and summary diff compatibility once AB lands.

Summary by CodeRabbit

  • New Features

    • Added uncaptured change state indicator in review interface for untracked modifications.
  • Improvements

    • Enhanced turn change display with improved file restore state visibility (applied, undone, redo-invalidated).
    • Better detection of file-writing commands during bash execution.
    • Refined session review panel state representation and synchronization.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@Astro-Han has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 33 minutes and 20 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, 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 have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7838c9fc-ce62-47e8-9157-678ebfd55657

📥 Commits

Reviewing files that changed from the base of the PR and between 81ac538 and 0cb9a9b.

⛔ Files ignored due to path filters (1)
  • packages/sdk/js/src/v2/gen/types.gen.ts is excluded by !**/gen/**
📒 Files selected for processing (14)
  • packages/app/src/pages/session/session-aggregate-files.ts
  • packages/app/src/pages/session/use-session-review-state.test.ts
  • packages/app/src/pages/session/use-session-review-state.ts
  • packages/app/src/pages/session/use-session-timeline-data.ts
  • packages/opencode/src/server/instance/session.ts
  • packages/opencode/src/session/revert.ts
  • packages/opencode/src/session/turn-change.ts
  • packages/opencode/src/tool/bash-write-heuristic.ts
  • packages/opencode/test/server/turn-change-aggregate-routes.test.ts
  • packages/opencode/test/session/session-artifacts.test.ts
  • packages/opencode/test/session/turn-change-aggregate.test.ts
  • packages/opencode/test/share/share-next.test.ts
  • packages/opencode/test/tool/bash-write-heuristic.test.ts
  • packages/opencode/test/tool/bash.test.ts
📝 Walkthrough

Walkthrough

This PR migrates session diff storage from arrays of snapshot file diffs to a new turn-change aggregation model using a discriminated union type. It replaces session_diff[sessionID]: SnapshotFileDiff[] with turn_change_aggregate[sessionID]: SessionDiffResponse, introduces write-command heuristics to track uncaptured edits, and updates frontend/backend consumers to derive turn changes and review diffs from the aggregated source instead of snapshot snapshots.

Changes

Turn-change aggregation and state migration

Layer / File(s) Summary
State shape and type migration
packages/app/src/context/global-sync/{types.ts, sync.tsx, child-store.ts, session-cache.ts, bootstrap.test.ts}, packages/ui/src/context/data.tsx
State now uses turn_change_aggregate: Record<sessionID, SessionDiffResponse | undefined> instead of session_diff arrays; imports SessionDiffResponse from SDK; event-reducer and session-cache logic updated to clear/drop turn_change_aggregate entries.
Backend turn-change aggregation
packages/opencode/src/session/{turn-change.ts, summary.ts, session.ts, session.sql.ts}, packages/opencode/src/server/instance/session.ts
Extends TurnChange service with recordUncaptured, aggregateTurnUnion, and aggregateSessionFromTurns; adds TurnChangeUncapturedTable and TurnChangeInvalidated event; refactors SessionSummary.diff() to return TurnChangeAggregate discriminated union (empty/captured/uncaptured/mixed) instead of array; updates API routes to use AggregateSchema.
Event-driven invalidation
packages/opencode/src/session/{revert.ts, turn-change.ts}, packages/app/src/context/global-sync/event-reducer.ts, packages/opencode/src/share/share-next.ts
Replaces Session.Event.Diff with TurnChangeInvalidated; revert/unrevert/cleanup now publish invalidation instead of persisting diffs; event-reducer clears aggregates on invalidation; share-next subscribes to invalidation and enqueues aggregated session payloads.
Write-command heuristics
packages/opencode/src/tool/{bash-write-heuristic.ts, bash.ts}
New isLikelyWriteCommand detects shell write operations (redirects, file ops, build/install commands); bash tool calls turnChange.recordUncaptured for untracked writes when expected_outputs is empty.
Frontend aggregation helpers
packages/app/src/pages/session/{use-session-timeline-data.ts, use-session-review-state.ts}
New aggregateFiles and aggregateFileCount helpers normalize SessionDiffResponse into SnapshotFileDiff[] by filtering applied restores; wire turn_change_aggregate into review state, timeline diffs, and comment routing instead of session_diff.
Turn-change display model
packages/app/src/pages/session/session-turn-changes.tsx, packages/ui/src/components/session-turn-{changes.ts, changes-panel.tsx, turn.css}
Refactor TurnChangeDisplay from fixed object to discriminated union keyed by kind (empty, uncaptured, captured, mixed); add per-file restoreState; update panel to derive visibility/actions from union variants and per-file state; add CSS for uncaptured label and restore-state indicators.
Data context and i18n
packages/ui/src/context/data.tsx, packages/ui/src/i18n/{en.ts, zh.ts}
Update Data context to expose turn_change_aggregate instead of session_diff; add i18n keys for uncaptured/superseded labels.
Export and sanitization
packages/opencode/src/session/{export.ts, message-v2.ts}
Remove diffs from exported session/message summaries; make User message diffs field optional.
E2E tests
packages/app/e2e/{inputs/select-review-filter.spec.ts, session/session-review.spec.ts, snap/session-turn-changes.snap.ts}
Update E2E tests to use aggregateFiles helper for polling applied diff files; add snap test for captured/uncaptured scenarios; adjust review panel tests to use new aggregation and session-turn-change-item locators.
Unit tests
packages/opencode/test/session/*.test.ts, packages/opencode/test/share/share-next.test.ts, packages/opencode/test/tool/bash*.test.ts, packages/ui/src/components/session-turn-turn-changes.test.ts
Update test stubs to return { kind: "empty", sessionID } shape; add comprehensive aggregate reader and union aggregation tests; update share-next tests for invalidation-driven sync; add bash-write-heuristic tests and update bash tool tests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related issues

  • #704: Fixes the review panel to use turn_change_aggregate (via the new aggregation APIs) instead of lastUserMessage()?.summary?.diffs, so external git state drift no longer pollutes the Last Turn view or session summary.

Possibly related PRs

  • Astro-Han/pawwork#408: Introduced the original turn-change undo/redo system; this PR refactors aggregation to use discriminated union shapes and adds uncaptured file tracking on top.
  • Astro-Han/pawwork#435: Implements and consumes the new per-turn turn-change aggregation APIs alongside this PR.
  • Astro-Han/pawwork#561: Related bash tool changes for declared artifact protocol and turnChange.recordWrite; this PR adds uncaptured fallback heuristic on top.
  • Astro-Han/pawwork#436: Prior refactor of use-session-review-state.ts to be directory-scoped; this PR rewires it to use aggregated diffs.
  • Astro-Han/pawwork#676: Extracted session turn changes UI work; this PR updates panel/actions to use the new kind-based discriminated union and per-file restoreState.

🐰 In aggregate form, turn-change takes flight,
No more snapshot drift in the review light,
Applied or undone, each file now clear,
Write heuristics catch what once disappeared. 🎯

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pawwork/issue-704-turn-change-aggregate

@github-actions github-actions Bot added app Application behavior and product flows ui Design system and user interface harness Model harness, prompts, tool descriptions, and session mechanics P2 Medium priority labels May 21, 2026

@github-actions github-actions 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.

Suggested priority: P2 (includes user-path files (packages/app/src/components/prompt-input/comment-routing.ts, packages/app/src/context/global-sync/bootstrap.test.ts, packages/app/src/context/global-sync/child-store.ts, packages/app/src/context/global-sync/event-reducer.test.ts, packages/app/src/context/global-sync/event-reducer.ts, packages/app/src/context/global-sync/session-cache.test.ts, packages/app/src/context/global-sync/session-cache.ts, packages/app/src/context/global-sync/types.ts, packages/app/src/context/sync.tsx, packages/app/src/pages/session.tsx, packages/app/src/pages/session/session-turn-changes.tsx, packages/app/src/pages/session/use-session-review-panel.tsx, packages/app/src/pages/session/use-session-review-state.ts, packages/app/src/pages/session/use-session-timeline-data.test.ts, packages/app/src/pages/session/use-session-timeline-data.ts)).

P1/P0 are reserved for maintainer confirmation. Please relabel manually if this is a release blocker, security issue, data-loss risk, or updater/runtime failure.

@Astro-Han Astro-Han added the bug Something isn't working label May 21, 2026

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request refactors session change tracking by introducing a turn-based aggregation system and a new table to track uncaptured shell activity. The review identifies a critical performance bottleneck caused by an N+1 query pattern during session-level aggregation and a regression where file changes are concatenated rather than consolidated per path. Additionally, the feedback points out fragile parsing logic in the revert summary heuristic and a redundant database query in the turn aggregation implementation.

Comment thread packages/opencode/src/session/turn-change.ts
Comment thread packages/opencode/src/session/turn-change.ts Outdated
Comment thread packages/opencode/src/session/revert.ts
Comment thread packages/opencode/src/session/turn-change.ts Outdated
@github-actions

github-actions Bot commented May 21, 2026

Copy link
Copy Markdown

Perf delta summary

Comparator: pass

Profile / Scenario interaction median interaction worst long task max tbt frame gap p95 frame gap max jank count cls status
default / homepage-cold 48 -> 32 (-16) 72 -> 64 (-8) 78 -> 76 (-2) 28 -> 26 (-2) 50 -> 16.8 (-33.2) 166.7 -> 183.4 (+16.7) 5 -> 3 (-2) 0 -> 0 (0) pass
default / long-session-input-lag 48 -> 48 (0) 64 -> 80 (+16) 0 -> 0 (0) 0 -> 0 (0) 16.8 -> 16.7 (-0.1) 16.8 -> 16.8 (0) 0 -> 0 (0) 0 -> 0 (0) pass
default / session-streaming-long 48 -> 56 (+8) 80 -> 80 (0) 0 -> 0 (0) 0 -> 0 (0) 16.8 -> 16.8 (0) 33.4 -> 33.4 (0) 0 -> 0 (0) 0 -> 0 (0) pass
default / tool-call-expand 16 -> 16 (0) 24 -> 16 (-8) 0 -> 0 (0) 0 -> 0 (0) 16.8 -> 16.8 (0) 16.8 -> 16.8 (0) 0 -> 0 (0) 0 -> 0 (0) pass
default / tool-default-open-heavy-bash 24 -> 24 (0) 24 -> 24 (0) 66 -> 67 (+1) 16 -> 17 (+1) 33.4 -> 33.4 (0) 166.6 -> 166.6 (0) 2 -> 2 (0) 0 -> 0 (0) pass
default / terminal-side-panel-open 56 -> 48 (-8) 56 -> 48 (-8) 0 -> 0 (0) 0 -> 0 (0) 33.3 -> 33.4 (+0.1) 33.3 -> 49.9 (+16.6) 0 -> 0 (0) 0 -> 0 (0) pass
default / session-scroll-reading 32 -> 32 (0) 32 -> 48 (+16) 0 -> 0 (0) 0 -> 0 (0) 33.4 -> 33.4 (0) 33.4 -> 33.4 (0) 0 -> 0 (0) 0 -> 0 (0) pass
low-end / session-scroll-reading-long 112 -> 72 (-40) 112 -> 104 (-8) 161 -> 145 (-16) 593 -> 404 (-189) 50 -> 33.4 (-16.6) 166.7 -> 166.7 (0) 21 -> 17 (-4) 0.011 -> 0.011 (0) pass
low-end / session-timeline-recompute 232 -> 240 (+8) 280 -> 248 (-32) 215 -> 226 (+11) 539 -> 504 (-35) 200 -> 199.9 (-0.1) 200 -> 216.7 (+16.7) 4 -> 4 (0) 0.497 -> 0.397 (-0.1) pass
low-end / concurrent-shimmer-extreme 0 -> 0 (0) 0 -> 0 (0) 0 -> 0 (0) 0 -> 0 (0) 16.7 -> 16.7 (0) 16.8 -> 16.8 (0) 0 -> 0 (0) 0 -> 0 (0) pass

@Astro-Han

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 5

Caution

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

⚠️ Outside diff range comments (1)
packages/opencode/src/server/instance/session.ts (1)

685-707: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Align route payload with declared aggregate schema.

Line 685 declares TurnChange.AggregateSchema, but Line 707 can return null (result ?? null). That breaks the API contract on the “no data” path and can break typed clients.

🤖 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 `@packages/opencode/src/server/instance/session.ts` around lines 685 - 707, The
route declares TurnChange.AggregateSchema but currently returns null on "no
data" (result ?? null), breaking the contract; update the handler that calls
TurnChange.Service.get (inside AppRuntime.runPromise) to never return null:
either change the response to return a proper AggregateSchema-compliant payload
when found and return an appropriate HTTP error (e.g., 404) or use
c.json(result) only when result is defined and call
c.notFound()/c.status(404).json(...) when undefined; do not return null unless
you also update TurnChange.AggregateSchema to allow nulls.
🧹 Nitpick comments (3)
packages/app/src/pages/session/use-session-timeline-data.ts (2)

144-149: ⚡ Quick win

Effect lacks guard against repeated fetch requests.

This effect will call sync.session.diff(id) every time any tracked dependency changes while turn_change_aggregate[id] remains undefined. If the async fetch is slow or fails silently, this could trigger multiple in-flight requests.

Consider adding a loading guard or tracking in-flight requests to prevent redundant calls.

♻️ Proposed guard pattern
+  const diffFetching = new Set<string>()
+
   createEffect(() => {
     const id = input.routeSessionID()
     if (!id) return
     if (input.sync.data.turn_change_aggregate[id] !== undefined) return
+    if (diffFetching.has(id)) return
+    diffFetching.add(id)
-    void input.sync.session.diff(id)
+    void input.sync.session.diff(id).finally(() => diffFetching.delete(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 `@packages/app/src/pages/session/use-session-timeline-data.ts` around lines 144
- 149, The effect calling createEffect currently triggers
input.sync.session.diff(id) repeatedly while
input.sync.data.turn_change_aggregate[id] is undefined; add a guard to track
in-flight fetches (e.g., a Set or Boolean map) so you only call
input.sync.session.diff(id) if there is not already a pending request for that
id, mark the id as in-flight before calling diff and clear it on
success/failure, and reference the existing symbols createEffect,
input.routeSessionID(), input.sync.data.turn_change_aggregate[id], and
input.sync.session.diff(id) when implementing the guard.

41-53: ⚡ Quick win

Duplicate aggregateFiles implementation across files.

The aggregateFiles helper is defined identically in both use-session-timeline-data.ts (lines 41-53) and use-session-review-state.ts (lines 22-34). Consider extracting this to a shared utility to maintain consistency and reduce duplication.

♻️ Proposed extraction

Create a shared helper, e.g., in @/utils/aggregate-files.ts:

import type { SessionDiffResponse, SnapshotFileDiff } from "`@opencode-ai/sdk/v2/client`"

export function aggregateFiles(aggregate: SessionDiffResponse | undefined): SnapshotFileDiff[] {
  if (!aggregate) return []
  if (aggregate.kind === "empty" || aggregate.kind === "uncaptured") return []
  return aggregate.files
    .filter((file) => file.restoreState === "applied")
    .map((file) => ({
      file: file.openPath ?? file.path,
      patch: file.patch ?? "",
      additions: file.additions ?? 0,
      deletions: file.deletions ?? 0,
      status: file.status,
    }))
}

Then import from both files.

Also applies to: 22-34

🤖 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 `@packages/app/src/pages/session/use-session-timeline-data.ts` around lines 41
- 53, The aggregateFiles function is duplicated; extract it into a single
exported utility named aggregateFiles that accepts a SessionDiffResponse |
undefined and returns SnapshotFileDiff[], then replace the local copies in both
hooks with imports of that utility; ensure the new utility imports the types
SessionDiffResponse and SnapshotFileDiff from the SDK and preserves the existing
behavior (return [] for falsy/empty/uncaptured, filter restoreState ===
"applied", and map file/openPath, patch, additions, deletions, status with
defaults).
packages/app/src/pages/session/use-session-timeline-data.test.ts (1)

30-31: 💤 Low value

Simplify the capturedAggregate helper type annotation.

The type SessionDiffResponse & { kind: "captured" } is narrowed but the function just returns the input. A cleaner approach:

-const capturedAggregate = (files: SessionDiffResponse & { kind: "captured" }) => files
+const capturedAggregate = (input: { kind: "captured"; sessionID: string; files: ReturnType<typeof appliedAggregateFile>[] }): SessionDiffResponse => input

Or simply inline the object literals in tests since the helper doesn't add much value.

🤖 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 `@packages/app/src/pages/session/use-session-timeline-data.test.ts` around
lines 30 - 31, The helper capturedAggregate unnecessarily annotates its
parameter as SessionDiffResponse & { kind: "captured" } while merely returning
the input; replace it with a simpler identity signature (e.g., const
capturedAggregate = <T extends { kind: "captured" }>(x: T) => x) or remove the
helper and inline the object literals in tests where used to keep types inferred
and reduce verbosity; update usages of capturedAggregate accordingly (refer to
the capturedAggregate helper in use-session-timeline-data.test.ts).
🤖 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 `@packages/app/src/pages/session/use-session-review-state.ts`:
- Around line 278-292: The createEffect callback is silently swallowing errors
and can compare against a stale captured scope; update the handler in
createEffect to (1) replace the empty .catch(() => {}) with an error handler
that logs or reports the error (include the caught error object and context like
sessionID and scope) instead of swallowing it, and (2) ensure the effect
re-triggers when executionScope changes or avoid stale comparisons by not
capturing scope once — either add input.executionScope() as a dependency to the
effect or re-read executionScope() inside the .then() before calling
shouldApplyExecutionResult; reference the createEffect, scope variable,
input.executionScope(), input.sdk.createClient(...).session.diff, and
shouldApplyExecutionResult symbols when making the change.

In `@packages/opencode/src/session/revert.ts`:
- Around line 49-57: The extraction using lastIndexOf(" b/") in revert.ts misses
quoted diff headers; update the parsing in the function containing
currentFile/files/line (where lastIndexOf(" b/") is used) to use a regex that
matches both quoted and unquoted `b/` paths (e.g. capture group for the path
after b/ allowing an optional surrounding double quote), strip any trailing
quote, trim, then set currentFile and add to files as before; ensure you replace
the lastIndexOf(" b/") branch with this regex-based extraction so paths with
spaces wrapped in quotes are correctly included.

In `@packages/opencode/src/session/turn-change.ts`:
- Around line 924-931: The mutedFiles list can contain duplicate entries for the
same file path because mutedRows may include multiple restore rows for one path;
before mapping/returning mutedFiles (constructed from mutedRows, using
toDisplay, openPath, restoreState and checked against appliedPaths), deduplicate
by row.data.path (or by the resulting displayFile.path) so capturedAggregate
emits only one AggregateFile per path—update the mutedFiles construction to
filter or reduce duplicates (e.g., use a Map or Set keyed by path) prior to the
final .map/.filter step.

In `@packages/opencode/test/session/session-artifacts.test.ts`:
- Around line 95-99: You recorded an uncaptured turn with
TurnChange.recordUncaptured({ sessionID: uncaptured.id, messageID: assistant })
but never finalized it, so the test may not exercise the uncaptured-aggregate
path; after creating the uncaptured change call TurnChange.finalize(...) for
that session/message (using the same sessionID and messageID returned by
makeAssistant) before asserting SessionSummary.artifacts({ sessionID:
uncaptured.id }) to ensure the uncaptured turn is finalized and the
empty-artifacts path is actually tested.

In `@packages/opencode/test/tool/bash.test.ts`:
- Around line 541-543: The test currently sets command to a PowerShell
Set-Content or a fallback `printf` for non-PowerShell shells, which fails when
the active shell is cmd.exe (no printf); change the non-PowerShell branch to
detect cmd-style shells and use `echo hello > <file>` for cmd.exe and keep
`printf 'hello\n' > <file>` for POSIX shells. Locate the assignment to `command`
(the `PS.has(sh()) ? ... : ...` expression) and update the fallback to inspect
sh() or the shell indicator and choose `echo` for cmd and `printf` for others,
using the existing `quote(target.replaceAll("\\", "/"))` and proper escaping.
Ensure the new branch preserves newline behavior and works cross-platform in the
test.

---

Outside diff comments:
In `@packages/opencode/src/server/instance/session.ts`:
- Around line 685-707: The route declares TurnChange.AggregateSchema but
currently returns null on "no data" (result ?? null), breaking the contract;
update the handler that calls TurnChange.Service.get (inside
AppRuntime.runPromise) to never return null: either change the response to
return a proper AggregateSchema-compliant payload when found and return an
appropriate HTTP error (e.g., 404) or use c.json(result) only when result is
defined and call c.notFound()/c.status(404).json(...) when undefined; do not
return null unless you also update TurnChange.AggregateSchema to allow nulls.

---

Nitpick comments:
In `@packages/app/src/pages/session/use-session-timeline-data.test.ts`:
- Around line 30-31: The helper capturedAggregate unnecessarily annotates its
parameter as SessionDiffResponse & { kind: "captured" } while merely returning
the input; replace it with a simpler identity signature (e.g., const
capturedAggregate = <T extends { kind: "captured" }>(x: T) => x) or remove the
helper and inline the object literals in tests where used to keep types inferred
and reduce verbosity; update usages of capturedAggregate accordingly (refer to
the capturedAggregate helper in use-session-timeline-data.test.ts).

In `@packages/app/src/pages/session/use-session-timeline-data.ts`:
- Around line 144-149: The effect calling createEffect currently triggers
input.sync.session.diff(id) repeatedly while
input.sync.data.turn_change_aggregate[id] is undefined; add a guard to track
in-flight fetches (e.g., a Set or Boolean map) so you only call
input.sync.session.diff(id) if there is not already a pending request for that
id, mark the id as in-flight before calling diff and clear it on
success/failure, and reference the existing symbols createEffect,
input.routeSessionID(), input.sync.data.turn_change_aggregate[id], and
input.sync.session.diff(id) when implementing the guard.
- Around line 41-53: The aggregateFiles function is duplicated; extract it into
a single exported utility named aggregateFiles that accepts a
SessionDiffResponse | undefined and returns SnapshotFileDiff[], then replace the
local copies in both hooks with imports of that utility; ensure the new utility
imports the types SessionDiffResponse and SnapshotFileDiff from the SDK and
preserves the existing behavior (return [] for falsy/empty/uncaptured, filter
restoreState === "applied", and map file/openPath, patch, additions, deletions,
status with defaults).
🪄 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: 69896e87-76a5-4fbd-8c43-dfc810931108

📥 Commits

Reviewing files that changed from the base of the PR and between a272e58 and 81ac538.

⛔ Files ignored due to path filters (1)
  • packages/sdk/js/src/v2/gen/types.gen.ts is excluded by !**/gen/**
📒 Files selected for processing (51)
  • packages/app/e2e/inputs/select-review-filter.spec.ts
  • packages/app/e2e/session/session-review.spec.ts
  • packages/app/e2e/snap/session-turn-changes.snap.ts
  • packages/app/src/components/prompt-input/comment-routing.ts
  • packages/app/src/context/global-sync/bootstrap.test.ts
  • packages/app/src/context/global-sync/child-store.ts
  • packages/app/src/context/global-sync/event-reducer.test.ts
  • packages/app/src/context/global-sync/event-reducer.ts
  • packages/app/src/context/global-sync/session-cache.test.ts
  • packages/app/src/context/global-sync/session-cache.ts
  • packages/app/src/context/global-sync/types.ts
  • packages/app/src/context/sync.tsx
  • packages/app/src/pages/session.tsx
  • packages/app/src/pages/session/session-turn-changes.tsx
  • packages/app/src/pages/session/use-session-review-panel.tsx
  • packages/app/src/pages/session/use-session-review-state.ts
  • packages/app/src/pages/session/use-session-timeline-data.test.ts
  • packages/app/src/pages/session/use-session-timeline-data.ts
  • packages/opencode/migration/20260521052709_turn_change_uncaptured/migration.sql
  • packages/opencode/migration/20260521052709_turn_change_uncaptured/snapshot.json
  • packages/opencode/src/server/instance/session.ts
  • packages/opencode/src/session/export.ts
  • packages/opencode/src/session/message-v2.ts
  • packages/opencode/src/session/revert.ts
  • packages/opencode/src/session/session.sql.ts
  • packages/opencode/src/session/session.ts
  • packages/opencode/src/session/summary.ts
  • packages/opencode/src/session/turn-change.ts
  • packages/opencode/src/share/share-next.ts
  • packages/opencode/src/tool/bash-write-heuristic.ts
  • packages/opencode/src/tool/bash.ts
  • packages/opencode/test/server/turn-change-aggregate-routes.test.ts
  • packages/opencode/test/session/compaction.test.ts
  • packages/opencode/test/session/id-monotonicity.test.ts
  • packages/opencode/test/session/processor-effect.test.ts
  • packages/opencode/test/session/processor-rate-limit.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/opencode/test/session/session-artifacts.test.ts
  • packages/opencode/test/session/snapshot-tool-race.test.ts
  • packages/opencode/test/session/turn-change-aggregate.test.ts
  • packages/opencode/test/share/share-next.test.ts
  • packages/opencode/test/tool/bash-write-heuristic.test.ts
  • packages/opencode/test/tool/bash.test.ts
  • packages/ui/src/components/session-turn-changes-panel.tsx
  • packages/ui/src/components/session-turn-changes.ts
  • packages/ui/src/components/session-turn-turn-changes.test.ts
  • packages/ui/src/components/session-turn.css
  • packages/ui/src/components/timeline-playground.stories.tsx
  • packages/ui/src/context/data.tsx
  • packages/ui/src/i18n/en.ts
  • packages/ui/src/i18n/zh.ts

Comment thread packages/app/src/pages/session/use-session-review-state.ts
Comment thread packages/opencode/src/session/revert.ts Outdated
Comment thread packages/opencode/src/session/turn-change.ts Outdated
Comment thread packages/opencode/test/session/session-artifacts.test.ts
Comment thread packages/opencode/test/tool/bash.test.ts Outdated
@Astro-Han
Astro-Han merged commit 012f639 into dev May 21, 2026
32 checks passed
@Astro-Han
Astro-Han deleted the pawwork/issue-704-turn-change-aggregate branch May 21, 2026 11:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app Application behavior and product flows bug Something isn't working harness Model harness, prompts, tool descriptions, and session mechanics P2 Medium priority ui Design system and user interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Review panel last-turn shows N files / additions / deletions from the PR being reviewed, not from agent edits

1 participant