Skip to content

✨ Describe terminal grids as executable document structure (#729) - #733

Merged
taras merged 4 commits into
mainfrom
agent/issue-729-terminal-grid
Sep 11, 2026
Merged

✨ Describe terminal grids as executable document structure (#729)#733
taras merged 4 commits into
mainfrom
agent/issue-729-terminal-grid

Conversation

@taras

@taras taras commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Closes #729. First implementation layer under Quest #717, stacked on #734.

Why

An author needs to be able to write, inspect, and validate a grid before any host attempts to present it. Later lifecycle and provider layers depend on that structure being static and fully known.

What changes

This layer adds reserved core syntax for a paired grid and its paired or self-closing panes. It validates a positive integer columns, a non-empty pane title, direct-child placement, nesting, and the exact row-major layout before any pane body, shell, Agent, or provider work begins.

The final authored surface, applied later by #781, is:

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

This PR's exact diff predates that rename and therefore uses the provisional names <Terminal.Grid> and <Terminal> in its source paths and tests. PR #797, now included in #771, replaces those unshipped names without compatibility aliases.

Executing a valid grid at this layer still refuses because no provider is installed. The refusal occurs only after the complete layout is derived and before pane content or a default shell runs.

Contract

  • Only whitespace and direct panes may appear immediately inside a grid.
  • A control structure cannot generate the pane set dynamically.
  • Titles are display labels; authored ordinal is pane identity.
  • Duplicate titles are valid and the final row may be short.
  • Syntax inspection and document validation use the same structural rules.
  • Deno, Node, and Bun describe and validate the same syntax without probing a terminal.

Review and evidence

Start with the structural grid test, then inspect the declarations, structural rules, expansion, and document validation.

At this layer the focused entrypoints retain their provisional filenames:

deno task test packages/core/tests/terminal-grid-structure.test.ts
deno task test packages/core/tests/syntax-catalog.test.ts
deno task test packages/core/tests/document-validation.test.ts
deno task test packages/cli/tests/syntax-cli.test.ts

TG1–TG4 distinguish the complete grammar, placement, non-observation, catalog, and one-through-five-pane row-major behavior.

Scope

Included: authored structure, validation, inspection, layout derivation, and fail-closed execution without a provider.

Excluded: concurrent pane execution, provider authority, native launch, tmux, durability, and replay. Those belong to the layers above this one.

Stack order: #734#733#738.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

PR #733: ✨ Describe terminal grids as executable document structure (#729)

9 files, +1293 / -2

Scope

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

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

Structural

Oxlint structural signals:

  • no-unused-vars ×7: packages/core/src/expand.ts
  • no-unnecessary-type-assertion ×1: packages/core/src/expand.ts

Slop

  • packages/core/src/document-validation.ts:1094 (removed)
  • packages/core/src/document-validation.ts:1099 (removed)

Oxlint slop signals:

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

Static Analysis

Oxlint: 25 diagnostics across 2 files (11 rules)
Density: 0.019 violations/added-line

no-unused-vars (7): packages/core/src/expand.ts
no-base-to-string (4): packages/core/src/expand.ts
no-unsafe-type-assertion (3): packages/core/src/expand.ts
no-inferrable-types (2): packages/core/src/expand.ts
no-array-sort (2): packages/core/src/document-validation.ts
unbound-method (2): packages/core/src/expand.ts
no-shadow (1): packages/core/src/expand.ts
restrict-template-expressions (1): packages/core/src/expand.ts
consistent-return (1): packages/core/src/document-validation.ts
no-unnecessary-type-assertion (1): packages/core/src/expand.ts
no-implied-eval (1): packages/core/src/expand.ts

Correctness

No extraneous code patterns detected.

@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 2 redundant comments. Inline suggestions to remove them below.

case "Terminal.Grid":
// The whole layout is decided from source, so every pane's own mistake
// is reported where it was written — and so is a construct written
// below the grid that the grid does not lay out.

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
// below the grid that the grid does not lay out.

case "Terminal":
// A well-placed `<Terminal>` is its grid's, and one placed wrongly
// under a grid is already reported by that grid's own structure. What
// is left is a pane with no grid above it at all.

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
// is left is a pane with no grid above it at all.

@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 3 redundant comments. Inline suggestions to remove them below.

case "Terminal.Grid":
// The whole layout is decided from source, so every pane's own mistake
// is reported where it was written — and so is a construct written
// below the grid that the grid does not lay out.

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
// below the grid that the grid does not lay out.

case "Terminal":
// A well-placed `<Terminal>` is its grid's, and one placed wrongly
// under a grid is already reported by that grid's own structure. What
// is left is a pane with no grid above it at all.

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
// is left is a pane with no grid above it at all.

// A well-placed <Terminal> is consumed by its <Terminal.Grid> and
// never expanded on its own. Reaching this branch means the pane sits
// outside every grid, so it names no component and is diagnosed
// rather than resolved from the filesystem.

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
// rather than resolved from the filesystem.

@taras
taras force-pushed the agent/issue-729-terminal-grid branch from cf6c7e6 to ff1144c Compare September 10, 2026 13:43
@taras
taras force-pushed the agent/issue-729-terminal-grid branch from ff1144c to 8986da0 Compare September 10, 2026 16:28

@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 3 redundant comments. Inline suggestions to remove them below.

case "Terminal.Grid":
// The whole layout is decided from source, so every pane's own mistake
// is reported where it was written — and so is a construct written
// below the grid that the grid does not lay out.

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
// below the grid that the grid does not lay out.


if (segment.name === "Terminal.Grid") {
// No raise() here, like the branches above: expandTerminalGrid
// reports every error it creates.

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
// reports every error it creates.

// A well-placed <Terminal> is consumed by its <Terminal.Grid> and
// never expanded on its own. Reaching this branch means the pane sits
// outside every grid, so it names no component and is diagnosed
// rather than resolved from the filesystem.

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
// rather than resolved from the filesystem.

@taras
taras force-pushed the agent/issue-729-terminal-grid branch from 8986da0 to bf2f228 Compare September 11, 2026 01:08

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

case "Terminal.Grid":
// The whole layout is decided from source, so every pane's own mistake
// is reported where it was written — and so is a construct written
// below the grid that the grid does not lay out.

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
// below the grid that the grid does not lay out.

case "Terminal":
// A well-placed `<Terminal>` is its grid's, and one placed wrongly
// under a grid is already reported by that grid's own structure. What
// is left is a pane with no grid above it at all.

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
// is left is a pane with no grid above it at all.


if (segment.name === "Terminal.Grid") {
// No raise() here, like the branches above: expandTerminalGrid
// reports every error it creates.

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
// reports every error it creates.

// A well-placed <Terminal> is consumed by its <Terminal.Grid> and
// never expanded on its own. Reaching this branch means the pane sits
// outside every grid, so it names no component and is diagnosed
// rather than resolved from the filesystem.

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
// rather than resolved from the filesystem.

source: "Terminal.Grid",
// The grid the author asked for, carried beside the sentence so an
// assertion is about the layout that was derived rather than about the
// wording of a refusal.

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
// wording of a refusal.

Base automatically changed from architect/717-terminal-grid-contract to main September 11, 2026 01:29
`<Terminal.Grid>` and `<Terminal>` become reserved core structural syntax.
This Story owns the authored structure alone: the grammar, the placement
rules, and the row-major layout a grid derives. No terminal authority,
provider, pane execution, shell, durability or replay is built here.

The grid's closed props hold one required positive-integer `columns`; a
pane's hold one required non-empty `title`. Titles are display labels and
identify nothing — a pane's structural identity is its ordinal among the
grid's direct children, and rows are derived in authored row-major order
with the last row left short.

`structural-rules.ts` decides what the source says, so expansion and
document validation cannot disagree about it: only whitespace and direct
`<Terminal>` panes may appear in a grid, and an empty grid, direct text, a
non-pane element, a control structure that would produce panes, a nested
grid, and a pane written anywhere else are refused. `terminal-grid.ts`
places the panes once `columns` and each `title` are known.

A grid the grammar accepts runs until a terminal provider would be asked
for one. This build installs none, so it refuses there, before any pane
body expands or a default shell starts, and carries the layout it derived
beside the refusal.

Evidence rows TG1-TG4: the new structural suite owns TG1, TG2 and TG4; the
catalog, validation and `xmd syntax` suites own TG3.
Both descriptions lead with what the author can do and show the invocation
to copy. The placement rule, the row-major detail and the title's display
role are the catalog's structured fields and §6.21's to state, not a second
reference entry beside the forms.

The frozen catalog entries in `syntax-catalog.test.ts` mirror the two
declarations, so they move with them.
@taras
taras force-pushed the agent/issue-729-terminal-grid branch from bf2f228 to 106028e Compare September 11, 2026 01:29
Two call sites still named `syntaxCatalog` and `parseCatalog`, which nothing
declares. The file imports `syntaxSymbols` and declares `parseSymbols`, and uses
them correctly everywhere else; these two were missed when the pair was renamed.

`deno task check` is the first step of every test job, so the whole matrix
stopped there rather than running anything: five Deno shards and four Node
shards reported a failure none of them had reached a test to find.

The two implicit-`any` reports beside them were the same defect seen twice —
with `syntaxCatalog` unresolved the catalog degraded to `any`, so the callbacks
reading its entries had nothing to infer from. Naming the helper settles both.

@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 3 redundant comments. Inline suggestions to remove them below.

case "Terminal.Grid":
// The whole layout is decided from source, so every pane's own mistake
// is reported where it was written — and so is a construct written
// below the grid that the grid does not lay out.

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
// below the grid that the grid does not lay out.

case "Terminal":
// A well-placed `<Terminal>` is its grid's, and one placed wrongly
// under a grid is already reported by that grid's own structure. What
// is left is a pane with no grid above it at all.

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
// is left is a pane with no grid above it at all.

// A well-placed <Terminal> is consumed by its <Terminal.Grid> and
// never expanded on its own. Reaching this branch means the pane sits
// outside every grid, so it names no component and is diagnosed
// rather than resolved from the filesystem.

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
// rather than resolved from the filesystem.

TG3 still froze the catalog at version 1. Its two neighbours in the same file
already read 2, and so does `inspect.ts` here, on main, and on every branch
above this one: the constant moved and this one assertion did not follow.

Both runtimes reported it, because the number is the catalog's rather than
either runtime's.

The rest of the row is untouched. What TG3 exists to freeze is the pair of
structural entries the catalog publishes, and that is what it still checks.
@taras
taras merged commit 06cf5fb into main Sep 11, 2026
38 checks passed
@taras
taras deleted the agent/issue-729-terminal-grid branch September 11, 2026 02:09
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.

Describe terminal grids as executable document structure

1 participant