Add foundational phase documentation for mod development
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
# Фаза 22. Удостоверение пака
|
||||
|
||||
## Зависимости
|
||||
|
||||
- [Фаза 4](04-create-editor.md) — моды и каталог уже ездят в create
|
||||
|
||||
## Зачем
|
||||
|
||||
Мод перестаёт быть именем папки. У него появляется название, версия и список паков, без которых он
|
||||
не работает, — а у сервера появляется право отказать во внятной форме вместо сломанного каталога.
|
||||
|
||||
## Задачи
|
||||
|
||||
- [ ] `pack.jsonc` в папке пака: `version` строкой, `requires` списком id. Файла нет — пак
|
||||
по-прежнему валиден: id вместо названия, версия пустая, зависимостей нет
|
||||
- [ ] Название пака — ключ по его id в его же `localizations/<lang>.jsonc`; второго способа
|
||||
называть вещи не заводить
|
||||
- [ ] `GET /api/mods` принимает `?lang=ru|en` и отдаёт `label`, `version` и `requires` рядом с
|
||||
`id` и `required`
|
||||
- [ ] У `core` такой же `pack.jsonc` и такое же название в локалях
|
||||
- [ ] Создание школы проверяет, что каждая зависимость выбрана; нет — `400` с кодом и id того,
|
||||
кого не хватает
|
||||
- [ ] Порядок загрузки выстраивает сервер: устойчивая топологическая сортировка поверх порядка
|
||||
игрока, `core` всегда первый. Цикл зависимостей — отказ
|
||||
- [ ] Разрешённый порядок виден: пишется в лог при старте школы и возвращается в ответе создания
|
||||
- [ ] Сейв хранит **разрешённый** порядок паков, чтобы школа поднималась тем же каталогом
|
||||
- [ ] Загрузчик пишет предупреждение, когда у конкретного def нет подписи в локали пака
|
||||
- [ ] `docs/protocol.md` и [`../design/foundation.md`](../design/foundation.md) правятся тем же
|
||||
коммитом, что и обработчики
|
||||
|
||||
## Тесты, без которых фаза не закрыта
|
||||
|
||||
- [ ] Пак без `pack.jsonc` виден в списке, id стоит вместо названия
|
||||
- [ ] Название приходит на языке запроса, у `core` тоже
|
||||
- [ ] Пак с невыбранной зависимостью не создаёт школу; в ответе видно, кого не хватает
|
||||
- [ ] Зависимость, выбранная после зависимого, всё равно грузится раньше
|
||||
- [ ] Цикл зависимостей — отказ, а не зависание
|
||||
- [ ] Def без подписи даёт предупреждение, но не роняет каталог
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
- В диалоге создания моды подписаны по-человечески, `core` заблокирован как раньше
|
||||
- Школа с модом поднимается после перезапуска тем же набором и в том же порядке
|
||||
- `dotnet test` и клиентские `npm test` / `run build` проходят
|
||||
|
||||
## Стоп
|
||||
|
||||
Не грузить DLL. Не давать менять набор модов у живой школы. Не делать UI перестановки паков.
|
||||
@@ -0,0 +1,39 @@
|
||||
# Фаза 23. Настоящий пример мода
|
||||
|
||||
## Зависимости
|
||||
|
||||
- [Фаза 22](22-mod-identity.md) — пример должен нести удостоверение, как все
|
||||
|
||||
## Зачем
|
||||
|
||||
Дорога от папки на диске до школы не проверена ни разу: в `mods/` лежит только `core`, а last-wins
|
||||
и патчи живут в тестах на документах в памяти. Один маленький настоящий пак делает весь путь
|
||||
проверяемым — и заодно служит образцом для мод-автора.
|
||||
|
||||
## Задачи
|
||||
|
||||
- [ ] Пак `mods/example` рядом с `core`: `pack.jsonc`, свои локали, пара черт, набор имён,
|
||||
патч чужого def и одна комната
|
||||
- [ ] Пак нарочно скучный: он образец и фикстура, а не контент. Ванильную игру он не меняет,
|
||||
пока не выбран
|
||||
- [ ] Пак едет в вывод сборки тестов теми же правилами, что и `core`
|
||||
- [ ] Короткий `README.md` внутри пака: что где лежит и как добавить своё
|
||||
- [ ] Ориентир в [`../design/defs.md`](../design/defs.md) показывает на него как на образец
|
||||
|
||||
## Тесты, без которых фаза не закрыта
|
||||
|
||||
- [ ] `GET /api/catalog?mods=example` отдаёт типы и подписи пака поверх `core`
|
||||
- [ ] Школа создаётся с паком и поднимается с ним после перезапуска
|
||||
- [ ] Патч пака виден в каталоге школы, а без пака его нет
|
||||
- [ ] Одинаковый `defName` в `core` и в паке — побеждает пак, в логе предупреждение
|
||||
- [ ] Карта пака проходит валидацию и годится для создания школы
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
- Создать школу с включённым паком и увидеть его содержимое внутри школы
|
||||
- Снять пак — школа создаётся прежней
|
||||
- `dotnet test` проходит
|
||||
|
||||
## Стоп
|
||||
|
||||
Не превращать пример в контент-пак: чем он меньше, тем дольше проживёт.
|
||||
@@ -0,0 +1,36 @@
|
||||
# Фаза 24. Числа поведения в данные
|
||||
|
||||
## Зависимости
|
||||
|
||||
- [Фаза 20](20-needs-actions.md) — `BehaviorDef` уже есть
|
||||
|
||||
## Зачем
|
||||
|
||||
[`../design/ai.md`](../design/ai.md) обещает: числа поведения — деф правил, как `StaffingDef` у
|
||||
штата. Порог нужды и разброс на дорогу туда уехали, а веса целей остались константами в коде.
|
||||
Пока они там, мод, меняющий одно число, вынужден быть форком.
|
||||
|
||||
## Задачи
|
||||
|
||||
- [ ] Веса целей переезжают в `BehaviorDef`: обязанность-урок, обязанность-переход, нужда на нуле,
|
||||
обед
|
||||
- [ ] Значения по умолчанию — сегодняшние; поведение не должно измениться ни на минуту
|
||||
- [ ] Каталог без `BehaviorDef` работает на константах кода, а не падает
|
||||
- [ ] Валидатор ловит отрицательные веса и порядок, который делает обед сильнее урока
|
||||
- [ ] Комментарий у каждого числа объясняет, что оно перевешивает — иначе мод-автор крутит вслепую
|
||||
|
||||
## Тесты, без которых фаза не закрыта
|
||||
|
||||
- [ ] Ванильные числа дают ровно те же решения, что и до переезда (таблица входов и выходов)
|
||||
- [ ] Пак, поднявший вес обеда выше урока, уводит класс с урока в столовую
|
||||
- [ ] Каталог без `BehaviorDef` принимает решения на значениях по умолчанию
|
||||
- [ ] Отрицательный вес роняет загрузку каталога
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
- Отпечаток недели из фазы 25 не меняется от самого переезда
|
||||
- `dotnet test` проходит
|
||||
|
||||
## Стоп
|
||||
|
||||
Не заводить формулы в данных. Число — да, выражение — нет.
|
||||
@@ -0,0 +1,43 @@
|
||||
# Фаза 25. Золотые файлы и мелкие долги
|
||||
|
||||
## Зависимости
|
||||
|
||||
Нет. Можно вести параллельно с 22–24.
|
||||
|
||||
## Зачем
|
||||
|
||||
Две регрессии сейчас ловятся только глазами: молчаливая смена генерации людей и сейв, который
|
||||
перестал грузиться после нового поля. Обе дешёвы в защите и дороги в разборе постфактум.
|
||||
|
||||
## Задачи
|
||||
|
||||
- [ ] Отпечаток ростера файлом в фикстурах: фиксированные сид, карта, набор имён и родной язык
|
||||
- [ ] Тест печатает **первую** несовпавшую строку, а не «строки различаются»
|
||||
- [ ] Правило записано рядом с фикстурой: отпечаток обновляется в том же коммите, что и генерация,
|
||||
и коммит объясняет, что поменялось
|
||||
- [ ] Файл сейва текущего формата в фикстурах хоста; тест поднимает из него школу
|
||||
- [ ] Рядом с ним — файл сейва **без** новых полей, чтобы «отсутствующее поле имеет разумное
|
||||
значение» проверялось, а не подразумевалось
|
||||
- [ ] Форма голодного дня закрепляется тестом: приходят сытыми, к своей смене около 0.4, уходят
|
||||
голодными, к утру снова полны. Числа подобраны замером и сейчас ничем не защищены
|
||||
- [ ] `uncovered` в staffing говорит, скольких учителей не хватает предмету
|
||||
- [ ] Клиент показывает это число в списке непокрытых, через `t(...)`
|
||||
|
||||
## Тесты, без которых фаза не закрыта
|
||||
|
||||
- [ ] Отпечаток ростера совпадает с файлом
|
||||
- [ ] Школа поднимается из сохранённого файла: те же люди, то же время, тот же штат
|
||||
- [ ] Сейв без поля родного языка грузится и не перетасовывает набор
|
||||
- [ ] Кривая голода за учебный день держится в заявленных границах
|
||||
- [ ] `PrimarySchool` на ванильной карте требует трёх учителей, и ответ это говорит
|
||||
- [ ] Назначение второго учителя уменьшает нехватку, но не закрывает её
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
- Изменить порядок бросков в генераторе — золотой тест краснеет
|
||||
- Добавить поле в сейв — старый файл по-прежнему грузится
|
||||
- `dotnet test` и клиентские `npm test` / `run build` проходят
|
||||
|
||||
## Стоп
|
||||
|
||||
Не превращать золотой файл в снимок всего мира: отпечаток должен читаться человеком.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Фаза 26. Свой сид у школы
|
||||
|
||||
## Зависимости
|
||||
|
||||
- [Фаза 7](07-people-in-school.md) — сид уже лежит в файле людей
|
||||
|
||||
## Зачем
|
||||
|
||||
Сегодня сид генерации — это `school.Id`. Отсюда три неприятности сразу: у двух игроков школа №1
|
||||
населена одинаково, состав школы зависит от того, сколько школ создали до неё, и воспроизвести
|
||||
чужой баг нельзя, не повторив последовательность идентификаторов. Ревью споткнулось об это
|
||||
дважды — тест, зелёный на машине разработчика и красный на чистом клоне, был ровно про это.
|
||||
|
||||
Сид уже хранится в файле людей. Не хватает одного: чтобы он был **свой**, а не производный.
|
||||
|
||||
## Задачи
|
||||
|
||||
- [ ] При создании школы сид берётся из генератора случайных чисел сервера, а не из id
|
||||
- [ ] `POST /api/schools` принимает необязательный `seed`; передали — берётся он, нет — бросается
|
||||
- [ ] Сид виден: в ответе `GET /api/schools` и на экране школы, чтобы его можно было переслать
|
||||
- [ ] Существующие школы не меняются: сид читается из файла людей, как и сейчас
|
||||
- [ ] Тесты хоста, которым нужен предсказуемый состав, передают сид явно, а не полагаются на
|
||||
порядок создания
|
||||
- [ ] `docs/protocol.md` и [`../design/people.md`](../design/people.md) правятся тем же коммитом
|
||||
|
||||
## Тесты, без которых фаза не закрыта
|
||||
|
||||
- [ ] Две школы, созданные с одним сидом, населены одинаково; с разными — по-разному
|
||||
- [ ] Школа, созданная без сида, после перезапуска поднимает тот же состав
|
||||
- [ ] Старый сейв, где сид совпадал с id, грузится и состав не меняется
|
||||
- [ ] Переданный сид виден в списке школ
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
- Создать две школы подряд без сида и увидеть разные фамилии в пятых классах
|
||||
- Создать школу с чужим сидом и получить ту же школу
|
||||
- `dotnet test` и клиентские `npm test` / `run build` проходят
|
||||
|
||||
## Стоп
|
||||
|
||||
Не делать сид редактируемым у живой школы. Не показывать его там, где он мешает.
|
||||
@@ -0,0 +1,45 @@
|
||||
# Фаза 27. Версия сейва и взгляд внутрь
|
||||
|
||||
## Зависимости
|
||||
|
||||
- [Фаза 2](02-school-worker.md) — сейв и дев-поверхность уже есть
|
||||
|
||||
## Зачем
|
||||
|
||||
Две дыры в обслуживании. Первая: `Format` в сейве штампуется при записи и копируется при чтении,
|
||||
но никогда не сравнивается — файл из будущей версии загрузится молча и будет понят неправильно, а
|
||||
места для миграции просто нет. Вторая: посмотреть внутрь живой школы нечем. Всё ревью замеры
|
||||
делались временными тестами, потому что другого способа узнать, кто где и с какими нуждами, не
|
||||
существует.
|
||||
|
||||
## Задачи
|
||||
|
||||
- [ ] Загрузка читает `Format`: новее своего — школу не стартовать, файл не трогать, в лог. Как с
|
||||
пропавшей папкой мода
|
||||
- [ ] Формат старее своего — явный шов для апгрейда: сегодня пустой, но названный и с комментарием
|
||||
- [ ] `GET /api/dev/schools/{id}/dump` — ростер, присутствие, расписание и нужды одним JSON. За тем
|
||||
же переключателем, что и `reload-schools`, и никогда в проде по умолчанию
|
||||
- [ ] Дамп читает опубликованные снимки и мейлбокс, а не лезет в `World` мимо работника
|
||||
- [ ] Раздел «Things that will bite you» в `AGENTS.md` пополняется тем, что нашло ревью: сид школы
|
||||
был её id; тесты хоста делят один сервер и одну папку сейвов, поэтому начинают с очистки;
|
||||
на паузе кадры присутствия не приходят вовсе
|
||||
- [ ] Классы тестов вокруг Arch перестают идти параллельно: `HSchool.Simulation.Tests` получает
|
||||
запрет параллельности, потому что нативная память Arch этого не любит
|
||||
- [ ] `docs/protocol.md` описывает дамп в разделе дев-ручек
|
||||
|
||||
## Тесты, без которых фаза не закрыта
|
||||
|
||||
- [ ] Сейв с `format` больше текущего оставляет школу незапущенной и файл нетронутым
|
||||
- [ ] Сейв текущего формата грузится как раньше
|
||||
- [ ] Дамп отдаёт людей, их узлы и текущее расписание для живой школы
|
||||
- [ ] Дамп неизвестной школы — `404`
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
- Подсунуть сейв из будущего — сервер стартует, эта школа не поднимается, в логе понятно почему
|
||||
- Снять дамп с идущей школы и увидеть, кто где стоит
|
||||
- `dotnet test` проходит; полный прогон решения стабилен
|
||||
|
||||
## Стоп
|
||||
|
||||
Не писать миграции, которых пока не нужно. Не открывать дев-ручки без переключателя.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Фаза 28. Тесты экранов
|
||||
|
||||
## Зависимости
|
||||
|
||||
- [Фаза 13](13-management-tab.md), [Фаза 17](17-timetable-screen.md) — самые крупные экраны уже есть
|
||||
|
||||
## Зачем
|
||||
|
||||
`AGENTS.md` говорит: у экранов тестов нет, потому что DOM-окружение стоило бы зависимости,
|
||||
которой у проекта нет. На первой фазе это было верно — экран состоял из часов. Сейчас в клиенте
|
||||
несколько тысяч строк с диалогами, сеткой расписания, редактором карты и панелью управления, а
|
||||
vitest уже стоит: DOM-окружение — это одна девзависимость.
|
||||
|
||||
Цель не «покрыть клиент», а закрыть те места, где на клиенте есть **логика**: сборка запроса из
|
||||
фильтров, разбор кода ошибки в текст, блокировки в диалоге создания.
|
||||
|
||||
## Задачи
|
||||
|
||||
- [ ] `happy-dom` как девзависимость и окружение vitest для тестов экранов
|
||||
- [ ] Тесты панели людей: фильтры собирают правильный запрос, пейджер не уезжает за границы
|
||||
- [ ] Тесты диалога создания: `core` нельзя снять, сброс карты возвращает дефолт, кнопка
|
||||
блокируется на время запроса
|
||||
- [ ] Тесты сетки расписания: код отказа планировщика превращается в текст, а не в молчание
|
||||
- [ ] Тесты панели управления: отказ по пределу фонда показывается текстом
|
||||
- [ ] Политика тестирования в `AGENTS.md` переписывается: что теперь проверяется тестом, а что
|
||||
по-прежнему глазами
|
||||
- [ ] Прогон экранов не должен заметно удлинять `npm test`
|
||||
|
||||
## Тесты, без которых фаза не закрыта
|
||||
|
||||
Сами тесты и есть содержание фазы; закрывают её четыре набора выше.
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
- `npm --prefix src/HSchool.Client test` гоняет экраны и проходит
|
||||
- CI не удлинился настолько, чтобы это раздражало
|
||||
- Сломать `t(...)` в одном из экранов — тест краснеет
|
||||
|
||||
## Стоп
|
||||
|
||||
Не тащить фреймворк ради тестов. Не проверять вёрстку и стили — только поведение.
|
||||
@@ -91,3 +91,25 @@
|
||||
| --- | --- | --- |
|
||||
| [20. Нужды и действия](20-needs-actions.md) | ✅ | `ActionDef` с полями, декей, восполнение |
|
||||
| [21. Выбор действия](21-decisions.md) | ✅ | Цели и веса, «дойти → сделать», уход с урока, рост навыка |
|
||||
|
||||
## Срез 6. Фундамент
|
||||
|
||||
Дизайн: [`../design/foundation.md`](../design/foundation.md).
|
||||
|
||||
Срез без единой новой кнопки для игрока. Мод становится настоящим объектом, числа поведения
|
||||
уезжают из кода в данные, сид перестаёт быть идентификатором, у сейва появляется версия, а то,
|
||||
что ревью ловило глазами, начинает ловиться тестом. Всё здесь дёшево поодиночке и заметно
|
||||
упрощает следующие срезы.
|
||||
|
||||
Порядок свободный: 25, 26 и 27 ни от чего не зависят, 23 стоит на 22. Начинать разумно с 25 —
|
||||
она страхует всё остальное.
|
||||
|
||||
| Фаза | Статус | Зачем |
|
||||
| --- | --- | --- |
|
||||
| [22. Удостоверение пака](22-mod-identity.md) | ⬜ | Название, версия, зависимости и порядок загрузки |
|
||||
| [23. Пример мода](23-example-pack.md) | ⬜ | Настоящая папка вместо документов в памяти |
|
||||
| [24. Числа поведения](24-behavior-numbers.md) | ⬜ | Веса целей в `BehaviorDef`, а не в коде |
|
||||
| [25. Золотые файлы](25-golden-fixtures.md) | ⬜ | Отпечаток ростера, старый сейв, кривая голода, нехватка учителей |
|
||||
| [26. Свой сид](26-school-seed.md) | ⬜ | Состав школы перестаёт зависеть от порядка создания |
|
||||
| [27. Версия сейва и дамп](27-serviceability.md) | ⬜ | Формат читается, внутрь школы можно заглянуть |
|
||||
| [28. Тесты экранов](28-screen-tests.md) | ⬜ | DOM-окружение и логика клиента под тестом |
|
||||
|
||||
Reference in New Issue
Block a user