10. Описания
Описание — это голая строка в любом теле: модуля, поверхности, интерфейса, проекции, процесса. Рендереры обращаются с текстом описания как с markdown с двумя расширениями: перекрёстные ссылки ([[…]]) и интерполяция значений (@field / @@aspect). Они появляются везде, где показываются описания: всплывающие подсказки, документация автодополнения, боковые панели, тела виджетов — и отдельные файлы .md используют ровно тот же диалект (Глава 10a).
module Payments { aspect team: "Payments"
aspect { 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}Описание рендерится с markdown-форматированием, с подставленными @@domain и @@security.zone, и с [[Orders]] и [[Notifications]], отрендеренными как кликабельные ссылки на соответствующие модули.
Два режима — и не раздувайте
Заголовок раздела «Два режима — и не раздувайте»У описания две законные формы; выбирайте ту, что подходит элементу.
Режим 1 — короткая однострочка. Скажите то, что не говорит одно лишь имя, и остановитесь.
Режим 2 — более длинный документ. Полный Markdown: внутреннее устройство, обзор API, заметки по дизайну — настоящий документ, если элемент того заслуживает.
Что не законно — так это пересказывать очевидное. Не повторяйте то, что уже говорят имя, вложенные модули, интерфейсы или процессы.
Правило. Избыточное описание хуже, чем никакого. Для
PaymentProcessor«Обрабатывает платежи» хуже, чем пустое описание, — оно добавляет длину, не добавляя информации. Полное отсутствие описания — это совершенно нормально, когда добавить нечего неочевидного.
Структурированные ссылки место в полях, а не в прозе
Заголовок раздела «Структурированные ссылки место в полях, а не в прозе»Описание — для прозы. Ссылка на внешнюю спецификацию или документ — страница Confluence, документ OpenAPI, запись в каталоге данных — это свойство элемента, а свойства идут в поля (Глава 9):
module Orders { repo.url: "https://github.com/acme/orders" docs.url: "https://wiki.acme.com/orders" api.spec: "https://specs.acme.com/orders/openapi.yaml"
"Order intake, validation, and fulfilment hand-off."}Помещение их в поля сохраняет их структурированными и пригодными для запросов — инструменты могут отрисовать их как кнопки, а модель становится хабом ссылок. Более длинное Markdown-описание всё ещё может нести случайную встроенную ссылку в своей прозе, но канонический указатель «где живёт спецификация» — это поле.
Подмножество markdown
Заголовок раздела «Подмножество markdown»Поддерживаются следующие конструкции CommonMark:
| Конструкция | Пример |
|---|---|
| Жирный | **bold** |
| Курсив | *italic* |
| Зачёркивание | ~~struck~~ |
| Встроенный код | `code` |
| Заголовки | # H1, ## H2, ### H3 |
| Списки | упорядоченные, неупорядоченные, вложенные |
| Таблицы | таблицы на вертикальных чертах в стиле GFM |
| Цитаты | > note |
| Блоки кода | огороженные тройными обратными кавычками, опционально с языком |
| Горизонтальная линия | --- |
| Внешние ссылки | [text](https://...) |
Следующее не поддерживается и либо рендерится как литеральный текст, либо вырезается:
- Сырой HTML (граница безопасности — рендереры агрессивно очищают).
- Изображения (описания — это текст; визуальное место в виджетах и проекциях).
- Сноски, списки определений, списки задач.
- Автоматическое связывание голых URL (используйте явное
[text](url)).
Один ранг заголовков
Заголовок раздела «Один ранг заголовков»Заголовки (#, ##, ###) все рендерятся с одинаковым визуальным рангом. У описаний один уровень заголовков; глубина не несёт семантического веса. Вы вольны использовать несколько # для читаемости исходника, но не полагайтесь на визуальную иерархию между H1 и H3.
Причина: описания появляются во всплывающих подсказках, боковых панелях и других стеснённых контекстах, где воспроизведение полной иерархии документа выглядит неуместно. Воспринимайте заголовки как названия разделов, а не как вложенность.
Перекрёстные ссылки: [[…]]
Заголовок раздела «Перекрёстные ссылки: [[…]]»[[…]] ссылается на другое объявление в модели. Две формы:
По стабильному идентификатору:
"Routes to [[#r3n8wt]] for downstream processing."Всегда разрешается, если цель существует в рабочем пространстве. Рендерится как человекочитаемое имя цели, связанное с местом её объявления.
По человекочитаемому имени:
"Published events are consumed by [[Orders]] and [[Notifications]]."Разрешается по всему рабочему пространству. Если два объявления имеют общее имя, ссылка неоднозначна — рендерер помечает её как ошибку, а LSP выдаёт диагностику с перечислением кандидатов. Снимите неоднозначность, переключившись на форму с идентификатором.
Имена могут быть с пространством имён; форма с пространством имён сопоставляется целиком:
"See [[Personal.Banking.Payments]] for the legacy path."Ссылки, которые не разрешаются, рендерятся как маркеры ошибки и выдают предупреждения LSP — опечатки всплывают сразу, а не гниют молча.
Ссылки на аспекты и разделы
Заголовок раздела «Ссылки на аспекты и разделы»Внутри скобок ведущий @@ помечает ключ аспекта — плоскость (Глава 9) — а не имя элемента:
"Runs in [[@@security-zone:pci]]; part of the [[@@domain]] plane."[[@@key]]ссылается на всю плоскость, названную ключом аспекта (.разделяет сегменты пути ключа).[[@@key:value]]ссылается на конкретное место на ней — аспект, — где:всегда вводит значение.
Они разрешаются в набор элементов, несущих этот аспект, и ведут на оверлей аспекта, а не на одно объявление.
Ссылка [[Doc#section]] указывает на заголовок # внутри документа .md (Глава 10a); голая [[Doc]] ссылается на документ целиком.
Чтение аспекта с другого узла: [[ref]]@@path
Заголовок раздела «Чтение аспекта с другого узла: [[ref]]@@path»[[ref]]@@aspectPath — это перекрёстная ссылка, скомпонованная с геттером значения (ниже): она читает аспект на том узле, на который ссылается, а не на владеющем.
"Part of the [[#pay001]]@@domain domain." // читает аспект domain у PaymentsСкобочной формы [[Name@field]] нет — доступ к чужому значению всегда идёт через скомпонованное [[ref]]@@aspectPath, и поскольку оно несёт собственный контекст, работает везде, включая отдельные файлы .md.
Обратные ссылки
Заголовок раздела «Обратные ссылки»Каждый элемент ведёт автоматический индекс «упоминается в» из всех ссылок [[…]], указывающих на него, — и в описаниях, и в документах .md. Инструменты показывают его на элементе, так что вы можете перепрыгнуть от объявления ко всем местам, где о нём говорится.
Интерполяция значений: @field / @@aspect
Заголовок раздела «Интерполяция значений: @field / @@aspect»Геттер подставляет значение в описание того же узла, к которому он прикреплён. Сигил называет ось: @path читает поле, @@path читает аспект — зеркаля поля key: value и блоки aspect { }, из которых берутся пути (Глава 9).
module Payments { version: "1.2"
aspect { domain: "Payments" sla.tier: "gold" }
"Version @version. SLA tier: @@sla.tier. Operates in the @@domain domain."}Рендерится примерно как: «Version 1.2. SLA tier: gold. Operates in the Payments domain.» — version — поле, читается одним @; sla.tier и domain — аспекты, читаются @@.
Разрешение идёт по цепочке типов — если узел не объявляет поле или аспект сам, но наследует его от родительского типа, используется унаследованное значение.
Локальная область видимости
Заголовок раздела «Локальная область видимости»@@ читает аспекты на владеющем узле, никогда — на узлах, на которые есть ссылки (то же самое верно для чтения голого поля @):
module A { aspect { domain: "Sales" } "Domain: @@domain" // resolves to "Sales"}
module B { aspect { domain: "Ops" } "Other module's domain: @@domain" // resolves to "Ops" (B's own aspect), // NOT A's domain}Голый @@ на модуле A не может читать аспекты на модуле B — для этого используйте чужой геттер [[B]]@@domain (выше). А поскольку голому геттеру нужен владеющий узел, он только для описаний: в отдельном документе .md (у которого нет владеющего узла) голый @ или @@ — ошибка, поэтому документы используют исключительно [[ref]]@@aspectPath (Глава 10a).
Отсутствующие аспекты всплывают
Заголовок раздела «Отсутствующие аспекты всплывают»Если @@aspectPath не разрешается, рендерер выводит литеральную метку-страж <missing:aspectPath>, а LSP выдаёт предупреждение в месте интерполяции. Тихий откат к пустой строке запрещён — опечатки и устаревшие ссылки обязаны быть видимы. (То же правило действует и для отсутствующего @field.)
module C { "Owner: @@team" // no 'team' aspect declared or inherited}Рендерится как: «Owner: <missing:team>» с предупреждением LSP.
Несколько описаний склеиваются
Заголовок раздела «Несколько описаний склеиваются»В теле может быть несколько голых строк. Они соединяются через \n в порядке объявления:
module Payments { "First paragraph about the service."
aspect team: "Payments"
"Second paragraph, declared after the team aspect. Order in the source doesn't matter for resolution but does matter for description joining."
interface authorize}Это позволяет длинным описаниям охватывать несколько строковых литералов, не вынуждая использовать одну гигантскую многострочную строку.
Разобранный пример
Заголовок раздела «Разобранный пример»Реальное описание из демо Payments:
module #k7m2qx Payments { aspect team: "Payments" docs.runbook: "https://wiki.acme.com/pci"
aspect { 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 [[#r3n8wt]]; publish `paymentEvents` consumed by [[Orders]] and [[Notifications]].
**Compliance.** PCI-DSS scope. "
interface authorize interface refund interface paymentEvents}Это рендерится как полная markdown-карточка с подстановками аспектов и живыми перекрёстными ссылками на #r3n8wt, Orders и Notifications. Обратите внимание: URL рунбука — это поле (docs.runbook), а не ссылка, зарытая в прозе, — структурированные ссылки остаются структурированными.
Где живёт резолвер
Заголовок раздела «Где живёт резолвер»Резолвер перекрёстных ссылок и интерполятор аспектов — часть пакета core, а не только LSP. И LSP (для диагностик и всплывающих подсказок), и клиентские просмотрщики (для рендеринга) используют один и тот же резолвер. Итог: описания выглядят идентично везде, где они показываются.
- Описания — голые строки, обрабатываемые как markdown с двумя расширениями ArchLang.
[[#id]]и[[Name]]ссылаются на объявления;[[@@key:value]]ссылается на аспект,[[Doc#section]]— на заголовок документа.@fieldподставляет поле,@@aspectподставляет аспект — оба на том же узле (только для описаний);[[ref]]@field/[[ref]]@@aspectчитает значение с другого узла и работает везде.- Каждый элемент получает автоматические обратные ссылки («упоминается в»), охватывающие описания и файлы
.md. - Заголовки все рендерятся с одним рангом; сырой HTML и изображения не поддерживаются.
- Отсутствующие ссылки и отсутствующие пути аспектов всплывают как ошибки, никогда не тихо.
- Несколько описаний голыми строками склеиваются.
- Тот же диалект рендерит отдельные документы
.md— первоклассную поверхность архпространства (Глава 10a).
Что дальше
Заголовок раздела «Что дальше»Глава 10a: Markdown-документы → — файлы .md как первоклассная поверхность архпространства, использующая ровно тот же флавор-markdown диалект.