Skip to content

Test persistent tmux pane workers for <Terminal.Grid> #726

Description

@taras

Story

As a maintainer preparing <Terminal.Grid>, I want an executable proof that an
XMD-controlled worker can live inside each tmux pane, so #717 can adopt a process
topology that supports reliable readiness, pane output, sequential native
launches, and complete teardown without guessing about terminal behavior.

The proof runs one 2-by-2 workspace. Each pane starts with a persistent worker
that owns the terminal tmux created for it. The parent XMD process can display
text in a pane, ask its worker to start an interactive child, learn whether that
child actually started, wait for its exit, display more text, and then start
another child in the same pane. The fourth worker starts an ordinary interactive
shell through the same boundary.

This is a prerequisite evaluation for #717, not the terminal-grid feature
itself. Its result decides whether the pane-worker topology enters the
implementation plan or the architecture returns to provider research.

Current gap

Tmux can create the required per-pane pseudo-terminals, but a tmux pane process
ID does not establish that the requested executable started. Tmux completes its
spawn command after forkpty; the child attempts exec afterwards. On tmux
3.6a, /definitely/missing x and a real /bin/sh -c "exit 1" both leave a dead
pane with status 1 and a pane PID. A %begin/%end response plus #{pane_pid}
therefore cannot implement #717's readiness boundary.

Starting the native process directly as the tmux pane command also leaves no
persistent endpoint for document text rendered before or after that process, or
for a later sequential launch in the same pane. send-keys and tmux buffers
inject process input; they are not a pane display sink.

The ordinary @effectionx/process operation is not the missing primitive. It
pipes stdout and stderr through process middleware and starts a detached process
group. A native UI must inherit the pane worker's terminal without capture. The
current native launcher already models that distinction, but it does not expose
a separate successful-spawn acknowledgement.

Evaluation contract

Tmux starts one persistent worker as the initial process in each pane. The worker
inherits that pane's controlling terminal and remains alive for the pane's
lifetime. The parent communicates with it through invocation-private IPC rather
than passing arbitrary native command vectors through tmux's textual command
parser.

The worker starts an interactive child through an Effection-owned process
resource derived from the existing native-launch semantics. Its host adapter may
use the same asynchronous child-process API as the current native launcher; the
proof does not put a direct Deno.Command.spawn() call or ordinary
@effectionx/process execution on the product boundary. The resource:

  1. inherits the worker's terminal for stdin, stdout, and stderr;
  2. distinguishes the child-process spawn event from its error event;
  3. acknowledges readiness only after successful spawn;
  4. reports exact exit code or signal independently of readiness; and
  5. owns cancellation, escalation, reaping, and finalization through its
    Effection scope.

The IPC transports a structured command vector, cwd, filtered environment, pane
display bytes, cancellation, readiness, and settlement. It is private to one
invocation, admits only the worker assigned to that pane, and is removed on
success, failure, and cancellation. Raw instruction text never enters argv or
environment. Provider socket paths, tmux identifiers, process identifiers, and
IPC credentials stay in temporary diagnostic evidence and do not become public
or durable values.

The proof constructs the authored column geometry explicitly. Tmux's
select-layout tiled may choose its column count from terminal dimensions and
does not by itself implement the columns prop.

Reader-visible sequence

The successful proof performs this sequence:

  1. Create a hidden invocation-private tmux server and a 2-by-2 layout.
  2. Start one worker in every pane and establish each private IPC channel.
  3. Send visible prelude text to at least one paired pane.
  4. Start interactive test children in all paired panes and the default shell in
    the shell pane.
  5. Wait until every worker reports successful child spawn, then attach the
    complete grid once.
  6. Interact with at least two children concurrently.
  7. Let one child exit while its siblings remain interactive, display its status,
    render epilogue text in that pane, and start a second child there.
  8. Close the grid, cancel the remaining children, await worker and provider
    teardown, restore the original terminal, and only then finish the proof.

No real Agent CLI or model turn is required. A deterministic interactive child
stands in for a native Agent UI and verifies terminal ownership, input, output,
readiness, settlement, and cancellation.

Acceptance

  • Every worker and interactive child reports that stdin, stdout, and stderr are
    terminals associated with the expected pane. The shell demonstrates ordinary
    interactive job control rather than merely printing through a PTY.
  • A missing executable produces a startup failure and never acknowledges pane
    readiness. A real executable that starts and immediately exits 1 acknowledges
    readiness and then reports exit 1. The evidence distinguishes those paths.
  • Command arguments containing spaces, quotes, newlines, semicolons, dollar
    signs, and backticks arrive byte-for-byte unchanged. They travel through
    structured IPC rather than tmux command-string interpolation.
  • The parent renders pane text before and after an interactive child without
    injecting that text as the child's input. Native child terminal bytes are not
    copied back through the parent.
  • One pane runs two interactive children sequentially without replacing its tmux
    pane or worker. Attempting two simultaneously on that pane refuses, while
    children in distinct panes remain concurrent.
  • A 2-by-2 layout and a five-pane layout with columns={2} preserve authored
    row-major placement at materially different terminal dimensions. The proof
    does not rely on select-layout tiled choosing the desired column count.
  • The visible attach begins only after every pane's interactive child has
    acknowledged successful spawn. A startup failure in any pane exposes no
    partial grid and tears down every worker and child already started.
  • Detaching the visible client is distinguishable from losing the control or IPC
    connection and from the tmux server stopping. None of those signals alone is
    treated as proof that pane work ended.
  • Reader close and parent cancellation each stop accepting launches, cancel the
    active child in every pane, await each worker's quiescence acknowledgement,
    stop the tmux server and its attach/control clients, remove the private IPC and
    socket paths, and restore the original terminal before the probe reports
    completion.
  • A negative child ignores the first interruption and forks a descendant in its
    inherited process group. Teardown escalates and establishes that neither can
    continue. A second negative child escapes that group; the result records
    whether the architecture can prove it stopped rather than silently treating
    initial-process or process-group disappearance as complete teardown.
  • Startup, all-pane readiness, attach, reader-close detection, and teardown are
    measured for 2, 4, and 8 panes over at least 20 runs on the same host. The
    report gives medians and ranges rather than inventing a pass threshold before
    measurement.
  • Cancellation is exercised at provider preparation, worker readiness, visible
    attachment, and active-child execution. Every acquired resource has one named
    Effection scope and finalizer in the evidence report.

Evidence and decision

The proof provides one documented command that runs from a prepared checkout,
plus a report containing:

  • the exact commit, operating system, architecture, tmux version, Deno version,
    and command used;
  • the observed terminal session and process-group relationships;
  • successful and negative-path results for every acceptance item;
  • startup and teardown measurements;
  • a resource-ownership diagram; and
  • the smallest provider and interactive-process interfaces the evidence
    supports.

The result changes #717 planning as follows:

  • If readiness, display, sequential reuse, job control, and teardown all hold,
    the Planner uses the persistent pane-worker topology and its measured lifecycle
    boundary.
  • If the terminal and IPC behavior holds but complete teardown cannot be proven,
    Quest: Add interactive terminal grids to executable documents #717 remains blocked while the Architect narrows or replaces the lifecycle
    contract. The implementation does not weaken it silently.
  • If a worker cannot host the native child with correct controlling-terminal or
    job-control behavior, the topology is rejected and Quest: Add interactive terminal grids to executable documents #717 returns to provider
    research.

The proof may live on a dedicated branch and does not need to ship in the product
binary. This issue closes when the exact evidence commit and report are linked
and the resulting architecture decision is recorded.

Out of scope

  • Shipping <Terminal.Grid> or <Terminal> syntax.
  • Durable grid identity, completed replay, or partial replay.
  • Real Agent-session preparation, ownership, attachment, or provider proofs.
  • Production status styling, key bindings, diagnostic wording, or terminal
    compatibility beyond what discriminates the topology.
  • Adding a native PTY dependency or a separate compiled helper binary.
  • Treating tmux-specific identifiers as public component, provider, durable, or
    diagnostic data.

Activity

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

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions