Skip to content

Open terminal grids with tmux in foreground runs #732

Description

@taras

Story

As a Deno or compiled xmd run user, I want an authored terminal grid to open
as one foreground tmux workspace, so I can use the provider-neutral grid on a
real terminal without putting tmux commands or identifiers in the document.

This is the first production-provider Story under #717. It implements the tmux
adapter for #730's terminal-provider contract. Closed #726 and PR #727 are
accepted feasibility and lifecycle evidence; they are not product
implementation and do not satisfy this Story by themselves.

Host contract

The Deno source entrypoint and compiled binary install the tmux grid provider
for ordinary foreground runs when the invocation has a terminal and the
required tmux capability is available. A missing terminal, tmux executable, or
other live prerequisite refuses before any pane starts or partial grid appears.
Node and Bun retain the same language, catalog, and validation from #729 but
install no operational grid provider in this delivery.

The provider creates one invocation-private tmux server per grid and one
persistent initial worker and session leader per pane. Per-pane Unix sockets
live under one short mode-0700 private directory. Each worker admits one
authenticated connection through a mode-0600 token, then removes the token.
Exact argv, cwd, and environment cross this private channel and never tmux's
command parser. The worker starts the interactive child with all three standard
streams inherited from the pane terminal, reports readiness only from the
runtime spawn event, writes provider display without reading child input, and
admits one live child at a time. It runs under Effection run(), not main(),
so terminal SIGINT remains job control for the foreground child.

The provider prepares the hidden panes, applies an explicit layout, and swaps
pane positions into authored row-major order because tmux ignores pane IDs in
layout leaves. It keeps the visible inherited-stdio attach client separate from
one no-output control-mode client. Reader detach, control-channel loss, and
server stop remain distinct lifecycle events. Visible attach happens only after
the all-pane readiness barrier. Loss of the root host terminal through SIGHUP
becomes structured grid cancellation.

Provider commands, socket and directory paths, authentication tokens, server,
session, window, pane, process, attach-client, and control-client identifiers
remain inside the provider closure. They appear in no component prop, binding,
public result, durable record, or diagnostic.

Teardown boundary

The accepted #726 topology and the teardown boundary in architecture.md are
required product behavior:

  • before the first cancellation signal, snapshot every observable descendant of
    the live child and every other member of its pane process group;
  • before reporting one launch settled, clearing the pane as active, or admitting
    a sequential launch, establish that the child, those descendants and group
    members stopped and that no other process holds the pane terminal;
  • repeat the terminal-holder proof while each persistent worker still makes its
    terminal observable during grid teardown;
  • detach the visible client before cancellation signals, quiesce every worker,
    close the private channels, stop the exact server, establish server-process
    disappearance and session refusal, remove the private directory, restore the
    root terminal, and only then settle the grid.

A PID, successful signal delivery, attach-client exit, or elapsed timeout proves
none of those facts. Failure to establish the bounded facts is a teardown
failure and the document does not continue.

The boundary is deliberately honest on macOS: a descendant that already
created a new session, closed every descriptor for the pane terminal, and
outlived the parent that connected it to the launch is no longer observable
through ancestry, process group, or terminal holders. The provider makes no
claim to discover or terminate that detached daemon. It does prove that no
process remains in a pane process group, no process remains descended from a
child alive when cancellation began, and no process holds a pane terminal.

The initial terminal-holder sweep may use lsof -t. #726 measured that sweep as
the dominant handoff and teardown cost, increasing with process count. That is a
design constraint, not a new proof task and not an acceptance latency threshold.
Holder enumeration belongs behind the host process adapter so it can be replaced
with a cheaper primitive without admitting another child before the same
quiescence fact is established.

Acceptance

  • The Deno source and compiled foreground hosts open the exact provider-neutral
    grid through one private tmux server; missing prerequisites refuse before pane
    start and leave no server, worker, socket, token, or terminal change behind.
  • Persistent workers authenticate over invocation-private IPC, preserve exact
    argv/cwd/environment bytes outside tmux parsing, give the child inherited
    terminal streams and real job control, and keep provider display out of child
    input.
  • A worker reports readiness only after the runtime spawn event and reports
    settlement only after the per-launch descendant, process-group, and
    terminal-holder sweep completes. An immediate next launch refuses while that
    sweep is active and is admitted only afterwards.
  • The explicit layout plus pane swaps produces authored row-major order at
    different terminal sizes without using tmux's automatic tiled layout.
  • The visible client attaches only after every pane is ready. Startup failure
    tears down the hidden server without presenting a partial grid.
  • Reader detach, control loss, server stop, root SIGHUP, and parent
    cancellation are classified independently and reach Execute concurrent terminal panes through a replaceable provider #730's specified close,
    failure, or cancellation outcome.
  • Teardown establishes the bounded process and terminal facts above, removes
    every worker/client/server and private path, restores terminal state, and
    preserves the documented detached-daemon limitation.
  • Provider identifiers and private launch data enter no authored, result,
    retained, or diagnostic surface.
  • Node and Bun catalog and validate the syntax but refuse execution from provider
    absence before pane start.
  • The provider satisfies the tmux half of TG18 while the controlled non-tmux
    provider remains the authority for core lifecycle semantics.

Focused evidence

Add production-adapter tests using fake tmux processes and exact
invocation-private handles; do not derive core outcomes from tmux identifiers:

deno task test packages/runtime/tests/terminal-processes.test.ts
deno task test packages/cli/tests/terminal-grid-tmux.test.ts
deno task test packages/cli/tests/session-launch-cli.test.ts

The deterministic suite covers prerequisite refusal, IPC authentication and
exactness, layout ordering, attach barrier, independent close signals, every
teardown phase, sequential handoff, SIGHUP, cleanup, and Deno/compiled versus
Node/Bun host installation. #726's deno task proof:tmux-pane-workers and PR
#727 remain design evidence; this Story does not create another POC or invent a
timing budget from those measurements.

Dependencies and exclusions

Verification stack

This Story is the fifth and final layer of #717's linear verification stack.

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