Skip to content

fix(orchestration): give an OpenCode worker its task only once OpenCode can submit it - #25101

Merged
OrcaWin merged 12 commits into
mainfrom
brennan/sta-9359-opencode-worker-reports
Oct 5, 2026
Merged

OrcaWin merged 12 commits into
mainfrom
brennan/sta-9359-opencode-worker-reports

Conversation

@OrcaWin

@OrcaWin OrcaWin commented Oct 3, 2026 •

Copy link
Copy Markdown
Collaborator
Files Added Deleted Net
Test 45 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1130 $\color{#cf222e}{\Huge{\mathbf{−}}}$​53 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1077
Prod 9 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​325 $\color{#cf222e}{\Huge{\mathbf{−}}}$​29 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​296

ELI5

When a coordinator starts an OpenCode worker, Orca waits for OpenCode's input box, types the task in, and presses Enter. OpenCode 2 draws the input box a moment before it can accept a message. On a busy machine that gap can be over a second, and an Enter pressed inside it is ignored without any sign: the task sits in the box unsent, and the worker never runs or reports back. Orca now waits for the row OpenCode draws under the input box once it is actually ready, so the Enter lands even on a slow start.

What Changed

The problem (STA-9359).

  • A coordinator runs orca orchestration worker-start --agent opencode.
  • The receipt says the input was accepted (stage: input_accepted), and the worker shows ready.
  • Minutes later the dispatch is still dispatched, with no heartbeat and no worker_done, and the terminal is still alive.
  • Codex and Grok workers in the same Run reported normally.

Reproduced on macOS with Orca 1.4.219 and OpenCode 2.0.21. OpenCode's screen showed the whole task sitting unsent in its input box as [Pasted ~82 lines], and one manual Enter made the worker run and report straight away.

Why it happened.

  • OpenCode 2 draws its input box and puts the cursor in it before it has loaded its list of agents from its server.
  • Until that list arrives, OpenCode ignores Enter: its submit returns early when no agent is selected.
  • Orca treated the cursor in the input box as "ready" (Wait for OpenCode worker input before the first dispatch #24601), pasted the task, and pressed Enter once about 0.5 s later.
  • Usually the agent list follows the input box within about 100 ms, so the Enter lands.
  • On a busy machine it lagged by 0.5–1.8 s. Recordings at a load average of about 44, from other work on the machine, show the Enter arriving inside that gap and being dropped, with the task left in the box.
  • Nothing noticed afterwards, because Orca cannot see whether an OpenCode turn started.

What is different for you.

  • An OpenCode or OpenCode 2 worker started by worker-start in a fresh terminal gets its task only once OpenCode is ready to submit it, so the task is not left unsent on a slow start.

  • Agent tabs launched with a prompt that Orca submits (quick launch, the checks "fix" launch, notes, source-control actions and the other launches that paste and press Enter) get the same fix, because they use the same readiness signal.

  • Drafts that Orca pastes without pressing Enter are unchanged. Typing into the input box doesn't need OpenCode's agent list.

  • On a normal machine, starts take about as long as before: the row usually follows the input box within about 100 ms.

  • If the input box appears but the row doesn't within 5 s, Orca goes ahead exactly as it does today. In every measured start the row came within 1.8 s, so this only applies where the row is never drawn. Examples:

    • a pane narrower than 44 columns, where OpenCode 2 leaves the agent name, and with it the separator, out of the row;
    • an OpenCode build or plugin with a different layout.

    Nothing becomes worse than today.

  • If the input box never appears, nothing changes: worker-start fails with a timeout, as it already does (Wait for OpenCode worker input before the first dispatch #24601), and launched tabs keep their existing fallback.

  • OpenCode 1.x is unchanged. It draws the row in the same frame as the input box, so the signal fires on the same read as before.

  • Not covered by this PR:

    • No model provider set up: OpenCode still draws the row (reading "No provider selected"), and an Enter opens its connect-a-provider prompt instead of submitting, the same as today. That is a configuration problem, not this race.
    • worker-start --terminal with an existing pane: it keeps today's general "agent looks idle" wait, unchanged here.

Mechanism.

  • Each agent row in the agent table can now name two readiness signals:
    • draftPasteReadySignal: ready for typed text, unchanged;
    • submitPasteReadySignal: ready to submit.
  • Deliveries that press Enter wait for the second one. Worker start always submits.
  • opencode and opencode2 set the submit signal to a new opencode-agent-row scanner (opencode-agent-row-scanner.ts). It is ready once OpenCode has drawn its agent/model row: the · between the agent and the model, on the line directly above the input box's bottom-left corner, inside OpenCode's full-screen view.
  • OpenCode draws that row only once its agent list has loaded, which is exactly what its submit checks.
  • The rule keys on that position, not on agent names, model names or colours. A · elsewhere on the screen doesn't count:
    • in the working-directory path in the footer;
    • in another session's title in the tab strip;
    • in the shell prompt before OpenCode started.
  • Leaving the full-screen view withdraws the row, so an earlier program's screen can't satisfy it. Bracketed paste being turned off withdraws the input-box cursor and its grace, as today's rule does.
  • When the input-box cursor is showing without the row, the scanner allows a 5 s grace. After that the waiter proceeds as it does today, and also at the readiness deadline if that comes first.
  • Recordings back the rule. They were made with a timestamping terminal recorder, scrubbed of usernames and paths, and are each replayed through the runtime in tests:
    • OpenCode 2.0.21 cold start, with and without hidden-pane terminal replies;
    • 2.0.21 attached to an already-running server, through the plain opencode command;
    • a 2.0.21 start at natural high load where today's Enter was dropped;
    • a 2.0.21 start where an Enter after the row was submitted;
    • a 2.0.21 start in a 40-column pane, with no · in the row, that submitted after the grace;
    • a 2.0.14 cold start;
    • the existing 1.18.32 and 2.0.18 recordings.

Why

  • Wait for real readiness rather than retry. An earlier revision of this PR pressed Enter again until OpenCode reported a turn start. Review found that design needed bookkeeping for which prompt a turn belongs to, and could press Enter into a busy OpenCode, where Enter answers a permission prompt. Waiting for the moment OpenCode can submit removes the race instead of guarding it, needs no hooks, and works the same with status hooks turned off. In every recording, an Enter after the row was submitted, so no retry is kept.
  • Not a fixed delay. A delay guesses the machine's speed; the row is OpenCode's own statement that it is ready.
  • Not opencode --prompt (yet). Letting OpenCode submit the task itself is the sturdiest cue, since OpenCode only auto-submits once it is fully ready. But it doesn't fit worker start without a redesign, so it is a follow-up:
    • the task text names the worker's terminal, which only exists once the terminal is created;
    • the task text is several KB, which overflows Windows cmd.exe's command-line limit;
    • reused --terminal workers still need the paste path;
    • worker start records the dispatch after readiness.

Out of scope:

  • Starting OpenCode as a structured chat worker (the ticket's first "expected" option). Structured chat exists only for Claude and Codex, and an OpenCode structured session would be a new feature.
  • worker-start --on <host> (a worker on a paired Orca host): that path gets the new readiness wait once the host runs this version. The home side still doesn't observe a turn start for any agent, which is a separate follow-up.

Known and out of scope: OpenCode subagents.

  • When an OpenCode 2 worker hands work to a subagent, the subagent's shell commands don't run with the worker's own Orca terminal identity.
  • They run with the identity of whichever terminal first started OpenCode's shared background server.
  • So if a subagent sends the worker's worker_done, Orca can refuse it as coming from another terminal ("This terminal is a Run coordinator…"). We reproduced this.
  • The worker's main session is not affected, and that is the path this PR fixes.
  • The cause is in OpenCode: a subagent session doesn't inherit its parent session's environment. It is being reported to OpenCode, and this PR doesn't change it.

Linked Issue

Fixes STA-9359 (Linear), for the failure reproduced there: the task reached OpenCode's input box, but its Enter was lost.

Related, not fixed by this PR: #22580 and #24913 (problem 1). Users there describe a different symptom: OpenCode's input box stays empty ("Ask anything…") and the task never appears.

Visual Proof

Isolated QA run on macOS (an app build of this branch with its own profile and home, hidden window, real OpenCode 2.0.21 on a free model).

An OpenCode worker started by worker-start: the task was submitted and the worker reported worker_done.

OpenCode worker after worker_done

An OpenCode tab launched by a quick command that submits its prompt:

OpenCode quick-command tab with its prompt submitted

Same flow on main, for comparison (at today's load main also submitted; the drop needs a busy machine, see the recorded drop transcript):

main: OpenCode worker after worker_done

Testing

  • I manually tested these changes locally (isolated app build on macOS; see the QA comment)

  • Automated tests added/updated, or explained why not below

  • Transcript replay, for every recording:

    • the signal fires at the agent row, never on a cursor-only frame;
    • a · substituted into the footer path (all seven OpenCode 2 recordings) or a tab title (the two recordings that show tabs) doesn't fire it early; these tests fail if the position check is removed;
    • the Enter after the row submits;
    • on the drop recording, today's Enter falls before the row and the new signal fires only at the row;
    • the 40-column recording falls back after the 5 s grace and submits.
  • Scanner tests: chunk boundaries; withdrawal when the full-screen view is left or bracketed paste is turned off; the grace timer isn't restarted by later output.

  • Renderer and main waiters: submit deliveries use the submit signal; drafts use the unchanged one.

  • Repo transcript secret scan: clean.

  • Typecheck: tc:node, tc:web and tc:cli.

Review

Author: @BrennanKB5

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

  • Windows: the row check relies on OpenCode's switch to its full-screen view, and on the · reaching Orca's Windows terminal. Other agents' signals already rely on that switch there. If either didn't arrive, or if Windows' terminal layer redrew the row with cursor moves the scanner doesn't follow, the 5 s grace falls back to today's behaviour. That would cost each start up to 5 s but never paste early. Not verified on Windows: the one Windows QA machine reachable from this run has no OpenCode installed.
  • SSH / paired hosts: the scanner runs wherever Orca already scans that terminal's output today. That is the client for launched tabs (including tabs on a remote runtime) and for SSH worker-start, so those get the fix with the client update. worker-start --on <host> scans on the host, which needs this version.
  • Wire: no change.
  • Performance: a small character-by-character parse of terminal output, only during the startup wait.

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)

m4air and others added 5 commits October 3, 2026 15:51
…sk starts

OpenCode 2 draws its input box before it has loaded its agent list from its
server, and silently drops an Enter that arrives in that gap. On a loaded
machine the gap outlasts the 0.5 s Orca waits after pasting, so a worker's
task sat unsent in the box while worker-start reported it ready.

OpenCode's turn start is now observed through its status hooks, like Codex
and Claude. While no turn has started, the prompt verifier presses Enter
again on an interval (per the agent row, opencode and opencode2) for as
long as the pane's hooks are reporting, never past an approval or question
prompt or a replaced pane, and at most a fixed number of times. A pane
whose hooks never report keeps today's single Enter and unobserved receipt.

Co-Authored-By: Claude <noreply@anthropic.com>
…code-worker

# Conflicts:
#	src/main/runtime/agent-prompt-submission-verification.ts
OpenCode 2 draws its input box, and shows its cursor there, before its agent
list has loaded from its server, and it silently drops an Enter that arrives in
that gap. Orca pasted a worker's task on the box's cursor and pressed Enter half
a second later, so on a loaded machine the task stayed unsent in the box while
the worker read as started.

OpenCode's paste readiness now also waits for the row under the input box
(`<agent> · <model>`), which OpenCode paints only once the agent list exists.
The rule keys on the row's separator inside OpenCode's alternate screen, not on
names or colours. Both the opencode and opencode2 rows use it, worker start and
the renderer's draft paste alike. OpenCode 1 paints the row in the box's own
frame, so it is ready on the same read as before.

Recorded OpenCode 2.0.21 boots (cold, hidden pane, four at once, attached to a
running server) are the evidence; two more show an Enter after the row
submitting and one at the box being dropped. The earlier resubmit loop and the
settlement-provider changes are removed.

Co-Authored-By: Claude <noreply@anthropic.com>
…ever paste a draft blind

Replaces three recordings made by launching four OpenCode boots at once with
natural single-run evidence: one cold start that pasted on the agent row and
pressed Enter on Orca's real 500 ms timing (submitted), and one cold start
recorded under heavy load from other sessions, where the paste on the box's
cursor and Orca's Enter came before the agent row and the brief stayed unsent.
Adds a 2.0.14 cold start as a cross-version check.

The renderer's draft paste no longer falls back to a blind paste when the
OpenCode agent-row signal never arrives: OpenCode would drop the Enter and
leave the draft in its box, so the caller gets its timeout notice instead.
Other agents keep the fallback.

Co-Authored-By: Claude <noreply@anthropic.com>
…gnals

Co-Authored-By: Claude <noreply@anthropic.com>
@OrcaWin OrcaWin changed the title fix(orchestration): an OpenCode worker's task starts even when OpenCode drops the first Enter fix(orchestration): give an OpenCode worker its task only once OpenCode can submit it Oct 4, 2026
m4air and others added 3 commits October 4, 2026 01:13
… it only before Enter

- The agent-row signal now keys on structure: the `·` must sit on the line
  directly above the input box's bottom-left corner, so a `·` in the footer
  path or a session tab title no longer counts. Leaving the alternate screen or
  turning bracketed paste off withdraws what each part established.
- Only deliveries that press Enter wait for the row (new
  `submitPasteReadySignal`); drafts keep the box-cursor rule, as on main.
  Worker start always submits.
- When the box cursor has shown but the row never comes (OpenCode 2 drops the
  agent below 44 columns), both waiters proceed after a 5 s grace, as main
  did. A natural 40-column capture proves that start submits.
- The renderer's blind-paste fallback is back to main's behaviour.

Co-Authored-By: Claude <noreply@anthropic.com>
…-row grace is pending

- Both paste waiters resolve ready, not timed out, when the hard deadline
  lands while the 5 s grace is pending: the box was seen, so this is the
  verdict the box rule gave before.
- Tab-bound draft paste is tested to wait on the agent row only when Enter
  follows.
- Scanner: a separator pairs with its corner only inside one synchronized
  frame, row 0 clamps to 1, and the grace comment names the transparent-theme
  case. The draft signal type no longer admits the agent-row signal, whose
  grace only the paste waiters honour.

Co-Authored-By: Claude <noreply@anthropic.com>
…alternate screen

If OpenCode exited after drawing its box but before its agent row, the grace
still ran out and the task was pasted into the shell. Leaving the alternate
screen now clears the box cursor, and once an app has left it a cursor the
shell shows outside it no longer starts a grace. A terminal that never
forwards the alternate screen still gets one.

Co-Authored-By: Claude <noreply@anthropic.com>
@OrcaWin

OrcaWin commented Oct 4, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status update: CI on 6ce7d6f finished green (run 37237091526: all test shards, verify, packaging). The earlier red run on a89d9a5 came from serialize-grid-transcript-replay.test.ts, which replays every captured terminal recording. The new OpenCode recordings showed the same known serializer differences the existing OpenCode recordings already carry, mostly background colour on restored cells and rows kept after a resize. They are now registered with exact counts and a reason; the serializer is unchanged. Main is merged in.

Final status (head 6ce7d6f)

The problem. An OpenCode worker started with orca orchestration worker-start --agent opencode could sit forever with its task unsent:

  • Orca pasted the task as soon as OpenCode's input box appeared, then pressed Enter about 0.5 s later.
  • OpenCode 2 ignores Enter until it has loaded its agent list, and on a busy machine that list arrived up to 1.8 s after the input box.
  • The Enter was lost and the task stayed in the box as [Pasted ~N lines]. The receipt still said input_accepted, and the worker never reported. That matches STA-9359.

What changes for users.

  • OpenCode and OpenCode 2 workers, and OpenCode tabs launched with a prompt that Orca submits, now get the task only once OpenCode draws the agent/model row under its input box. That is the first moment it can submit.
  • If that row never appears (for example, a pane narrower than 44 columns, where OpenCode leaves it out), Orca goes ahead as it does today after 5 s, or at the readiness deadline if that comes first.
  • Drafts pasted without Enter, OpenCode 1.x and every other agent are unchanged.

Fixed in review. The first version of this PR pressed Enter again until a turn started. Review showed that needed per-prompt bookkeeping that could pin later turns to stale prompts. It could also press Enter into a busy OpenCode, where Enter approves a permission prompt. That version was replaced with the readiness wait. Review of the readiness wait then fixed:

  • a · in a folder path or session title firing the signal early;
  • panes narrower than 44 columns never becoming ready;
  • a grace that ignored the remaining time budget;
  • a grace that outlived OpenCode exiting, which could have pasted the task into the shell.

The final readiness review found no P0–P2 issues.

Deferred / not in this PR.

  • Starting OpenCode as a structured chat worker. That would be a new feature.
  • worker-start --on <host>: the home side doesn't observe turn start for any agent.
  • --terminal reuse of an existing pane keeps today's "agent looks idle" wait.
  • No model provider configured: OpenCode can't submit there anyway.
  • Delivering the task through OpenCode's own --prompt. It is the sturdiest readiness cue, but it needs a worker-start redesign, so it is a follow-up.
  • Known, out of scope: OpenCode 2 subagents run their shell commands with the Orca identity of whichever terminal first started OpenCode's shared background server, so a subagent's worker_done can be refused. The worker's main session is unaffected. The cause is in OpenCode: a subagent session doesn't inherit its parent's environment. It is being reported to OpenCode, and this PR doesn't change it.

Verified.

  • Unit and transcript tests, plus typecheck of the node, web and CLI projects.
  • Real OpenCode 2.0.14, 2.0.21 and 1.18.32 recordings replayed through the runtime. They include a 2.0.21 start recorded at natural high load where today's Enter was dropped and the new signal waited for the row, and a 40-column start that falls back after the grace and submits.
  • Isolated macOS app QA of this branch (QA ran on a build at 719cc3d. The commits since then were not re-run in the app; they are covered by tests and green CI: the exit case above, the replay-test registration 0ec73d9, and the merge of main 6ce7d6f):
    • 5/5 OpenCode workers submitted and reported worker_done;
    • a quick-command OpenCode tab submitted its prompt;
    • main passed the same 5 at today's low load, as expected, since the drop needs a busy machine.

Not verified.

  • Windows: the only reachable Windows QA machine has no OpenCode installed. If Windows didn't pass the row through, the 5 s grace falls back to today's behaviour, never an early paste.
  • A live pane under 44 columns: the isolated app's hidden window could not be made that narrow before delivery. The narrow case is covered by the 40-column recording.

OpenCode worker after worker_done
OpenCode quick-command tab with its prompt submitted

Note: these images were uploaded from the OrcaWin account. They load for signed-in GitHub users but returned 404 to an anonymous request, so if they don't render for you, that is why. The originals are on the M4Air under ~/orca-qa/pr-25101/qa/jpg/.

@OrcaWin
OrcaWin marked this pull request as ready for review October 4, 2026 09:01
@coderabbitai

coderabbitai Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

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

🧰 Additional context used
📚 Code guidelines (1)
AGENTS.md — auto-discovered
📝 Walkthrough

Walkthrough

The change adds a structural scanner that detects OpenCode’s agent row above the input box. OpenCode configurations use this signal when a paste will be submitted. Renderer and runtime readiness waits now handle scanner-provided grace delays. New timed captures, census records, and tests cover startup, submission, delayed agent-row rendering, and narrow-pane behavior.

Priority: ➖ Normal

Merge Risk: 🔵 Low · up to 39170

If OpenCode exits during the readiness wait, a launch can report its task as delivered when it was not. Cancel readiness on PTY exit before merging, or accept this bounded failure case.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 39170

The change improves startup timing without granting new access. However, a stopped session can still be reported as having received its task, weakening failure detection and recovery.

Retained concerns

  • Low · reliability · inferred: The new renderer grace can turn stale readiness into successful launch delivery. If the composer appears without its agent row and the terminal exits without further invalidating output, the timer still resolves ready. Delivery then skips foreground fallback inspection and uses the captured PTY ID. The existing local sender can promote a rejected write to success through its fire-and-forget fallback, allowing the launch owner to invoke its delivered callback rather than report failure. The PR introduces a multi-second opportunity for this pre-existing write-result weakness to hide a failed launch; delivery to a different terminal incarnation remains unproven.
Security review details

Security Blast Radius

  • observed — The inspected delivery paths remain scoped to the selected terminal handle or PTY ID. Remote desktop writes derive the destination environment from that PTY's owner identifier and send through the existing terminal route; readiness does not itself select another environment or grant additional authority.

Trust Boundaries and Controls

  • inferred — Terminal-rendering markers should be treated as timing evidence rather than proof of identity or task acceptance. The main-process path keeps separate ownership controls, while the desktop path relies on a captured PTY and transport result after readiness. The reviewed evidence supports a stale-success failure path, but does not establish unauthorized cross-session delivery or privilege escalation.

Resilience and Maintainability Implications

  • observed — The desktop readiness waiter listens for data but does not subscribe to local exit or supply remote end callbacks. Remote streaming already exposes end and transport-close notifications. Consequently, cessation of a session does not independently cancel the new readiness grace.

Hardening Proposals

  • proposed — Bind delayed readiness and final delivery to the same live session lifetime, cancel readiness on terminal end, and distinguish a rejected write from an unacknowledged fallback. This would strengthen failure containment without treating screen markers as an authorization mechanism.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 25 functions across 19 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 main change: OpenCode workers receive their tasks only when OpenCode can submit them.
Description check ✅ Passed The description is detailed and mostly complete. It explains the problem, change, rationale, linked issue, visual proof, testing, and known limitations. Some checklist items remain unchecked, and the …
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 💡 2
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@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
A new submit-readiness signal lets Orca wait until OpenCode can actually accept its task before pasting and pressing Enter, closing the STA-9359 race where the Enter was dropped on a slow start.

  • New structural scanner (src/shared/opencode-agent-row-scanner.ts) — detects OpenCode's <agent> · <model> row by screen position (separator on the line directly above the box's ╹ corner, inside the alt screen, with the box cursor shown under bracketed paste), and asks for a 5 s grace while the box is up without the row.
  • Signal plumbing — resolvePasteReadySignal(config, submit) plus submitPasteReadySignal in the agent config; opencode/opencode2 use opencode-agent-row only when Enter follows the paste, so draft-only pastes keep render-cursor-after-bracketed-paste.
  • Waiters — main and renderer readiness waiters arm a one-shot grace from readyAfterMs (withdrawn on null, not restarted), and the hard deadline takes the box when a grace is pending.
  • Worker start — waitForFreshWorkerComposer now passes submit: true, so a fresh OpenCode worker's task is delivered only once the row is painted.
  • Evidence — new timed fixtures (cold standalone, hidden pane, warm server, natural-load Enter-dropped, narrow pane, enter-after-row) with meta/timing JSON and census snapshots, plus scanner, chunking, worker-lane, and launched-agent transcript suites. The tests replay the real emulator and assert the ready read against the row position; the dropped-Enter and grace cases are pinned to recorded timings.

The implementation reads cleanly against the existing single-signal scanner: leftAltScreen correctly prevents a shell cursor outside the alt screen from re-arming the grace, and the OpenCode 1 row-before-corner case is handled by the synchronized-frame pending set. Windows remains the main uncertainty (documented in the PR): if the alt-screen/bracketed-paste/╹ frames are not forwarded, the row cannot fire and the 5 s grace falls back to today's behavior rather than pasting early.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

@OrcaWin
OrcaWin marked this pull request as draft October 4, 2026 21:33
m4air and others added 2 commits October 4, 2026 14:38
…d-restore counts

The six OpenCode 2 captures this PR adds hit the serializer limitations the
1.18.32 and 2.0.18 captures are already registered for: the live pen's
background (and once its bold bit) on restored cells, rows kept below the
source after a shrink, and the cursor one column short after a shrink. No
I1 or I3 failure; the serializer is unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
@OrcaWin
OrcaWin marked this pull request as ready for review October 4, 2026 22:18

@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
This is a re-review of the OpenCode submit-readiness fix (STA-9359): a submitted task now waits for OpenCode's <agent> · <model> row, not just the input-box cursor, before Orca pastes and presses Enter. Since the prior pullfrog review approved a89d9a5, the only delta is a test-baseline commit and a merge of origin/main.

  • New structural scanner (src/shared/opencode-agent-row-scanner.ts) — detects OpenCode's <agent> · <model> row by screen position (separator on the line directly above the box's ╹ corner, inside the alt screen, with the box cursor shown under bracketed paste), asks for a 5 s grace while the box is up without the row, and withdraws readiness/grace on alt-screen leave or bracketed-paste off. leftAltScreen prevents a shell cursor outside the alt screen from re-arming the grace.
  • Signal plumbing — resolvePasteReadySignal(config, submit) plus submitPasteReadySignal; opencode/opencode2 use opencode-agent-row only when Enter follows the paste, so draft-only pastes keep render-cursor-after-bracketed-paste.
  • Waiters — main and renderer readiness waiters arm a one-shot grace from readyAfterMs (withdrawn on null, not restarted); the hard deadline takes the box when a grace is pending. waitForFreshWorkerComposer passes submit: true.
  • Serializer baseline (0ec73d9) — registers KNOWN_PREEXISTING_I2_FAILURES counts for the six new OpenCode captures. I re-ran serialize-grid-transcript-replay.test.ts filtered to opencode against the merged (changed-on-main) serializer: all 13 transcripts pass, so the exact-count registrations are still correct post-merge.
  • Evidence — scanner/transcript/chunking/worker-lane/launched-agent/runtime-readiness suites all pass; the dropped-Enter and grace cases are pinned to recorded timings.

The Windows gap (if alt-screen/2004/╹ are not forwarded, the 5 s grace falls back to today's behavior rather than pasting early) remains documented and unverified, as the PR states.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

m4air and others added 2 commits October 5, 2026 12:25
…code-worker

# Conflicts:
#	src/main/runtime/launched-agent-composer-readiness.test.ts
#	src/main/runtime/orca-runtime-activate-managed-worktree.ts
#	src/main/runtime/runtime-worktree-startup-readiness.ts
With a grace pending, the deadline runs one more settle check, and over SSH
its foreground read could hold a past-due wait for the channel's 30 s
timeout. It now gives up after 2 s. Tests cover a hand-off, a failure and an
abort during that check, a refused grace settled at the deadline, the bound,
and the grace restarting at a hand-off.

Co-Authored-By: Claude <noreply@anthropic.com>

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

Caution

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

⚠️ Outside diff range comments (1)

🟡 Minor · Cancel readiness when the PTY exits. · agent-draft-readiness.ts:61-71

src/renderer/src/lib/agent-draft-readiness.ts:61-71
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Cancel readiness when the PTY exits.

When OpenCode’s submit scanner sees the box cursor without the agent row, it arms a 5-second grace timer. The waiter does not settle on local PTY exit, and its remote stream subscription does not pass onEnd; with no further data, the timer still resolves true. pasteDraftWhenAgentReady then skips its process-check fallback and sends to the captured ptyId. If the local PTY has exited, writeAccepted returns false, but sendRuntimePtyInputVerified falls back to fire-and-forget pty.write and returns true. The launch path can report the dropped prompt as delivered. Settle the wait as false on local PTY exit or remote stream end.

Suggested fix
 import { subscribeToPtyData } from '@/components/terminal-pane/pty-data-sidecar-subscriptions'
+import { subscribeToPtyExit } from '@/components/terminal-pane/pty-dispatcher'
 import { replayPreHandlerPtyData } from '@/components/terminal-pane/pty-pre-handler-buffer'
 ...
     let hardTimer: number | null = null
     let unsubscribe: (() => void) | null = null
+    let unsubscribeExit: (() => void) | null = null
 ...
       unsubscribe?.()
+      unsubscribeExit?.()
       resolve(value)
 ...
         `desktop:paste-ready:${ptyId}`,
-        observeData
+        observeData,
+        { onEnd: () => finish(false) }
       )
 ...
     } else {
+      unsubscribeExit = subscribeToPtyExit(ptyId, () => finish(false))
       unsubscribe = subscribeToPtyData(ptyId, observeData)

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c126ec3d-3d1c-4c31-bb2b-00d544f5f63a
📥 Commits

Reviewing files that changed from the base of the PR and between 6ce7d6f and 391700e.

📒 Files selected for processing (6)
  • src/main/runtime/launched-agent-composer-readiness.test.ts
  • src/main/runtime/orca-runtime-activate-managed-worktree.ts
  • src/main/runtime/runtime-worktree-startup-readiness.test.ts
  • src/main/runtime/runtime-worktree-startup-readiness.ts
  • src/renderer/src/lib/agent-paste-draft.ts
  • src/shared/draft-paste-ready-scanner.ts

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

@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
Incremental re-review of the OpenCode submit-readiness fix (STA-9359), covering everything since pullfrog's prior review at 6ce7d6f: the merge of origin/main (integration with #24257's agent-launch refactor) and a follow-up commit that bounds the readiness deadline's last settle check.

  • Merged origin/main's launch-readiness refactor (#24257) — isShellInFront, accept, hand-off tracking, and the settleSignal/recheckAt machinery are preserved intact from main; this PR's submit signal and grace timer layer on top through the extracted passesSettleChecks, which is semantically identical to the inline checks it replaces.
  • submit: true for the fresh-worker composer — waitForFreshWorkerComposer now waits on OpenCode's agent row for every caller. All of its call sites (worker start, agent.launch terminal prompt, the grok marker race, and the non-marker idle fallback) paste and then press Enter, so the harder signal is only reached by deliveries that actually submit; other composer-marker agents fall back to their unchanged draft signal.
  • Deadline settle bound (391700e) — the deadline's one final settle check is capped at DEADLINE_SETTLE_CHECK_MS (2 s), so an SSH foreground read can no longer hold a past-due wait to the channel timeout. A failure, abort, or shell hand-off during that check settles null, and a refused grace still takes the box once the agent is proven in front.
  • Test coverage — the new main-lane and renderer cases pin grace arming/withdrawal, the deadline fallback, and the 2 s bound with exact assertions (advanceTimersByTimeAsync(1999) then (1), not loose truthiness).

I re-ran the readiness and scanner suites (runtime-worktree-startup-readiness, launched-agent-composer-readiness, opencode-worker-composer-transcript, opencode-agent-row-enter/structure-transcript, draft-paste-ready-scanner-chunking, draft-paste-ready-scanner) — 238 tests pass. The Windows gap (if alt-screen/2004/╹ are not forwarded, the 5 s grace falls back to today's behavior rather than pasting early) remains documented and unverified, as the PR states.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

@OrcaWin
OrcaWin merged commit 990b0ed into main Oct 5, 2026
95 of 103 checks passed
@brennanb2025
brennanb2025 deleted the brennan/sta-9359-opencode-worker-reports branch October 6, 2026 00:14
brennanb2025 added a commit that referenced this pull request Oct 6, 2026
…ch-prompt-argv-carry

Conflicts resolved against #23962's head this branch had merged (83de487), so each keeps
main's squashed #23962 plus this branch's change:
- daemon protocol v42 (launch files), v41 (#23962's staged startup lines) now in PREVIOUS;
  build compatibility contract and pinned version tests follow.
- agent-draft-readiness keeps main's OpenCode agent-row grace (#25101) and this branch's
  bracketed-paste-off revocation of the quiet window.
- startup-line-prompt-carry.test.ts stays deleted; its rule lives in launch-prompt-carry.

Semantic follow-ups from #23672 (CR submission, one-line PowerShell quoting):
- windowsLaunchLineVerdict reads line breaks from the prompt too: PowerShell now quotes a
  multi-line prompt onto one physical line, which hid the measured multi-line rows (a 9 KB
  multi-line prompt read as exact) and the shim's line-break cut.
- Typed launch lines end in CR; raw multi-line lines submit each break with CR; the PowerShell
  multi-line expectations use the one-line backtick form.
- A duplicated pair of #23962 staging tests left by the auto-merge is removed.
- agent-paste-draft: the two identical launch-paste tails share sendLaunchPaste; tui-agent-config
  writes devin on one line like jcode (main sat at the line cap).
8bitbish pushed a commit to 8bitbish/orca that referenced this pull request Oct 7, 2026
…de can submit it (stablyai#25101)

* fix(orchestration): keep pressing Enter until an OpenCode worker's task starts

OpenCode 2 draws its input box before it has loaded its agent list from its
server, and silently drops an Enter that arrives in that gap. On a loaded
machine the gap outlasts the 0.5 s Orca waits after pasting, so a worker's
task sat unsent in the box while worker-start reported it ready.

OpenCode's turn start is now observed through its status hooks, like Codex
and Claude. While no turn has started, the prompt verifier presses Enter
again on an interval (per the agent row, opencode and opencode2) for as
long as the pane's hooks are reporting, never past an approval or question
prompt or a replaced pane, and at most a fixed number of times. A pane
whose hooks never report keeps today's single Enter and unobserved receipt.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(opencode): give a worker its task only once OpenCode can submit it

OpenCode 2 draws its input box, and shows its cursor there, before its agent
list has loaded from its server, and it silently drops an Enter that arrives in
that gap. Orca pasted a worker's task on the box's cursor and pressed Enter half
a second later, so on a loaded machine the task stayed unsent in the box while
the worker read as started.

OpenCode's paste readiness now also waits for the row under the input box
(`<agent> · <model>`), which OpenCode paints only once the agent list exists.
The rule keys on the row's separator inside OpenCode's alternate screen, not on
names or colours. Both the opencode and opencode2 rows use it, worker start and
the renderer's draft paste alike. OpenCode 1 paints the row in the box's own
frame, so it is ready on the same read as before.

Recorded OpenCode 2.0.21 boots (cold, hidden pane, four at once, attached to a
running server) are the evidence; two more show an Enter after the row
submitting and one at the box being dropped. The earlier resubmit loop and the
settlement-provider changes are removed.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(opencode): prove the agent-row signal with natural captures and never paste a draft blind

Replaces three recordings made by launching four OpenCode boots at once with
natural single-run evidence: one cold start that pasted on the agent row and
pressed Enter on Orca's real 500 ms timing (submitted), and one cold start
recorded under heavy load from other sessions, where the paste on the box's
cursor and Orca's Enter came before the agent row and the brief stayed unsent.
Adds a 2.0.14 cold start as a cross-version check.

The renderer's draft paste no longer falls back to a blind paste when the
OpenCode agent-row signal never arrives: OpenCode would drop the Enter and
leave the draft in its box, so the caller gets its timeout notice instead.
Other agents keep the fallback.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(opencode): type the single-signal predicate against the public signals

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(opencode): read the agent row by its place on screen and wait for it only before Enter

- The agent-row signal now keys on structure: the `·` must sit on the line
  directly above the input box's bottom-left corner, so a `·` in the footer
  path or a session tab title no longer counts. Leaving the alternate screen or
  turning bracketed paste off withdraws what each part established.
- Only deliveries that press Enter wait for the row (new
  `submitPasteReadySignal`); drafts keep the box-cursor rule, as on main.
  Worker start always submits.
- When the box cursor has shown but the row never comes (OpenCode 2 drops the
  agent below 44 columns), both waiters proceed after a 5 s grace, as main
  did. A natural 40-column capture proves that start submits.
- The renderer's blind-paste fallback is back to main's behaviour.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(opencode): take the box at the readiness deadline while the agent-row grace is pending

- Both paste waiters resolve ready, not timed out, when the hard deadline
  lands while the 5 s grace is pending: the box was seen, so this is the
  verdict the box rule gave before.
- Tab-bound draft paste is tested to wait on the agent row only when Enter
  follows.
- Scanner: a separator pairs with its corner only inside one synchronized
  frame, row 0 clamps to 1, and the grace comment names the transparent-theme
  case. The draft signal type no longer admits the agent-row signal, whose
  grace only the paste waiters honour.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(opencode): withdraw the agent-row grace when OpenCode leaves the alternate screen

If OpenCode exited after drawing its box but before its agent row, the grace
still ran out and the task was pasted into the shell. Leaving the alternate
screen now clears the box cursor, and once an app has left it a cursor the
shell shows outside it no longer starts a grace. A terminal that never
forwards the alternate screen still gets one.

Co-Authored-By: Claude <noreply@anthropic.com>

* test(serialize): register the new OpenCode captures' pre-existing grid-restore counts

The six OpenCode 2 captures this PR adds hit the serializer limitations the
1.18.32 and 2.0.18 captures are already registered for: the live pen's
background (and once its bold bit) on restored cells, rows kept below the
source after a shrink, and the cursor one column short after a shrink. No
I1 or I3 failure; the serializer is unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(opencode): bound the readiness deadline's last settle check

With a grace pending, the deadline runs one more settle check, and over SSH
its foreground read could hold a past-due wait for the channel's 30 s
timeout. It now gives up after 2 s. Tests cover a hand-off, a failure and an
abort during that check, a refused grace settled at the deadline, the bound,
and the grace restarting at a hand-off.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude <noreply@anthropic.com>
(cherry picked from commit 990b0ed)
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