Skip to content

fix(opencode): keep OpenCode 2 panes Working across plugin reloads - #23700

Merged
brennanb2025 merged 6 commits into
mainfrom
brennanb2025/fix-opencode2-done-while
Sep 29, 2026
Merged

brennanb2025 merged 6 commits into
mainfrom
brennanb2025/fix-opencode2-done-while

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 4 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​319 $\color{#cf222e}{\Huge{\mathbf{−}}}$​2 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​317
Prod 5 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​59 $\color{#cf222e}{\Huge{\mathbf{−}}}$​18 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​41

ELI5

OpenCode 2 restarts a plugin (and the plugins loaded after it) whenever its file changes, but the agent keeps working through the restart. When Orca's status plugin was shut down for the restart, it told Orca "this session is finished", so the pane showed Done while the agent was still running. Orca also rewrote that plugin file every time you opened any terminal, which is what kept causing the restarts.

Now, under OpenCode 2, the plugin says nothing about a session when it is shut down, and Orca only rewrites the plugin file when its contents actually changed. OpenCode 1 keeps its old behaviour, because there a shutdown really does stop every running turn.

What Changed

Before (user): with an OpenCode 2 agent mid-turn, opening a terminal anywhere in Orca flipped that agent's pane to Done with an unread bell. It stayed Done until the turn really ended or something else refreshed it.

After (user): the pane stays Working for the whole turn and turns Done when OpenCode finishes the turn.

Mechanism, before:

  • OpenCode 2 runs one background server for every OpenCode pane, and Orca's status plugin runs inside it. When a plugin file's modification time changes, the server disposes that plugin and every plugin loaded after it and sets them up again, while sessions keep running.
  • The plugin's dispose published a final Idle, or re-published whatever another plugin instance still tracked. On a reload that was a false "turn ended". The re-created instance had no record of the running turn, so nothing corrected the pane until the turn's own end event.
  • OpenCodeHookService.buildPtyEnv ran on every PTY spawn and rewrote the plugin file unconditionally, both in OpenCode's global config folder and in the per-user overlay. So each new terminal changed the plugin file and triggered that reload.

Mechanism, after:

  • Under OpenCode 2, dispose (status-plugin-factory-source.ts) releases only its own bookkeeping: tracked busy owners, pending blockers and any unsent preview. It cancels its retry and publishes nothing. The pane keeps its last delivered state, and the next lifecycle event from the re-created instance settles it. A turn's end reports Done, and a question or permission prompt reports waiting.
  • The generated plugin file is one module that both OpenCode versions load: OpenCode 1 through server(), OpenCode 2 through setup(). So the choice is made at runtime: the OpenCode 2 setup() entry point (status-plugin-setup-source.ts) tells the shared factory that sessions outlive disposal. The OpenCode 1 server() path keeps main's disposal exactly: a final Idle, or a hand-off to a surviving instance.
  • The plugin file is written only when the installed bytes differ, both locally (hook-service.ts) and in the SSH relay's install into the remote OpenCode config folder (opencode-canonical-config.ts), through one shared check (src/shared/opencode-installed-plugin.ts). In the global config folder and the relay install, the check compares the bytes of the file a symlink points to, because OpenCode 2 loads through symlinks and reads the target's modification time; a user whose plugin file is a per-file dotfile symlink no longer gets a write through the link, and a reload, on every spawn. Only the per-user overlay also requires a regular file, because a symlink there mirrors a user entry, never Orca's file. When the bytes differ, each site writes exactly as before (the global folder writes through a link; the relay and overlay replace it). Each installer skips the write inside a guarded block rather than returning early, so any later install step in the same function still runs. Opening a terminal, or launching another OpenCode pane over SSH, no longer rewrites Orca's plugin file. (With a user-set OPENCODE_CONFIG_DIR, Orca still re-links the user's own plugins into its overlay on each spawn, as on main. A symlink or hard link keeps the original file's modification time, so this does not reload anything; the one exception is Windows when a hard link fails (for example across drives) and Orca copies the file instead, which can still trigger a reload, but that reload no longer shows a false Done.)

Why

  • Disposal is not a session outcome. It means this observer stopped watching, not that the work ended. Claiming Idle there is the same mistake as reading loss of contact as process death. Whether a turn ended comes from the session's own start and end events. Whether the agent process ended comes from Orca's existing ended-process reconciliation, which the terminal shell integration drives when the agent command exits. So dropping the dispose Idle removes a second, weaker "process ended" signal. It does not remove the only one.
  • OpenCode 1 keeps the dispose Idle because there it is true. OpenCode 1 disposes a plugin only when it tears the whole instance down (for example after a provider login or a config change while the TUI stays open), and that teardown cancels every running session. The cancelled run's own idle event can race the plugin's unsubscribe and never arrive, so without the dispose Idle the pane would stay Working until the next prompt.
  • Rejected: keep the Idle and re-derive busy after setup. OpenCode 2's plugin context has no way to ask which sessions are still running (session exposes get, wait, hook, and so on, but no status list). A reloaded instance would have to guess, and in the meantime the pane has already flashed Done and fired an unread bell.
  • Rejected: hand the dying instance's busy records to the next one. A reload disposes every instance before the first new one exists, so there is nothing to hand them to. It would also add cross-instance ownership rules to fix a problem that goes away once dispose stops making claims.
  • Write-if-changed stops the self-inflicted reloads. Rewriting identical bytes on every spawn had no benefit and restarted Orca's plugin and every plugin loaded after it, including the user's own. Comparing content before writing is the usual way to install a plugin file.
  • Bug class removed: any OpenCode 2 plugin reload during a turn caused a false Done: an Orca terminal opening, an Orca update rewriting the plugin, or the user editing their own plugins. The first trigger is gone, and the rest are now harmless.

Known weaknesses:

  • If OpenCode really unloads the plugin, and not as part of a reload, while a turn is running and the process stays alive, the pane keeps its last state until the next session event or until the agent command exits. Before, it jumped to Done. I did not find a way to trigger that path in OpenCode 2.
  • If a status post was still waiting to be retried at dispose time, it is re-sent at the next lifecycle event, not by a timer.
  • If an OpenCode 2 turn ends during a plugin reload, in the gap after the old plugin stops listening and before the new one subscribes, the new plugin never sees the end event. The pane then stays Working until that session's next event or until the agent command exits. The gap is a few milliseconds: OpenCode finishes delivering every event the old plugin already received before it disposes it, and the test rig measured about 1 to 3 ms from dispose to the next setup. Orca no longer causes reloads, so this needs an Orca update or a user plugin edit to land at the same moment a turn ends. A server plugin cannot ask OpenCode 2 which sessions are still running, so this PR cannot re-check the state after a reload. The per-pane plugin in fix(opencode): report OpenCode 2 status from each pane's own TUI #23722 re-reads each session's status when it starts, which closes this gap for OpenCode 2 panes. Before this PR the dispose Idle covered the gap by accident, at the cost of the false Done on every reload.

Linked Issue

Linear STA-8629 (OpenCode2 shows done while still working)

Visual Proof

Real OpenCode 2.0.14 in an isolated dev Orca. In each shot the agent is in the middle of sleep 40.

Before (main): a plain terminal was opened while the agent was sleeping. The OpenCode 2 server reloaded its plugins, and within 2 seconds the main row shows a green check and an unread bell even though the agent is still running.

before: pane shows Done mid-turn

After (this branch): opening a terminal no longer reloads the plugins. To test the dispose change on its own, a reload was forced mid-turn by editing the installed plugin file. The main row keeps its working spinner through the reload and turns Done only after OpenCode reports the turn finished.

after: pane stays Working through a mid-turn plugin reload

Testing

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

Live, macOS, real opencode 2.0.14 through Orca's agent launcher, with a recorder plugin logging every OpenCode event:

  • Before: tool started 13:20:56, plugins disposed and set up again at 13:21:08 when a terminal was opened, pane went Done at 13:21:10, and the turn actually ended at 13:21:39.
  • After, new terminal mid-turn: no plugin reload. The pane stayed Working until the turn ended (13:35:43) and went Done at 13:35:44.
  • After, forced reload mid-turn: plugins disposed and set up again at 13:36:22. The pane stayed Working until the turn ended (13:37:00) and went Done at 13:37:01, reported by the re-created plugin instance.

Automated:

  • The seven existing tests that pin OpenCode 1's disposal ("dispose publishes Idle", "dispose re-publishes the survivor") are unchanged from main.
  • New host tests (hook-plugin-dispose-host.test.ts) run the generated module through each entry point: server() disposal mid-turn publishes Idle, and an OpenCode 2 setup() reload mid-turn publishes nothing while the re-created instance reports Done when the turn ends.
  • Three tests cover write-if-changed for the global plugin file, the overlay plugin file and the SSH relay's install. They check the file's modification time, so they fail without the fix on any filesystem.
  • Symlink tests: a symlinked plugin with current bytes is left untouched (target modification time unchanged) in the global folder and the relay install; a stale one is written through (global) or replaced (relay); a unit test pins that only the overlay check refuses a symlink.
  • Ablations: dropping the OpenCode 2 flag at setup(), or making the factory ignore it, fails both OpenCode 2 reload tests; forcing OpenCode 2 behaviour onto server() fails 9 tests (the seven OpenCode 1 disposal tests and both new OpenCode 1 host tests); requiring a regular file in the global/relay check fails 3 symlink tests; letting the overlay check follow symlinks fails 1.
  • 344 tests pass (1 skipped) across src/main/opencode, src/main/opencode2, src/main/mimo/hook-service, the relay plugin-overlay and OpenCode tests, the shared file-compare and installed-plugin tests, and the PTY env tests that mention OpenCode.
  • pnpm tc:node and check:code-quality:changed pass.

Not run: Windows, Linux and SSH. The plugin change only edits the generated JavaScript source, which is identical on every host and has no platform branches. SSH and WSL panes run the same plugin source. The install change is a byte compare (plus lstat in the overlay), which behaves the same on every platform.

Review

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • Security: no new inputs. The plugin just does less work on an OpenCode 2 dispose.
  • Cross-platform: see Testing.
  • SSH: the remote host runs the same generated plugin, and the relay's install on the remote skips the write when the plugin is already current.
  • Mobile: mobile reads the same host status store, so no change there.
  • Backwards compatibility: no wire or schema change. An older plugin file already installed is replaced once, because its bytes differ.
  • Performance: opening a terminal no longer rewrites a ~50 KB file or restarts OpenCode plugins.

Out of scope, seen during QA:

  • With two OpenCode panes in the same folder, status is shown on the pane that started the shared OpenCode server. This is the same-folder session attribution problem and is unchanged by this PR.
  • OpenCode 2.0.14 names its per-step events session.step.* and session.tool.*. The plugin's lifecycle mapping expects session.next.* for those, so they never mark a pane busy. This PR does not rely on them.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

OpenCode 2 disposes and re-sets-up every plugin whenever its plugins dir
changes, while sessions keep running. The status plugin published a final
Idle on dispose, so a pane read Done mid-turn. Orca also rewrote the plugin
file on every PTY spawn, so opening any terminal triggered that reload.

Dispose now releases the factory's bookkeeping without publishing a
verdict; the next lifecycle event settles the pane, and Orca's ended-process
reconciliation still retires panes whose agent exited. The plugin file is
written only when its bytes differ.
…stall too

The relay's canonical-config install still unlinked and rewrote the status plugin on every OpenCode launch over SSH, which restarts every plugin in a remote OpenCode 2 server. Share one install-currency check (lstat + the existing byte comparison) between the local and relay writers, and pin write-if-changed with mtime so the tests also fail on filesystems that reuse a freed inode.
@brennanb2025
brennanb2025 force-pushed the brennanb2025/fix-opencode2-done-while branch from 85ca09b to 75c16dc Compare September 28, 2026 23:43
… down

OpenCode 1 disposes a plugin only when it tears the instance down, and that
teardown cancels every running session, so the Idle published on dispose is
true there; the cancelled run's own idle may never reach the plugin. Only
OpenCode 2 disposes on a hot reload while turns keep running. The generated
module serves both hosts, so the OpenCode 2 setup() entry point now tells the
shared factory that sessions outlive disposal; the server() path keeps the
previous disposal behaviour, including the hand-off to a surviving factory.
OpenCode 2 loads plugins through file-level symlinks and reads the revision
from the target's mtime, so a user whose Orca plugin file is a symlink (per-file
dotfile managers) failed the regular-file check and got a write through the
link, and a reload, on every spawn. The config-dir and relay installs now skip
the write when the resolved target already has Orca's bytes; when stale they
behave as before. Only the per-source overlay keeps the regular-file check,
since a link there mirrors a user entry. Installers also skip the write inside
a guarded block rather than returning early, so later install steps still run.
…eighbours

Creating a file symlink on Windows needs Developer Mode or admin rights.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review status: ready for human review

Verdict: the fix does what the ticket asks. An OpenCode 2 pane now stays Working for the whole turn when a terminal opens or the plugin reloads, and shows Done when the turn really ends. Three review rounds, an architecture review, and start and end release-readiness checks are done. The last review round and the final readiness check came back clean, with nothing blocking.

Commits added during review

  • 75c16dcb2b3: the SSH relay also skips rewriting the plugin file when it is unchanged. Before, launching a second OpenCode pane on an SSH host still made the shared OpenCode server reload its plugins mid-turn.
  • d1b44361fda: OpenCode 1 keeps its final Idle when a plugin instance is disposed. In OpenCode 1, disposal happens only when the whole instance is torn down, and that cancels every running turn, so the Idle is true there. Only OpenCode 2 disposes plugins on a hot reload while turns keep running, so only OpenCode 2 publishes nothing on dispose. The OpenCode 2 setup() entry point passes a flag to tell the two apart at runtime.
  • 8a906351e6d: if Orca's plugin file is a symlink (for example from a dotfile manager), Orca compares the bytes of the file it points to. Before, Orca wrote through the symlink on every terminal spawn, which changed the target's modification time and reloaded OpenCode's plugins. Each install step now skips its write in a guarded block instead of returning early.
  • 5b84e2b48cc and 66b75769ba9: test-only. The new symlink tests are skipped on Windows like the tests around them, and one test no longer uses a type assertion (this fixes the changed-code quality check).
  • The branch was also rebased onto main to resolve a conflict with fix(opencode): stop OpenCode 2 loading a stale plugin from the retired shared hooks dir #23500.

Live test on a separate Mac with real OpenCode 2.0.18

Each build ran as a hidden dev app with its own isolated home folder and its own OpenCode background service. main = d0db35c18cb, this PR = 8a906351e6d (the two later commits change tests only).

Scenario main this PR
Normal turn Working, then Done Working, then Done
New terminal opened mid-turn Done mid-turn; plugin rewritten and reloaded Stays Working; plugin not rewritten, no reload; Done at the end
Plugin reload forced mid-turn Done mid-turn Stays Working through a real reload; Done at the end
Quit OpenCode mid-turn Leaves Working within 3 s (agent row removed) Leaves Working within 6 s (shows Interrupted); not stuck
Permission prompt, then reload, then answer Prompt state lost on reload; shows Done during the rest of the turn Prompt state survives the reload; stays on Needs permission after the answer until the turn ends (see Known limits)

Opening a terminal while the agent runs sleep 40. main shows a false Done:

main: false Done mid-turn after opening a terminal

This PR keeps the Working spinner:

PR: stays Working after opening a terminal

Plugin reload forced mid-turn. main shows Done while sleep 40 is still running:

main: Done mid-turn after a forced plugin reload

This PR stays Working through the reload:

PR: Working through a forced plugin reload

This PR then shows Done once OpenCode prints FINISHED:

PR: Done after the turn ends

Quitting OpenCode mid-turn does not leave the pane stuck on Working:

PR: Interrupted after quitting mid-turn

Known limits, handled by other PRs

Validation

  • tc:node typecheck passes.
  • OpenCode, OpenCode 2, MiMo, SSH relay and shared unit tests: 23 files, 344 passed, 1 skipped.
  • Each fix was removed in turn to check that its tests fail without it.
  • The changed-code quality check passes locally.
  • CI at 66b75769ba9: all 30 checks pass, 11 skipped. An earlier persistence-job failure was in the terminal daemon's process-suspension tests, which this PR does not touch, and it did not come back on the rerun.
  • Not tested on Windows or on a live SSH host. Those paths are covered only by unit tests.

@brennanb2025
brennanb2025 marked this pull request as ready for review September 29, 2026 02:25

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

✅ No new issues found.

Reviewed changes

  • Host-aware plugin disposal — the generated factory gains sessionsOutliveDispose; OpenCode 2's setup() sets it so dispose releases only its own bookkeeping and publishes nothing, while OpenCode 1's server() path keeps main's final-Idle / fallback hand-off. This is the fix for the false Done.
  • Write-if-changed plugin install — new src/shared/opencode-installed-plugin.ts (isInstalledOpenCodePluginCurrent follows symlinks; isOverlayOpenCodePluginCurrent requires a regular file) wired into the local global installer, the per-user overlay, and the relay's canonical-config install, so a spawn no longer bumps the file's mtime and no longer triggers OpenCode 2's plugin reload.
  • Tests — new host-entry dispose tests run the generated module through both server() and setup(); write-if-changed and symlink tests cover the global file, overlay, and relay install; the generated-source SHA digests are updated.

I read the full diff plus the surrounding runtime/installer sources, and ran the four changed test files: 76 pass. The assertions are exact (toEqual on the emitted event-name sequence), so the OpenCode 2 reload test genuinely fails if dispose publishes Idle — the coverage is real, not tautological.

ℹ️ Residual risk when the plugin unloads without a reload

Dropping the OpenCode 2 dispose Idle means that if a plugin is ever unloaded mid-turn outside a reload while the process stays alive, the pane keeps its last state until the next session event or the agent command exits. The author already documents this and could not trigger the path; Orca's ended-process reconciliation still covers the process-death case. Flagging it only so the trade-off stays visible if OpenCode's unloading behavior changes.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@greptile-apps

greptile-apps Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 3/5

[Medium risk] Optimizes plugin file writes to avoid unnecessary reloads.

The PR should not merge until reloads can settle answered prompts and completed turns when session lookup fails.

Findings

  1. P1 Answered prompt stays Waiting ▶
  2. P1 Completed turn stays Working ▶

Summary

The PR avoids rewriting unchanged OpenCode plugin files and makes OpenCode 2 plugin disposal leave delivered status intact during reloads, while retaining OpenCode 1’s final-Idle behavior.

  • The write-if-changed paths cover local configuration, overlays, and the SSH relay installer.
  • Two reload lifecycle paths still need attention: resolving a prompt after reload and settling a turn when session lookup fails.

Reviews (1) · Last reviewed commit: "test(opencode): stub fetch without a typ..."

Comment on lines +248 to +252
' if (sessionsOutliveDispose) {',
' if (desiredFactoryID === factoryID) {',
' clearStatusRetry();',
' statusRevision += 1;',
' }',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Answered prompt stays Waiting When OpenCode 2 reloads the plugin while a question or permission prompt is displayed, disposal removes the record that links the prompt to its owner but leaves the pane Waiting. The reloaded plugin cannot match the user's reply to that record, so it sends no status update and the pane can stay Waiting until another lifecycle event arrives.

Knowledge Base Used: Agent integrations and hooks

Comment on lines +248 to +252
' if (sessionsOutliveDispose) {',
' if (desiredFactoryID === factoryID) {',
' clearStatusRetry();',
' statusRevision += 1;',
' }',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Completed turn stays Working After a mid-turn OpenCode 2 reload, the new plugin has no record of the running turn. If its session lookup fails or times out when the turn-end event arrives, it discards that event because it has no record to clear. Disposal no longer sends a final Idle, so the pane remains Working even though the turn ended.

Knowledge Base Used: Agent integrations and hooks

@coderabbitai

coderabbitai Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 3e6bfd74-2c6e-44b5-953e-10dbebf8419c

📥 Commits

Reviewing files that changed from the base of the PR and between 2aed2cf and 66b7576.

📒 Files selected for processing (9)
  • src/main/opencode/hook-plugin-dispose-host.test.ts
  • src/main/opencode/hook-service.test.ts
  • src/main/opencode/hook-service.ts
  • src/main/opencode/status-plugin-factory-source.ts
  • src/main/opencode2/status-plugin-setup-source.ts
  • src/relay/opencode-canonical-config.ts
  • src/relay/plugin-overlay.test.ts
  • src/shared/opencode-installed-plugin.test.ts
  • src/shared/opencode-installed-plugin.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review.


📝 Walkthrough

Walkthrough

The status plugin now handles disposal differently when sessions outlive it. The OpenCode 2 setup enables this behavior, and tests cover disposal and reload events. OpenCode plugin installers now check whether generated source matches the installed file before rewriting it. Tests cover matching and stale files, timestamps, and symlinks.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to 66b75

The change keeps OpenCode 2 panes showing Working across plugin reloads and avoids rewriting an unchanged plugin file. The author reports live testing and passing CI. No blocking risk was found in the supplied context.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 66b75

The change addresses false Done states during plugin reloads without an identified expansion of access or file-writing authority. It remains unclear how a pane recovers its status if the plugin unloads permanently while a turn is active.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The inspected changes affect pane-status reporting and writes to existing OpenCode plugin locations, not a newly introduced endpoint or file destination. This bounds the identified change, but does not establish complete security coverage.

Trust Boundaries and Controls

  • observed — Plugin filenames remain selected by agent variant rather than supplied by a new caller. The overlay writer still unlinks a non-current plugin path before writing, avoiding a write through a mirrored symlink.

Hardening Proposals

  • proposed — If the host supports unload without replacement, consider a separate session-state reconciliation path so suppressing a false disposal-time Idle cannot leave a pane indefinitely Busy.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 9 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary fix: keeping OpenCode 2 panes in the Working state during plugin reloads.
Description check ✅ Passed The description is complete and follows the required template. It explains the user impact, implementation, rationale, linked issue, visual proof, testing, limitations, platform considerations, and ch…
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.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR

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.

@brennanb2025
brennanb2025 merged commit 2ae7c00 into main Sep 29, 2026
83 checks passed
brennanb2025 added a commit that referenced this pull request Sep 29, 2026
…3700, #22943, #23720, #23793, #23776, #23796) into #23059

#23573 (keep a turn's bar on the prompt that opened it) is ported onto the
turn-record membership instead of kept as a second owner lookup:
- The running turn's bar comes from nativeChatTurnMembership, which now
  returns barTurnKey beside liveTurnKey. Where the host states each row's
  turn they are the same key: the running root turn's anchor, and a
  message handed over mid-turn is already scoped to that turn. Where the
  host states no scope, rows still group by position, so barTurnKey is the
  user entry the running record names (through the same anchors) while
  liveTurnKey keeps the positional live group, as #23573 kept row liveness
  on the newest prompt.
- activeTurnOpenedBy, selectStructuredAgentTurnBars' openedBy and
  selectNativeChatActiveTurnKey are removed; the desktop list, the slot
  builder and both turn-status hooks read barTurnKey.
- The queued-send clock (queuedUntil: a turn counts from the previous
  turn's end when its send waited behind it) is kept, read in the same
  one-pass walk over root turn records that keys timing by anchor.
- Its tests are adapted to pass a journal: each runs on a host that
  states no scope (the original expectations), and the desktop and mobile
  bar tests also run on a scoped host.

#23621 (a message Claude folds into its running turn no longer splits the
turn) is host-only and merged as is: the translator contract carries both
openTurnInLiveProviderCycle and #23059's command-turn members.

Both sides grew the Claude translator past its line budget, so its result
branch moves unchanged into claude-result-journaling.ts, beside
claude-message-journaling.ts (the pre-commit lint refuses the merge
without it).

#23801's Codex child path (no error-driven end) and #23059's Codex /compact
guard (a command turn ends only on turn/completed) both stand.
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