✨ Describe terminal grids as executable document structure (#729) - #733
✨ Describe terminal grids as executable document structure (#729)#733taras wants to merge 2 commits into
Conversation
`<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.
PR #733: ✨ Describe terminal grids as executable document structure (#729)12 files, +2070 / -36 Scope🔴 PR has 2106 lines changed. Split into focused PRs. 🟡 2106 lines changed. PRs under 400 receive more thorough review. StructuralOxlint structural signals:
Slop
Oxlint slop signals:
Static AnalysisOxlint: 25 diagnostics across 2 files (11 rules) no-unused-vars (7): packages/core/src/expand.ts CorrectnessFILE: packages/core/src/expand.ts, PATTERN: no-unused-vars, CONCERN: Unused variable |
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.
| 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. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // 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. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // is left is a pane with no grid above it at all. |
Closes#729. First implementation Story under Quest #717.
Why
An author cannot yet write a terminal grid, and nothing can tell them whether
the one they wrote is well formed. #717's later Stories build the terminal
authority, the concurrent pane lifecycle and the tmux provider on top of an
authored structure that has to exist and be checkable first — on every runtime,
including the ones that will never install a provider.
This Story owns that structure and nothing else.
What changes
<Terminal.Grid>and<Terminal>become reserved core structural syntax.Before: both names were ordinary component names.
<Terminal title="A" />resolved a repository file or a registration, or failed as an unresolved
component; nothing knew what a grid was.
After: both are the language's own syntax. A registration cannot claim either
name and a repository file named after one never stands in for it. The grammar,
the placement rules and the derived row-major layout are decided from source,
shared by expansion and document validation, and reported by
xmd syntax:<Terminal.Grid columns={2}> <Terminaltitle="Agent">…</Terminal> <Terminaltitle="Shell" /> </Terminal.Grid>The grid's closed props hold one required positive-integer
columns; a pane'shold one required non-empty
title. Titles are display labels and may repeat —a pane's structural identity is its ordinal among the grid's direct children.
Rows are derived in authored row-major order and the last row is left short.
Executing a valid grid refuses, because no terminal provider exists yet, and it
refuses before any pane body expands or a default shell starts.
How it works
structural-rules.tsgainsterminalGridStructure()beside the rules for everyother construct, so expansion and validation cannot disagree about what a grid's
source says: which panes it holds, which form each was written in, and what is
wrong with the way it was written. Only whitespace and direct
<Terminal>panesmay appear in a grid; a control structure that would produce panes as it ran
cannot be one, because which panes exist is what the grid must know before it
opens anything.
terminal-grid.tsis where the source facts and the computedcolumnsmeet.It places the panes and reports the rows they fill — provider-neutral data
naming no terminal, socket, process or window.
expandTerminalGrid()runs that order and then refuses. The refusal carries thederived layout as its structured
cause, the way<Session.Launch>carries itsfailure class, so a test asserts on the grid that was derived rather than on the
wording of a sentence.
Review guide
Start with:
packages/core/tests/terminal-grid-structure.test.tsThen review:
packages/core/src/structural.ts— the two declarations, which is what makesthe names reserved and what
xmd syntaxprints.packages/core/src/structural-rules.ts—terminalGridStructure()and thepane, column and title rules it reads from source.
packages/core/src/terminal-grid.ts— the derived layout.packages/core/src/expand.ts—expandTerminalGrid(), the order it decidesin, and the stray-
<Terminal>branch.packages/core/src/document-validation.ts— the same rules, reported asdiagnostics.
Look carefully at:
expandTerminalGrid(). Source structure is decided first, thencolumns, then every pane'stitle, and only a complete grid reaches therefusal. Anything that moved provider contact earlier would let a partial grid
escape.
misplacedTerminalViolations(). A<Terminal>deeper than a direct child, anda
<Terminal.Grid>below another one, are found by walking rather than by alexical flag, so a pane hidden inside an
<If>inside a grid is still refused.What must stay true
terminalGridStructure(), and checked by the grammar rows interminal-grid-structure.test.tsand the matching invalid-form table indocument-validation.test.ts.RESERVED_STRUCTURAL, and checked bythe TG3 row in
syntax-catalog.test.tsthat putscomponents/Terminal.mdandcomponents/Terminal/Grid.mdon disk and still finds both names structural,and refuses a registration of either.
declarations rather than a host, and checked by the TG3 row in
syntax-cli.test.tsthat trapsAPI.Process.execwhile building the catalog.above, and checked by rows that trap component resolution, expression
evaluation and code-block execution and assert all three stayed empty.
How to verify it
integer
columnsand one or more panes, and both pane forms with a non-emptytitle; it fails if an unknown prop,as, a self-closing grid, anon-integer or non-positive
columns, or an absent or emptytitlewereadmitted — as a literal or as a computed value.
by control flow, a nested grid and a pane outside a grid are all refused, and
that whitespace between panes is inert; it fails if any of them expanded, or
if a refusal reported the wrong construct.
exact row-major positions, that duplicate titles stay valid, and that the
ordinal rather than the title is identity; it fails if the layout balanced or
padded the last row, or if two panes sharing a title collapsed.
on a body that does expand — a
<Boom />to resolve, aseen()call toevaluate and a
```bash execblock to run were confirmed recorded outside agrid before the rows relying on their silence were trusted.
xmd syntaxreports both entries with their exact forms andcontracts, and that document validation returns the same diagnostics without
reaching exec, fetch, a write or eval compilation; it fails if either name
moved out of the structural category, or if any refused boundary fired.
Run under all three runtimes:
Scope
Included
Intentionally unchanged
architecture.mdandspecs/executable-mdx-spec.mddescribe the whole ofQuest: Add interactive terminal grids to executable documents #717 and are unchanged here; §6.21 and Tier TG were settled by the
architecture commit this branch starts from.
New abstractions
TerminalPaneandTerminalGridStructureinstructural-rules.tsexistbecause the grid's panes are read once and used by two consumers — expansion
and document validation — exactly as
SwitchCase/SwitchStructureare.terminal-grid.ts(TerminalGridLayout,TerminalGridCell,PlacedPane,terminalGridLayout()) exists because placement needs both the source factsand the computed
columns, which is a different question from what the sourcesays. It is the value Quest: Add interactive terminal grids to executable documents #717's execution Story hands a provider.
resolveStructuralProp()inexpand.tsexists because bothcolumnsandtitleare read the same way — literal or computed, with absence meaningabsence — and duplicating that would let the two drift.
Each new abstraction has multiple concrete uses or a clear justification.
No speculative functionality is included.
Risks and limitations
736b909d.Its focused diff begins at
01bb083f, with the approved descriptionrefinement in
97be0632. The remaining Quest: Add interactive terminal grids to executable documents #717 Stories stack above this PR,and the complete feature is verified at the final stack head before any layer
merges.
heaviest weight the current corpus records until Measure test weights runs
on a runner. That is the documented behavior for a new file, not a defect.
contract — the execution Story replaces the refusal — but it does mean the
syntax is writable and inspectable before it is runnable.
Scope confirmation