Skip to content

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:

ConstructExample
Bold**bold**
Italic*italic*
Strikethrough~~struck~~
Inline code`code`
Headers# H1, ## H2, ### H3
Listsordered, unordered, nested
TablesGFM-style pipe tables
Blockquotes> note
Code blockstriple-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.
  • @labelPath substitutes 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.