Skip to content

Keep command output inside its round in xmd repl #1026

Closed

Description

@taras

Story

As a person running commands in xmd repl, I want their stdout and stderr to appear inside the owning round, so command output cannot overwrite the transcript, Sidekick or input controls.

An ordinary command needs no capture binding to protect the interface:

```sh exec
printf 'command output\n'
printf 'command diagnostic\n' >&2
```

Both streams are presented through the REPL while the command runs. The terminal layout, focus and any unsent draft remain intact.

Current gap and reproduction

In the reported session a5544ec760b14ac84c36bd563106f292, Entry 3 runs pwd and git status --short --branch in an ordinary sh exec fence. Its command result retains stdout, but its root rendered output is only a newline. The foreground bytes bypass the REPL output view and overwrite the terminal panes.

Entry 4 runs the same commands with as="location" and explicitly renders {location.stdout}. Its root output contains the expected text, which appears in the Output pane. This is a useful negative control, not a workaround authors should need.

Core's ordinary foreground routing forwards both process channels. That behavior is appropriate for xmd run; the interactive REPL does not currently contain that forwarding at its execution boundary.

Contract

  • Route foreground stdout and stderr from REPL-owned command execution through the owning round's presentation. Stream output while work is running rather than waiting for exit.
  • Only the REPL terminal renderer writes presentation bytes to the physical terminal while the UI owns it. Command text and terminal control sequences cannot draw outside the output surface, clear other panes or change terminal modes.
  • Preserve stdout/stderr identity, retained command evidence, exit status, capture bindings and the document's existing output semantics. Captured or hidden output does not become visible merely because the REPL is hosting it; explicitly rendered captured output appears once.
  • Credit output to the round that produced it, including late output during cleanup, background Sidekick activity and changes to the selected history position. Output arrival does not move focus, change the selected round or edit a draft.
  • Preserve ordinary command forwarding for xmd run and its stdout/stderr contracts. Implement the REPL display policy through the existing scoped host/process APIs rather than changing global process streams.

Acceptance and evidence

  1. Reproduce the unbound fence through a mounted production REPL. Verify both streams appear in the owning round while it is running and that every physical terminal write follows the REPL renderer path. The same test fails without the routing fix.
  2. Compare unbound execution with as="result" plus explicit interpolation, and with hidden/captured routing. Verify correct visibility, no duplicate output, and unchanged saved stream values and exit status.
  3. Exercise long lines, rapid output, split Unicode, carriage returns and terminal cursor/clear sequences. Neighboring panes and editable drafts remain intact; output remains inspectable within its surface.
  4. Exercise nonzero exit, cancellation and held cleanup. Preserve diagnostics and existing lifecycle behavior, and never attribute an earlier command's output to a later round.
  5. Switch history and reopen completed history. Saved command evidence still identifies the correct round and channels; viewing it launches no command.
  6. Run focused REPL regressions and the existing xmd run foreground/capture tests. Update the REPL specification to describe the routing and terminal ownership boundary.

Relationships

#1005 owns headless child-session startup and interactive attachment. #1006 owns Sidekick inspection of saved command diagnostics. This issue owns ordinary command output containment in the current interactive REPL; neither broader capability is a prerequisite.

Investigation entrypoints: packages/cli/src/repl/session.ts, packages/core/src/foreground.ts, the exec routing in packages/core/src/execute.ts, and the runtime process/stdio APIs.

Activity

  1. added
    bugSomething isn't working
    UXUser-facing usability and interaction improvements
    on Oct 30, 2026
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

    UXUser-facing usability and interaction improvementsbugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions