10. Descriptions
A description is a bare string in any body — module, facet, interface, view, process. Renderers treat description text as markdown with two extensions: cross-references ([[…]]) and label interpolation (@…). These appear everywhere descriptions are shown: hover tooltips, completion docs, side panels, widget bodies.
module Payments { team: Payments
labels { domain: Payments security.zone: PCI }
" # Payments
Core payment processor for the **@domain** domain, operating in the *@security.zone* zone. Published events are consumed by [[Orders]] and [[Notifications]].
> Every transaction in the platform flows through this service. "
interface Authorize interface Refund}The description renders with markdown formatting, with @domain and @security.zone substituted, and with [[Orders]] and [[Notifications]] rendered as clickable links to the respective modules.
Markdown subset
These CommonMark constructs are supported:
| Construct | Example |
|---|---|
| Bold | **bold** |
| Italic | *italic* |
| Strikethrough | ~~struck~~ |
| Inline code | `code` |
| Headers | # H1, ## H2, ### H3 |
| Lists | ordered, unordered, nested |
| Tables | GFM-style pipe tables |
| Blockquotes | > note |
| Code blocks | triple-backtick fenced, optional language |
| Horizontal rule | --- |
| External links | [text](https://...) |
These are not supported and are either rendered as literal text or stripped:
- Raw HTML (security boundary — renderers sanitize aggressively).
- Images (descriptions are text; visuals belong in widgets and views).
- Footnotes, definition lists, task lists.
- Auto-linking of bare URLs (use explicit
[text](url)).
One header rank
Headers (#, ##, ###) all render at the same visual rank. Descriptions have one header level; depth carries no semantic weight. You’re free to use multiple # for source readability, but don’t rely on visual hierarchy between H1 and H3.
The reason: descriptions appear in tooltips, side panels, and other constrained contexts where reproducing a full document hierarchy looks wrong. Treat headers as section titles, not as nesting.
Cross-references: [[…]]
[[…]] links to another declaration in the model. Two forms:
By stable ID:
"Routes to [[#pay002]] for downstream processing."Always resolves if the target exists in the workspace. Renders as the target’s human-readable name, linked to its declaration site.
By human-readable name:
"Published events are consumed by [[Orders]] and [[Notifications]]."Resolves against the whole workspace. If two declarations share the name, the reference is ambiguous — the renderer marks it as an error and the LSP emits a diagnostic listing the candidates. Resolve ambiguity by switching to the ID form.
Names can be namespaced; the namespaced form is matched whole:
"See [[Personal.Banking.Payments]] for the legacy path."References that fail to resolve render as error markers and emit LSP warnings — typos surface immediately rather than rotting silently.
Label interpolation: @…
@labelPath substitutes the value of a label declared on (or cascaded into) the same node the description is attached to. The path mirrors the qualified-name form used in labels { }:
module Payments { labels { domain: Payments sla.tier: gold }
"Owned by @team. SLA tier: @sla.tier. Operates in @domain."}Renders as something like: “Owned by Payments. SLA tier: gold. Operates in Payments.”
Resolution walks the type chain — if the node doesn’t declare the label itself but inherits it from a parent type, the inherited value is used.
Scope is local
@ reads labels on the owning node, never on referenced nodes:
module A { labels { domain: Sales } "Domain: @domain" // resolves to "Sales"}
module B { labels { domain: Ops } "Other module's domain: @domain" // resolves to "Ops" (B's own label), // NOT A's domain}A description on module A cannot read labels on module B. For cross-node access, use [[B]] — but a link doesn’t interpolate.
Missing labels surface
If @labelPath doesn’t resolve, the renderer outputs the literal sentinel <missing:labelPath> and the LSP emits a warning at the interpolation site. Silent fallback to empty string is forbidden — typos and stale references have to be visible.
module C { "Owner: @owner" // no 'owner' label declared or inherited}Renders as: “Owner: <missing:owner>” with an LSP warning.
Multiple descriptions concatenate
A body may have multiple bare strings. They join with \n in declaration order:
module Payments { "First paragraph about the service."
team: Payments
"Second paragraph, declared after the team field. Order in the source doesn't matter for resolution but does matter for description joining."
interface Authorize}This lets long descriptions span multiple string literals without forcing one giant multi-line string.
Worked example
A real description from the Payments demo:
module #pay001 Payments { team: Payments
labels { domain: Payments security.zone: PCI criticality: High }
" Core **payment processing** service for the *@domain* domain. Operates in the `@security.zone` zone with criticality *@criticality*.
> Every transaction in the platform flows through this service > before reaching an external processor.
**Capabilities.** Authorize, capture, and refund transactions; route to processors via [[#pay002]]; publish `PaymentEvents` consumed by [[Orders]] and [[Notifications]].
**Compliance.** PCI-DSS scope. See the [internal runbook](https://wiki.acme.com/pci). "
interface Authorize interface Refund interface PaymentEvents}That renders as a full markdown card with label substitutions and live cross-references to #pay002, Orders, and Notifications.
Where the resolver lives
The cross-reference resolver and label interpolator are part of the core package — not LSP-only. Both the LSP (for diagnostics and hover) and client-only viewers (for rendering) consume the same resolver. The result: descriptions look identical wherever they’re displayed.
Summary
- Descriptions are bare strings, treated as markdown with two Archlang extensions.
[[#id]]and[[Name]]link to other declarations.@labelPathsubstitutes the value of a label on the same node.- Headers all render at one rank; raw HTML and images are not supported.
- Missing references and missing label paths surface as errors, never silently.
- Multiple bare-string descriptions concatenate.
What’s next
Chapter 11: The Standard Library → — from bare kinds to service, command, event, and the rest of the stdlib vocabulary.