Skip to content

[Bug]: OpenCode panes that share a directory attribute every session to the first pane that started the shared server #22838

Closed

Description

@werlang

Operating system

macOS (the code involved is platform-neutral; the same path runs on Linux and Windows)

Orca version

1.4.210, and still reproducible on main at 7a4f080086

Details

Symptoms

  1. Two panes show a spinning "working" icon for one running task.
  2. The pane that actually owns the task shows grey/idle while it works.

These two appear together, which is the tell: one session's status is landing on the wrong pane.

Before / after as the user sees it

Open two OpenCode agents in panes that share a directory — split panes inside one worktree, or two sibling folder-workspace panes, which are backed by the same folder. Whichever pane happened to start OpenCode's background service reports all of the work: it spins while the other pane's task runs, and the pane that really owns the task never lights up.

Expected: each pane reports only its own session.

Relationship to previous attempts

This is the residual case of #21359, which reported this same symptom and was closed as fixed by #21577. The reporter there identified the exact root cause this issue covers: "both panes here share one project directory, so even the directory cannot tell them apart."

#21577 introduced the rebinding mechanism — the session→pane registry, the correlator and the binder round all land in 646fa3645f. But the correlator only emits an owner when exactly one pane qualifies, and a shared directory makes two qualify, so the case #21359 actually described stayed unbound. That limitation is deliberate and locked in by that PR's own test:

it('stays unbound when both same-directory panes evidence a client', ... expect(results).toEqual([])

So this is neither a new bug nor a regression — it is the tie the first fix consciously declined to guess at, now given evidence that can break it.

Other attempts adjacent to this symptom, for anyone landing here first: #22764 / #22768 cover a different cause of grey panes (the plugin failing to load at all, because the OPENCODE_CONFIG_DIR copy is never regenerated), and #11210 was flagged on #21359 as possibly this same single-slot projection, seen by remote clients — unverified here.

Mechanism

Plain opencode (without --standalone) runs one shared background service for every pane, so the status plugin runs inside that service and the ORCA_PANE_KEY stamped on each post is frozen to whichever pane started it. #21577 fixed this by rebinding the pane after the fact, keyed on session id.

The rebinding only happens when the binder has a binding, and correlateOpenCodeSessionOwners produces one only when exactly one pane qualifies as the session's creator. Qualification needs a pane whose directory contains the session and a live OpenCode client that brackets the session's creation — and clientCouldCreate deliberately accepts any client that started before and was seen alive after, so a TUI left open for days counts.

With two panes sharing a directory, both qualify, the tie is unresolvable, and the session stays unbound. Its posts then keep the frozen stamp forever. The existing test stays unbound when both same-directory panes evidence a client locks this behavior in.

This is the common case, not a corner:

  • split panes in one worktree share worktreeId, hence one directory;
  • folder projects back several workspaces with a single directory by design (FOLDER_WORKSPACE_INSTANCE_SUFFIX is stripped before containment comparison).

Fix

Two signals, in order, each required to produce an unambiguous in-window leader:

  1. Launch recency — the pane whose OpenCode client booted within 20s of the session row appearing. This has to run first because Orca launches agents with --prompt, so the pane that created the session never writes a keystroke of its own; on input alone that session would be handed to whichever sibling pane had recently been typed into.
  2. Input recency — the last interactive write against each pane's PTY, taken at the point of dispatch and excluding terminal query replies, inside a 60-second window ending at creation and never after it.

Anything short of that stays unbound rather than guessed, because a wrong owner reproduces the reported bug rather than softening it.

Not covered by this issue / the fix

  • Grey panes caused by the plugin never loading at all: that is [Bug]: stale opencode-hooks/shared status plugin copy is never regenerated, so OPENCODE_CONFIG_DIR keeps loading a pre-setup() template (Orca 1.4.210, Linux) #22764, with a fix in flight as fix(opencode): regenerate shared status plugin for OPENCODE_CONFIG_DIR #22768.
  • "The icon stops spinning too early": not yet root-caused. The leading candidate is that the shared plugin aggregates to a single session per update — publishAggregateStatus in status-plugin-ownership-source.ts posts one sessionID, so with two sessions busy across panes only one of them is ever spoken for. STALE_WORKING_TITLE_TIMEOUT_MS is the other candidate.
  • A launch that takes over 20s to mint its session falls outside the launch-recency window and is then decided on input, where a sibling pane typed into during that minute could still win. Narrowed from "every flag launch" to "very slow boots".
  • Only well-formed terminal query replies are filtered out of the input signal. Full user-input provenance stops in the renderer — wasUserInput is consumed by the replay guard and never reaches the transport — so an automatic write that is not a recognisable reply can still stamp a pane.

Activity

  1. AmethystLiang commented on Sep 25, 2026

    @AmethystLiang
    Contributor

    thanks for reporting. looking into it.

  2. werlang commented on Sep 26, 2026

    @werlang
    ContributorAuthor

    Thanks — a fix is already in flight as #22847, which this issue is the Fixes target for.

    Two things that may save you time investigating:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions