An embed stands where other content belongs: src= names a section, a block or a run of prose in another document (or this one), and at render time that content appears here. It has no body of its own; what it has is a set of rules about what to take, how, and what must never be taken: part= selects which part of a heading, the embedded document is parsed as a document in its own right, the target must be .geml, schemes are restricted, cycles are errors. Inline it has a small sibling, ![[#id]]. All of this is measured here, together with two drafts: GEP-0010's use of embed for translation, and GEP-0011's leaf-value coordinates as projection targets.
Specified written into the specification. GEP draft defined by a proposal, not yet in the spec. Observed not specified; what the reference implementation does today. No implementation gap was found on this page.
| Rule | Source | Status | |
|---|---|---|---|
| 1 | src= names the content: a document, optionally with a fragment. No src is an embed-missing-src error; a body is ignored with an ignored-embed-body warning. src= is reference-checked like any reference: a fragment that does not exist is an unresolved-reference error. | §3 · §5 · A.2 | Specified |
| 2 | A fragment naming a heading selects the whole section: the heading and every block up to the next heading of the same or higher level. Naming a block selects that block. Naming a prose address (#budget-before-detail) selects that run of prose. No fragment selects the whole document. | §3 · §4 | Specified |
| 3 | part= applies to heading targets: head the heading line alone, body everything under it, intro up to the first subheading, whole the default. head and body partition whole. The same words as geml get --head/--body/--intro. On a non-heading target the whole target stands; an unknown value is a bad-embed-part warning and the whole target stands, because quietly selecting nothing is the failure §8.2 exists to prevent. | §3 · A.3 | Specified |
| 4 | The embedded document is parsed as a document in its own right and the target selected from the result — never spliced as text. Its meta, references, relative-path base and external data resolve against itself; in a chain, each document is the base for the next. | §3 | Specified |
| 5 | The target must be .geml: anything else is an embed-target-not-geml error and its bytes are never parsed as GEML. Only http, https, mailto and tel schemes are allowed; any other is refused when building the model and the attribute blanked (unsafe-embed-scheme error), ignoring U+0000–U+0020 in the scheme check. Cross-document resolution is confined to --root, symlinks resolved first, failing closed. | §3 · §9.4 · §9.5 · A.2 | Specified |
| 6 | A chain returning to a document already being expanded is a transclusion-cycle error; the chain is reported and never followed. | §9.3 · A.2 | Specified |
| 7 | Inline projection ![[#id]] / ![[doc.geml#id]]: the target must be a single-paragraph text block, whose inlines are inserted into the sentence; , embedding a document with media syntax, is a media-target-is-document error. ! is the projection prefix; a bare […] navigates. | §5.1 · §5.2 · B.4 | Specified |
| 8 | Rendering: a block embed expands into section.transclusion id data-src with the content verbatim; an inline projection is span.transclusion-inline data-src. | --to html | Observed |
| 9 | Coordinates: in this document's model an embed is a raw block with an empty body and no addressable units inside, so #embedB[1]["col"] does not resolve, for reading or writing, and the error names the same coordinate on the embed's source. The converse is allowed for two shapes: a leaf value — ![[a.geml#vars["version"]]] yields 1.4 — and a whole row, inline as its cells joined by ", " on one line, as an embed a one-row table under the header; a whole column may not. | §5.2 · GEP-0011 | Specified |
| 10 | Translation projection: translate-to= is admitted by the geml-translator/v1 profile; on meta it is the document default, on an embed it overrides, none holds one block back; translator= is reserved until there is a second engine. A translated document contains nothing but embeds, so ids, order and every non-prose byte have one home. Without the profile it is an unknown-attribute warning. | GEP-0010 | GEP draft |
| 11 | External data and media src are fetched by the renderer at render time; the renderer treats them as untrusted, may confine them to the same origin and may require opt-in. | §9.4 | Specified |
One heading target, five ways to take it. a.geml on the left is the source; b.geml embeds it.
=== meta title = "Source A" version = "1.4" === # Budget {#budget} Lead-in paragraph of the budget section. ## Detail {#detail} Detail paragraph. === table {#tbl format=csv} Item,Cost Hosting,120 === === text {#para} One paragraph of prose to project inline. === === data {#vars} {"version": "1.4", "owner": "docs"} ===
=== embed {#whole src=a.geml#budget} === === embed {#head src=a.geml#budget part=head} === === embed {#intro src=a.geml#budget part=intro} === === embed {#body src=a.geml#budget part=body} === === embed {#odd src=a.geml#budget part=sideways} ===
warning: embed: `part=sideways` is not `whole`, `head`, `body` or
`intro`; the whole target stands (line 13)
Lead-in paragraph of the budget section.
Detail paragraph.
+ table #tbl, text #para, data #vars — the section runs to end of documentLead-in paragraph of the budget section.
Lead-in paragraph of the budget section.
Detail paragraph.
+ everything after, without the heading line#budget is an H1 with no other H1 after it, so the section runs to the end and includes #tbl, #para and #vars.geml get --head/--body/--intro; a document address and a command-line selector do not each invent a vocabulary.unknown-attribute, since the key is defined and only the value is not; the whole target stands rather than nothing, because quietly selecting nothing is the failure §8.2 guards against.Relative paths, meta and external data resolve against the source document. The embedding side decides only where the result appears.
=== embed {#lead src=a.geml#budget-before-detail} %% the prose address §4 derives === === embed {#all src=a.geml} %% no fragment: the whole document === === embed {#local src=#b-local} %% a block in this document === === text {#b-local} Local text embedded above. ===
<section class="transclusion" id="lead" data-src="a.geml#budget-before-detail"> <p>Lead-in paragraph of the budget section. <section class="transclusion" id="all" data-src="a.geml"> <h1>Budget … <h2>Detail … table, text, data, all of it
| Inside a.geml | Resolves against |
|---|---|
=== table {src=rows.csv} | a.geml's directory, not b.geml's |
{{version}} | a.geml's meta: 1.4 |
[[#detail]] | a.geml's id space |
| a chain a → b → c | each document is the base for the next |
src= names must be parsed as a document in its own right, the target then selected from the result — never a run of text spliced in. So rows.csv inside a.geml resolves against a.geml's directory, exactly as when a.geml is opened on its own. The embedding side decides only where the result appears.#budget-before-detail is the address §4 derives for prose between two blocks: container #budget, next block #detail, nothing before. That it can be embedded is what keeps prose from being lost when a document is assembled out of embeds.Five errors and two warnings, matching A.2 row for row.
=== embed {#nosrc} ← no src === === embed {#withbody src=a.geml#tbl} this body is ignored === === embed {#md src=notes.md} ← not .geml === === embed {#js src=javascript:alert(1)} === === embed {#gone src=a.geml#nope} ← fragment does not exist === Inline: … and media . ← a document embedded with media syntax %% c1.geml embeds c2.geml#c2; c2.geml embeds c1.geml#c1
error: embed: missing `src=` (line 22) warning: embed body is ignored; the target lives in `src=` (line 24) error: embed: `notes.md` is not a GEML document; `src=` names a `.geml` file (optionally with a #fragment) (line 27) error: embed: `src=javascript:alert(1)` names a disallowed URL scheme (line 29) error: unresolved reference `a.geml#nope` (line 31) error: `` projects a GEML document, which is not media: for block content use `=== embed {src=a.geml}`, for a phrase use `![[a.geml]]` (line 33) c1.geml error: transclusion cycle: c1.geml → c2.geml → c1.geml → c2.geml (line 5)
--root, symlinks are resolved before the boundary is judged, and with no root nothing resolves (unresolvable-document). Every probe runs with --root ..![[…]] puts a phrase or one value into a sentence. GEP-0011 draws two lines: what may be a target, and whether a coordinate can reach through an embed.
Inline: ![[a.geml#para]] and a leaf value ![[a.geml#vars["version"]]]. Row through embed: [[#tbl]] but not #tbl[1]["Item"].
$ geml get b.geml '#tbl[1]["Item"]' #tbl is an embed in b error: `#tbl[1]["Item"]`: `embed` carries no addressable units inside it — a coordinate needs a table, a `data` block, or `meta` (GEP 0011); address it on the embed's source instead: `a.geml#tbl[1]["Item"]` $ geml get --root . a.geml '#tbl[1]["Item"]' read it on the source Hosting
Inline: One paragraph of prose to project inline. and a leaf value 1.4.
The first isspan.transclusion-inline data-src="a.geml#para"; the second is the one leaf ["version"] of data block #vars.
#tickets[2], #tickets[summary]) — inline, a row is its cells joined by ", " on one line; as an embed, a one-row table under the header. A whole column, or a value-tree node holding more nodes, is not: it is as many values as it has members. A row index is positional, like a cell's; when "which row" is really a predicate, project from a view whose where= selects it.#tbl[1]["Item"] does not resolve in b.geml. Writing through it would edit another document from this one, breaking §6's one-source rule; reading through it would make one value depend on two moving parts. The error names the address that does resolve, the same coordinate on the embed's source: a.geml#tbl[1]["Item"].GEP-0010: a translation is a projection along the language axis, the same shape as the .md projection along the format axis. The profile is registered; the spec has not taken it in.
translate-to=: a default on meta, an override on an embed, none to hold back GEP draft=== meta title = "translated" profile = "geml-translator/v1" translate-to = "zh" %% the document default === === embed {src=a.geml#budget} %% inherits zh === === embed {src=a.geml#tbl translate-to=none} %% this one is not translated ===
=== embed {src=a.geml#budget translate-to=zh} %% no profile declared ===
tr.geml ok: no diagnostics tr-noprofile.geml warning: unknown attribute `translate-to` for block type `embed` (line 4)
A.geml projecting to A.md, moved to the language axis.translate-to is the default on meta, an override on an embed, and none holds one block back. Not lang, because code {lang=} says what a body already is, while this says what to do — two value spaces and two word classes cannot share a key. translator= is reserved: with one engine, a key choosing an engine would "parse, do nothing, and read as supported".All run with the repository's current build, node geml-parser/dist/geml.js, with --root .. The probes live under emb/ in the session scratch directory.
| Probe | Covers | check result | Where on this page |
|---|---|---|---|
| a.geml · b.geml · notes.md | whole section, head, intro, body, unknown part, block target, same-document target, no src, body, non-geml, disallowed scheme, missing fragment, media on a document, inline projection, leaf-value projection | 5 error 2 warning | part=, Failures, Inline |
| b2.geml | a prose-address target, a whole-document target | 0 diagnostics | Base |
| c1.geml · c2.geml | two documents embedding each other | 1 error transclusion-cycle | Failures |
| tr.geml · tr-noprofile.geml | translate-to with and without the profile | 0 diagnostics / 1 warning | Translation |
| get b.geml '#tbl[1]["Item"]' | a coordinate through an embed | error naming a.geml#tbl[1]["Item"] | Inline |