# Проверенные срезы Журнал ревью. Фаза с ✅ в [`README.md`](README.md) — заявка автора; строка здесь — проверенный факт: каждое обещание фазы сверено с кодом и тестом, найденное дописано или исправлено. Коммит в «Проверен на» делает журнал самопроверяющимся. Срез перестаёт считаться проверенным, как только `git log <коммит>..HEAD -- <пути>` перестаёт быть пустым — поэтому «Пути» обязательны. ## Срез 1. Оболочка и карта - **Фазы:** 0–4 - **Проверен на:** `b135a9c`, 2026-08-19 - **Пути:** `src/HSchool.Client`, `src/HSchool.Protocol`, `src/HSchool.Content`, `src/HSchool.Server/Game`, `src/HSchool.Server/Api/ModEndpoints.cs`, `src/HSchool.Server/Api/SchoolEndpoints.cs`, `src/HSchool.Server/Net`, `tests/HSchool.Content.Tests`, `tests/HSchool.Protocol.Tests`, `tests/HSchool.AppHost.Tests/SchoolApiTests.cs`, `tests/HSchool.AppHost.Tests/GameSocketTests.cs` - **Итог:** дописано 2 теста (оба прогнаны на чистом `b135a9c`), расхождений кода с дизайном не найдено кроме одного — панель «Общие события» из фазы 1 снята с экрана в фазе 8 и решение нигде не записано (оставлено открытым, см. ниже) Что подтверждено: - **Фаза 0.** `pixi.js` нет ни в `package.json`, ни в lockfile, ни в импортах; `npm run build` проходит. - **Фаза 1.** Дерево карты, панель локации со всеми пятью секциями и пустыми состояниями, клик по дереву фильтрует на клиенте, `localize()` перекрашивает подписи. Панели «Общие события» на экране нет — единственное расхождение среза. - **Фаза 2.** `SchoolWorker` — `LongRunning`-поток, свой `PeriodicTimer`, свой `World`, аккумулятор с потолком в 5 шагов. Супервизор держит таблицу и не трогает `World` (инвариант 3). Сейв пишется на create, на shutdown и по `SaveIntervalSeconds`; настройки коалесцируются. Требуемый фазой тест «create → reload → та же школа с тем же временем» — это `GameSocketTests.ReloadFromDisk_RestoresAPausedClock` (точное равенство времени, паузы и скорости), плюс `SchoolApiTests.ReloadFromDisk_RestoresCreatedSchools`. Независимость школ — `GameSocketTests.PausingOneSchool_DoesNotStopAnother`. - **Фаза 3.** Все восемь тестов из списка фазы на месте в `HSchool.Content.Tests` (JSONC-фикстуры, last-wins по `defName` и по ключу локали, parent/abstract и цикл, патч `add` в `actions`, связность, пустая комната, одна комната без двора). Ванильная раскладка валидируется (`VanillaCoreTests.CoreDefaultMap_PassesValidation`). Пропавшая папка мода не стартует школу и не трогает файл: исключение летит до создания `School`, поэтому `finally` с `Persist()` недостижим, а супервизор только снимает школу с таблицы. - **Фаза 4.** `GET /api/mods`, `GET /api/catalog?lang=`, `POST /api/schools` с картой и модами, снимок карты при OpenSchool в языке Hello — всё покрыто тестами (`OpeningASchool_SendsAMapSnapshot`, `LabelsTheSnapshotInTheHelloLocale`, `WithACustomMap_ReturnsThatLayout`, `ReloadFromDisk_RestoresACustomMap`). Кодек, `protocol.ts` и `docs/protocol.md` описывают один и тот же `MapSnapshot`, байтовые смещения проверены с обеих сторон; версия 7 совпадает в `ProtocolConstants` и `PROTOCOL_VERSION`. - **Инварианты.** `Content`/`People`/`Schedule`/`Ai` не ссылаются на Arch и ASP.NET, `Simulation` — только на Arch; `DateTime.Now`/`UtcNow` в этих проектах нет. - **«Things that will bite you».** Ни одна из относящихся к срезу регрессий не вернулась: `modal.ts` резолвит промис явно, `.dialog--screen` держит `height: fit-content`, `DefaultStartDate` форсирует UTC-kind, аккумулятор работника на месте, `MapSnapshotSize` считается от сообщения, `GameClient` переносит оба `WaitToReadAsync` между итерациями, outbox роняет старейший кадр, меню патчит карточки через `card.update`. Дописано: - `SchoolApiTests.Catalog_WithAnUnknownMod_IsRejected` — `GET /api/catalog?mods=` с неизвестной пачкой описан в `docs/protocol.md` как `400 unknown-mod`, но не проверялся. - `SchoolApiTests.ModId_ThatEscapesTheModsFolder_IsRejectedOnBothEndpoints` — инвариант 6: id пачки приходит из браузера и подставляется в путь. `ModContent.IsSafePackId` его чистит на обоих входах (каталог и создание школы), тестом это закреплено не было. Открыто: - **Панель «Общие события» пропала.** Фаза 1 требует три области, `design/near-term.md` держит их в «Что уже зафиксировано», а `design/people.md` описывает список людей как панель «рядом с картой и событиями». Коммит `d8f8db6` (фаза 8) заменил панель вкладками Карта/Люди, не сказав об этом; ключи `eventsTitle`/`eventsEmpty` остались в `i18n/strings.ts` мёртвыми. Решать надо одно из двух: вернуть пустую панель (или третью вкладку) — или записать отказ от неё в дизайн и убрать ключи. Молча менять экран ревью не стало. Замечено рядом (за границами среза): - `SchoolRegistry` после фазы 2 в бою не используется: сервер тикает школы работниками, а из класса вызывается только `TryNormalizeName`. `SchoolRegistryTests.Tick_AdvancesOnlyRunningSchools` проверяет путь, которым сервер больше не ходит. - **`PeopleApiTests.Card_IncludesFamilyAndLiveNeeds` зависит от порядка тестов** (фаза 8, срез 2). Проверено на чистом checkout `b135a9c` в отдельном worktree: в одиночку класс проходит (8/8), в полном прогоне класса падает на `Assert.Equal(2, card.Family.Parents.Count)` — ожидалось 2, пришёл 1. Причина: тест берёт `People[0]` и требует у него двух родителей, а состав семьи зависит от сида, сид — от id школы, id — от того, сколько школ создали тесты до него (и от `saves/index.json` на машине). На машине с накопленными сейвами тест зелёный, на чистом клоне — красный. Чинить надо тест: брать ученика, у которого два родителя, а не первого попавшегося. Неполные семьи — сознательное решение `design/people.md`, не баг генерации. - **`GameSocketTests.OpeningASchoolDuringAMathLesson_...` не выдерживает полный прогон** (срез 4). В одиночку проходит за 1 с, в полном прогоне падает с «No presence frame matched within 40 frames» через 22 с. Похоже на нехватку кадров под нагрузкой, а не на ошибку присутствия. - Полный `dotnet test` один раз упал: процесс `HSchool.Simulation.Tests` умер с `0xC0000005`, успев отчитаться о 44 тестах из 63, и два `ActivityTests` (фаза 20) не прошли. Повторить не удалось. Скорее всего дерево правилось прямо во время прогона (см. ниже). - На момент проверки в рабочем дереве шла правка фазы 21 (`Decision.cs`, `LessonLearning.cs`, `LessonLearningSystem.cs`, `PresenceSystem.cs`, `gameScreen.ts`), и какое-то время решение не компилировалось. Выводам по срезу 1 это не мешает: его файлы правка не трогает, а дописанные тесты прогнаны и на чистом `b135a9c`, и на дереве после починки сборки.