Skip to content

OpenCode subagent ends from the parent record: close the cases left open before it can be on by default #3027

Closed

Description

@devswha

Follow-up to #1999 and #3022.

#3022 reads an OpenCode subagent's end from its parent session's record (the subagent call block, or the background notice), ready for anomalyco/opencode#57418. It ships behind Settings → Appearance → Read OpenCode subagent ends from their session (experimental), off by default. With it off, the reader takes no parent rows and behaves as before. The review of #3022 left these cases open. Each one blocks turning the setting on by default.

1. (Fixed in #3022 before merge.) A crashed foreground call held a child that the restarted service woke and ended. A waiting call last written before the service started now waits on nothing.

2. A late background notice ends a newer call (reproduced with a fixture; the upstream ordering is not verified).
A background run finishes, the parent calls the same child again (foreground) before the old notice lands, and the notice is written after the new call. The newest row wins, so the reader takes the stale notice and ends the new call while the parent still waits. A notice does not name the call it answers. A fix needs to pair notices with calls, for example by the job's start, or to ignore a notice for a child whose newer call is still running. main reads this case the same way, from the child's idle row.

3. A cold read of a long parent history can miss the turn-end frame (traced, not reproduced).
OpencodeSubagentStatus.poll takes two synchronous steps. The first read after a pane binds, or a read after its log starts over (/undo), pages the parent's rows first. With more than about 1000 parent rows, that read can finish only after the frame that ends the turn, so a turn that started a subagent may be told DONE before the count arrives. Warm reads are not affected. A fix could read a cold parent log only back to the oldest child still kept, or let the poll that ends a turn finish its read before telling.

5. Turning the setting off mid-wait holds BG up to a minute (from review). If a parent still waits on an idle child when the setting goes off, the count drops to 0 and BackgroundWait's 60-second grace starts. With the setting off throughout, that child would never have held anything. A fix could drop the hold at once when the count falls because of the toggle.

4. A staged /undo is applied after only the newest record per child is kept (reproduced, same on main).
If a later call to the same child is hidden by a staged /undo, the older visible call cannot take over, and the child's own rows decide. The boundary should be applied before "newest wins".

Measured cost with the setting on, a 100 MB parent (5000 steps, 100 children), through the tracker: first read about 220 ms, then warm polls about 10-15 ms over main (one pass over the row headers). Codex measured about 90 ms on 250,000 rows.

Activity

  1. added a commit that references this issue on Nov 6, 2026
    10dddf8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions