Repository navigation
Conversation
An `ExecutionInstallation` may now declare a coordinated structural parent and
child form and supply the implementation that expands them. `declarations`
becomes one discriminated catalog carrying that arm beside the existing declared
Markdown tier, and canonical core captures and validates the whole catalog
before any installation hook, middleware or document code runs — so selection,
`xmd syntax`, `xmd plan`, document validation and execution all read the same
definitions and cannot describe different environments.
`Execution.expand` is the public policy surface for one occurrence. A handler
receives the authored form, the source position and the evaluated, frozen props,
and may observe, refuse or delegate; it receives no child regions and no
implementation, so only canonical core hands an occurrence to the installation
that declared it. Each accepted child is a region whose `expand()` returns a
resource-scoped stream of `{ text, exact }` chunks with per-chunk rendezvous
backpressure: the producer does not advance past a delivered chunk until the
consumer advances, region output never travels through `DocumentOutput`, and
leaving the handler's scope halts and joins the producer.
The terminal grid becomes the first consumer. Its syntax, validation, layout and
providerless refusal move to the new `@executablemd/terminal` package — a
provider-neutral root that loads no engine, and an `/xmd` subpath that depends on
core — and core keeps no terminal name, rule or dispatch branch. An ordinary run
installs it on every runtime; the `xmd test` root and workflow executions do not.
Review feedback on 8c2b9b1, without changing the settled architecture. The capture now reads the discriminant rather than trusting it: an entry whose kind is neither "markdown" nor "structural" refuses the catalog instead of being treated as the other arm. Structural entries are copied at capture and frozen through — `structuredClone` detached the schema but left every nested object writable — and the admitted copies are what selection, execution, `<Syntax>`, inspection and validation all read, so a host that mutates what it handed over from inside its own `install()` changes nothing. Whole-catalog validation that depends on nothing but the declarations moved ahead of the first `install()`: an unknown discriminant, a malformed Markdown entry, and a name claimed by both arms or by a private closure. The conflicts that genuinely need a prepared execution — a reserved registration, a workflow bundle — stay at that boundary. Declared Markdown keeps its own admission, so its digest, schema, form and selection semantics are unchanged. Syntax ordering is restored to the frozen contract: protected engine constructs first, then installed declarations in captured installation and declaration order. Admission emits in declaration order rather than grouping parents ahead of their children. New regressions: mutation during `install()` reaching neither execution, `<Syntax>`, nor the captured catalog; an unknown discriminant and an intrinsic cross-arm collision refusing before `install()`; a three-way ordering fixture that separates capture order from alphabetical and from parents-first; and RS3 producer-child and finalizer latches proving early stop, scope exit, handler failure and cancellation each halt *and* join the producer, with a detached-work control that shows the absence assertions can fail.
| // above is the entrypoint's: a `host="run"` child is an ordinary run | ||
| // whatever command is hosting it, so it installs the run profile's | ||
| // structural syntax even under `xmd test`, which installs none for its own | ||
| // document. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // document. |
| // Kept per installation rather than flattened, because a structural | ||
| // declaration and the `expand` that implements it are owned together: a | ||
| // flattened catalog could not say which installation core must dispatch an | ||
| // occurrence to. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // occurrence to. |
| // Emitted in the order the installation declared them, which is the order | ||
| // `xmd syntax` lists them in. Grouping parents ahead of their children here | ||
| // would make what a reader is shown a fact about this loop rather than | ||
| // about the catalog. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // about the catalog. |
| // `as` is not stripped the way an ordinary component's is. An installed form | ||
| // produces no value, so there is nothing for a binding to capture, and the | ||
| // declared schema refuses it as the unknown prop it is — in the declaration's | ||
| // own wording rather than in a second sentence about bindings. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // own wording rather than in a second sentence about bindings. |
| *#next(): Operation<IteratorResult<ExpansionChunk, void>> { | ||
| // Releasing here rather than where the chunk was taken is the whole | ||
| // contract: the producer stays suspended across the consumer's use of the | ||
| // chunk it delivered, and resumes only when the consumer asks for another. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // chunk it delivered, and resumes only when the consumer asks for another. |
| // The engine's own constructs, and the installed declarations, are kept apart | ||
| // while the walk runs: the category's order is the frozen contract — protected | ||
| // engine syntax first, then what a host installed, in the order it was | ||
| // captured — and a single name-sorted list cannot express the second half. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // captured — and a single name-sorted list cannot express the second half. |
|
|
||
| // Only when nothing else in the body was already reported. Content that is | ||
| // not an accepted child has its own violation above, and saying the parent is | ||
| // also short of children repeats that complaint in a second sentence. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // also short of children repeats that complaint in a second sentence. |
| properties: { | ||
| // A positive integer, spelled as a number with a whole-number constraint | ||
| // rather than as `integer`: the two report a fractional count under | ||
| // different keywords, and the sentence a reader gets is decided by which. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // different keywords, and the sentence a reader gets is decided by which. |
PR #807: ✨ Install structural Markdown syntax from a package (#806)61 files, +5690 / -757 Scope🔴 PR has 6447 lines changed. Split into focused PRs. 🟡 6447 lines changed. PRs under 400 receive more thorough review. 🟡 61 files changed. Are all changes related? 🟡 Changes span 9 directories. 🟡 PR mixes config and source changes. Structural🟡 Type declarations with no consumers: DeclaredMarkdownComponent.
Oxlint structural signals:
Slop
Oxlint slop signals:
Static AnalysisOxlint: 95 diagnostics across 14 files (20 rules) no-unused-vars (17): packages/cli/tests/support/plan-harness.ts, packages/cli/src/plan-component.ts, packages/cli/src/plan.ts (+4) CorrectnessNo extraneous code patterns detected. |
|
Closing this implementation and restarting #806 from the merged main. The branch is intentionally retained as forensic evidence only: its unplanned diagnostic mini-language, request-authority protocol, and combined core/package/profile migration are not inputs to the replacement implementation. |
Why
Adding a structural Markdown form meant editing core: its reserved-name table,
its structural rules, document validation, the syntax catalog and
expand.tsdispatch. That couples the engine to every extension. The terminal-grid stack is
the worked example — a provider-neutral terminal package still needed core to
recognize and execute terminal syntax.
Closes #806.
What changes
A trusted host can now install a coordinated structural parent and child form,
and the installation that declares them supplies the implementation that expands
them.
Before:
<Terminal.Grid>and<Terminal>were engine syntax. Core reserved both names,carried their grammar, their placement rules and their layout, and dispatched
them from
expand.ts. Every execution had them, and no package could addanything like them.
After:
ExecutionInstallation.declarationsis one discriminated catalog:kind: "markdown"is the existing declared-Markdown tier, andkind: "structural"is an installed form.@executablemd/terminal/xmddeclaresthe grid and its panes and expands them; core contains no terminal name, rule or
dispatch branch. An ordinary run installs the package on every runtime, and the
xmd testroot and workflow executions install none — where the names thenresolve to nothing at all.
How it works
Core decides that an occurrence is wrong; the declaration decides only how the
sentence reads, through inert templates with three substitutions.
Review guide
Start with:
packages/core/src/execution-declarations.tsThen review:
packages/core/src/expansion-request.ts— the public request, and thecanonical issuance that makes a forged, stale, sibling or twice-delegated
request a protocol refusal.
packages/core/src/expansion-region.ts— the per-chunk rendezvous.packages/core/src/expand.ts—expandInstalledStructuralandexpandRegionScoped, replacing the terminal dispatch.packages/core/src/execute.ts— capture, the intrinsic pass before hooks,and the prepared-execution conflicts.
packages/terminal/— the neutral root and the/xmdintegration.packages/cli/src/cli.ts— the explicit"run" | "test" | "workflow"profile and the installation factory threaded to all four entrypoints.
Look carefully at:
expandInstalledStructural. A failureanywhere before the last publishes no request, invokes no middleware, reaches
no region producer and starts nothing the installing package owns.
regionStreamteardown: the producer is spawned inside the resource, soleaving the handler's scope halts and joins it.
What must stay true
by
admitStructuralDeclarations; checked by thestructural-without-expand/expand-without-structuralrows inexecution-declarations.test.ts.detachStructural+freezeThrough; checked by the mutation-during-install()rows.Enforced by
issueExpansion; checked by the EX1 rows instructural-expansion.test.ts.in
regionStream; checked by the four RS3 halt-and-join latches.xmd-package-boundary.test.tswalks core's module graph and thergscans inthe verification section.
How to verify it
RS2: the producer is suspended on the chunk the consumer holdsproves therendezvous and fails if delivery stops waiting for acknowledgement.
RS3: … halts and joins …rows prove teardown and all four fail ifthe producer is spawned outside the resource. The
RS3: the control — work the region does not ownrow exists so their absenceassertions are known to be capable of failing.
DC1: engine constructs come first, then installed forms in capture orderuses a
Mid, Zeta, Alphafixture, so capture order, alphabetical order andparents-first order are three different lists; it fails against either wrong
ordering.
TG1/TG2/TG4inpackages/terminal/tests/terminal-grid-structure.test.tsare the pre-existing core suite, moved and driven through the installation —
the grammar, placement, one-to-five row-major layouts, duplicate-title
ordinals and the
{ layout }cause are unchanged.PF1/PF2inpackages/cli/tests/structural-profile.test.tsrun under Deno,Node and Bun and prove a run has the syntax while a profile that installs none
reports
Cannot resolve component: Terminal.Grid.Static boundary evidence, run as part of the focused set:
Scope
Included
Execution.expandand private owner dispatch.@executablemd/terminaland its/xmdsubpath."run" | "test" | "workflow"profile assembly across all fourruntime entrypoints,
xmd syntax,xmd planand the nested-run host.architecture.md,specs/executable-mdx-spec.md(new §6.1.1) andspecs/release-process-spec.md.Intentionally unchanged
failure, reader close, cancellation and teardown. Those are Execute concurrent terminal panes through a replaceable provider #730's, and this
PR routes only the behavior already on
mainthrough the new boundary.digest, schema, form, private-closure and selection semantics.
installation cannot claim.
system; no contextual region-output routing; no duplicate-use enforcement.
New abstractions
ExecutionDeclaration/StructuralDeclarationexist because a profile mustbe able to describe a form to
xmd syntax,xmd planand validation withoutrunning it. Consumers: core selection, inspection, validation, execution, and
@executablemd/terminal/xmd.ExpansionRequest/ExpansionRegionexist because a structural parent needsits children as things it expands, which no function component can receive.
regionStreamexists because the alternative — a channel — does not wait forits consumer, which is the one property a pane needs.
New dependencies
@executablemd/terminal(new workspace member,0.12.1)integration.
must not depend on it, and the CLI must.
Generated or mechanical changes
.github/workflows/publish-packages.ymlcomes fromdeno task gen:publish-workflow; it adds theterminaljob and puts itbetween
coreandcli.deno.lock,pnpm-lock.yamlandbun.lockcome fromdeno install --frozen=false,deno task setupandbun install --lockfile-onlyfor the new workspace member.packages/terminal/src/layout.tsandpackages/terminal/tests/terminal-grid-structure.test.tsare moves frompackages/core(git records them as renames at 75% and 61%); the test'sharness is adapted to drive the installation, and its assertions are unchanged.
Risks and limitations
@executablemd/terminalis not on npm — the name currently returns 404.The documented bootstrap (
components/BootstrapNpmPackage.md) must becompleted before a tagged release publishes it or a CLI that depends on it.
This PR mutates no registry.
deno task lint,check,check:jsr,the compiled build with
scripts/tests/structural-profile-compiled.test.ts(CP1, never executed),verify:clean, and test-weight measurement are all outstanding. The corpusand the dependency graph both moved, so Measure Test Weights is required
on the final head before merge.
main → #806 → rebased #738/#730 → #731 → #732 → #781. It is based onmainand depends on none of them.
Scope confirmation