Перейти к содержимому

Как писать эту документацию

Добавить или поправить страницу этого сайта может любой. Здесь рассказано, для чего нужна страница, какие в ней поля и компоненты, как пометить возможность, которой еще нет, и что проверить перед PR. Сайт лежит в docs-site/, а как запустить его локально, написано в README.

Канон: docs/, PERF.md, DECISIONS.md Этот сайт README и лендинг
Для кого ИИ-агенты, авторы спецификаций плагинщики из JVM, контрибьюторы, Rust-разработчики, админы наблюдатели, игроки, любопытные разработчики
Вопрос читателя Что именно требует правило? Как сделать X и почему так? Что это и для меня ли?
Глубина полная 1-4 экрана, дальше ссылка в канон экран на секцию

Канон ради сайта никогда не правят, поэтому страница объясняет канон и ссылается на него. Когда читателю нужно точное правило, дайте ссылку и не копируйте правило. Когда идея для новичка новая, перескажите ее на 1-2 экрана. Причину решения тоже не пересказывайте: поставьте ссылку на issue с меткой decision и добавьте одну строку своими словами.

Из заметок по генерации мира, то есть из docs/worldgen.md, structures.md, surface.md, scattering.md, heightmap.md и aquifer.md, не копируйте ничего. В них есть имена кода игры.

Не пишите число по памяти. Версия игры записана в assets/minecraft/version.json, цифры производительности в PERF.md, поведение в docs/spec/, решения в issue.

Счетчик, который повторяют две страницы, и id снапшота игры лежат в src/data/facts.yml. Страница пишет такое значение как {fact('crates')} после строки import { fact } from '~/lib/facts.mjs';, а status.yml пишет его как {crates}. Id snapshot читается из assets/minecraft/version.json. У каждой записи есть поле how, в нем сказано, как это значение измерить. Число, которое называет только одна страница, остается в ее тексте.

В русском после числа меняется форма слова (“122 крейта”, “122 крейтов”), поэтому при смене числа русские страницы правят вручную. Нужные места находит grep -rn "fact('crates')" src/content/docs/ru.

Плашка вверху главной страницы называет коммит origin/main, с которым сверялось последнее обновление документации. Коммит и его дата записаны в facts.yml как docs_commit и docs_commit_date, и обновление документации двигает оба значения. npm test падает, если дата не совпадает с датой этого коммита. При каждом заходе плашка спрашивает у GitHub, сколько коммитов появилось в main после этого коммита, и прячет строку, если GitHub не ответил.

Основной язык - английский. Страница лежит в docs-site/src/content/docs/en/<группа>/<slug>.mdx, а ее русская версия - по тому же пути в ru/. Группы называются start, plugins, concepts, crates, server, client, reference, contributing и project. Сайдбар строится по каталогам, а порядок внутри группы задает sidebar.order.

Страница начинается с блока полей, его называют frontmatter. Схема блока лежит в docs-site/src/content.config.ts, и неверное поле ломает сборку.

---
title: Your first plugin
description: One sentence.
sidebar:
order: 3
kind: explanation # tutorial | how-to | reference | explanation | landing
audience: [jvm, rust] # любые из jvm, contributor, rust, admin, player, observer
stability: changing # stable | changing | wip
tracking: [283, 636] # номера issue или PR, обязательно если не stable
sources: # пути от корня репозитория, issue:N, pr:N
- crates/mcrs_minecraft_chat_server/src/chat_plugin.rs
- issue:283
verified:
snapshot: 26.4-snapshot-7
commit: 51d45c26a
date: 2026-11-09
translated_from: 51d45c26a # только в ru-файлах
---
Поле Что в нем писать
kind Тип страницы, и он у страницы один. Туториал ведет читателя за руку, how-to решает одну задачу, reference перечисляет факты, а explanation отвечает на вопрос “почему”.
sources Все файлы репозитория, на которые опирается страница. Страница без sources ревью не проходит. Берите пути из самих файлов, а не из текста старых issue, потому что крейты переименовывают.
verified Снапшот, коммит и дата, когда вы последний раз сверили страницу с кодом. Не копируйте это поле с другой страницы.

Баннер стабильности вверху страницы и блок “Источники и проверка” внизу сайт рисует из frontmatter, поэтому руками их не пишите.

В русском файле есть только title, description, sidebar и translated_from. Поля kind, audience, stability, tracking, sources и verified берутся из английской страницы, и русский файл с любым из них ломает сборку.

Импортируйте нужные компоненты сразу после frontmatter:

import Status from '~/components/Status.astro';
import StatusRows from '~/components/StatusRows.astro';
import Example from '~/components/Example.astro';
import Demo from '~/components/Demo.astro';
import { Aside, Tabs, TabItem, Card, CardGrid, LinkCard } from '@astrojs/starlight/components';
Компонент Для чего
<Status issue={78} /> Бейдж состояния со ссылкой на issue, pull request или документ.
<StatusRows areas="network,persistence" /> Строки из docs-site/src/data/status.yml для перечисленных id.
<Example file="path" from="fn run" to="..." title="..." /> Фрагмент файла репозитория, который вырезается при сборке по текстовым якорям.
<Demo src="/docs/demos/cows.html" title="..." /> Интерактивная страница в iframe.
Tabs с syncKey="jvm" Paper и mcrs рядом.

Каждая issue и каждый pull request на сайте идут через Status. Не пишите #78 текстом и не дописывайте состояние словами рядом с бейджем. Состояние issue берется из docs-site/src/data/issues.yml, и это одно из значений works, partial, wip, undecided или declined. Поэтому state рядом с issue ломает сборку, как и issue, которой нет в реестре. У pull request состояние свое, потому что оно относится к одному утверждению, и влитый pull request пишется как <Status pr={636} state="works" />. Бейдж может вести и на документ: doc="docs/spec/login.md".

Код из репозитория никогда не вставляйте в MDX руками. Берите Example: он вырезает код из файла при сборке, поэтому страница не покажет код, которого уже нет. Код, которого в репозитории нет, например сниппет для Paper, идет в обычный блок с меткой java.

Example находит фрагмент по двум текстовым якорям, from и to. Якорь, которого нет в файле или который найден дважды, ломает сборку, поэтому выбирайте якоря внимательно:

  • В from берите самый короткий текст, который уникален в файле и переживет переименование соседей. Хорошо подходит сигнатура вроде pub fn add_command, fn run_echo( или impl Plugin for CountTicks. Не ставьте якорь на комментарий и на строку вида }.
  • Без to фрагментом будет блок, который открывается в строке from, до закрывающей его скобки.
  • Добавьте to, когда нужен кусок функции или несколько элементов подряд. Тогда фрагмент кончается на первой строке после from, в которой есть to.
  • Скобки в строковых литералах сбивают счет скобок, в этом случае тоже задайте to.
  • Оба якоря могут занимать несколько строк. Разделяйте строки через \n внутри {"..."}.

Номера строк для ссылки под блоком считаются по якорям.

Ссылки между страницами абсолютные, с base и слешем в конце: /docs/en/start/status/, а в русских файлах /docs/ru/.... Ссылка на файл репозитория выглядит как https://github.com/andreypfau/mcrs/blob/main/<путь>.

Вот честная строка про загрузчик плагинов: “Загрузчика jar, wasm или скриптов нет, и решения про него нет”. На этом она заканчивается, потому что проект отклонил только фасад и Bukkit-подобный API, а больше ничего не решил.1

Владелец решил, что возможность, которой еще нет, можно подробно описывать уже сейчас, если ее описывает какой-то источник: issue, спецификация, файл в docs/ или код. Обмана в этом нет, потому что пометка говорит, что возможности пока нет. Когда она появится, поправьте текст и снимите пометку.

  • Целая страница о такой возможности получает stability: wip и tracking.
  • Блок внутри стабильной страницы получает <Aside type="caution" title="WIP"> со ссылкой на issue или бейдж <Status issue={N} />.
  • Не выдумывайте. Опирайтесь на issue (gh issue view N -R andreypfau/mcrs), спецификацию, файл в docs/ или код. Если источника нет, напишите “решения пока нет” и больше ничего.
  • Не превращайте цель в утверждение. Пишите “направление, не обязательство”.
  • Гайд по установке и запуску делайте коротким: несколько строк, ссылка на Quick start в README и на технические заметки, пометка “подробный гайд появится к альфе”. Не записывайте шаги по догадке, не прогнав их.

У возможности есть состояние, у страницы - стабильность

Заголовок раздела «У возможности есть состояние, у страницы - стабильность»

Страница про плагины может быть changing, потому что issue еще изменит механизм, а внутри нее одна строка помечена works, а другая wip. Первая пометка - стабильность страницы, две другие - состояния возможностей.

Состояние возможности показывает Status в тексте и в таблицах. Состояние issue - это одна строка в docs-site/src/data/issues.yml, и вместе с ней меняются все бейджи этой issue на обоих языках.

Состояние Когда ставить
works Есть тест, который это запускает, или замер в PERF.md. Одного коммита мало.
partial Работает часть, а остальное в issue или PR. Ссылка обязательна.
wip В коде этого нет или оно не закончено, и есть issue. “Направление, не обязательство”.
undecided Issue нет, или на ней стоит метка needs-decision.
declined Только когда закрыта issue с меткой decision.

Стабильность страницы - это баннер, который сайт рисует по полю stability. У страницы stable баннера нет, страница changing пишет “Меняется, следите: #N”, а страница wip пишет, что в коде этого еще нет.

Если вы меняете код, на который опирается страница, правьте страницу в том же PR. В шаблоне PR для этого есть чекбокс.

Workflow docs запускается на каждом PR. Его скрипт, docs-site/scripts/check-sources.mjs, сравнивает измененные файлы с sources каждой страницы и пишет предупреждение по каждой странице, которую задевает ваш PR. Сборку предупреждение никогда не роняет. Когда увидите его, откройте страницу и проверьте ее. Потом поправьте текст и блок verified или напишите в PR, почему правки не нужны. Ту же проверку можно запустить локально: npm run check-sources в docs-site/.

Тот же workflow запускается каждый понедельник в 06:00 UTC и вручную. Этот запуск выполняет node docs-site/scripts/check-sources.mjs --stale и пишет в сводку job отчет из трех разделов:

Доки догоняют main пачками. Для этого запустите скилл mcrs-update-docs в агенте для кода. Он проходит pull request, влитые после самого старого verified.commit, и обновляет страницы, строки статуса, реестр issue и новости. Потом он двигает docs_commit и убирает признаки AI-текста из того, что сам поменял, сверяясь со свежей копией руководства Википедии.

  • Английские страницы, у которых после verified.commit появились коммиты в sources.
  • Русские страницы, которые отстали от английских.
  • Пути из sources, которых не существует.

Коммит, которого нет в истории, попадает в отчет как unknown commit. Отчет сборку никогда не роняет, а вот несуществующий путь в sources роняет npm test.

Еще еженедельная задача запускает check-issues, который сверяет состояния в issues.yml с GitHub.

Эти проверки читают имена файлов и ничего не знают о смысле. Они не заметят, что состояние issue изменилось после даты verified страницы, поэтому раз в месяц владелец обходит страницу Статус.

Русский файл лежит по тому же пути и повторяет структуру и порядок английского, а содержания, которого нет в английском, в нем нет. Если русского файла нет, сайт показывает английскую страницу с пометкой, что она еще не переведена.

Поле translated_from: <коммит> называет коммит английской страницы, с которой вы переводили. Когда английская страница уходит вперед, разницу видно в git log между двумя коммитами. Еженедельный отчет считает русскую страницу отставшей, если у английской после translated_from есть коммит, которого нет у русского файла. Коммит, который правит оба языка сразу, отставанием не считается, поэтому после совместной правки translated_from двигать не нужно.

Устоявшиеся англицизмы, такие как тик, чанк, крейт, плагин и снапшот, в русском тексте остаются, а при первом упоминании английский термин стоит в скобках. Заголовки issue остаются английскими.

У интерактивной объяснялки вроде демки с коровами 12 обязательных частей. Объяснялку, в которой нет хотя бы одной, не публикуют.

  1. Одно утверждение: одно предложение о том, что сравнивается.
  2. Две стороны на общих часах: ваниль слева, mcrs справа, один индекс кадра, никаких отдельных таймеров. На узком экране они идут друг под другом в том же порядке.
  3. Одинаковый вход: один сид, одна сцена, одно число сущностей. Если вход разный, напишите почему.
  4. Декларативные данные сценария: кадры в массиве или JSON рядом с файлом. Рендер про сценарий не знает.
  5. Плеер: пауза, шаг вперед, шаг назад, сначала, счетчик “шаг N из M”, клавиши-стрелки, остановка на последнем кадре.
  6. Подпись к каждому кадру: одна-две фразы с номером тика.
  7. Таблица отличий: 3-6 строк, после анимации.
  8. Мост для читателей из JVM: одна строка “в Paper это выглядело бы так: …”.
  9. Источник: ссылки на правило спецификации, на docs/*.md с разделом и на issue. Механизм в разработке получает бейдж Status.
  10. Честная сноска: что упрощено, чего в игре сегодня нет (пишите “цель, не готово: #N”) и что не замер.
  11. Доступность: светлая и темная тема, prefers-reduced-motion без автоплея, aria-label у SVG, фокус на кнопках, контраст текста не ниже 4.5 к 1.
  12. Мета: lang, версия снапшота и дата, когда вы сверили объяснялку с кодом.

Цвета сравнения постоянные: ваниль - охра #b8741a, mcrs - бирюза #1d7a70.

У объяснялки четыре правила честности. Механизм не равен замеру. Показывайте то, что реализовано, а цель помечайте. Игрушечная модель называет свои упрощения. Поведение ванили берется из спецификации или из оракула и никогда из памяти автора.

Пройдите по этому списку перед каждым PR, который добавляет или меняет страницу.

  1. У каждого числа рядом стоят машина, сценарий и ссылка на PERF.md или issue. Цифры берутся только из PERF.md, вместе с машиной (M4 Max, macOS) и сценарием.
  2. “Работает” пишется только тогда, когда это показывает тест, коммит или запись в PERF.md. Иначе пишите “частично” или “wip”.
  3. Состояние и цель визуально различаются: бейдж, ярлык или Aside.
  4. Каждое “нет” ссылается на issue или решение.
  5. Нет чисел, снятых не на тихой машине, и нет сравнений с ванилью, Paper или Horizon в цифрах.
  6. У утверждения о чужом проекте есть источник, иначе его не пишут. Для продакшена называем только Paper, Folia, Minestom и Velocity. Rust-проекты идут одной нейтральной строкой без оценок.
  7. Версия игры и счетчики берутся из fact(...) и никогда не набираются руками: {fact('snapshot')} читает assets/minecraft/version.json, а {fact('crates')} читает facts.yml.
  8. У страницы с меняющимся содержимым есть дата и коммит последней проверки.
  9. “Мы” не относится к большей группе, чем есть на самом деле. Никаких заявлений про “сообщество”.
  10. Рядом с каждой командой запуска стоит предупреждение о безопасности: у сервера нет аутентификации, поэтому не открывайте его порт наружу.
  11. Цели и issue подаются как “направление, не обязательство”.

В двух фактах черновики ошибаются часто. Во-первых, конфликты между системами Bevy компилятор не проверяет. Доступ к данным проверяет планировщик, когда строит граф. Во-вторых, проект отклонил только фасад и Bukkit-подобный API, а о загрузчике плагинов в этом решении ничего нет.1

Правила clean-room для кода действуют и в документации. Не пишите имена классов, методов и полей кода игры, кроме ResourceKey, TagKey, BlockPos, SectionPos, ColumnPos и PalettedContainer. Публичные API Paper, Bukkit, Velocity и Minestom называть можно. Не пишите, что mcrs - порт. Сами правила объясняет страница Clean-room на практике.

Пишите для человека, который читает страницу один раз, сверху вниз, и никогда не открывал репозиторий. Что именно выдает машинный текст, лучше всего описано в Википедии: Признаки сгенерированности текста для русского и Signs of AI writing для английского. Обе страницы правят почти каждый день, потому что с каждой новой моделью шаблоны меняются. Перед тем как писать или проверять страницу, откройте актуальную версию и сверьте с ней свой текст.

Правила ниже - общая выжимка, сделанная 2026-11-10, и она устареет. Если правила расходятся со страницами Википедии или не упоминают шаблон, который там есть, верьте Википедии. Из-за таких шаблонов читатель перестает доверять тексту, кто бы его ни написал.

Объясняйте, а не перечисляйте утверждения

Заголовок раздела «Объясняйте, а не перечисляйте утверждения»
  • Начните страницу с того, что читатель из нее получит и для кого она. Двух-трех предложений хватит.
  • Сначала пример, потом правило, которое он показывает. Примером может быть фрагмент кода, команда с выводом или конкретный случай.
  • У правила должна быть причина рядом. Связывайте предложения словами “потому что”, “поэтому”, “но”, “когда”. Если подряд идут короткие несвязанные утверждения, читателю приходится восстанавливать логику самому.
  • Термин объясните в нескольких словах там, где он встречается на странице впервые, или дайте ссылку на глоссарий. Не рассчитывайте, что читатель знает Bevy.
  • Пишите полными предложениями. В абзаце одна тема и обычно от 2 до 5 предложений. Длина предложений должна меняться.
  • Обращайтесь к читателю на “вы”. Активный залог, настоящее время.
  • Заголовок говорит, о чем раздел или что читатель в нем делает: “Игрок - это строка в таблице”, “Запустите сервер”. Заголовки “Обзор” и “Введение” не нужны.
  • Нумерованный список нужен для шагов, которые читатель выполняет, таблица - для значений, которые он ищет, маркированный список - для коротких однородных пунктов. Все остальное пишется прозой.
  • Aside нужен для предупреждения или для текста, который можно пропустить. Если на странице больше двух-трех Aside, их никто не замечает.
  • Issue или pull request - это источник, а источник уходит в сноску, как в Википедии. Напишите предложение так, чтобы факт был в нем самом, и поставьте [^357] после точки. В конце файла напишите [^357]: <Status issue={357} /> Одна строка о том, что эта issue решает или отслеживает. Читатель, который не откроет issue, ничего не теряет. В тексте не пишите “см. #357”, “решение #357 говорит” или “отслеживается в #357”.
  • В ячейке таблицы состояние и есть содержимое, поэтому там <Status issue={357} /> остается в ячейке. В журнале изменений на странице новостей ссылки на pull request остаются в записях.
  • Путь к файлу ставьте в конец предложения или в отдельное предложение. Не разрывайте мысль скобками.
Шаблон Пример Как писать
Противопоставление тому, чего никто не утверждал, в любом порядке “Дело не в новом синтаксисе, а в другом способе мыслить.” “Это таблица, а не объект.” Скажите, что это. “Не” и “вместо” оставьте для ошибки, которую читатель и правда может сделать.
Двоеточие как барабанная дробь “Правило короткое: не полагайтесь на порядок.” “Не полагайтесь на порядок.”
Тройка по привычке “быстро, безопасно и просто” Назовите столько пунктов, сколько есть: один, два или пять.
Предложение о том, что следующее важно “Важно отметить, что…” Удалите его и скажите суть.
Итог в конце “Что это значит”, “Вкратце”, пересказ страницы списком Страница заканчивается там, где кончилось содержание. Одной ссылки “Дальше” достаточно.
Раздутые и рекламные слова “ключевой”, “мощный”, “бесшовный”, “надежный”, “обеспечивает”, “демонстрирует”, “подчеркивает”, “отражает” Простое слово или число.
Глагол на месте простого “это” или “есть” “выступает”, “служит”, “предлагает” “это”, “у X есть”.
Хвост с деепричастием “…, обеспечивая одинаковый результат” Новое предложение о том, что происходит и почему.
Разные слова для одного и того же “измерение”, потом “мир”, потом “реальность” Один термин на одну вещь на всем сайте.
Список с жирными заголовками “- Скорость: работает быстро” Таблица или обычные предложения.
Размытые утверждения “многие разработчики”, “широко используется” Источник, число или ничего.
Жирный шрифт по всему абзацу Жирным выделяйте только предупреждение, которое нельзя пропустить.
  • Английский текст пишется простым языком, для читателя, который не носитель.
  • Конкретика: у утверждения есть ссылка или число.
  • Только ASCII-минус, без длинного и среднего тире. Кавычки прямые.
  • Без жесткого переноса: абзац - одна строка.
  • Страница занимает 1-4 экрана. Если страница разрослась, разбейте ее или дайте ссылку на канон.
  • Пишите так, как русскоязычный разработчик объяснил бы тему коллеге. Переводите смысл абзаца, а не структуру английских фраз.
  • Глаголы вместо цепочек существительных, активный залог.
  • Без канцелярита (“является”, “осуществлять”, “данный”, “в рамках”, “представляет собой”).
  • Без штампов: “стоит отметить”, “важно понимать”, “играет ключевую роль”, “таким образом”, “в заключение”.
  • Без повисших деепричастных оборотов (“используя этот метод, результаты улучшаются”).
  • Без диапазона “от X до Y”, когда его концы не лежат на одной шкале. Перечислите пункты.
  • После двоеточия строчная буква. В заголовке с заглавной буквы пишется только первое слово.
  • Буква с двумя точками не используется: везде пишем “е”.
  1. Frontmatter соответствует схеме, у страниц wip и changing есть tracking.
  2. Якорь каждого Example находит свой код, и фрагмент показывает то, о чем говорит текст рядом.
  3. Ссылки ведут на страницы, которые есть в дереве.
  4. npm run build в docs-site/ проходит.
  5. В странице нет длинных тире, нет буквы с двумя точками и нет имен из кода игры.
  1. Не планируется#437 Решение не делать фасад или API-крейт: публичная поверхность - сами крейты и плагины. ↩ ↩2

Источники и проверка

Проверено: 26.4-snapshot-7, 3540f457b, 2026-11-10

Переведено с версии 3540f457b