Skip to content

🚚 Name executable document grids <Grid> and <Pane> (#781) - #797

Merged
taras merged 2 commits into
agent/issue-717-terminal-packagesfrom
agent/issue-781-grid-pane
Sep 10, 2026
Merged

🚚 Name executable document grids <Grid> and <Pane> (#781)#797
taras merged 2 commits into
agent/issue-717-terminal-packagesfrom
agent/issue-781-grid-pane

Conversation

@taras

@taras taras commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Closes #781. Stacked on #771 (agent/issue-717-terminal-packages) — the #717
quest, still unmerged. This PR carries two commits: the settled contract
(cfe4d0ba, architecture and specifications) and the implementation
(860448b6).

Why

Terminal.Grid and <Terminal> named a whole presentation after the terminal
that one kind of pane happens to need. A read-only Agent session view is pane
content without being a terminal, and a second multiplexer or a host-native
composite would have had to adopt terminal-specific vocabulary to implement the
same contract. The stack has not merged or shipped, so none of those names is a
compatibility surface — this is the last moment they are free to change.

What changes

Before:

<Terminal.Grid columns={2}>
  <Terminal title="Implementor">…</Terminal>
  <Terminal title="Shell" />
</Terminal.Grid>

After:

<Grid columns={2}>
  <Pane title="Implementor">…</Pane>
  <Pane title="Shell" />
</Grid>

Schemas, forms, direct-child rules, row-major layout, concurrency, replay,
failure containment, cancellation, leases and teardown are all unchanged.
<Grid> remains an xmd run provider and is not added to Workflow execution.

The packages move with the names:

Was Is
packages/terminal / @executablemd/terminal packages/grid / @executablemd/grid
packages/terminal-tmux / @executablemd/terminal-tmux packages/grid-tmux / @executablemd/grid-tmux
packages/core/src/terminal packages/core/src/grid

xmd syntax now says "Open several panes in one view" for <Grid>, and
<Pane> keeps "Expand Markdown or open a shell in a pane."

How it works

One rule per role, applied everywhere:

grid orchestration → Grid*    authored children and claims → Pane*    provider APIs → GridProvider*

So TerminalGridRequestGridRequest, TerminalPaneClaimPaneClaim,
TerminalProviderUnavailableErrorGridProviderUnavailableError,
createTerminalAuthoritycreateGridAuthority, openTerminalGrid
openGrid, TerminalTeardownFailedGridTeardownFailed. The two contextual
descriptors keep their stable names in the spelling the architecture settled:
TERMINAL_GRIDS_APIGRIDS_API ("Grids") and TERMINAL_PROVIDERS_API
GRID_PROVIDERS_API ("GridProviders").

Review guide

Start with: packages/grid/tests/package-boundary.test.ts

Then review:

  1. packages/core/src/structural.ts and structural-rules.ts — the authored
    grammar and its diagnostics.
  2. packages/grid/mod.ts, lifecycle.ts, processes.ts, posix.ts,
    testing.ts — the five public roots.
  3. packages/core/src/grid/journal.ts — the durable identity, deliberately
    unchanged.
  4. .github/workflows/publish-packages.yml — regenerated, not edited.

Look carefully at:

  • The line between a grid and a terminal. Every remaining Terminal in
    this diff should describe a PTY, a lease, a signal or a process boundary.

What must stay true

  • Nothing a journal already holds is invalidated — the durable event kind
    terminal_grid_layout and the identity terminal_grid:${path}:layout keep
    their exact spelling; enforced by packages/core/src/grid/journal.ts and
    checked by the TG17 replay rows in packages/core/tests/grid.test.ts.
  • The tmux protocol is untouched — the provider identity tmux and the
    hidden terminal-worker verb are unchanged; checked by
    packages/grid-tmux/tests/grid-tmux.test.ts TW12 and PANE_WORKER_COMMAND
    in TG21q's exact root.
  • Terminal survives as a capabilityNO_TERMINAL, reserveTerminal,
    NativeLauncher, PaneTerminal, TerminalProcesses, TerminalSignal,
    TerminalLost; checked by TG21r, which fails if the rename deleted the
    capability instead of renaming the presentation.
  • No compatibility surface remains — no old package, directory, alias,
    forwarding barrel or reserved construct; checked by TG21e, TG21m, TG21n and
    TG21o.
  • The neutral domain depends on nothing above it — checked by TG21a–c.

How to verify it

  • TG21o proves only Grid and Pane are declared and neither
    Terminal.Grid nor Terminal is reserved, and fails if an alias is restored.
  • TG21p is the discriminator for every absence row above: it plants each
    rejected spelling into the exact scanner that must report it, so those rows
    cannot pass because the scanner is blind.
  • TG21q pins all six public roots as exact sets rather than required
    names, so a symbol that reaches a root by being added to it is caught.
  • TG21r proves the technical terminal exports are still reachable while no
    facet brings back a rejected presentation name — without it, deleting the
    terminal capability outright would satisfy every other absence row.
  • TG21m reads the manifests, all three lockfiles and the generated publish
    workflow by name, because a source that imports a deleted package fails
    loudly while a manifest that still names one fails nothing until a release.
  • Focused suites run, all passing: authored grammar
    (grid-structure, syntax-catalog, document-validation, syntax-cli);
    provider lifecycle and replay (grid-provider, core/grid); pane-native
    routing (native-launcher, terminal-processes, agent-session-launch,
    three native-launch suites, grid-native-launch); tmux and host teardown
    (grid-tmux, grid-host, session-launch-cli); publication and discovery
    (package-boundary, both publish-workflow suites, runtime-exclusions,
    test-file-discovery, jsr-consumer-documentation).
  • deno task check exits 0, deno task check:jsr ends Success Dry run complete, and package-boundary.test.ts passes under Node and Bun as well
    as Deno.

Scope

Included

  • The authored syntax, the two packages, core's adapters, and every consumer,
    manifest, lockfile, generated workflow and test path that named them.
  • The strengthened TG21 boundary regression, moved to packages/grid/tests/.
  • Four repairs to defects that already existed on the base and blocked the
    evidence — see Risks.

Intentionally unchanged

  • The site's React Terminal presentation component (site/routes/index.tsx).
    It is technical UI and outside this contract.
  • The durable event kind, durable identity, tmux provider identity and protocol,
    and the terminal-worker command spelling.
  • <Grid> is still not available to Workflow execution.
  • The private context key core.terminal.pane, which carries a PaneTerminal.

Generated or mechanical changes

  • .github/workflows/publish-packages.yml comes from
    deno task gen:publish-workflow. It now orders grid after durable-streams,
    grid-tmux and core after grid, and cli after all of them.
  • deno.lock, pnpm-lock.yaml and bun.lock were regenerated in the order
    AGENTS.md prescribes: deno install --frozen=false, deno task setup,
    bun install.
  • Every other file outside packages/grid*, packages/core/src/grid and
    packages/grid/tests/package-boundary.test.ts is an identifier or path
    rename with no intended behavior change.

Risks and limitations

deno task lint fails on this branch, and it already failed on the base.
Twenty local/require-scope-bound-event-registration errors sit in
packages/grid/src/posix-launcher.ts, five grid-tmux sources,
grid-tmux/tests/grid-tmux.test.ts and cli/src/grid-host.ts. The flagged
lines are byte-identical to cfe4d0ba, and #757 — which installed that rule —
is an ancestor of this base. The #717 stack was written before the rule landed
and was never rebased onto it. Repairing them is Effection listener-lifetime
work on #717 rather than naming, so this PR does not attempt it; it needs a
decision about where that work belongs.

Three further defects already on the base blocked the evidence matrix itself
and are repaired here, conservatively:

  • reap was imported from native-launcher.ts; it is defined in
    posix-launcher.ts.
  • syntax-cli.test.ts called syntaxCatalog/parseCatalog, which Add read-only XMD information requests to xmd plan #762 renamed
    to syntaxSymbols/parseSymbols, and syntax-catalog.test.ts TG3 asserted
    catalog version 1 where every other row in the same file asserts 2.
  • jsr-consumer-documentation.test.ts staged its MEMBERS without the neutral
    domain package, so core's import of it could not resolve for a JSR consumer.
  • cli.ts passed a third argument to two-parameter resolveAuthorshipStack.
    The argument is dropped rather than threaded, which preserves today's
    behavior: xmd plan installs no grid provider. If xmd plan was meant to
    present grids, that is a separate product decision.

One judgment call inside the rename: the private context key
core.terminal.installation became core.grid.installation. It carries a
GridInstallation, is not exported, and is live-only rather than durable.

Delivery gates not yet run: the full deno task test, pnpm test:node,
bun run test:bun, deno task build, deno task verify:clean, and the
Measure-test-weights dispatch.

Scope confirmation

  • Every changed file supports the purpose described above.
  • Unrelated cleanup and formatting changes are excluded.
  • Generated or mechanical changes are clearly identified.
  • The description matches the final diff and test results.

`Terminal.Grid` and `<Terminal>` named a whole presentation after the terminal that one kind of pane happens to need. The authored syntax is now `<Grid>` and `<Pane>`, the provider-neutral domain ships as `@executablemd/grid`, and the tmux provider as `@executablemd/grid-tmux`. Core's adapters move to `packages/core/src/grid`.

Public symbols follow one rule: grid orchestration is `Grid*`, authored children and claims are `Pane*`, provider APIs are `GridProvider*`. `TERMINAL_GRIDS_API` becomes `GRIDS_API` under the stable name `Grids`, and `TERMINAL_PROVIDERS_API` becomes `GRID_PROVIDERS_API` under `GridProviders`.

Terminal stays where it names a real capability. `NO_TERMINAL`, `reserveTerminal`, `NativeLauncher`, `PaneTerminal`, `TerminalProcesses`, `TerminalSignal` and `TerminalLost` are unchanged, as are the tmux provider identity, the hidden `terminal-worker` verb, the durable event kind `terminal_grid_layout` and the durable identity `terminal_grid:${path}:layout`. Nothing a journal already holds is invalidated.

No compatibility package, re-export, alias, forwarding barrel or old implementation directory remains, and the generated publication workflow places grid after durable-streams, grid-tmux and core after grid, and CLI after all of them.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found 5 redundant comments. Inline suggestions to remove them below.

// The launch runs in a scope of its own so that this owner can bring
// it down deliberately and watch how that goes. A cancelled launch —
// the reader closing a terminal grid is one — unwinds past every
// the reader closing a grid is one — unwinds past every

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// the reader closing a grid is one — unwinds past every

Comment thread packages/cli/src/cli.ts
// gets no machine sessions at all, which is the ordinary ACP behaviour.
sessions?: MachineSessionAssembly,
// What presents a terminal grid on this host. Deno and the compiled binary
// What presents a grid on this host. Deno and the compiled binary

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// What presents a grid on this host. Deno and the compiled binary

Comment thread packages/core/mod.ts
export { useNormalizedOutput } from "./src/output/normalize.ts";
export { useTerminalOutput } from "./src/output/terminal.ts";
// The terminal domain is `@executablemd/terminal`'s, and a caller names it
// The terminal domain is `@executablemd/grid`'s, and a caller names it

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// The terminal domain is `@executablemd/grid`'s, and a caller names it

if (segment.name === "Terminal.Grid") {
// No raise() here, like the branches above: expandTerminalGrid
if (segment.name === "Grid") {
// No raise() here, like the branches above: expandGrid

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// No raise() here, like the branches above: expandGrid

const layout = gridLayout(columns.value, placed);
// The grid renders nothing into the document: what a pane shows belongs to
// that pane, and the sibling after `</Terminal.Grid>` renders to the root
// that pane, and the sibling after `</Grid>` renders to the root

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// that pane, and the sibling after `</Grid>` renders to the root

@github-actions

Copy link
Copy Markdown

PR #797: 🚚 Name executable document grids <Grid> and <Pane> (#781)

93 files, +2001 / -1864

Scope

🔴 PR has 3865 lines changed. Split into focused PRs.

🟡 3865 lines changed. PRs under 400 receive more thorough review.

🟡 93 files changed. Are all changes related?

🟡 Changes span 15 directories.

🟡 PR mixes config and source changes.

🟡 package.json changed without dependency justification.

Structural

Oxlint structural signals:

  • no-unused-vars ×28: packages/core/src/agent/function-components.ts, packages/core/src/agent/launch-owner.ts, packages/test-agent/src/components.ts (+4)
  • no-unnecessary-type-assertion ×9: packages/core/src/expand.ts, packages/grid/src/grid.ts, packages/core/src/grid/journal.ts
  • no-empty-function ×8: packages/grid-tmux/src/pane-worker.ts, packages/test-agent/src/components.ts, packages/grid/src/native-launcher.ts (+3)
  • no-redundant-type-constituents ×5: packages/grid-tmux/src/pane-channel.ts, packages/grid/src/posix-launcher.ts, packages/grid-tmux/src/pane-child.ts (+1)
  • no-unnecessary-type-arguments ×2: packages/grid-tmux/src/pane-channel.ts, packages/grid-tmux/src/tmux-grid.ts

Slop

  • packages/acp/src/provider.ts:2763// the reader closing a grid is one — unwinds past every
  • packages/cli/src/cli.ts:2817// What presents a grid on this host. Deno and the compiled binary
  • packages/core/mod.ts:155// The terminal domain is @executablemd/grid's, and a caller names it
  • packages/core/src/expand.ts:1187// No raise() here, like the branches above: expandGrid
  • packages/core/src/expand.ts:2163// that pane, and the sibling after renders to the root

Oxlint slop signals:

  • no-console ×3: packages/cli/src/cli.ts
  • no-inferrable-types ×2: packages/core/src/expand.ts

Static Analysis

Oxlint: 127 diagnostics across 24 files (22 rules)
Density: 0.063 violations/added-line

no-unused-vars (28): packages/core/src/agent/function-components.ts, packages/core/src/agent/launch-owner.ts, packages/test-agent/src/components.ts (+4)
consistent-function-scoping (12): packages/grid-tmux/tests/fixtures/fake-tmux.ts, packages/grid-tmux/src/pane-worker.ts, packages/acp/src/provider.ts
no-floating-promises (11): packages/grid-tmux/src/pane-channel.ts, packages/grid-tmux/src/tmux-grid.ts, packages/test-agent/src/components.ts (+4)
no-unnecessary-type-assertion (9): packages/core/src/expand.ts, packages/grid/src/grid.ts, packages/core/src/grid/journal.ts
no-shadow (8): packages/test-agent/src/controller.ts, packages/grid/src/controlled-composite.ts, packages/grid/src/posix-launcher.ts (+3)
no-empty-function (8): packages/grid-tmux/src/pane-worker.ts, packages/test-agent/src/components.ts, packages/grid/src/native-launcher.ts (+3)
unbound-method (6): packages/test-agent/src/controller.ts, packages/grid-tmux/src/provider.ts, packages/core/src/expand.ts (+1)
no-unsafe-type-assertion (6): packages/core/src/expand.ts, packages/cli/src/deno.ts, packages/acp/src/provider.ts (+1)
no-redundant-type-constituents (5): packages/grid-tmux/src/pane-channel.ts, packages/grid/src/posix-launcher.ts, packages/grid-tmux/src/pane-child.ts (+1)
consistent-return (5): packages/grid-tmux/src/pane-channel.ts, packages/grid-tmux/src/provider.ts, packages/core/src/document-validation.ts (+2)
no-base-to-string (5): packages/core/src/agent/function-components.ts, packages/core/src/expand.ts
no-underscore-dangle (4): packages/grid-tmux/src/pane-protocol.ts
no-useless-spread (4): packages/grid-tmux/src/pane-channel.ts, packages/acp/src/provider.ts
no-array-sort (4): packages/core/src/document-validation.ts, packages/grid-tmux/src/layout.ts, packages/grid-tmux/src/tmux-grid.ts
no-console (3): packages/cli/src/cli.ts
no-inferrable-types (2): packages/core/src/expand.ts
no-unnecessary-type-arguments (2): packages/grid-tmux/src/pane-channel.ts, packages/grid-tmux/src/tmux-grid.ts
no-useless-fallback-in-spread (1): packages/core/src/expand.ts
no-unmodified-loop-condition (1): packages/cli/src/cli.ts
no-unnecessary-type-conversion (1): packages/core/src/agent/function-components.ts
restrict-template-expressions (1): packages/core/src/expand.ts
no-implied-eval (1): packages/core/src/expand.ts

Correctness

No extraneous code patterns detected.

@taras
taras marked this pull request as ready for review September 10, 2026 13:29
@taras
taras merged commit ceb158b into agent/issue-717-terminal-packages Sep 10, 2026
2 checks passed
@taras
taras deleted the agent/issue-781-grid-pane branch September 10, 2026 13:29
taras added a commit that referenced this pull request Sep 10, 2026
* 📝 Name interactive presentation as Grid and Pane (#781)

* 🚚 Rename the interactive presentation domain to Grid and Pane (#781)

`Terminal.Grid` and `<Terminal>` named a whole presentation after the terminal that one kind of pane happens to need. The authored syntax is now `<Grid>` and `<Pane>`, the provider-neutral domain ships as `@executablemd/grid`, and the tmux provider as `@executablemd/grid-tmux`. Core's adapters move to `packages/core/src/grid`.

Public symbols follow one rule: grid orchestration is `Grid*`, authored children and claims are `Pane*`, provider APIs are `GridProvider*`. `TERMINAL_GRIDS_API` becomes `GRIDS_API` under the stable name `Grids`, and `TERMINAL_PROVIDERS_API` becomes `GRID_PROVIDERS_API` under `GridProviders`.

Terminal stays where it names a real capability. `NO_TERMINAL`, `reserveTerminal`, `NativeLauncher`, `PaneTerminal`, `TerminalProcesses`, `TerminalSignal` and `TerminalLost` are unchanged, as are the tmux provider identity, the hidden `terminal-worker` verb, the durable event kind `terminal_grid_layout` and the durable identity `terminal_grid:${path}:layout`. Nothing a journal already holds is invalidated.

No compatibility package, re-export, alias, forwarding barrel or old implementation directory remains, and the generated publication workflow places grid after durable-streams, grid-tmux and core after grid, and CLI after all of them.
taras added a commit that referenced this pull request Sep 11, 2026
* 📝 Name interactive presentation as Grid and Pane (#781)

* 🚚 Rename the interactive presentation domain to Grid and Pane (#781)

`Terminal.Grid` and `<Terminal>` named a whole presentation after the terminal that one kind of pane happens to need. The authored syntax is now `<Grid>` and `<Pane>`, the provider-neutral domain ships as `@executablemd/grid`, and the tmux provider as `@executablemd/grid-tmux`. Core's adapters move to `packages/core/src/grid`.

Public symbols follow one rule: grid orchestration is `Grid*`, authored children and claims are `Pane*`, provider APIs are `GridProvider*`. `TERMINAL_GRIDS_API` becomes `GRIDS_API` under the stable name `Grids`, and `TERMINAL_PROVIDERS_API` becomes `GRID_PROVIDERS_API` under `GridProviders`.

Terminal stays where it names a real capability. `NO_TERMINAL`, `reserveTerminal`, `NativeLauncher`, `PaneTerminal`, `TerminalProcesses`, `TerminalSignal` and `TerminalLost` are unchanged, as are the tmux provider identity, the hidden `terminal-worker` verb, the durable event kind `terminal_grid_layout` and the durable identity `terminal_grid:${path}:layout`. Nothing a journal already holds is invalidated.

No compatibility package, re-export, alias, forwarding barrel or old implementation directory remains, and the generated publication workflow places grid after durable-streams, grid-tmux and core after grid, and CLI after all of them.
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