diff --git a/docs/design/defs.md b/docs/design/01-shell/defs.md similarity index 99% rename from docs/design/defs.md rename to docs/design/01-shell/defs.md index 75e4f2b..f38a98f 100644 --- a/docs/design/defs.md +++ b/docs/design/01-shell/defs.md @@ -2,7 +2,7 @@ Договорённость на ближайшее планирование, не текущий код. Экран игрока: [`near-term.md`](near-term.md). Потоки: [`runtime.md`](runtime.md). -Проекты: [`projects.md`](projects.md). Как устроен хост сейчас: [`../architecture.md`](../architecture.md). +Проекты: [`projects.md`](projects.md). Как устроен хост сейчас: [`../architecture.md`](../../architecture.md). Идея как у RimWorld: **ядро — код систем**, содержание игры — **данные (defs)**. Новый стул, кабинет или действие, которое уже умеет код, появляются без пересборки `HSchool.Simulation`. Сборка нужна, diff --git a/docs/design/near-term.md b/docs/design/01-shell/near-term.md similarity index 99% rename from docs/design/near-term.md rename to docs/design/01-shell/near-term.md index 494064e..6898001 100644 --- a/docs/design/near-term.md +++ b/docs/design/01-shell/near-term.md @@ -1,7 +1,7 @@ # Ближайший горизонт: что видит игрок Это не текущее состояние кода, а договорённости на ближайшее планирование. -Как устроено сейчас — в [`architecture.md`](../architecture.md). +Как устроено сейчас — в [`architecture.md`](../../architecture.md). ## Решение: не PixiJS, а менеджер на DOM diff --git a/docs/design/projects.md b/docs/design/01-shell/projects.md similarity index 100% rename from docs/design/projects.md rename to docs/design/01-shell/projects.md diff --git a/docs/design/runtime.md b/docs/design/01-shell/runtime.md similarity index 100% rename from docs/design/runtime.md rename to docs/design/01-shell/runtime.md diff --git a/docs/design/people.md b/docs/design/02-people/people.md similarity index 98% rename from docs/design/people.md rename to docs/design/02-people/people.md index c793416..fcaec2a 100644 --- a/docs/design/people.md +++ b/docs/design/02-people/people.md @@ -1,8 +1,8 @@ # Люди: генерация, характеристики, просмотр Договорённость на следующий срез, не текущий код. -Типы и карта: [`defs.md`](defs.md). Экран игрока: [`near-term.md`](near-term.md). -Потоки: [`runtime.md`](runtime.md). Проекты: [`projects.md`](projects.md). +Типы и карта: [`defs.md`](../01-shell/defs.md). Экран игрока: [`near-term.md`](../01-shell/near-term.md). +Потоки: [`runtime.md`](../01-shell/runtime.md). Проекты: [`projects.md`](../01-shell/projects.md). Школа перестаёт быть пустой коробкой. Появляются ученики, работники и родители: у каждого имя, тело, навыки, черты и нужды. **Ничего из этого пока ни на что не влияет** — люди не ходят, не @@ -209,7 +209,7 @@ Скорость — данные, не код. Когда появится еда и сон, цифра меняется в JSONC, а не в C#. Отменено срезом 5: в `core` ненулевой `decayPerHour`, сон и голод восстанавливаются вне школы, -см. [`ai.md`](ai.md). +см. [`ai.md`](../05-ai/ai.md). ## Имена @@ -343,16 +343,16 @@ Protocol ← Server → Simulation → People → Content - Не отправлять черты и навыки в списке — только в карточке. Список читают глазами, карточку открывают по одному человеку. - Сид на семью, а не один на школу: иначе любая правка генератора переставит всех. -- Ненулевую скорость нужд не включать, пока нечем их восполнять — сделано срезом 5, см. [`ai.md`](ai.md). +- Ненулевую скорость нужд не включать, пока нечем их восполнять — сделано срезом 5, см. [`ai.md`](../05-ai/ai.md). ## Заведомо не сейчас Срез 2 закрыт. Строки, которые отменили следующие срезы, помечены. -- Поведение: расписание, уроки, перемещение по карте — отменено срезами 4–5, см. [`schedule.md`](schedule.md), [`ai.md`](ai.md). +- Поведение: расписание, уроки, перемещение по карте — отменено срезами 4–5, см. [`schedule.md`](../04-schedule/schedule.md), [`ai.md`](../05-ai/ai.md). - Влияние характеристик на что-либо. Черты и навыки в этом срезе — текст в карточке — отменено срезом 5 (маршрут, обучение). - Вызов родителя в школу. Задел есть (родитель — сущность), реализации нет. -- Наём и увольнение вручную. Должности закрываются генератором — отменено срезом 3, см. [`staffing.md`](staffing.md). Школа открывается без штата. +- Наём и увольнение вручную. Должности закрываются генератором — отменено срезом 3, см. [`staffing.md`](../03-staffing/staffing.md). Школа открывается без штата. - Смеси наборов имён с весами. - Новые роли из модов: ученик, работник, родитель — код, не данные. - Портреты и любой арт. @@ -391,7 +391,7 @@ Protocol ← Server → Simulation → People → Content | Выдача навыков | Не полный каталог: always, родной, предметы года, взрослые лишние, родственные языки | | Черты в `core` | Около десяти | | Нужды в `core` | Сон, голод, туалет, общение | -| Нужды | Машинерия есть; убывание в `core` — срез 5, не ноль, см. [`ai.md`](ai.md) | +| Нужды | Машинерия есть; убывание в `core` — срез 5, не ноль, см. [`ai.md`](../05-ai/ai.md) | | Слои по ролям | Одинаковые виды данных; ключи навыков зависят от роли и возраста | | Набор имён | Один на школу, выбор в create, в `core` славянский; прочие — моды | | Родной язык | Один из `nativeLanguages` набора, выбор в create; остальные языки списка — часто низкий навык | diff --git a/docs/design/staffing.md b/docs/design/03-staffing/staffing.md similarity index 97% rename from docs/design/staffing.md rename to docs/design/03-staffing/staffing.md index ec90211..a7c481d 100644 --- a/docs/design/staffing.md +++ b/docs/design/03-staffing/staffing.md @@ -1,8 +1,8 @@ # Наём, предметы и бюджет Договорённость на следующий срез, не текущий код. -Люди: [`people.md`](people.md). Типы и карта: [`defs.md`](defs.md). -Экран игрока: [`near-term.md`](near-term.md). Проекты: [`projects.md`](projects.md). +Люди: [`people.md`](../02-people/people.md). Типы и карта: [`defs.md`](../01-shell/defs.md). +Экран игрока: [`near-term.md`](../01-shell/near-term.md). Проекты: [`projects.md`](../01-shell/projects.md). Первый срез, где игрок **что-то решает**. До сих пор школа собиралась сама: генератор закрывал все должности, игрок смотрел. Теперь школа открывается пустой — без единого учителя, — и её @@ -49,7 +49,7 @@ - **сколько часов в неделю** в каждой из них — учебный план. Часы здесь не задел на расписание, а рабочая величина уже этого среза: из них считается нагрузка -учителя, а из нагрузки — зарплата. Расписание ([`schedule.md`](schedule.md)) только расставит эти +учителя, а из нагрузки — зарплата. Расписание ([`schedule.md`](../04-schedule/schedule.md)) только расставит эти часы по дням; сколько их — решает учебный план. Так «начальные классы» получаются естественно: один предмет, который развивает всё понемногу и @@ -89,7 +89,7 @@ на каждый предмет, преподаваемый в существующих параллелях, нужен хотя бы один учитель. Нетренерские должности — директор, секретарь, медсестра, библиотекарь, повар — по-прежнему рождаются -из комнат, как описано в [`defs.md`](defs.md). Их игрок тоже нанимает, но предметов им не назначают. +из комнат, как описано в [`defs.md`](../01-shell/defs.md). Их игрок тоже нанимает, но предметов им не назначают. ## Соискатели @@ -198,7 +198,7 @@ ## Заведомо не сейчас -- Само расписание: когда именно идут эти часы. Следующий срез, [`schedule.md`](schedule.md). +- Само расписание: когда именно идут эти часы. Следующий срез, [`schedule.md`](../04-schedule/schedule.md). Здесь предмет — нагрузка в часах, а не событие в кабинете. - Уроки, звонки, оценки, посещаемость. - Классное руководство: класс закреплён за кабинетом, но не за учителем. diff --git a/docs/design/schedule.md b/docs/design/04-schedule/schedule.md similarity index 97% rename from docs/design/schedule.md rename to docs/design/04-schedule/schedule.md index a8f199e..4cc0e31 100644 --- a/docs/design/schedule.md +++ b/docs/design/04-schedule/schedule.md @@ -1,8 +1,8 @@ # Расписание Договорённость на срез после штата, не текущий код. -Штат и предметы: [`staffing.md`](staffing.md). Люди: [`people.md`](people.md). -Типы и карта: [`defs.md`](defs.md). Экран: [`near-term.md`](near-term.md). +Штат и предметы: [`staffing.md`](../03-staffing/staffing.md). Люди: [`people.md`](../02-people/people.md). +Типы и карта: [`defs.md`](../01-shell/defs.md). Экран: [`near-term.md`](../01-shell/near-term.md). Часы из учебного плана раскладываются по дням и кабинетам. Школа впервые начинает **жить по времени**: в 10:15 у 5Б математика в кабинете 204, и это видно на карте. @@ -140,9 +140,9 @@ | Место урока | Кабинет класса, если у предмета нет требования к типу помещения | | Нет учителя | Урок не ставится; предмет числится непокрытым | | Карта | В дереве рядом с комнатой — что и у кого там сейчас | -| Панель локации | «Сейчас» и «Персонажи» заполняются из расписания — с срезом 5 из присутствия, см. [`ai.md`](ai.md) | +| Панель локации | «Сейчас» и «Персонажи» заполняются из расписания — с срезом 5 из присутствия, см. [`ai.md`](../05-ai/ai.md) | | Расписание класса | В разделе «Управление», сетка «день × урок» | | Расписание учителя | В его карточке, там же где навыки и семья | | Общая сетка школы | Не делаем: её читает только тот, кто раскладывает вручную | | Где живёт код | Отдельная библиотека рядом с `HSchool.People` | -| Люди и место | Находятся по расписанию; ходьбы нет — отменено срезом 5, присутствие стало состоянием ([`ai.md`](ai.md)) | +| Люди и место | Находятся по расписанию; ходьбы нет — отменено срезом 5, присутствие стало состоянием ([`ai.md`](../05-ai/ai.md)) | diff --git a/docs/design/ai.md b/docs/design/05-ai/ai.md similarity index 98% rename from docs/design/ai.md rename to docs/design/05-ai/ai.md index 1227c72..433aa52 100644 --- a/docs/design/ai.md +++ b/docs/design/05-ai/ai.md @@ -1,8 +1,8 @@ # Жизнь школы Договорённость на срез после расписания, не текущий код. -Расписание: [`schedule.md`](schedule.md). Штат: [`staffing.md`](staffing.md). -Люди: [`people.md`](people.md). Типы и карта: [`defs.md`](defs.md). Экран: [`near-term.md`](near-term.md). +Расписание: [`schedule.md`](../04-schedule/schedule.md). Штат: [`staffing.md`](../03-staffing/staffing.md). +Люди: [`people.md`](../02-people/people.md). Типы и карта: [`defs.md`](../01-shell/defs.md). Экран: [`near-term.md`](../01-shell/near-term.md). Люди начинают **ходить и решать**. До сих пор человек находился там, где его застало расписание; теперь он приходит утром, идёт коридором в кабинет, может уйти с урока в туалет и вернуться — @@ -129,7 +129,7 @@ не сидят вдвоём. Это связывает мебель на карте с поведением, а не только с вместимостью класса. Так мод добавляет поведение, не написав ни строчки кода, — ровно как обещано в -[`defs.md`](defs.md). +[`defs.md`](../01-shell/defs.md). **Обязанности из каталога действий не выбирают.** Урок приходит из расписания, работа по должности — из `RoomDef.works`, который уже проставлен в `core` (`TeachLesson` у кабинета, diff --git a/docs/design/foundation.md b/docs/design/06-foundation/foundation.md similarity index 100% rename from docs/design/foundation.md rename to docs/design/06-foundation/foundation.md diff --git a/docs/design/inventory.md b/docs/design/07-inventory/inventory.md similarity index 99% rename from docs/design/inventory.md rename to docs/design/07-inventory/inventory.md index 79fb54a..9d2432a 100644 --- a/docs/design/inventory.md +++ b/docs/design/07-inventory/inventory.md @@ -1,8 +1,8 @@ # Одежда, вещи и климат Договорённость на следующий срез, не текущий код. -Люди: [`people.md`](people.md). Типы и карта: [`defs.md`](defs.md). Жизнь школы: [`ai.md`](ai.md). -Экран: [`near-term.md`](near-term.md). Проекты: [`projects.md`](projects.md). +Люди: [`people.md`](../02-people/people.md). Типы и карта: [`defs.md`](../01-shell/defs.md). Жизнь школы: [`ai.md`](../05-ai/ai.md). +Экран: [`near-term.md`](../01-shell/near-term.md). Проекты: [`projects.md`](../01-shell/projects.md). У человека появляется то, что на нём надето и что он несёт. Школа получает климат и правила одежды. Игрок это **смотрит** и выбирает правила; надевает не он — система сама приводит гардероб diff --git a/docs/design/session.md b/docs/design/08-session/session.md similarity index 99% rename from docs/design/session.md rename to docs/design/08-session/session.md index b930af4..de88e31 100644 --- a/docs/design/session.md +++ b/docs/design/08-session/session.md @@ -1,8 +1,8 @@ # Сессия и темп Договорённость на следующий срез, не текущий код. -Оболочка: [`near-term.md`](near-term.md). Рантайм: [`runtime.md`](runtime.md). -Протокол: [`../protocol.md`](../protocol.md). +Оболочка: [`near-term.md`](../01-shell/near-term.md). Рантайм: [`runtime.md`](../01-shell/runtime.md). +Протокол: [`../protocol.md`](../../protocol.md). Один процесс сервера, несколько браузеров в локальной сети. Это не аккаунты и не интернет: общий пароль альфы пускает к двери, имя отличает игроков. Школу по-прежнему тикает её работник, diff --git a/docs/design/social.md b/docs/design/09-social/social.md similarity index 98% rename from docs/design/social.md rename to docs/design/09-social/social.md index bf3a31d..2ec01bb 100644 --- a/docs/design/social.md +++ b/docs/design/09-social/social.md @@ -1,8 +1,8 @@ # Общение, отношения и романтический пак Договорённость на срез после сессии/темпа, не текущий код. -Люди: [`people.md`](people.md). ИИ: [`ai.md`](ai.md). Одежда (уместность как образец): -[`inventory.md`](inventory.md). Моды: [`defs.md`](defs.md), [`foundation.md`](foundation.md). +Люди: [`people.md`](../02-people/people.md). ИИ: [`ai.md`](../05-ai/ai.md). Одежда (уместность как образец): +[`inventory.md`](../07-inventory/inventory.md). Моды: [`defs.md`](../01-shell/defs.md), [`foundation.md`](../06-foundation/foundation.md). У человека появляется, **с кем** он говорит и **что** о нём думают другие. Нужда «общение» уже есть, действие `Chat` уже есть — но это соло в коридоре. Срез делает разговор кружком, тему — diff --git a/docs/design/craft.md b/docs/design/10-craft/craft.md similarity index 94% rename from docs/design/craft.md rename to docs/design/10-craft/craft.md index 8e19ebc..bf39581 100644 --- a/docs/design/craft.md +++ b/docs/design/10-craft/craft.md @@ -1,8 +1,8 @@ # Ремесло: слои, страховка тестов, горячие точки Договорённость на срез после одежды, сессии и склейки урока, не текущий код. -Фундамент: [`foundation.md`](foundation.md). Проекты: [`projects.md`](projects.md). -Вне очереди: [`off-queue.md`](off-queue.md). Общение (не ждём 44/46): [`social.md`](social.md). +Фундамент: [`foundation.md`](../06-foundation/foundation.md). Проекты: [`projects.md`](../01-shell/projects.md). +Вне очереди: [`off-queue.md`](../off-queue/README.md). Общение (не ждём 44/46): [`social.md`](../09-social/social.md). Игра уже живая: люди ходят, одеваются, учатся, болтают. Рядом лежат четыре копии одного запрета на ссылки, `npm test` зелёный при красном `tsc`, работник школы на тысячу строк и @@ -18,7 +18,7 @@ ## На какие вопросы отвечает -- Слои из [`projects.md`](projects.md) и инвариант 4 — тест в одном месте, не четыре копипасты. +- Слои из [`projects.md`](../01-shell/projects.md) и инвариант 4 — тест в одном месте, не четыре копипасты. - `npm test` клиента = typecheck + vitest; дубликат в `strings.ts` красный без `npm run build`. - `SchoolWorker` и сборка карточки режутся по швам, поведение то же. @@ -26,7 +26,7 @@ - Новых экранов, полей сейва, байтов сокета. - Фаз 44 и 46, портретов/SwarmUI как фичи, `GET /api/catalog.lunchBreaks`. -- Багов сессии (второе имя при живой куке, ключи DataProtection) — [`../bugs/README.md`](../bugs/README.md). +- Багов сессии (второе имя при живой куке, ключи DataProtection) — [`../bugs/README.md`](../../bugs/README.md). - Инверсии Game → Api DTO и «пройти SOLID по всему дереву». ## Что меняется в уже принятом @@ -50,7 +50,7 @@ `GetReferencedAssemblies` и чтение `*.cs`. Копии `Assembly_DoesNotReference*` и `Sources_DoNotUseWallClock` из People/Ai/Schedule/Simulation уезжают сюда. -Список (как [`projects.md`](projects.md)): +Список (как [`projects.md`](../01-shell/projects.md)): | Сборка | Не ссылается | Не содержит в исходниках | | --- | --- | --- | diff --git a/docs/design/README.md b/docs/design/README.md new file mode 100644 index 0000000..f5f8c10 --- /dev/null +++ b/docs/design/README.md @@ -0,0 +1,17 @@ +# Дизайн + +Договорённости срезов. Не читать целиком: открой папку среза. + +| Срез | Папка | +| --- | --- | +| 1. Оболочка и карта | [01-shell](01-shell/) | +| 2. Люди | [02-people](02-people/people.md) | +| 3. Штат | [03-staffing](03-staffing/staffing.md) | +| 4. Расписание | [04-schedule](04-schedule/schedule.md) | +| 5. Жизнь школы | [05-ai](05-ai/ai.md) | +| 6. Фундамент | [06-foundation](06-foundation/foundation.md) | +| 7. Одежда, вещи и климат | [07-inventory](07-inventory/inventory.md) | +| 8. Сессия и темп | [08-session](08-session/session.md) | +| 9. Общение и романтический пак | [09-social](09-social/social.md) | +| 10. Ремесло | [10-craft](10-craft/craft.md) | +| Вне очереди | [off-queue](off-queue/) | diff --git a/docs/design/off-queue.md b/docs/design/off-queue.md deleted file mode 100644 index 1200ed4..0000000 --- a/docs/design/off-queue.md +++ /dev/null @@ -1,178 +0,0 @@ -# Вне очереди - -Задачи, которых нет в нумерованных срезах. Срез 10 — ремесло -([`craft.md`](craft.md)), не сюда. Сюда — склейка уже живых систем и мелкий DX. После любой -фазы школа целая. - -Люди: [`people.md`](people.md). Штат: [`staffing.md`](staffing.md). Расписание: -[`schedule.md`](schedule.md). Присутствие: [`ai.md`](ai.md). Одежда и климат: -[`inventory.md`](inventory.md). - -## Связность урока - -Урок уже растёт навык, если ученик стоит в кабинете и не голоден. Рядом лежат числа, которые -игрок видит и которые на этот рост не влияют: учитель в туалете, навык учителя, тепло, -учебник в сумке, снег на дороге. - -Этот кусок отвечает на вопрос «почему нанимать сильного, чинить тепло и класть учебник в -сумку». Оценок, посещаемости как журнала и приказов нет. - -### Что меняется в уже принятом - -| Было | Стало | Почему | -| --- | --- | --- | -| Ученик в кабинете качает навык, учитель может быть где угодно | Рост только если назначенный учитель стоит в том же узле и не идёт | Зазор «обязанность / место» уже есть; урок его не смотрел | -| Зарплата зависит от навыков, урок — нет | Навык учителя по предмету масштабирует `Gain` | Иначе «математика 78» в пуле — только цена | -| Голод режет урок, тепло — нет | Та же `NeedFactor` на тепло | Полоска уже живая; [`inventory.md`](inventory.md) откладывал штраф | -| Учебники сегодняшних предметов едут в сумке | Нет в сумке на этом уроке — половинный рост | Переноска и шкафчик иначе остаются двумя числами | -| Погода одевает, на приход не влияет | Дождь и снег добавляют минуты к запасу на дорогу | Часы уже показывают осадки; ленивый уже опаздывает от черты | - -### Формула - -`HSchool.Ai.LessonLearning.Gain` по-прежнему чистое число. Множители: - -- **нужды** — `NeedFactor(голод) × NeedFactor(тепло)`; нет нужды в каталоге — множитель 1; -- **черты ученика** — как сейчас; -- **учитель** — `NeedFactor(навык / 100)`, навык — среднее по `SubjectDef.skills` у учителя, - нет ключа — `Range.Min` этого навыка; учителя нет в комнате — множитель 0, рост не пишется; -- **учебник** — 1, если в сумке есть экземпляр с `subject` этого урока; иначе - `BehaviorDef.lessonNoTextbookFactor` (ваниль 0.5). - -Учитель «стоит в комнате»: `NodeId` = кабинет урока, путь пуст, остаток минут 0. Действие -учителя не отменяет урок (дежурство — не `ActionDef`). Нет человека с этим id — учителя нет. - -Дорога: к нынешнему `SlackMinutes` (черты + бросок) прибавляются минуты из -`BehaviorDef.commuteRainMinutes` / `commuteSnowMinutes`, если в момент сборки плана дня на -улице дождь или снег. `HSchool.Ai` осадки не знает — симуляция передаёт уже посчитанные минуты. - -### Что видит игрок - -Числа на карточке. Плюс строка в сегодняшней истории, **один раз на ключ в сутки**, не каждый -тик: - -| Тип | Когда | `thingDef` | -| --- | --- | --- | -| `lesson-no-teacher` | Урок шёл, учителя не было в кабинете | предмет | -| `lesson-cold` | На уроке, который иначе учил, тепло ниже `needThreshold` | предмет | -| `lesson-no-textbook` | На уроке нет учебника этого предмета в сумке | предмет | - -Опоздание из-за снега в лог не пишем: весь состав получил бы одну и ту же строку, а место -человека и так видно. - -Клиент по-прежнему рисует `label` с сервера. Сокет и сейв не меняются. Версию протокола не -бампить. - -### Где живёт код - -Нового проекта нет. Формула и план дня — `HSchool.Ai`. Присутствие учителя, тепло, сумка, -погода на входе в план — `HSchool.Simulation`. Подписи лога — каталог. Список типов — -`docs/protocol.md` (HTTP лога). - -### Заведомо не сейчас - -- Оценки, журнал посещаемости, выговор за прогул. -- Перегруз замедляет ходьбу. -- Насмешки над формой и мнения — срез 9. -- Вызов родителей, деньги за одежду, болезнь от промокания. -- Клиент сам считает «учится хуже». - -### Зафиксировано этим разговором - -| Тема | Решение | -| --- | --- | -| Нарезка | Пять фаз во «Вне очереди», не срез 10 | -| Стоп | После каждой фазы школа целая | -| Протокол | Не бампить; новые `type` только в HTTP лога | -| Сейв | Не трогать | -| Учитель на уроке | Тот же узел, не идёт; нет id — нет роста | -| Навык учителя | Среднее по скиллам предмета, та же кривая что у нужды | -| Тепло | Рядом с голодом, произведение двух `NeedFactor` | -| Учебник | Только сумка, не шкафчик и не дом; ваниль ×0.5 | -| Погода на дорогу | Минуты в `BehaviorDef`; лога нет | - -## Пропуск сборки при запуске - -### Зачем - -`run-aspire.cmd` каждый раз вызывает `dotnet run`, а тот — MSBuild графа Aspire, даже когда -исходники не менялись. Повторный запуск после Ctrl+C стоит десятки секунд впустую: SDK -пересчитывает хост, хотя dll на месте. - -### Было / Стало / Почему - -**Было.** Скрипт всегда `dotnet run --project src/HSchool.AppHost`. Аргументы проходили насквозь. - -**Стало.** Если `HSchool.AppHost.dll` и `HSchool.Server.dll` новее входов (C#, csproj, props, -`global.json`), скрипт делает `dotnet run --no-build`. Иначе — `dotnet build`, потом `--no-build`. -`--rebuild` собирает всегда. Конфигурация (`-c` / `--configuration`) выбирает, какие dll сравнивать; -по умолчанию Debug, как у `dotnet run`. - -**Почему.** Инкрементальный MSBuild всё равно оценивает Aspire SDK. `--no-build` это пропускает. -Клиентский TypeScript и JSONC модов не входы: Vite и каталог читают диск (`ContentRoot`), а не -`bin`. Тестовые проекты AppHost не ссылается — их правки запуск не пересобирают. - -Правила «устарел ли билд» живут в `tools/apphost-uptodate.ps1` (то, что вызывает `run-aspire.ps1`) и в -`LaunchBuildStamp` (то, что проверяют тесты). Менять надо оба. - -### Что не входит - -Голый `dotnet run --project src/HSchool.AppHost` по-прежнему собирает — так удобнее агентам с -грязным деревом. Linux-обёртки нет: точка входа Windows — `.cmd`. Протокол, HTTP и сейв не -трогаем. - -## Что нового после входа - -### Зачем - -Альфа обновляется часто. Игрок не смотрит git: если с прошлого захода в этот браузер накатили -другую сборку Server, один раз показать, что вошло в неё. - -### Было / Стало / Почему - -**Было.** Нет версии сборки на экране. Сессионная кука HttpOnly и про вход. - -**Стало.** Сборка Server вшивает `HEAD` и `git log --first-parent`. `GET /api/changelog?since=` -отдаёт текущий SHA и коммиты после якоря. Клиент держит `hschool.seen-rev` (год, `Path=/`, -`SameSite=Lax`, не HttpOnly). Первый заход без куки окно не показывает — только запоминает -текущий SHA. Окно — после входа, не при возврате из школы. - -**Почему.** «Накатанный» — то, что в dll, а не грязный HEAD при `--no-build`. First-parent после -`--no-ff` — это слитые фазы, не внутренняя нарезка ветки. `Mark phase N` оставляем в цепочке -якорем, в список не рисуем: иначе кука на таком HEAD потеряла бы следующее слияние. - -### Что не входит - -Ручной `CHANGELOG.md`. Запись на пользователе в `users.json`. Вызов git из бегущего процесса. -Бамп версии сокета. Окно внутри открытой школы. - -## Состояние экрана - -### Зачем - -Менеджер — списки с фильтрами. Сейчас они живут только в RAM: F5 всегда меню, повторный вход -сбрасывает вкладки и запросы. Игрок сравнивает людей и соискателей; потерять «ученики 5А» на -обновлении — дырка в уже живых экранах, не новая фича. - -### Было / Стало / Почему - -**Было.** SPA без адреса. `GameScreen.show` и `PeoplePanel.show` обнуляют вкладки, фильтры и -выбор. Язык уже в `localStorage`; игровые часы — на сервере. - -**Стало.** Query URL держит *где я*: школа, обзор/управление, карта/люди, выбранные комната и -человек. `sessionStorage` по `schoolId` держит фильтры, поиски, сорт, страницу, вкладку карточки -и класс сетки — и последний маршрут, чтобы вход с меню поднял то же место. F5 с `?school=` -шлёт тот же `OpenSchool`, что карточка. Вход/выход — `pushState`; смена вкладки и фильтра — -`replaceState`, чтобы «назад» не листал каждый клик. - -**Почему.** Сервер не знает про вкладки; класть их в сейв — лишний HTTP. `localStorage` как у -языка держал бы «5А» через месяц. URL без фильтров остаётся коротким и копируемым. - -### Что видит игрок - -F5 внутри школы — та же школа, те же вкладки и фильтры. Назад — меню. Снова открыть ту же -школу — не сброс на карту. Гость не входит в управление по ссылке. Скорость часов, пауза, -открытый пул соискателей и мастер создания не вспоминаются. - -### Что не входит - -Поиск по имени в списках, где его нет. Роутер-библиотека. Состояние на сервере. Бамп протокола. diff --git a/docs/design/off-queue/README.md b/docs/design/off-queue/README.md new file mode 100644 index 0000000..e4e9284 --- /dev/null +++ b/docs/design/off-queue/README.md @@ -0,0 +1,15 @@ +# Вне очереди + +Склейка уже живых систем и мелкий DX. Именованный срез — не сюда. +После любой фазы школа целая. Не читать все темы: открой файл задачи. + +| Тема | Фазы | +| --- | --- | +| [Связность урока](lesson-consequences.md) | 48, 50–53 | +| [Пропуск сборки](skip-stale-build.md) | 49 | +| [Что нового](whats-new.md) | 54 | +| [Состояние экрана](view-state.md) | 55 | + +Люди: [`../02-people/people.md`](../02-people/people.md). Штат: [`../03-staffing/staffing.md`](../03-staffing/staffing.md). +Расписание: [`../04-schedule/schedule.md`](../04-schedule/schedule.md). Присутствие: [`../05-ai/ai.md`](../05-ai/ai.md). +Одежда: [`../07-inventory/inventory.md`](../07-inventory/inventory.md). Ремесло: [`../10-craft/craft.md`](../10-craft/craft.md). diff --git a/docs/design/off-queue/lesson-consequences.md b/docs/design/off-queue/lesson-consequences.md new file mode 100644 index 0000000..28b1815 --- /dev/null +++ b/docs/design/off-queue/lesson-consequences.md @@ -0,0 +1,83 @@ +Часть [Вне очереди](README.md). Соседей по папке не читать, если задача не про них. + +# Связность урока + +Урок уже растёт навык, если ученик стоит в кабинете и не голоден. Рядом лежат числа, которые +игрок видит и которые на этот рост не влияют: учитель в туалете, навык учителя, тепло, +учебник в сумке, снег на дороге. + +Этот кусок отвечает на вопрос «почему нанимать сильного, чинить тепло и класть учебник в +сумку». Оценок, посещаемости как журнала и приказов нет. + +### Что меняется в уже принятом + +| Было | Стало | Почему | +| --- | --- | --- | +| Ученик в кабинете качает навык, учитель может быть где угодно | Рост только если назначенный учитель стоит в том же узле и не идёт | Зазор «обязанность / место» уже есть; урок его не смотрел | +| Зарплата зависит от навыков, урок — нет | Навык учителя по предмету масштабирует `Gain` | Иначе «математика 78» в пуле — только цена | +| Голод режет урок, тепло — нет | Та же `NeedFactor` на тепло | Полоска уже живая; [`inventory.md`](../07-inventory/inventory.md) откладывал штраф | +| Учебники сегодняшних предметов едут в сумке | Нет в сумке на этом уроке — половинный рост | Переноска и шкафчик иначе остаются двумя числами | +| Погода одевает, на приход не влияет | Дождь и снег добавляют минуты к запасу на дорогу | Часы уже показывают осадки; ленивый уже опаздывает от черты | + +### Формула + +`HSchool.Ai.LessonLearning.Gain` по-прежнему чистое число. Множители: + +- **нужды** — `NeedFactor(голод) × NeedFactor(тепло)`; нет нужды в каталоге — множитель 1; +- **черты ученика** — как сейчас; +- **учитель** — `NeedFactor(навык / 100)`, навык — среднее по `SubjectDef.skills` у учителя, + нет ключа — `Range.Min` этого навыка; учителя нет в комнате — множитель 0, рост не пишется; +- **учебник** — 1, если в сумке есть экземпляр с `subject` этого урока; иначе + `BehaviorDef.lessonNoTextbookFactor` (ваниль 0.5). + +Учитель «стоит в комнате»: `NodeId` = кабинет урока, путь пуст, остаток минут 0. Действие +учителя не отменяет урок (дежурство — не `ActionDef`). Нет человека с этим id — учителя нет. + +Дорога: к нынешнему `SlackMinutes` (черты + бросок) прибавляются минуты из +`BehaviorDef.commuteRainMinutes` / `commuteSnowMinutes`, если в момент сборки плана дня на +улице дождь или снег. `HSchool.Ai` осадки не знает — симуляция передаёт уже посчитанные минуты. + +### Что видит игрок + +Числа на карточке. Плюс строка в сегодняшней истории, **один раз на ключ в сутки**, не каждый +тик: + +| Тип | Когда | `thingDef` | +| --- | --- | --- | +| `lesson-no-teacher` | Урок шёл, учителя не было в кабинете | предмет | +| `lesson-cold` | На уроке, который иначе учил, тепло ниже `needThreshold` | предмет | +| `lesson-no-textbook` | На уроке нет учебника этого предмета в сумке | предмет | + +Опоздание из-за снега в лог не пишем: весь состав получил бы одну и ту же строку, а место +человека и так видно. + +Клиент по-прежнему рисует `label` с сервера. Сокет и сейв не меняются. Версию протокола не +бампить. + +### Где живёт код + +Нового проекта нет. Формула и план дня — `HSchool.Ai`. Присутствие учителя, тепло, сумка, +погода на входе в план — `HSchool.Simulation`. Подписи лога — каталог. Список типов — +`docs/protocol.md` (HTTP лога). + +### Заведомо не сейчас + +- Оценки, журнал посещаемости, выговор за прогул. +- Перегруз замедляет ходьбу. +- Насмешки над формой и мнения — срез 9. +- Вызов родителей, деньги за одежду, болезнь от промокания. +- Клиент сам считает «учится хуже». + +### Зафиксировано этим разговором + +| Тема | Решение | +| --- | --- | +| Нарезка | Пять фаз во «Вне очереди», не срез 10 | +| Стоп | После каждой фазы школа целая | +| Протокол | Не бампить; новые `type` только в HTTP лога | +| Сейв | Не трогать | +| Учитель на уроке | Тот же узел, не идёт; нет id — нет роста | +| Навык учителя | Среднее по скиллам предмета, та же кривая что у нужды | +| Тепло | Рядом с голодом, произведение двух `NeedFactor` | +| Учебник | Только сумка, не шкафчик и не дом; ваниль ×0.5 | +| Погода на дорогу | Минуты в `BehaviorDef`; лога нет | diff --git a/docs/design/off-queue/skip-stale-build.md b/docs/design/off-queue/skip-stale-build.md new file mode 100644 index 0000000..4c84bad --- /dev/null +++ b/docs/design/off-queue/skip-stale-build.md @@ -0,0 +1,31 @@ +Часть [Вне очереди](README.md). Соседей по папке не читать, если задача не про них. + +# Пропуск сборки при запуске + +### Зачем + +`run-aspire.cmd` каждый раз вызывает `dotnet run`, а тот — MSBuild графа Aspire, даже когда +исходники не менялись. Повторный запуск после Ctrl+C стоит десятки секунд впустую: SDK +пересчитывает хост, хотя dll на месте. + +### Было / Стало / Почему + +**Было.** Скрипт всегда `dotnet run --project src/HSchool.AppHost`. Аргументы проходили насквозь. + +**Стало.** Если `HSchool.AppHost.dll` и `HSchool.Server.dll` новее входов (C#, csproj, props, +`global.json`), скрипт делает `dotnet run --no-build`. Иначе — `dotnet build`, потом `--no-build`. +`--rebuild` собирает всегда. Конфигурация (`-c` / `--configuration`) выбирает, какие dll сравнивать; +по умолчанию Debug, как у `dotnet run`. + +**Почему.** Инкрементальный MSBuild всё равно оценивает Aspire SDK. `--no-build` это пропускает. +Клиентский TypeScript и JSONC модов не входы: Vite и каталог читают диск (`ContentRoot`), а не +`bin`. Тестовые проекты AppHost не ссылается — их правки запуск не пересобирают. + +Правила «устарел ли билд» живут в `tools/apphost-uptodate.ps1` (то, что вызывает `run-aspire.ps1`) и в +`LaunchBuildStamp` (то, что проверяют тесты). Менять надо оба. + +### Что не входит + +Голый `dotnet run --project src/HSchool.AppHost` по-прежнему собирает — так удобнее агентам с +грязным деревом. Linux-обёртки нет: точка входа Windows — `.cmd`. Протокол, HTTP и сейв не +трогаем. diff --git a/docs/design/off-queue/view-state.md b/docs/design/off-queue/view-state.md new file mode 100644 index 0000000..e138a65 --- /dev/null +++ b/docs/design/off-queue/view-state.md @@ -0,0 +1,33 @@ +Часть [Вне очереди](README.md). Соседей по папке не читать, если задача не про них. + +# Состояние экрана + +### Зачем + +Менеджер — списки с фильтрами. Сейчас они живут только в RAM: F5 всегда меню, повторный вход +сбрасывает вкладки и запросы. Игрок сравнивает людей и соискателей; потерять «ученики 5А» на +обновлении — дырка в уже живых экранах, не новая фича. + +### Было / Стало / Почему + +**Было.** SPA без адреса. `GameScreen.show` и `PeoplePanel.show` обнуляют вкладки, фильтры и +выбор. Язык уже в `localStorage`; игровые часы — на сервере. + +**Стало.** Query URL держит *где я*: школа, обзор/управление, карта/люди, выбранные комната и +человек. `sessionStorage` по `schoolId` держит фильтры, поиски, сорт, страницу, вкладку карточки +и класс сетки — и последний маршрут, чтобы вход с меню поднял то же место. F5 с `?school=` +шлёт тот же `OpenSchool`, что карточка. Вход/выход — `pushState`; смена вкладки и фильтра — +`replaceState`, чтобы «назад» не листал каждый клик. + +**Почему.** Сервер не знает про вкладки; класть их в сейв — лишний HTTP. `localStorage` как у +языка держал бы «5А» через месяц. URL без фильтров остаётся коротким и копируемым. + +### Что видит игрок + +F5 внутри школы — та же школа, те же вкладки и фильтры. Назад — меню. Снова открыть ту же +школу — не сброс на карту. Гость не входит в управление по ссылке. Скорость часов, пауза, +открытый пул соискателей и мастер создания не вспоминаются. + +### Что не входит + +Поиск по имени в списках, где его нет. Роутер-библиотека. Состояние на сервере. Бамп протокола. diff --git a/docs/design/off-queue/whats-new.md b/docs/design/off-queue/whats-new.md new file mode 100644 index 0000000..d5a7246 --- /dev/null +++ b/docs/design/off-queue/whats-new.md @@ -0,0 +1,26 @@ +Часть [Вне очереди](README.md). Соседей по папке не читать, если задача не про них. + +# Что нового после входа + +### Зачем + +Альфа обновляется часто. Игрок не смотрит git: если с прошлого захода в этот браузер накатили +другую сборку Server, один раз показать, что вошло в неё. + +### Было / Стало / Почему + +**Было.** Нет версии сборки на экране. Сессионная кука HttpOnly и про вход. + +**Стало.** Сборка Server вшивает `HEAD` и `git log --first-parent`. `GET /api/changelog?since=` +отдаёт текущий SHA и коммиты после якоря. Клиент держит `hschool.seen-rev` (год, `Path=/`, +`SameSite=Lax`, не HttpOnly). Первый заход без куки окно не показывает — только запоминает +текущий SHA. Окно — после входа, не при возврате из школы. + +**Почему.** «Накатанный» — то, что в dll, а не грязный HEAD при `--no-build`. First-parent после +`--no-ff` — это слитые фазы, не внутренняя нарезка ветки. `Mark phase N` оставляем в цепочке +якорем, в список не рисуем: иначе кука на таком HEAD потеряла бы следующее слияние. + +### Что не входит + +Ручной `CHANGELOG.md`. Запись на пользователе в `users.json`. Вызов git из бегущего процесса. +Бамп версии сокета. Окно внутри открытой школы. diff --git a/docs/phases/00-drop-pixi.md b/docs/phases/01-shell/00-drop-pixi.md similarity index 100% rename from docs/phases/00-drop-pixi.md rename to docs/phases/01-shell/00-drop-pixi.md diff --git a/docs/phases/01-manager-shell.md b/docs/phases/01-shell/01-manager-shell.md similarity index 93% rename from docs/phases/01-manager-shell.md rename to docs/phases/01-shell/01-manager-shell.md index 382a624..6ca0c70 100644 --- a/docs/phases/01-manager-shell.md +++ b/docs/phases/01-shell/01-manager-shell.md @@ -19,7 +19,7 @@ > **Отменено в фазе 8.** Панель общих событий убрана с экрана: пока ленты событий нет, постоянно > пустая треть экрана мешала, и место отдано вкладкам «Карта» и «Люди». Решение записано в -> [`../design/near-term.md`](../design/near-term.md); появится лента — вернётся и панель. +> [`docs/design/01-shell/near-term.md`](../../design/01-shell/near-term.md); появится лента — вернётся и панель. ## Критерий готовности diff --git a/docs/phases/02-school-worker.md b/docs/phases/01-shell/02-school-worker.md similarity index 100% rename from docs/phases/02-school-worker.md rename to docs/phases/01-shell/02-school-worker.md diff --git a/docs/phases/03-defs-map.md b/docs/phases/01-shell/03-defs-map.md similarity index 100% rename from docs/phases/03-defs-map.md rename to docs/phases/01-shell/03-defs-map.md diff --git a/docs/phases/04-create-editor.md b/docs/phases/01-shell/04-create-editor.md similarity index 100% rename from docs/phases/04-create-editor.md rename to docs/phases/01-shell/04-create-editor.md diff --git a/docs/phases/01-shell/README.md b/docs/phases/01-shell/README.md new file mode 100644 index 0000000..0e368be --- /dev/null +++ b/docs/phases/01-shell/README.md @@ -0,0 +1,18 @@ +Индекс срезов: [`../README.md`](../README.md). + +## Срез 1. Оболочка и карта + +Дизайн закрыт: [`../design/near-term.md`](../../design/01-shell/near-term.md), +[`../design/defs.md`](../../design/01-shell/defs.md), +[`../design/runtime.md`](../../design/01-shell/runtime.md), +[`../design/projects.md`](../../design/01-shell/projects.md). + +Новых игровых фич в эти фазы не добавлять. 1 и 2 можно вести параллельно (клиент / сервер). + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [0. Убрать PixiJS](00-drop-pixi.md) | ✅ | Сцена не планируется | +| [1. Оболочка менеджера](01-manager-shell.md) | ✅ | Панели с секциями среза, пока без данных | +| [2. Работник школы и диск](02-school-worker.md) | ✅ | Поток + World + сейв — основа | +| [3. Каталог def и карта](03-defs-map.md) | ✅ | JSONC, core, валидация раскладки | +| [4. Моды и редактор в create](04-create-editor.md) | ✅ | Выбор модов, карта в POST, снимок при открытии | diff --git a/docs/phases/01-shell/reviewed.md b/docs/phases/01-shell/reviewed.md new file mode 100644 index 0000000..0004f96 --- /dev/null +++ b/docs/phases/01-shell/reviewed.md @@ -0,0 +1,42 @@ +# Журнал ревью + +Сводка: [`../reviewed.md`](../reviewed.md). Индекс среза: [`README.md`](README.md). + +## Срез 1. Оболочка и карта + +- **Фазы:** 0–4 +- **Проверен на:** `418a4ea`, 2026-08-20 +- **Пути:** `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` +- **Итог:** обещания фаз 0–4 целы; дописаны 3 теста; протокол v7 в трёх местах + +Что подтверждено: + +- PixiJS нет; дерево карты, пять секций локации, клиентский фильтр, `localize()`. Оболочка — + вкладки Карта/Люди, не три панели; отказ от ленты событий записан в фазе 1 и в `design/near-term.md`. +- `SchoolWorker` — `LongRunning`, свой `World`, аккумулятор с потолком 5. Супервизор таблицу + держит и `World` не трогает. Сейв на create/shutdown/`SaveIntervalSeconds`; + `ReloadFromDisk_RestoresAPausedClock`, `PausingOneSchool_DoesNotStopAnother`. +- Восемь тестов каталога/карты в `HSchool.Content.Tests`. Ванильная раскладка валидна. + Пропавшая папка мода не стартует школу и файл не трогает. +- `GET /api/mods`, каталог, create с картой и модами, снимок при OpenSchool. `core` в диалоге + создания нельзя снять. Кодек, `protocol.ts` и `docs/protocol.md` описывают один `MapSnapshot`. +- `modal.ts` резолвит промис явно; `.dialog--screen` — `height: fit-content`; + `DefaultStartDate` UTC; `MapSnapshotSize` от сообщения; outbox `DropOldest`; + `WaitToReadAsync` переносится между итерациями; меню патчит `card.update`. +- Родной язык — HTTP/JSON без бампа протокола. Старый сейв без поля берёт первый язык набора, + не бросает кубик (`ReloadWithoutASavedPick_TakesTheFirstLanguageInsteadOfRolling`). + +Дописано: + +- `SchoolApiTests.Catalog_WithAnUnknownMod_IsRejected` +- `SchoolApiTests.ModId_ThatEscapesTheModsFolder_IsRejectedOnBothEndpoints` +- `SkillGrantTests.ReloadWithoutASavedPick_TakesTheFirstLanguageInsteadOfRolling` + +Исправлено: + +- Два поля `speedIndex` в клиентском `School` (`api.ts`) — артефакт мержа фазы 26; vitest не + ловил, `npm run build` падал с TS2300. diff --git a/docs/phases/05-people-defs.md b/docs/phases/02-people/05-people-defs.md similarity index 98% rename from docs/phases/05-people-defs.md rename to docs/phases/02-people/05-people-defs.md index 84e1153..2ee6cd2 100644 --- a/docs/phases/05-people-defs.md +++ b/docs/phases/02-people/05-people-defs.md @@ -2,7 +2,7 @@ ## Зависимости -- [Фаза 3](03-defs-map.md) +- [Фаза 3](../01-shell/03-defs-map.md) ## Зачем diff --git a/docs/phases/06-people-generator.md b/docs/phases/02-people/06-people-generator.md similarity index 96% rename from docs/phases/06-people-generator.md rename to docs/phases/02-people/06-people-generator.md index 4bf7455..ae5f6a4 100644 --- a/docs/phases/06-people-generator.md +++ b/docs/phases/02-people/06-people-generator.md @@ -28,7 +28,7 @@ - [x] Тот же сид, карта и набор имён — ровно тот же ростер - [x] Тринадцатая семья не меняет, как разыграны первые двенадцать (фамилия, состав, имена). Класс ребёнка при этом меняется — места раздаются вперемешку по школе, иначе братья и - сёстры всегда оказывались одноклассниками; см. [`../design/people.md`](../design/people.md) + сёстры всегда оказывались одноклассниками; см. [`docs/design/02-people/people.md`](../../design/02-people/people.md) - [x] Фамилии, родовые формы и отчества внутри семьи согласованы - [x] Ученик с телосложением «полное» не получает высокую ловкость - [x] Все ученические места заполнены, все должности закрыты diff --git a/docs/phases/07-people-in-school.md b/docs/phases/02-people/07-people-in-school.md similarity index 97% rename from docs/phases/07-people-in-school.md rename to docs/phases/02-people/07-people-in-school.md index def22bc..75789bc 100644 --- a/docs/phases/07-people-in-school.md +++ b/docs/phases/02-people/07-people-in-school.md @@ -3,7 +3,7 @@ ## Зависимости - [Фаза 6](06-people-generator.md) -- [Фаза 2](02-school-worker.md) +- [Фаза 2](../01-shell/02-school-worker.md) ## Зачем diff --git a/docs/phases/08-people-browser.md b/docs/phases/02-people/08-people-browser.md similarity index 94% rename from docs/phases/08-people-browser.md rename to docs/phases/02-people/08-people-browser.md index f4bfa5b..cfc2343 100644 --- a/docs/phases/08-people-browser.md +++ b/docs/phases/02-people/08-people-browser.md @@ -3,7 +3,7 @@ ## Зависимости - [Фаза 7](07-people-in-school.md) -- [Фаза 1](01-manager-shell.md) +- [Фаза 1](../01-shell/01-manager-shell.md) ## Зачем @@ -18,7 +18,7 @@ `TaskCompletionSource`, потому что нужды живые - [x] Панель «Люди» в оболочке менеджера: фильтры, сортируемые колонки, пейджер. Сделана **вкладкой рядом с картой**, а не нижним рядом — заодно с экрана ушла пустая панель - событий из фазы 1; см. [`../design/people.md`](../design/people.md) + событий из фазы 1; см. [`docs/design/02-people/people.md`](../../design/02-people/people.md) - [x] Строка списка — только лёгкие поля: ФИО, роль, класс или должность, возраст, пол - [x] Карточка: тело, навыки, черты, нужды, семья со ссылками на родных — по ссылке открывается карточка родственника diff --git a/docs/phases/09-yearly-intake.md b/docs/phases/02-people/09-yearly-intake.md similarity index 100% rename from docs/phases/09-yearly-intake.md rename to docs/phases/02-people/09-yearly-intake.md diff --git a/docs/phases/02-people/README.md b/docs/phases/02-people/README.md new file mode 100644 index 0000000..85eb224 --- /dev/null +++ b/docs/phases/02-people/README.md @@ -0,0 +1,19 @@ +Индекс срезов: [`../README.md`](../README.md). + +## Срез 2. Люди + +Дизайн: [`../design/people.md`](../../design/02-people/people.md). + +Школа наполняется людьми. Ученики, работники и родители существуют, показываются и обновляются +раз в год — но **ничего не делают**: ни расписания, ни перемещений, ни влияния характеристик. + +Фазы идут по порядку, кроме 8 и 9: обе зависят только от 7 и могут вестись параллельно +(клиент / симуляция). + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [5. Дефы человека](05-people-defs.md) | ✅ | Навыки, черты, тело, нужды, наборы имён в каталоге | +| [6. Библиотека генерации](06-people-generator.md) | ✅ | `HSchool.People`: семьи из карты и сида | +| [7. Люди в школе](07-people-in-school.md) | ✅ | Сущности в `World`, ростер на диске, 11 кабинетов в `core` | +| [8. Просмотр людей](08-people-browser.md) | ✅ | Панель со списком, фильтрами и карточкой | +| [9. Годовой набор](09-yearly-intake.md) | ✅ | Первое сентября: переход, выпуск, набор | diff --git a/docs/phases/02-people/reviewed.md b/docs/phases/02-people/reviewed.md new file mode 100644 index 0000000..fee81ea --- /dev/null +++ b/docs/phases/02-people/reviewed.md @@ -0,0 +1,44 @@ +# Журнал ревью + +Сводка: [`../reviewed.md`](../reviewed.md). Индекс среза: [`README.md`](README.md). + +## Срез 2. Люди + +- **Фазы:** 5–9 +- **Проверен на:** `bdb1253`, 2026-08-20 +- **Пути:** `src/HSchool.People`, `src/HSchool.Content/PeopleDefs.cs`, + `src/HSchool.Simulation/RosterSpawner.cs`, `src/HSchool.Simulation/School.cs`, + `src/HSchool.Server/Api/SchoolEndpoints.cs`, `src/HSchool.Server/Game/PersonCardReader.cs`, + `src/HSchool.Server/Game/SchoolWorker.cs`, + `src/HSchool.Client/src/ui/peoplePanel.ts`, `src/HSchool.Client/src/ui/personCard.ts`, + `tests/HSchool.People.Tests`, `tests/HSchool.Content.Tests/PeopleDefTests.cs`, + `tests/HSchool.Simulation.Tests/PeopleInSchoolTests.cs`, + `tests/HSchool.AppHost.Tests/PeopleApiTests.cs`, + `tests/HSchool.AppHost.Tests/SchoolSeedTests.cs` +- **Итог:** обещания фаз 5–9 целы; починены два API-теста, дописан один на reload после набора + +Что подтверждено: + +- Дефы человека: разбор, наследование, патч, неизвестная ссылка, склонение, мод со своим набором. +- Генератор: шесть тестов списка фазы, без Arch/ASP.NET/`DateTime.Now`. Неполные семьи 5–10%. + Сид — поле школы, не `school.Id`; хостовые тесты, которым нужен состав, передают сид явно. +- Одиннадцать кабинетов, сущности в `World`, `PersistPeople` только при смене состава. + Штат при создании пуст — это фаза 11. +- Список с опубликованного снимка, карточка через `TaskCompletionSource`. Фильтры, пейджер, + семейные ссылки, всё через `t(...)`. Вкладка рядом с картой, не нижний ряд — так записано + в фазе 8 и в `../../design/02-people/people.md`. +- Выпуск старшей имеющейся, вместимость, семьи через поколения, reload после набора. + +Дописано: + +- `PeopleApiTests.ReloadAfterTheYearlyIntake_RestoresTheNewComposition` — ждёт 1 сентября и + требует состав **после** набора, отличный от исходного (детерминизм от сида иначе скрыл бы + потерянный файл). + +Исправлено: + +- `Card_IncludesFamilyAndLiveNeeds` брал `People[0]` и требовал двух родителей. Состав зависит + от сида; на чистом клоне первый ученик иногда из неполной семьи. Теперь ищет ученика с матерью + и проверяет диапазон. +- `List_SortsBySurname` требовал ordinal-порядок, сервер ставит Ё на место Е. Совпадали, пока + в `core` не было фамилий на Ё. diff --git a/docs/phases/10-subjects.md b/docs/phases/03-staffing/10-subjects.md similarity index 96% rename from docs/phases/10-subjects.md rename to docs/phases/03-staffing/10-subjects.md index eabb50c..e77c935 100644 --- a/docs/phases/10-subjects.md +++ b/docs/phases/03-staffing/10-subjects.md @@ -2,8 +2,8 @@ ## Зависимости -- [Фаза 5](05-people-defs.md) -- [Фаза 7](07-people-in-school.md) +- [Фаза 5](../02-people/05-people-defs.md) +- [Фаза 7](../02-people/07-people-in-school.md) ## Зачем diff --git a/docs/phases/11-applicants.md b/docs/phases/03-staffing/11-applicants.md similarity index 98% rename from docs/phases/11-applicants.md rename to docs/phases/03-staffing/11-applicants.md index 9c5cdd7..a4c858d 100644 --- a/docs/phases/11-applicants.md +++ b/docs/phases/03-staffing/11-applicants.md @@ -3,7 +3,7 @@ ## Зависимости - [Фаза 10](10-subjects.md) -- [Фаза 6](06-people-generator.md) +- [Фаза 6](../02-people/06-people-generator.md) ## Зачем diff --git a/docs/phases/12-hiring-budget.md b/docs/phases/03-staffing/12-hiring-budget.md similarity index 100% rename from docs/phases/12-hiring-budget.md rename to docs/phases/03-staffing/12-hiring-budget.md diff --git a/docs/phases/13-management-tab.md b/docs/phases/03-staffing/13-management-tab.md similarity index 97% rename from docs/phases/13-management-tab.md rename to docs/phases/03-staffing/13-management-tab.md index c451d5d..14676e8 100644 --- a/docs/phases/13-management-tab.md +++ b/docs/phases/03-staffing/13-management-tab.md @@ -3,7 +3,7 @@ ## Зависимости - [Фаза 12](12-hiring-budget.md) -- [Фаза 8](08-people-browser.md) +- [Фаза 8](../02-people/08-people-browser.md) ## Зачем diff --git a/docs/phases/03-staffing/README.md b/docs/phases/03-staffing/README.md new file mode 100644 index 0000000..9de5fe5 --- /dev/null +++ b/docs/phases/03-staffing/README.md @@ -0,0 +1,18 @@ +Индекс срезов: [`../README.md`](../README.md). + +## Срез 3. Штат + +Дизайн: [`../design/staffing.md`](../../design/03-staffing/staffing.md). + +Первый срез, где игрок принимает решения. Школа открывается пустой: надо нанять людей из пула +соискателей, раздать им предметы и уложиться в выделенную на месяц сумму. Уроков по-прежнему нет — +предмет это запись в нагрузке, а не событие в кабинете. + +Фазы идут строго по порядку: каждая опирается на данные предыдущей. + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [10. Предметы и мебель](10-subjects.md) | ✅ | `SubjectDef`, одна учительская должность, кабинет как число мест | +| [11. Пустая школа и пул](11-applicants.md) | ✅ | Школа без сотрудников, соискатели с запросом по зарплате | +| [12. Наём и бюджет](12-hiring-budget.md) | ✅ | Наём, назначение предметов, предел фонда оплаты | +| [13. Раздел «Управление»](13-management-tab.md) | ✅ | Деньги, соискатели, штат и назначения на экране | diff --git a/docs/phases/03-staffing/reviewed.md b/docs/phases/03-staffing/reviewed.md new file mode 100644 index 0000000..8831c8e --- /dev/null +++ b/docs/phases/03-staffing/reviewed.md @@ -0,0 +1,44 @@ +# Журнал ревью + +Сводка: [`../reviewed.md`](../reviewed.md). Индекс среза: [`README.md`](README.md). + +## Срез 3. Штат + +- **Фазы:** 10–13 +- **Проверен на:** `bdb1253`, 2026-08-20 +- **Пути:** `src/HSchool.People/Staffing.cs`, `src/HSchool.People/ApplicantPool.cs`, + `src/HSchool.Content/SubjectDef` и `defs/staffing`, `src/HSchool.Server/Api/SchoolEndpoints.cs` + (staffing/hire/subjects), `src/HSchool.Server/mods/core/defs/{subjects,positions,rooms,staffing,traits}`, + `src/HSchool.Client/src/ui/managementPanel.ts`, `src/HSchool.Client/src/net/api.ts`, + `tests/HSchool.People.Tests/{StaffingTests,ApplicantPoolTests}.cs`, + `tests/HSchool.AppHost.Tests/StaffingApiTests.cs`, + `src/HSchool.Client/src/ui/managementPanel.test.ts` +- **Итог:** обещания фаз 10–13 целы; дописаны 3 теста; `docs/protocol.md` дополнен `?lang=` + +Что подтверждено: + +- `PETeacher` нет; кабинеты без учительской должности; места через `defaultSeats`. + Неизвестный навык в предмете роняет каталог. Стартовая дата 31 марта 2012. +- Пул: детерминизм по сиду и неделе, частичное обновление, устойчивый размер, «сильный просит + больше», пустой штат, соискатель вне ростера до найма. Пул 32. +- Фонд оплаты, деление часов, пол ставки, покрытие. `GET /staffing` читает снимок, наём и + назначение — `GameCommand` с `TaskCompletionSource`. `MaxWeeklyHours` не запрещает назначение, + а оставляет предмет непокрытым. `PrimarySchool` на ванили требует трёх учителей (`teachersShort`). +- Экран: деньги, непокрытые, пул, штат, назначение с карточки, отказ по пределу текстом, + всё через `t(...)`. +- Полностью укомплектованная школа влезает в 100 000 впритык (запас 5–13%). Нанять пул целиком + без назначений предела достигает. Раскладка «каждому все предметы» — самая дешёвая, не + «укомплектованная школа». + +Дописано: + +- `StaffingApiTests.AssigningPastTheCap_IsRejectedWithTheNumbers` — фаза 12 требовала API-тест + на отказ по пределу; единственное место было написано через `if (over.StatusCode == Conflict)` + и проходило при любом исходе. +- `ApplicantPoolTests.ConfidentTraits_AskForMoreAndQuietOnesForLess` +- В `AssignAndUnassign_HonourTheCap_AndReloadKeepsAssignments` — тот же пул с теми же ставками + после перезапуска. + +Исправлено: + +- `docs/protocol.md` не упоминал `?lang=` у hire/subjects, хотя клиент его шлёт. diff --git a/docs/phases/14-school-calendar.md b/docs/phases/04-schedule/14-school-calendar.md similarity index 97% rename from docs/phases/14-school-calendar.md rename to docs/phases/04-schedule/14-school-calendar.md index cb34c58..bedd2c2 100644 --- a/docs/phases/14-school-calendar.md +++ b/docs/phases/04-schedule/14-school-calendar.md @@ -2,7 +2,7 @@ ## Зависимости -- [Фаза 10](10-subjects.md) +- [Фаза 10](../03-staffing/10-subjects.md) ## Зачем diff --git a/docs/phases/15-timetable-planner.md b/docs/phases/04-schedule/15-timetable-planner.md similarity index 98% rename from docs/phases/15-timetable-planner.md rename to docs/phases/04-schedule/15-timetable-planner.md index 0cf95c4..99a7afa 100644 --- a/docs/phases/15-timetable-planner.md +++ b/docs/phases/04-schedule/15-timetable-planner.md @@ -3,7 +3,7 @@ ## Зависимости - [Фаза 14](14-school-calendar.md) -- [Фаза 12](12-hiring-budget.md) +- [Фаза 12](../03-staffing/12-hiring-budget.md) ## Зачем diff --git a/docs/phases/16-timetable-in-school.md b/docs/phases/04-schedule/16-timetable-in-school.md similarity index 100% rename from docs/phases/16-timetable-in-school.md rename to docs/phases/04-schedule/16-timetable-in-school.md diff --git a/docs/phases/17-timetable-screen.md b/docs/phases/04-schedule/17-timetable-screen.md similarity index 97% rename from docs/phases/17-timetable-screen.md rename to docs/phases/04-schedule/17-timetable-screen.md index a6057ed..b6d9b4a 100644 --- a/docs/phases/17-timetable-screen.md +++ b/docs/phases/04-schedule/17-timetable-screen.md @@ -3,7 +3,7 @@ ## Зависимости - [Фаза 16](16-timetable-in-school.md) -- [Фаза 13](13-management-tab.md) +- [Фаза 13](../03-staffing/13-management-tab.md) ## Зачем diff --git a/docs/phases/04-schedule/README.md b/docs/phases/04-schedule/README.md new file mode 100644 index 0000000..0a4c219 --- /dev/null +++ b/docs/phases/04-schedule/README.md @@ -0,0 +1,16 @@ +Индекс срезов: [`../README.md`](../README.md). + +## Срез 4. Расписание + +Дизайн: [`../design/schedule.md`](../../design/04-schedule/schedule.md). + +Часы учебного плана раскладываются по дням и кабинетам. Школа начинает жить по времени: видно, +что идёт в комнате прямо сейчас. Люди при этом по-прежнему **не ходят** — они находятся там, где +их застало расписание. + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [14. Каркас дня и каникулы](14-school-calendar.md) | ✅ | Звонки, длина недели, каникулы | +| [15. Планировщик](15-timetable-planner.md) | ✅ | Раскладка часов по слотам, четыре запрета | +| [16. Расписание в школе](16-timetable-in-school.md) | ✅ | Сейв, снимок, «кто где сейчас» | +| [17. Расписание на экране](17-timetable-screen.md) | ✅ | Скобки в дереве, сетка класса, расписание учителя | diff --git a/docs/phases/04-schedule/reviewed.md b/docs/phases/04-schedule/reviewed.md new file mode 100644 index 0000000..6f13fd6 --- /dev/null +++ b/docs/phases/04-schedule/reviewed.md @@ -0,0 +1,40 @@ +# Журнал ревью + +Сводка: [`../reviewed.md`](../reviewed.md). Индекс среза: [`README.md`](README.md). + +## Срез 4. Расписание + +- **Фазы:** 14–17 +- **Проверен на:** `bdb1253`, 2026-08-20 +- **Пути:** `src/HSchool.Schedule`, `src/HSchool.Content` (каркас дня, каникулы, `SubjectDef.room`), + `src/HSchool.Simulation/SchoolTimetables.cs`, `src/HSchool.Server/Api/TimetableEndpoints.cs`, + `src/HSchool.Server/Game/SchoolWorker.cs` (пин, перестроение), + `src/HSchool.Client/src/ui/timetableGrid.ts`, `src/HSchool.Client/src/ui/gameScreen.ts`, + `tests/HSchool.Schedule.Tests`, `tests/HSchool.Content.Tests/CalendarTests.cs`, + `tests/HSchool.Content.Tests/LunchBreakTests.cs`, + `tests/HSchool.AppHost.Tests/TimetableApiTests.cs`, + `src/HSchool.Client/src/ui/timetableGrid.test.ts` +- **Итог:** обещания фаз 14–17 целы; дописан тест на коды отказа пина; две строки дизайна + помечены как отменённые срезом 5 + +Что подтверждено: + +- Календарь: суббота, каникулы и ночь — «вне учебного дня»; 10:20 вторника — третий урок; + шестидневка; зима через 1 января. Физкультура в спортзале. Обеденные смены в каркасе дня + (младшие после 3-го, старшие после 4-го). +- Планировщик: четыре запрета, класс не влезает в меньший кабинет, без Arch/ASP.NET/`DateTime.Now`. +- Перестроение только при `PeopleChanged` + `TimetableDirty`, не по тику. `GET /timetable` + читает снимок, пин — `TaskCompletionSource`. `Reload_RestoresLockedLessons`. +- Сетка, закрепление, тексты отказа (`pin-rejected` / `no-teacher` / `unknown-lesson`), + скобки в дереве из присутствия, личное расписание в карточке. Всё через `t(...)`. +- Занятость узла в протоколе v7 живёт в кадре `Presence`, не в снимке карты — фаза 19. + +Дописано: + +- `TimetableApiTests.PinAndUnpinRefusals_CarryTheDocumentedCodes` — `no-teacher`, `unknown-class`, + `unknown-subject`, `unknown-room`, `unknown-lesson`, `invalid-query`. + +Исправлено: + +- В `../../design/04-schedule/schedule.md` «панель локации из расписания» и «люди находятся по расписанию» помечены + как отменённые срезом 5. diff --git a/docs/phases/18-presence-walking.md b/docs/phases/05-ai/18-presence-walking.md similarity index 98% rename from docs/phases/18-presence-walking.md rename to docs/phases/05-ai/18-presence-walking.md index a5592c0..b0593f5 100644 --- a/docs/phases/18-presence-walking.md +++ b/docs/phases/05-ai/18-presence-walking.md @@ -2,7 +2,7 @@ ## Зависимости -- [Фаза 16](16-timetable-in-school.md) +- [Фаза 16](../04-schedule/16-timetable-in-school.md) ## Зачем @@ -34,7 +34,7 @@ - [x] Стабильный порядок обхода людей — собственный список, а не порядок сущностей в Arch - [x] Адаптер в `HSchool.Simulation`: собирает значения из компонентов, зовёт библиотеку, записывает ответ обратно. Горячий путь — движение — границу не пересекает -- [x] Ориентир в `AGENTS.md`, `docs/architecture.md` и `docs/design/projects.md` знает про новый +- [x] Ориентир в `AGENTS.md`, `docs/architecture.md` и `docs/design/01-shell/projects.md` знает про новый проект - [x] Место, путь и назначение едут в файл часов; кого в файле нет, тот расставляется по обязанности diff --git a/docs/phases/19-presence-screen.md b/docs/phases/05-ai/19-presence-screen.md similarity index 98% rename from docs/phases/19-presence-screen.md rename to docs/phases/05-ai/19-presence-screen.md index d5cea6d..bb0806a 100644 --- a/docs/phases/19-presence-screen.md +++ b/docs/phases/05-ai/19-presence-screen.md @@ -3,7 +3,7 @@ ## Зависимости - [Фаза 18](18-presence-walking.md) -- [Фаза 17](17-timetable-screen.md) +- [Фаза 17](../04-schedule/17-timetable-screen.md) ## Зачем diff --git a/docs/phases/20-needs-actions.md b/docs/phases/05-ai/20-needs-actions.md similarity index 100% rename from docs/phases/20-needs-actions.md rename to docs/phases/05-ai/20-needs-actions.md diff --git a/docs/phases/21-decisions.md b/docs/phases/05-ai/21-decisions.md similarity index 100% rename from docs/phases/21-decisions.md rename to docs/phases/05-ai/21-decisions.md diff --git a/docs/phases/05-ai/README.md b/docs/phases/05-ai/README.md new file mode 100644 index 0000000..85e9662 --- /dev/null +++ b/docs/phases/05-ai/README.md @@ -0,0 +1,24 @@ +Индекс срезов: [`../README.md`](../README.md). + +## Срез 5. Жизнь школы + +Дизайн: [`../design/ai.md`](../../design/05-ai/ai.md). + +Люди начинают ходить и решать. Присутствие перестаёт вычисляться из расписания и становится +состоянием: человек приходит утром, идёт коридором, может уйти с урока в туалет и вернуться. + +Срез идёт **двумя этапами**, и между ними можно остановиться — школа останется целостной. + +**Этап A — присутствие.** Люди ходят по обязанности; выбора у них ещё нет. + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [18. Присутствие и ходьба](18-presence-walking.md) | ✅ | `HSchool.Ai`, место человека, маршруты, приход и уход | +| [19. Присутствие на экране](19-presence-screen.md) | ✅ | Своё сообщение ~2 Гц, числа в дереве, статический снимок карты, пропуск пустого времени | + +**Этап B — поведение.** Появляются нужды, действия и выбор между ними и обязанностью. + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [20. Нужды и действия](20-needs-actions.md) | ✅ | `ActionDef` с полями, декей, восполнение | +| [21. Выбор действия](21-decisions.md) | ✅ | Цели и веса, «дойти → сделать», уход с урока, рост навыка | diff --git a/docs/phases/05-ai/reviewed.md b/docs/phases/05-ai/reviewed.md new file mode 100644 index 0000000..2a5029f --- /dev/null +++ b/docs/phases/05-ai/reviewed.md @@ -0,0 +1,42 @@ +# Журнал ревью + +Сводка: [`../reviewed.md`](../reviewed.md). Индекс среза: [`README.md`](README.md). + +## Срез 5. Жизнь школы + +- **Фазы:** 18–21 +- **Проверен на:** `cbe739a`, 2026-08-20 +- **Пути:** `src/HSchool.Ai`, `src/HSchool.Simulation/{PresenceSystem,ActivitySystem,LessonLearningSystem,School}.cs`, + `src/HSchool.Protocol` (кадр `Presence`, пропуск в кадре часов), + `src/HSchool.Server/Game/PresenceFrame.cs`, `src/HSchool.Client/src/ui/gameScreen.ts`, + `src/HSchool.Server/mods/core/defs/{actions,behavior,needs}`, `tests/HSchool.Ai.Tests`, + `tests/HSchool.Simulation.Tests/{PresenceTests,DecisionTests,ActivityTests,NeedDecayTests,LunchTests}.cs`, + `tests/HSchool.AppHost.Tests/GameSocketTests.cs` +- **Итог:** все 32 теста из списков фаз на месте; дописаны 2 теста явки; `SetTimetable` сбрасывает + планы дня. Голод закрыт данными. AppHost после правки не гонялся — Aspire держал DLL. + +Что подтверждено: + +- Ходьба: 13 тестов списка фазы 18 (маршрут, `travelMinutes`, воскресенье/каникулы, звонок, + детерминизм, пропуск пустого времени, лето, промотанная неделя). +- Присутствие: раскладка с обеих сторон, круговой прогон, люди не в снимке карты, пропуск в + учебное время отбрасывается. На паузе кадры **идут** (occupancy + подписи урока); людей в них + может не быть. +- Нужды: декей, обед, стулья, ночь вне школы, неизвестная нужда, детерминизм. + `Hunger` восстанавливается вне школы, в столовой 112 стульев, обед — смена по параллелям. + `SleepAndHunger_ReturnToMaxOffCampus`, `AfterASchoolDay_NobodyIsLeftStarving`. +- Решения: восемь тестов фазы 21, в том числе туалет с урока и возврат, потолок отложенных, + детерминизм за неделю. `HSchool.Ai` без Arch/ASP.NET/`DateTime.Now`. + +Дописано: + +- `HireAndAssignMath_DuringLessonThree_PutsSomeoneOnCampus` +- `AssigningMath_AfterDayPlansWereBuilt_PutsSomeoneOnCampus` + +Исправлено: + +- `School.SetTimetable` обнуляет `PlanDay`. Иначе после назначения предмета без смены численности + `EnsurePlans` считает планы актуальными, `AppearAt` остаётся `null`, кампус пуст. Найм это + маскировал (численность растёт). +- `OpeningASchoolDuringAMathLesson_...` после снятия паузы сразу ждал людей и съедал бюджет на + паузные кадры. Теперь ждёт `clock.Running`, потом людей. diff --git a/docs/phases/22-mod-identity.md b/docs/phases/06-foundation/22-mod-identity.md similarity index 93% rename from docs/phases/22-mod-identity.md rename to docs/phases/06-foundation/22-mod-identity.md index 0c2d43b..67a9022 100644 --- a/docs/phases/22-mod-identity.md +++ b/docs/phases/06-foundation/22-mod-identity.md @@ -2,7 +2,7 @@ ## Зависимости -- [Фаза 4](04-create-editor.md) — моды и каталог уже ездят в create +- [Фаза 4](../01-shell/04-create-editor.md) — моды и каталог уже ездят в create ## Зачем @@ -25,7 +25,7 @@ - [x] Разрешённый порядок виден: пишется в лог при старте школы и возвращается в ответе создания - [x] Сейв хранит **разрешённый** порядок паков, чтобы школа поднималась тем же каталогом - [x] Загрузчик пишет предупреждение, когда у конкретного def нет подписи в локали пака -- [x] `docs/protocol.md` и [`../design/foundation.md`](../design/foundation.md) правятся тем же +- [x] `docs/protocol.md` и [`docs/design/06-foundation/foundation.md`](../../design/06-foundation/foundation.md) правятся тем же коммитом, что и обработчики ## Тесты, без которых фаза не закрыта diff --git a/docs/phases/23-example-pack.md b/docs/phases/06-foundation/23-example-pack.md similarity index 93% rename from docs/phases/23-example-pack.md rename to docs/phases/06-foundation/23-example-pack.md index 3cc5753..130bcae 100644 --- a/docs/phases/23-example-pack.md +++ b/docs/phases/06-foundation/23-example-pack.md @@ -18,7 +18,7 @@ пока не выбран - [x] Пак едет в вывод сборки тестов теми же правилами, что и `core` - [x] Короткий `README.md` внутри пака: что где лежит и как добавить своё -- [x] Ориентир в [`../design/defs.md`](../design/defs.md) показывает на него как на образец +- [x] Ориентир в [`docs/design/01-shell/defs.md`](../../design/01-shell/defs.md) показывает на него как на образец ## Тесты, без которых фаза не закрыта diff --git a/docs/phases/24-behavior-numbers.md b/docs/phases/06-foundation/24-behavior-numbers.md similarity index 89% rename from docs/phases/24-behavior-numbers.md rename to docs/phases/06-foundation/24-behavior-numbers.md index 5622b44..d0653d6 100644 --- a/docs/phases/24-behavior-numbers.md +++ b/docs/phases/06-foundation/24-behavior-numbers.md @@ -2,11 +2,11 @@ ## Зависимости -- [Фаза 20](20-needs-actions.md) — `BehaviorDef` уже есть +- [Фаза 20](../05-ai/20-needs-actions.md) — `BehaviorDef` уже есть ## Зачем -[`../design/ai.md`](../design/ai.md) обещает: числа поведения — деф правил, как `StaffingDef` у +[`docs/design/05-ai/ai.md`](../../design/05-ai/ai.md) обещает: числа поведения — деф правил, как `StaffingDef` у штата. Порог нужды и разброс на дорогу туда уехали, а веса целей остались константами в коде. Пока они там, мод, меняющий одно число, вынужден быть форком. diff --git a/docs/phases/25-golden-fixtures.md b/docs/phases/06-foundation/25-golden-fixtures.md similarity index 100% rename from docs/phases/25-golden-fixtures.md rename to docs/phases/06-foundation/25-golden-fixtures.md diff --git a/docs/phases/26-school-seed.md b/docs/phases/06-foundation/26-school-seed.md similarity index 91% rename from docs/phases/26-school-seed.md rename to docs/phases/06-foundation/26-school-seed.md index bc287be..6afbf30 100644 --- a/docs/phases/26-school-seed.md +++ b/docs/phases/06-foundation/26-school-seed.md @@ -2,7 +2,7 @@ ## Зависимости -- [Фаза 7](07-people-in-school.md) — сид уже лежит в файле людей +- [Фаза 7](../02-people/07-people-in-school.md) — сид уже лежит в файле людей ## Зачем @@ -21,7 +21,7 @@ - [x] Существующие школы не меняются: сид читается из файла людей, как и сейчас - [x] Тесты хоста, которым нужен предсказуемый состав, передают сид явно, а не полагаются на порядок создания -- [x] `docs/protocol.md` и [`../design/people.md`](../design/people.md) правятся тем же коммитом +- [x] `docs/protocol.md` и [`docs/design/02-people/people.md`](../../design/02-people/people.md) правятся тем же коммитом ## Тесты, без которых фаза не закрыта diff --git a/docs/phases/27-serviceability.md b/docs/phases/06-foundation/27-serviceability.md similarity index 96% rename from docs/phases/27-serviceability.md rename to docs/phases/06-foundation/27-serviceability.md index ebf7d14..a58b90b 100644 --- a/docs/phases/27-serviceability.md +++ b/docs/phases/06-foundation/27-serviceability.md @@ -2,7 +2,7 @@ ## Зависимости -- [Фаза 2](02-school-worker.md) — сейв и дев-поверхность уже есть +- [Фаза 2](../01-shell/02-school-worker.md) — сейв и дев-поверхность уже есть ## Зачем diff --git a/docs/phases/28-screen-tests.md b/docs/phases/06-foundation/28-screen-tests.md similarity index 94% rename from docs/phases/28-screen-tests.md rename to docs/phases/06-foundation/28-screen-tests.md index db316a8..4999809 100644 --- a/docs/phases/28-screen-tests.md +++ b/docs/phases/06-foundation/28-screen-tests.md @@ -2,7 +2,7 @@ ## Зависимости -- [Фаза 13](13-management-tab.md), [Фаза 17](17-timetable-screen.md) — самые крупные экраны уже есть +- [Фаза 13](../03-staffing/13-management-tab.md), [Фаза 17](../04-schedule/17-timetable-screen.md) — самые крупные экраны уже есть ## Зачем diff --git a/docs/phases/06-foundation/README.md b/docs/phases/06-foundation/README.md new file mode 100644 index 0000000..d540015 --- /dev/null +++ b/docs/phases/06-foundation/README.md @@ -0,0 +1,23 @@ +Индекс срезов: [`../README.md`](../README.md). + +## Срез 6. Фундамент + +Дизайн: [`../design/foundation.md`](../../design/06-foundation/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-окружение и логика клиента под тестом | diff --git a/docs/phases/06-foundation/reviewed.md b/docs/phases/06-foundation/reviewed.md new file mode 100644 index 0000000..a4836e6 --- /dev/null +++ b/docs/phases/06-foundation/reviewed.md @@ -0,0 +1,44 @@ +# Журнал ревью + +Сводка: [`../reviewed.md`](../reviewed.md). Индекс среза: [`README.md`](README.md). + +## Срез 6. Фундамент + +- **Фазы:** 22–28 +- **Проверен на:** `cbe739a`, 2026-08-20 +- **Пути:** `src/HSchool.Content` (`PackManifest`, `PackLoadOrder`, `BehaviorDef`), + `src/HSchool.Server/mods/{core/pack.jsonc,example}`, `src/HSchool.Server/Game/SchoolStore.cs`, + `src/HSchool.Server/Api/DevEndpoints.cs`, `src/HSchool.Server/Game/SchoolDumpReader.cs`, + `src/HSchool.People/RosterJson.cs`, `src/HSchool.Client/src/ui/*.test.ts`, + `tests/HSchool.Content.Tests/{PackIdentityTests,ExamplePackTests,BehaviorDefTests}.cs`, + `tests/HSchool.People.Tests/GoldenRosterTests.cs`, + `tests/HSchool.AppHost.Tests/{ExamplePackTests,GoldenSaveTests,SchoolSeedTests,ServiceabilityTests}.cs` +- **Итог:** все тесты из списков семи фаз на месте; дописан один клиентский; `foundation.md` + приведён к закрытому срезу. Хостовые тесты сверены по коду, не прогоном. + +Что подтверждено: + +- Пак: манифест, `missing-mod`, порядок — выбор игрока плюс топология (не алфавит), цикл — + `mod-cycle`, def без подписи предупреждает и грузится. +- `mods/example` на диске; каталог/create/reload; патч только с паком; ваниль без выбора не меняется. +- Веса в `BehaviorDef`; пак с обедом выше урока уводит класс; отрицательный вес роняет загрузку. +- Золотой ростер и сейв; `PrimarySchool` требует трёх; `teachersShort` в API и в `t(...)`. +- Сид — поле школы. Format новее — школа не стартует, файл нетронут. Дамп через снимок + мейлбокс, + не в `World` с HTTP. `HSchool.Simulation.Tests` без параллельности. +- Клиентские экраны в `happy-dom`: фильтры/пейджер, create, сетка, предел фонда текстом. + +Дописано: + +- `managementPanel.test.ts` — непокрытый предмет показывает `teachersShort`. + +Исправлено: + +- `AGENTS.md` утверждал, что на паузе кадры присутствия не приходят. Работник их шлёт + (occupancy + подписи). Тест, который ждёт *людей* на паузе, зависнет; тест на подпись урока — нет. +- `../../design/06-foundation/foundation.md`: порядок загрузки — не алфавит; «почему сейчас» про веса в `Decision.cs` + устарел. + +Замечено рядом: + +- `Dump_ReturnsPeopleNodesAndTimetable` принимает `NodeId is not null || Needs.Count > 0`. + Нужды есть у всех, так что узлы на кампусе не проверяются. Усиливать до явки — срез 5. diff --git a/docs/phases/29-country.md b/docs/phases/07-inventory/29-country.md similarity index 97% rename from docs/phases/29-country.md rename to docs/phases/07-inventory/29-country.md index ab2fe05..c0c5fe7 100644 --- a/docs/phases/29-country.md +++ b/docs/phases/07-inventory/29-country.md @@ -2,8 +2,8 @@ ## Зависимости -- [Фаза 5](05-people-defs.md) -- [Фаза 26](26-school-seed.md) +- [Фаза 5](../02-people/05-people-defs.md) +- [Фаза 26](../06-foundation/26-school-seed.md) ## Зачем diff --git a/docs/phases/30-apparel-defs.md b/docs/phases/07-inventory/30-apparel-defs.md similarity index 93% rename from docs/phases/30-apparel-defs.md rename to docs/phases/07-inventory/30-apparel-defs.md index c82d0c3..b3bcb96 100644 --- a/docs/phases/30-apparel-defs.md +++ b/docs/phases/07-inventory/30-apparel-defs.md @@ -2,7 +2,7 @@ ## Зависимости -- [Фаза 3](03-defs-map.md) +- [Фаза 3](../01-shell/03-defs-map.md) ## Зачем @@ -18,7 +18,7 @@ ошибка - [x] Вещь с несколькими `layers` допустима (платье). Пересечение слоёв у двух надетых сразу валидатор не проверяет — это состояние человека, не каталога -- [x] Ваниль по [`../design/inventory.md`](../design/inventory.md): бельё, носки, низы, верх, +- [x] Ваниль по [`docs/design/07-inventory/inventory.md`](../../design/07-inventory/inventory.md): бельё, носки, низы, верх, свитер, куртка/пальто/шуба, обувь, шапка, аксессуары, платье, форма физкультуры, портфель, учебник, телефон, бутылка - [x] Палитра цветов и локали ru/en на все новые def diff --git a/docs/phases/31-dress-generator.md b/docs/phases/07-inventory/31-dress-generator.md similarity index 98% rename from docs/phases/31-dress-generator.md rename to docs/phases/07-inventory/31-dress-generator.md index 91595c3..46f6ff2 100644 --- a/docs/phases/31-dress-generator.md +++ b/docs/phases/07-inventory/31-dress-generator.md @@ -4,7 +4,7 @@ - [Фаза 29](29-country.md) - [Фаза 30](30-apparel-defs.md) -- [Фаза 6](06-people-generator.md) +- [Фаза 6](../02-people/06-people-generator.md) ## Зачем diff --git a/docs/phases/32-weather-warmth.md b/docs/phases/07-inventory/32-weather-warmth.md similarity index 98% rename from docs/phases/32-weather-warmth.md rename to docs/phases/07-inventory/32-weather-warmth.md index 955e180..1495328 100644 --- a/docs/phases/32-weather-warmth.md +++ b/docs/phases/07-inventory/32-weather-warmth.md @@ -3,7 +3,7 @@ ## Зависимости - [Фаза 29](29-country.md) -- [Фаза 20](20-needs-actions.md) +- [Фаза 20](../05-ai/20-needs-actions.md) ## Зачем diff --git a/docs/phases/33-lockers-technician.md b/docs/phases/07-inventory/33-lockers-technician.md similarity index 98% rename from docs/phases/33-lockers-technician.md rename to docs/phases/07-inventory/33-lockers-technician.md index b3cbde5..2b69fdc 100644 --- a/docs/phases/33-lockers-technician.md +++ b/docs/phases/07-inventory/33-lockers-technician.md @@ -4,7 +4,7 @@ - [Фаза 31](31-dress-generator.md) - [Фаза 32](32-weather-warmth.md) -- [Фаза 11](11-applicants.md) +- [Фаза 11](../03-staffing/11-applicants.md) ## Зачем diff --git a/docs/phases/34-wear-home.md b/docs/phases/07-inventory/34-wear-home.md similarity index 98% rename from docs/phases/34-wear-home.md rename to docs/phases/07-inventory/34-wear-home.md index 13975f0..2c04a34 100644 --- a/docs/phases/34-wear-home.md +++ b/docs/phases/07-inventory/34-wear-home.md @@ -3,7 +3,7 @@ ## Зависимости - [Фаза 31](31-dress-generator.md) -- [Фаза 18](18-presence-walking.md) +- [Фаза 18](../05-ai/18-presence-walking.md) ## Зачем diff --git a/docs/phases/35-appropriateness.md b/docs/phases/07-inventory/35-appropriateness.md similarity index 98% rename from docs/phases/35-appropriateness.md rename to docs/phases/07-inventory/35-appropriateness.md index dad5e79..b5d314d 100644 --- a/docs/phases/35-appropriateness.md +++ b/docs/phases/07-inventory/35-appropriateness.md @@ -5,7 +5,7 @@ - [Фаза 32](32-weather-warmth.md) - [Фаза 33](33-lockers-technician.md) - [Фаза 34](34-wear-home.md) -- [Фаза 21](21-decisions.md) +- [Фаза 21](../05-ai/21-decisions.md) ## Зачем diff --git a/docs/phases/36-person-card-tabs.md b/docs/phases/07-inventory/36-person-card-tabs.md similarity index 96% rename from docs/phases/36-person-card-tabs.md rename to docs/phases/07-inventory/36-person-card-tabs.md index 7b8b776..fbab9f2 100644 --- a/docs/phases/36-person-card-tabs.md +++ b/docs/phases/07-inventory/36-person-card-tabs.md @@ -3,8 +3,8 @@ ## Зависимости - [Фаза 31](31-dress-generator.md) -- [Фаза 8](08-people-browser.md) -- [Фаза 28](28-screen-tests.md) +- [Фаза 8](../02-people/08-people-browser.md) +- [Фаза 28](../06-foundation/28-screen-tests.md) ## Зачем diff --git a/docs/phases/37-school-rules.md b/docs/phases/07-inventory/37-school-rules.md similarity index 93% rename from docs/phases/37-school-rules.md rename to docs/phases/07-inventory/37-school-rules.md index 2223f7e..4cb99a3 100644 --- a/docs/phases/37-school-rules.md +++ b/docs/phases/07-inventory/37-school-rules.md @@ -4,7 +4,7 @@ - [Фаза 35](35-appropriateness.md) - [Фаза 36](36-person-card-tabs.md) -- [Фаза 13](13-management-tab.md) +- [Фаза 13](../03-staffing/13-management-tab.md) ## Зачем @@ -14,7 +14,7 @@ - [x] В «Управлении» вкладка **«Правила»** рядом со штатом, не новый верхний режим - [x] Четыре комбобокса: форма учеников, цвет учеников, форма сотрудников, цвет сотрудников. - Значения — как в [`../design/inventory.md`](../design/inventory.md) + Значения — как в [`docs/design/07-inventory/inventory.md`](../../design/07-inventory/inventory.md) - [x] Сохранение пишет завтрашнее правило; на экране видно, что действует сегодня и что вступит завтра - [x] HTTP: чтение текущих+отложенных, запись отложенных. Отказ по неизвестному коду правила — diff --git a/docs/phases/07-inventory/README.md b/docs/phases/07-inventory/README.md new file mode 100644 index 0000000..8b7683b --- /dev/null +++ b/docs/phases/07-inventory/README.md @@ -0,0 +1,41 @@ +Индекс срезов: [`../README.md`](../README.md). + +## Срез 7. Одежда, вещи и климат + +Дизайн: [`../design/inventory.md`](../../design/07-inventory/inventory.md). + +Человек одевается, несёт вещи, мёрзнет и переодевается. Игрок смотрит гардероб и выбирает +правила школы; надевает не он. Погода видна у часов. Срез можно остановить после этапа A — +школа останется одетой, даже если климат ещё не живой. + +**Этап A — данные и гардероб.** Страна вместо набора имён, вещи умеют быть одеждой, генератор +одевает. + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [29. Страна](29-country.md) | ✅ | `CountryDef`, create выбирает страну, климат-пресет от сида | +| [30. Слои и палитра](30-apparel-defs.md) | ✅ | Поля `ThingDef`, `ColorDef`, ванильная одежда и ноша | +| [31. Генератор одевает](31-dress-generator.md) | ✅ | Гардероб на человеке, ноша-число, `people.json` | + +29 и 30 независимы; 31 стоит на обеих. + +**Этап B — климат.** Улица, нужда тепла, техник, раздевалки. + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [32. Погода и тепло](32-weather-warmth.md) | ✅ | Пресет крутит улицу, кадр часов, нужда, две черты | +| [33. Техник и шкафчики](33-lockers-technician.md) | ✅ | Комната техника, две раздевалки, раздача шкафчиков | + +32 стоит на 29; 33 — на 31 и 32. + +**Этап C — жизнь одежды.** Износ, переодевание, экран. + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [34. Износ и дом](34-wear-home.md) | ✅ | Прочность, утренняя замена, из ветхого не выходят | +| [35. Уместность](35-appropriateness.md) | ✅ | Правила, переодевание действием, физкультура | +| [36. Вкладки карточки](36-person-card-tabs.md) | ✅ | Одежда, ноша, сейчас + история за сегодня | +| [37. Правила школы](37-school-rules.md) | ✅ | Вкладка в «Управлении», четыре комбобокса, с завтра | + +34 стоит на 31; 35 — на 32–34; 36 — на 31 и может идти параллельно с 35 в части вкладок одежды, +история — после 35; 37 стоит на 35 и 36. diff --git a/docs/phases/07-inventory/reviewed.md b/docs/phases/07-inventory/reviewed.md new file mode 100644 index 0000000..9ce94d1 --- /dev/null +++ b/docs/phases/07-inventory/reviewed.md @@ -0,0 +1,100 @@ +# Журнал ревью + +Сводка: [`../reviewed.md`](../reviewed.md). Индекс среза: [`README.md`](README.md). + +## Срез 7. Этап A — данные и гардероб + +- **Фазы:** 29–31 +- **Проверен на:** `d5547b7`, 2026-08-20 +- **Пути:** `src/HSchool.Content/{CountryDef,ApparelDefs,CatalogLoader}.cs`, + `src/HSchool.People/{DressGenerator,CountryClimate,RosterJson}.cs`, + `src/HSchool.Server/{Game/SchoolStore.cs,Api/{ModEndpoints,SchoolEndpoints}.cs,Api/PeopleModels.cs}`, + `src/HSchool.Client/src/ui/{createSchoolDialog.ts,personCard.ts}`, + `tests/HSchool.Content.Tests/{CountryDefTests,ApparelDefTests,VanillaCoreTests}.cs`, + `tests/HSchool.People.Tests/{DressGeneratorTests,GoldenRosterTests}.cs`, + `tests/HSchool.AppHost.Tests/{SchoolApiTests,InventoryApiTests,GoldenSaveTests,ServiceabilityTests}.cs`, + `src/HSchool.Client/src/ui/createSchoolDialog.test.ts` +- **Итог:** все 17 тестов из списков фаз на месте; исправлен дубликат `peopleTabOverview` в + `strings.ts` (ломал `npm run build`); одно замечание рядом про вкладки карточки + +Что подтверждено: + +- **29:** `CountryDef`/`ClimatePresetDef`; `namesets/` убран; ваниль `Russia` с двумя пресетами; + каталог отдаёт `countries`; create принимает `countryId`, `unknown-country` на чужую страну; + пресет от сида (`CreateSchool_WithTheSameSeed_GetsTheSameClimatePreset`); `nameSetId: Slavic` + поднимается через `UpgradeOlderSave` (`GoldenSaveTests`, `legacy-no-native/`); формат без + страны не стартует (`ServiceabilityTests`); диалог — страны и языки (`createSchoolDialog.test.ts`); + золотой ростер обновлён (`GoldenRosterTests`). +- **30:** поля `ThingDef`/`ColorDef`, слои как роли, мебель без `layers`; ванильная палитра и + одежда в `VanillaCoreTests`; локали ru/en (`EveryVanillaLabel_ExistsInBothLocales`); учебник + один def без `subject` на def. +- **31:** `InventoryItem` с `def`/`color`/`condition`/`location`/`subject`; `DressGenerator` + детерминирован (`DressGeneratorTests`, `InventoryApiTests.SameSeed_YieldsTheSameShirt`); слои + без пересечения, платье на два слоя; пол/яркость/учебник/родители/соискатели/масса/Hauling; + карточка отдаёт worn+carried, список — нет (`InventoryApiTests`); старый `people.json` без + items одевается на reload (`PeopleJsonWithoutInventory_IsDressedOnReloadAndRewritten`); + `HSchool.People`/`HSchool.Content` без ASP.NET; протокол не тронут. + +Дописано: + +- (нет новых тестов — списки фаз закрыты существующими) + +Исправлено: + +- Дубликат ключа `peopleTabOverview` в `strings.ts` (ru/en): vitest не ловит TS2300/TS1117, + `npm run build` падал; ключ перенесён, `peopleTabPortrait` рядом с остальными вкладками. +- Повтор `using HSchool.Content` в `SchoolStore.cs` (CS0105). + +Замечено рядом: + +- Карточка уже с вкладками (`personCard.ts`, фаза 31 обещала один свиток — позже по + `../../design/07-inventory/inventory.md`); портрет/SwarmUI-строки в `strings.ts` — не этап A. +- `ResolveClimatePreset` в `SchoolWorker` при reload без сохранённого пресета берёт первый + пресет страны (`schoolSeed: 0`, `rollIfOmitted: false`) — по дизайну для legacy; create + кидает пресет из `GameLoopService` до spawn. + +## Срез 7. Этап B–C — климат, жизнь одежды и правила + +- **Фазы:** 32–37 +- **Проверен на:** `5dcdee6`, 2026-08-20 +- **Пути:** `src/HSchool.Simulation/{WeatherSampler,WarmthDecay,PlaceClimate,ApparelDresser,ApparelWear,MorningDress,PersonLogBrowser}.cs`, + `src/HSchool.People/LockerAssigner.cs`, `src/HSchool.Content/{Appropriateness,ClimatePreset,DressRules}.cs`, + `src/HSchool.Ai/ApparelGoals.cs`, `src/HSchool.Protocol`, `src/HSchool.Server/Api/{SchoolEndpoints,DressRulesModels}.cs`, + `src/HSchool.Server/Game/{PersonCardReader,SchoolWorker}.cs`, `src/HSchool.Client/src/{ui/dressRulesPanel.ts,ui/managementPanel.ts,format/weather.ts,net/api.ts}`, + `docs/protocol.md`, `tests/HSchool.Simulation.Tests`, `tests/HSchool.People.Tests/LockerAssignerTests.cs`, + `tests/HSchool.Content.Tests/{ClimatePresetTests,AppropriatenessTests,ChangingRoomMapTests}.cs`, + `tests/HSchool.AppHost.Tests/{PersonLogApiTests,DressRulesApiTests}.cs`, `tests/HSchool.Protocol.Tests`, + `src/HSchool.Client/src/ui/{personCard,dressRulesPanel,managementPanel}.test.ts` +- **Итог:** фазы 32–37 подтверждены; дописано 5 тестов симуляции; исправлены 2 бага переодевания и 2 + регрессии тестов (не протокол) + +Что подтверждено: + +- **32–36:** погода детерминирована; skip выставляет утро; кадр часов v8 (27 байт) в трёх местах; + `Warmth` и техник; шкафчики; износ и утренняя замена; уместность и PE; вкладки карточки и HTTP-лог. +- **37:** вкладка «Правила» в «Управлении»; четыре комбобокса; GET/POST `dress-rules` с pending; + `unknown-form`/`unknown-color` → `400`; правило с завтра; `ColorAllowed` для `whiteTopBlackBottom`; + `dressRulesPanel.test.ts` — combobox без API до Apply, ошибка текстом; `ManagementPanel` не ломает штат + и сетку. + +Дописано (32–36, прошлый проход): + +- `AppropriatenessSimulationTests.MorningDress_StagesPeKitForTodaysLesson` +- `AppropriatenessSimulationTests.PeBreak_CompletesChangeBeforeLesson` +- `AppropriatenessSimulationTests.PeLesson_WearsPeThenReturnsToEveryday` +- `AppropriatenessSimulationTests.AfterPeBreak_ModeExpectsEveryday` +- `PresenceTests.PhysicalEducationBeforeLesson_GoesToChangingRoomOnTheBreak` + +Исправлено: + +- `PresenceSystem`: ходьба по duty не обрывает активный `ChangeClothes`. +- `ApparelPresence.ModeAt`: после перемены following PE — режим everyday. +- `managementPanel.test.ts`: панель штата скрывается через `hidden`, не удаляется из DOM. +- `PortraitApiTests.GetSwarmUiSettings_ReturnsPresets`: URL `/api/settings/swarmui` (CS1503). +- `dressRulesPanel.ts`: неиспользуемый импорт `clear` — TS6133 на `npm run build`. + +Замечено рядом: + +- `PhysicalEducationBeforeLesson_…` не гоняет полный цикл «зал с формой» — это в + `AppropriatenessSimulationTests.PeLesson_…`. +- Портреты/SwarmUI — вне среза 7, но `PortraitApiTests` чинился, чтобы AppHost-сборка не падала. diff --git a/docs/phases/38-session.md b/docs/phases/08-session/38-session.md similarity index 95% rename from docs/phases/38-session.md rename to docs/phases/08-session/38-session.md index 365cf5e..a94b3cc 100644 --- a/docs/phases/38-session.md +++ b/docs/phases/08-session/38-session.md @@ -2,9 +2,9 @@ ## Зависимости -- [Фаза 1](01-manager-shell.md) -- [Фаза 2](02-school-worker.md) -- [Фаза 28](28-screen-tests.md) +- [Фаза 1](../01-shell/01-manager-shell.md) +- [Фаза 2](../01-shell/02-school-worker.md) +- [Фаза 28](../06-foundation/28-screen-tests.md) ## Зачем diff --git a/docs/phases/39-school-owners.md b/docs/phases/08-session/39-school-owners.md similarity index 98% rename from docs/phases/39-school-owners.md rename to docs/phases/08-session/39-school-owners.md index 6fc51fa..58816c8 100644 --- a/docs/phases/39-school-owners.md +++ b/docs/phases/08-session/39-school-owners.md @@ -3,7 +3,7 @@ ## Зависимости - [Фаза 38](38-session.md) -- [Фаза 13](13-management-tab.md) +- [Фаза 13](../03-staffing/13-management-tab.md) ## Зачем diff --git a/docs/phases/40-clock-tempo.md b/docs/phases/08-session/40-clock-tempo.md similarity index 96% rename from docs/phases/40-clock-tempo.md rename to docs/phases/08-session/40-clock-tempo.md index 06999c1..5089bf2 100644 --- a/docs/phases/40-clock-tempo.md +++ b/docs/phases/08-session/40-clock-tempo.md @@ -2,8 +2,8 @@ ## Зависимости -- [Фаза 2](02-school-worker.md) -- [Фаза 20](20-needs-actions.md) +- [Фаза 2](../01-shell/02-school-worker.md) +- [Фаза 20](../05-ai/20-needs-actions.md) ## Зачем diff --git a/docs/phases/08-session/README.md b/docs/phases/08-session/README.md new file mode 100644 index 0000000..4e4c202 --- /dev/null +++ b/docs/phases/08-session/README.md @@ -0,0 +1,30 @@ +Индекс срезов: [`../README.md`](../README.md). + +## Срез 8. Сессия и темп + +Дизайн: [`../design/session.md`](../../design/08-session/session.md). + +Несколько браузеров на одном сервере: общий пароль альфы, имя игрока, свои школы и чужие +отдельным списком. Часы становятся смотрибельными — минута за секунду, на высоких скоростях +тяжёлые системы тикают реже. Срез можно остановить после этапа B: мультиплеер жив, даже если +темп ещё старый. + +**Этап A — кто вошёл.** Пароль, имя, кука. + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [38. Сессия](38-session.md) | ✅ | Пароль альфы, имя, кука, сокет после входа | + +**Этап B — чьи школы.** Хозяин, слоты, гость только смотрит. + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [39. Хозяин школы](39-school-owners.md) | ✅ | `owner`, 2 слота на игрока, мои / чужие, гость без управления | + +39 стоит на 38. + +**Этап C — темп.** Не зависит от A/B, можно вести параллельно с 38. + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [40. Темп часов](40-clock-tempo.md) | ✅ | ×1 = минута/с, кнопки ×½ ×1 ×2 ×5 ×10, страйд тяжёлых систем | diff --git a/docs/phases/08-session/reviewed.md b/docs/phases/08-session/reviewed.md new file mode 100644 index 0000000..670ece3 --- /dev/null +++ b/docs/phases/08-session/reviewed.md @@ -0,0 +1,34 @@ +# Журнал ревью + +Сводка: [`../reviewed.md`](../reviewed.md). Индекс среза: [`README.md`](README.md). + +## Срез 8. Сессия и темп + +- **Фазы:** 38–40 +- **Проверен на:** `1c3ae9a`, 2026-08-20 +- **Пути:** `src/HSchool.Server/{Session,Api/SessionEndpoints.cs,Api/SchoolAccess.cs,Game/SchoolOwnership.cs,Game/UserStore.cs,Net/ClientRegistry.cs,Net/GameSocketHandler.cs,Game/GameLoopService.cs}`, `src/HSchool.Simulation/{ClockSpeed.cs,GameClock.cs,School.cs,SimulationOptions.cs}`, `src/HSchool.Client/src/{ui/sessionGate.ts,ui/mainMenu.ts,ui/gameScreen.ts,ui/schoolCard.ts,net/protocol.ts,net/api.ts,main.ts}`, `docs/protocol.md`, `../../design/08-session/session.md`, `tests/HSchool.AppHost.Tests/{SessionApiTests.cs,SchoolOwnerApiTests.cs,GameSocketTests.cs}`, `tests/HSchool.Simulation.Tests/{ClockTempoTests.cs,GameClockTests.cs}`, `src/HSchool.Client/src/ui/{sessionGate,gameScreen,schoolCard,mainMenu}.test.ts` +- **Итог:** обещания 38–40 на месте; дописано 4 теста; вторая вкладка с той же кукой больше не получает Welcome. Срез 9 (44/45/47) в работе — не ревьюился + +Что подтверждено: + +- **38:** `HSchool:AlphaPassword` (пустой — `ValidateOnStart`); `POST/GET/DELETE /api/session`; кука HttpOnly, `SameSite=Lax`, `Path=/`, `Max-Age` из `SessionCookieDays` (14); `users.json` каноническое имя; повтор другим регистром — та же запись; игровой HTTP без куки — `401`; `/health` и три ручки сессии — без неё; сокет без куки — close `PolicyViolation`, без Welcome; Hello по-прежнему версия + локаль; клиент — сначала сессия, потом сокет; хостовые тесты логинятся в `ResetAsync`/`LoginAsync`. Версия протокола не бампилась (v8). +- **39:** `MaxSchools` = 2 на игрока, `MaxSchoolsTotal` = 16 (в AppHost-тестах Total = 7); create пишет `owner`; `limit-reached` в коде — `school-limit-reached` (как в `protocol.md`); `server-full`; `GET /api/schools` — `schools` / `others`, `mine`, `owner` или `null`; Welcome.`MaxSchools` — слоты игрока; гость: найм `403` `not-owner`, `SetRunning` не меняет `running`; бесхозная в `others`, удаляется вторым; меню «Мои»/«Чужие»; в чужой школе нет «Управление» и кнопок часов. Портретный POST без `RequireManage`. Формат сейва не бампился. +- **40:** база 1 игровая минута/с; таблица `0.5, 1, 2, 5, 10` в `ClockSpeed`, `protocol.ts` и `protocol.md`; 20 импульсов ×1 = 1 минута, индексы 3/4 = 5 и 10 минут; на ×10 тяжёлые 10 раз с квантом 1; на ×5 — 5 раз; индекс 5 игнорируется; воркер по-прежнему 20 Гц и потолок догона 5; пять кнопок на экране. `HSchool.Simulation` без ASP.NET. + +Дописано: + +- `SessionApiTests.Login_SetsHttpOnlyLaxSessionCookie` +- `SessionApiTests.SecondWebSocket_ForAnOnlineName_ClosesWithoutWelcome` +- `ClockTempoTests.AtX5_TwentyImpulses_RunHeavyFiveTimesAndAdvanceFiveGameMinutes` (и календарь +1 на ×1 в `AtX1_…`) +- `mainMenu.test.ts` — чужая карточка без удаления, бесхозная с удалением + +Исправлено: + +- Второй WebSocket того же имени принимался и слал Welcome: `POST` проверял «онлайн», upgrade — нет. Две вкладки с живой кукой обходили `409` `name-online`. Теперь `ClientRegistry.TryClaimUserName` под тем же замком, второй сокет закрывается `PolicyViolation` без Welcome. + +Открыто: + +- `POST /api/session` с другим именем при живой куке перезаписывает сессию без `DELETE`. Дизайн: «Без выхода второе имя с той же куки не взять». Кода ошибки нет — новый HTTP-код молча не вводился. +- `AddDataProtection()` без ключей рядом с `saves/`. Рестарт того же профиля Windows куки, скорее всего, не сбрасывает; новый контейнер — может. +- Текст фазы 39 пишет `limit-reached`; живой код и `protocol.md` — `school-limit-reached` (так было до среза). Не менялось. +- Бесхозная карточка показывает «Хозяин: —», не голый прочерк. `GET /api/schools/{id}` из дизайна нет: `mine`/`owner` живут в списке меню. diff --git a/docs/phases/41-opinions.md b/docs/phases/09-social/41-opinions.md similarity index 96% rename from docs/phases/41-opinions.md rename to docs/phases/09-social/41-opinions.md index f785840..a547a6a 100644 --- a/docs/phases/41-opinions.md +++ b/docs/phases/09-social/41-opinions.md @@ -2,8 +2,8 @@ ## Зависимости -- [Фаза 8](08-people-browser.md) -- [Фаза 36](36-person-card-tabs.md) +- [Фаза 8](../02-people/08-people-browser.md) +- [Фаза 36](../07-inventory/36-person-card-tabs.md) ## Зачем diff --git a/docs/phases/42-talk-circles.md b/docs/phases/09-social/42-talk-circles.md similarity index 97% rename from docs/phases/42-talk-circles.md rename to docs/phases/09-social/42-talk-circles.md index 0c90e0e..c27646d 100644 --- a/docs/phases/42-talk-circles.md +++ b/docs/phases/09-social/42-talk-circles.md @@ -3,8 +3,8 @@ ## Зависимости - [Фаза 41](41-opinions.md) -- [Фаза 21](21-decisions.md) -- [Фаза 31](31-dress-generator.md) +- [Фаза 21](../05-ai/21-decisions.md) +- [Фаза 31](../07-inventory/31-dress-generator.md) ## Зачем diff --git a/docs/phases/43-whisper.md b/docs/phases/09-social/43-whisper.md similarity index 100% rename from docs/phases/43-whisper.md rename to docs/phases/09-social/43-whisper.md diff --git a/docs/phases/44-quarrel-fight.md b/docs/phases/09-social/44-quarrel-fight.md similarity index 100% rename from docs/phases/44-quarrel-fight.md rename to docs/phases/09-social/44-quarrel-fight.md diff --git a/docs/phases/45-presence-talk.md b/docs/phases/09-social/45-presence-talk.md similarity index 97% rename from docs/phases/45-presence-talk.md rename to docs/phases/09-social/45-presence-talk.md index d9a7438..d34f76a 100644 --- a/docs/phases/45-presence-talk.md +++ b/docs/phases/09-social/45-presence-talk.md @@ -3,7 +3,7 @@ ## Зависимости - [Фаза 42](42-talk-circles.md) -- [Фаза 19](19-presence-screen.md) +- [Фаза 19](../05-ai/19-presence-screen.md) ## Зачем diff --git a/docs/phases/46-speech-home.md b/docs/phases/09-social/46-speech-home.md similarity index 95% rename from docs/phases/46-speech-home.md rename to docs/phases/09-social/46-speech-home.md index 1fa09e8..25b68bb 100644 --- a/docs/phases/46-speech-home.md +++ b/docs/phases/09-social/46-speech-home.md @@ -3,8 +3,8 @@ ## Зависимости - [Фаза 42](42-talk-circles.md) -- [Фаза 37](37-school-rules.md) -- [Фаза 34](34-wear-home.md) +- [Фаза 37](../07-inventory/37-school-rules.md) +- [Фаза 34](../07-inventory/34-wear-home.md) ## Зачем diff --git a/docs/phases/47-romance-pack.md b/docs/phases/09-social/47-romance-pack.md similarity index 98% rename from docs/phases/47-romance-pack.md rename to docs/phases/09-social/47-romance-pack.md index d17c1ae..59574c6 100644 --- a/docs/phases/47-romance-pack.md +++ b/docs/phases/09-social/47-romance-pack.md @@ -3,7 +3,7 @@ ## Зависимости - [Фаза 42](42-talk-circles.md) -- [Фаза 23](23-example-pack.md) +- [Фаза 23](../06-foundation/23-example-pack.md) - [Фаза 41](41-opinions.md) ## Зачем diff --git a/docs/phases/09-social/README.md b/docs/phases/09-social/README.md new file mode 100644 index 0000000..959f7ff --- /dev/null +++ b/docs/phases/09-social/README.md @@ -0,0 +1,41 @@ +Индекс срезов: [`../README.md`](../README.md). + +## Срез 9. Общение и романтический пак + +Дизайн: [`../design/social.md`](../../design/09-social/social.md). + +Разговор становится кружком, мнение — числом, вражда — ссорой. Романтика не ваниль: отдельный +пак. Срез можно остановить после этапа B — школа болтает, даже если драк и пака ещё нет. + +**Этап A — мнения.** Число и вкладка. + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [41. Мнения и «Связи»](41-opinions.md) | ✅ | −100…100, семья-старт, вкладка карточки | + +**Этап B — разговоры.** Кружок, язык, шёпот. + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [42. Темы и кружки](42-talk-circles.md) | ✅ | `TopicDef`, 2–4 человека, учительская, телефон, вместе | +| [43. Шёпот и учитель](43-whisper.md) | ✅ | Шёпот на уроке, шанс оборвать, разговор после звонка | + +42 стоит на 41; 43 — на 42. + +**Этап C — конфликт и экран.** + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [44. Ссора и драка](44-quarrel-fight.md) | 🔄 | Жертва задиры, заступник, извинение, двор/физкультура | +| [45. Кружок в присутствии](45-presence-talk.md) | ✅ | Id участников и темы в кадре, протокол +1 | + +44 и 45 стоят на 42, можно параллельно. + +**Этап D — правила, дом, пак.** + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [46. Политика речи и дом](46-speech-home.md) | ⬜ | Комбобоксы речи с завтра; утреннее семейное событие | +| [47. Пак romance](47-romance-pack.md) | ✅ | Ориентация, симпатия, пара 18+, патч тем | + +46 стоит на 37 и 42; 47 — на 42 и 23. `example` не трогать. diff --git a/docs/phases/56-architecture-tests.md b/docs/phases/10-craft/56-architecture-tests.md similarity index 91% rename from docs/phases/56-architecture-tests.md rename to docs/phases/10-craft/56-architecture-tests.md index 8944ce9..0495552 100644 --- a/docs/phases/56-architecture-tests.md +++ b/docs/phases/10-craft/56-architecture-tests.md @@ -6,14 +6,14 @@ ## Зачем -Инвариант 4 и таблица в [`projects.md`](../design/projects.md) должны ломаться одним красным +Инвариант 4 и таблица в [`projects.md`](../../design/01-shell/projects.md) должны ломаться одним красным тестом, а не «забыли дописать пятую копию». Content и Protocol сейчас молчат. ## Задачи - [ ] Проект `tests/HSchool.Architecture.Tests` в solution, `IsTestProject`, без AppHost и без нового NuGet. Ссылки на Protocol, Content, People, Schedule, Ai, Simulation -- [ ] Тесты ссылок: каждая библиотека из таблицы в [`craft.md`](../design/craft.md) — этап A +- [ ] Тесты ссылок: каждая библиотека из таблицы в [`craft.md`](../../design/10-craft/craft.md) — этап A - [ ] Тесты исходников: в `src/HSchool.{Protocol,Content,People,Schedule,Ai,Simulation}` нет `DateTime.Now` / `DateTime.UtcNow` - [ ] Удалить копипасты `Assembly_DoesNotReference*` и `Sources_DoNotUseWallClock` из People, diff --git a/docs/phases/57-client-typecheck.md b/docs/phases/10-craft/57-client-typecheck.md similarity index 100% rename from docs/phases/57-client-typecheck.md rename to docs/phases/10-craft/57-client-typecheck.md diff --git a/docs/phases/58-worker-card-split.md b/docs/phases/10-craft/58-worker-card-split.md similarity index 100% rename from docs/phases/58-worker-card-split.md rename to docs/phases/10-craft/58-worker-card-split.md diff --git a/docs/phases/59-dump-nodes.md b/docs/phases/10-craft/59-dump-nodes.md similarity index 100% rename from docs/phases/59-dump-nodes.md rename to docs/phases/10-craft/59-dump-nodes.md diff --git a/docs/phases/10-craft/README.md b/docs/phases/10-craft/README.md new file mode 100644 index 0000000..fe3d6a5 --- /dev/null +++ b/docs/phases/10-craft/README.md @@ -0,0 +1,31 @@ +Индекс срезов: [`../README.md`](../README.md). + +## Срез 10. Ремесло + +Дизайн: [`../design/craft.md`](../../design/10-craft/craft.md). + +Срез без новой кнопки. Слои ловятся одним тестовым проектом, клиентский `npm test` гоняет tsc, +работник и карточка режутся по швам, дамп проверяет явку. После этапа A школа целая, даже если +файлы ещё толстые. Не ждёт фазы 44 и 46. + +Порядок свободный: 56, 57 и 59 ни от чего не зависят, 58 удобнее после 57. Начинать разумно с +56 или 57. + +**Этап A — страховка.** Слои и tsc. + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [56. Слои одной пачкой](56-architecture-tests.md) | ⬜ | Один проект вместо четырёх копипаст | +| [57. tsc в npm test](57-client-typecheck.md) | ⬜ | Дубликат ключа красный без `npm run build` | + +**Этап B — горячие точки.** Поведение то же. + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [58. SchoolWorker и карточка](58-worker-card-split.md) | ⬜ | partial / модули по швам, один поток | + +**Этап C — дамп.** + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [59. Дамп проверяет узлы](59-dump-nodes.md) | ⬜ | После найма в дампе есть `NodeId` | diff --git a/docs/phases/README.md b/docs/phases/README.md index cb95ee5..743487b 100644 --- a/docs/phases/README.md +++ b/docs/phases/README.md @@ -1,276 +1,24 @@ # Фазы -Индекс. Статус: ⬜ не начата, 🔄 в работе, ✅ готова. - -Регрессии уже сделанного — [`../bugs/README.md`](../bugs/README.md), не сюда. - -## Срез 1. Оболочка и карта - -Дизайн закрыт: [`../design/near-term.md`](../design/near-term.md), -[`../design/defs.md`](../design/defs.md), -[`../design/runtime.md`](../design/runtime.md), -[`../design/projects.md`](../design/projects.md). - -Новых игровых фич в эти фазы не добавлять. 1 и 2 можно вести параллельно (клиент / сервер). - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [0. Убрать PixiJS](00-drop-pixi.md) | ✅ | Сцена не планируется | -| [1. Оболочка менеджера](01-manager-shell.md) | ✅ | Панели с секциями среза, пока без данных | -| [2. Работник школы и диск](02-school-worker.md) | ✅ | Поток + World + сейв — основа | -| [3. Каталог def и карта](03-defs-map.md) | ✅ | JSONC, core, валидация раскладки | -| [4. Моды и редактор в create](04-create-editor.md) | ✅ | Выбор модов, карта в POST, снимок при открытии | - -## Срез 2. Люди - -Дизайн: [`../design/people.md`](../design/people.md). - -Школа наполняется людьми. Ученики, работники и родители существуют, показываются и обновляются -раз в год — но **ничего не делают**: ни расписания, ни перемещений, ни влияния характеристик. - -Фазы идут по порядку, кроме 8 и 9: обе зависят только от 7 и могут вестись параллельно -(клиент / симуляция). - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [5. Дефы человека](05-people-defs.md) | ✅ | Навыки, черты, тело, нужды, наборы имён в каталоге | -| [6. Библиотека генерации](06-people-generator.md) | ✅ | `HSchool.People`: семьи из карты и сида | -| [7. Люди в школе](07-people-in-school.md) | ✅ | Сущности в `World`, ростер на диске, 11 кабинетов в `core` | -| [8. Просмотр людей](08-people-browser.md) | ✅ | Панель со списком, фильтрами и карточкой | -| [9. Годовой набор](09-yearly-intake.md) | ✅ | Первое сентября: переход, выпуск, набор | - -## Срез 3. Штат - -Дизайн: [`../design/staffing.md`](../design/staffing.md). - -Первый срез, где игрок принимает решения. Школа открывается пустой: надо нанять людей из пула -соискателей, раздать им предметы и уложиться в выделенную на месяц сумму. Уроков по-прежнему нет — -предмет это запись в нагрузке, а не событие в кабинете. - -Фазы идут строго по порядку: каждая опирается на данные предыдущей. - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [10. Предметы и мебель](10-subjects.md) | ✅ | `SubjectDef`, одна учительская должность, кабинет как число мест | -| [11. Пустая школа и пул](11-applicants.md) | ✅ | Школа без сотрудников, соискатели с запросом по зарплате | -| [12. Наём и бюджет](12-hiring-budget.md) | ✅ | Наём, назначение предметов, предел фонда оплаты | -| [13. Раздел «Управление»](13-management-tab.md) | ✅ | Деньги, соискатели, штат и назначения на экране | - -## Срез 4. Расписание - -Дизайн: [`../design/schedule.md`](../design/schedule.md). - -Часы учебного плана раскладываются по дням и кабинетам. Школа начинает жить по времени: видно, -что идёт в комнате прямо сейчас. Люди при этом по-прежнему **не ходят** — они находятся там, где -их застало расписание. - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [14. Каркас дня и каникулы](14-school-calendar.md) | ✅ | Звонки, длина недели, каникулы | -| [15. Планировщик](15-timetable-planner.md) | ✅ | Раскладка часов по слотам, четыре запрета | -| [16. Расписание в школе](16-timetable-in-school.md) | ✅ | Сейв, снимок, «кто где сейчас» | -| [17. Расписание на экране](17-timetable-screen.md) | ✅ | Скобки в дереве, сетка класса, расписание учителя | - -## Срез 5. Жизнь школы - -Дизайн: [`../design/ai.md`](../design/ai.md). - -Люди начинают ходить и решать. Присутствие перестаёт вычисляться из расписания и становится -состоянием: человек приходит утром, идёт коридором, может уйти с урока в туалет и вернуться. - -Срез идёт **двумя этапами**, и между ними можно остановиться — школа останется целостной. - -**Этап A — присутствие.** Люди ходят по обязанности; выбора у них ещё нет. - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [18. Присутствие и ходьба](18-presence-walking.md) | ✅ | `HSchool.Ai`, место человека, маршруты, приход и уход | -| [19. Присутствие на экране](19-presence-screen.md) | ✅ | Своё сообщение ~2 Гц, числа в дереве, статический снимок карты, пропуск пустого времени | - -**Этап B — поведение.** Появляются нужды, действия и выбор между ними и обязанностью. - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [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-окружение и логика клиента под тестом | - -## Срез 7. Одежда, вещи и климат - -Дизайн: [`../design/inventory.md`](../design/inventory.md). - -Человек одевается, несёт вещи, мёрзнет и переодевается. Игрок смотрит гардероб и выбирает -правила школы; надевает не он. Погода видна у часов. Срез можно остановить после этапа A — -школа останется одетой, даже если климат ещё не живой. - -**Этап A — данные и гардероб.** Страна вместо набора имён, вещи умеют быть одеждой, генератор -одевает. - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [29. Страна](29-country.md) | ✅ | `CountryDef`, create выбирает страну, климат-пресет от сида | -| [30. Слои и палитра](30-apparel-defs.md) | ✅ | Поля `ThingDef`, `ColorDef`, ванильная одежда и ноша | -| [31. Генератор одевает](31-dress-generator.md) | ✅ | Гардероб на человеке, ноша-число, `people.json` | - -29 и 30 независимы; 31 стоит на обеих. - -**Этап B — климат.** Улица, нужда тепла, техник, раздевалки. - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [32. Погода и тепло](32-weather-warmth.md) | ✅ | Пресет крутит улицу, кадр часов, нужда, две черты | -| [33. Техник и шкафчики](33-lockers-technician.md) | ✅ | Комната техника, две раздевалки, раздача шкафчиков | - -32 стоит на 29; 33 — на 31 и 32. - -**Этап C — жизнь одежды.** Износ, переодевание, экран. - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [34. Износ и дом](34-wear-home.md) | ✅ | Прочность, утренняя замена, из ветхого не выходят | -| [35. Уместность](35-appropriateness.md) | ✅ | Правила, переодевание действием, физкультура | -| [36. Вкладки карточки](36-person-card-tabs.md) | ✅ | Одежда, ноша, сейчас + история за сегодня | -| [37. Правила школы](37-school-rules.md) | ✅ | Вкладка в «Управлении», четыре комбобокса, с завтра | - -34 стоит на 31; 35 — на 32–34; 36 — на 31 и может идти параллельно с 35 в части вкладок одежды, -история — после 35; 37 стоит на 35 и 36. - -## Срез 8. Сессия и темп - -Дизайн: [`../design/session.md`](../design/session.md). - -Несколько браузеров на одном сервере: общий пароль альфы, имя игрока, свои школы и чужие -отдельным списком. Часы становятся смотрибельными — минута за секунду, на высоких скоростях -тяжёлые системы тикают реже. Срез можно остановить после этапа B: мультиплеер жив, даже если -темп ещё старый. - -**Этап A — кто вошёл.** Пароль, имя, кука. - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [38. Сессия](38-session.md) | ✅ | Пароль альфы, имя, кука, сокет после входа | - -**Этап B — чьи школы.** Хозяин, слоты, гость только смотрит. - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [39. Хозяин школы](39-school-owners.md) | ✅ | `owner`, 2 слота на игрока, мои / чужие, гость без управления | - -39 стоит на 38. - -**Этап C — темп.** Не зависит от A/B, можно вести параллельно с 38. - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [40. Темп часов](40-clock-tempo.md) | ✅ | ×1 = минута/с, кнопки ×½ ×1 ×2 ×5 ×10, страйд тяжёлых систем | - -## Срез 9. Общение и романтический пак - -Дизайн: [`../design/social.md`](../design/social.md). - -Разговор становится кружком, мнение — числом, вражда — ссорой. Романтика не ваниль: отдельный -пак. Срез можно остановить после этапа B — школа болтает, даже если драк и пака ещё нет. - -**Этап A — мнения.** Число и вкладка. - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [41. Мнения и «Связи»](41-opinions.md) | ✅ | −100…100, семья-старт, вкладка карточки | - -**Этап B — разговоры.** Кружок, язык, шёпот. - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [42. Темы и кружки](42-talk-circles.md) | ✅ | `TopicDef`, 2–4 человека, учительская, телефон, вместе | -| [43. Шёпот и учитель](43-whisper.md) | ✅ | Шёпот на уроке, шанс оборвать, разговор после звонка | - -42 стоит на 41; 43 — на 42. - -**Этап C — конфликт и экран.** - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [44. Ссора и драка](44-quarrel-fight.md) | 🔄 | Жертва задиры, заступник, извинение, двор/физкультура | -| [45. Кружок в присутствии](45-presence-talk.md) | ✅ | Id участников и темы в кадре, протокол +1 | - -44 и 45 стоят на 42, можно параллельно. - -**Этап D — правила, дом, пак.** - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [46. Политика речи и дом](46-speech-home.md) | ⬜ | Комбобоксы речи с завтра; утреннее семейное событие | -| [47. Пак romance](47-romance-pack.md) | ✅ | Ориентация, симпатия, пара 18+, патч тем | - -46 стоит на 37 и 42; 47 — на 42 и 23. `example` не трогать. - -## Срез 10. Ремесло - -Дизайн: [`../design/craft.md`](../design/craft.md). - -Срез без новой кнопки. Слои ловятся одним тестовым проектом, клиентский `npm test` гоняет tsc, -работник и карточка режутся по швам, дамп проверяет явку. После этапа A школа целая, даже если -файлы ещё толстые. Не ждёт фазы 44 и 46. - -Порядок свободный: 56, 57 и 59 ни от чего не зависят, 58 удобнее после 57. Начинать разумно с -56 или 57. - -**Этап A — страховка.** Слои и tsc. - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [56. Слои одной пачкой](56-architecture-tests.md) | ⬜ | Один проект вместо четырёх копипаст | -| [57. tsc в npm test](57-client-typecheck.md) | ⬜ | Дубликат ключа красный без `npm run build` | - -**Этап B — горячие точки.** Поведение то же. - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [58. SchoolWorker и карточка](58-worker-card-split.md) | ⬜ | partial / модули по швам, один поток | - -**Этап C — дамп.** - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [59. Дамп проверяет узлы](59-dump-nodes.md) | ⬜ | После найма в дампе есть `NodeId` | - -## Вне очереди - -Дизайн: [`../design/off-queue.md`](../design/off-queue.md). - -Склейка уже живых систем и мелкий DX, не новый кусок игры. Номера не спорят с именованными -срезами. После каждой игровой фазы школа целая. 50–52 стоят на 48; 53 — на 18 и 32, можно -параллельно с 48. 49, 54 и 55 от них не зависят. Баг в уже сделанном — -[`../bugs/README.md`](../bugs/README.md). - -| Фаза | Статус | Зачем | -| --- | --- | --- | -| [48. Учитель в кабинете](48-lesson-teacher-present.md) | ✅ | Нет учителя на месте — нет роста навыка | -| [49. Пропуск сборки при запуске](49-skip-stale-build.md) | ✅ | `run-aspire.cmd` не гоняет MSBuild, если dll свежие | -| [50. Навык учителя](50-teacher-skill-gain.md) | ✅ | Соискатель 78 учит лучше, чем 41 | -| [51. Тепло на уроке](51-warmth-lesson.md) | ✅ | Замёрзший учится хуже, как голодный | -| [52. Учебник на уроке](52-textbook-lesson.md) | ✅ | Нет в сумке — половинный рост | -| [53. Погода на дороге](53-weather-commute.md) | ✅ | Снег и дождь добавляют минуты к приходу | -| [54. Что нового](54-whats-new.md) | ✅ | После входа — окно коммитов с прошлого визита | -| [55. Состояние экрана](55-view-state.md) | ✅ | F5 и переходы не сбрасывают школу, вкладки и фильтры | +Каталог срезов. Статусы и таблицы фаз — в индексе среза, не здесь. +⬜ не начата, 🔄 в работе, ✅ готова. + +Регрессии — [`../bugs/README.md`](../bugs/README.md). Сводка ревью — [`reviewed.md`](reviewed.md); +журнал среза — `reviewed.md` в его папке. + +Агенту: это оглавление. Не читай индексы всех срезов. Номер фазы → `docs/phases/*/-*.md`. +Срез → папка и её `README.md`. Дизайн → `docs/design/<та же папка>/`. + +| Срез | Фазы | Индекс | Дизайн | +| --- | --- | --- | --- | +| 1. Оболочка и карта | 0–4 | [01-shell](01-shell/README.md) | [01-shell](../design/01-shell/) | +| 2. Люди | 5–9 | [02-people](02-people/README.md) | [people](../design/02-people/people.md) | +| 3. Штат | 10–13 | [03-staffing](03-staffing/README.md) | [staffing](../design/03-staffing/staffing.md) | +| 4. Расписание | 14–17 | [04-schedule](04-schedule/README.md) | [schedule](../design/04-schedule/schedule.md) | +| 5. Жизнь школы | 18–21 | [05-ai](05-ai/README.md) | [ai](../design/05-ai/ai.md) | +| 6. Фундамент | 22–28 | [06-foundation](06-foundation/README.md) | [foundation](../design/06-foundation/foundation.md) | +| 7. Одежда, вещи и климат | 29–37 | [07-inventory](07-inventory/README.md) | [inventory](../design/07-inventory/inventory.md) | +| 8. Сессия и темп | 38–40 | [08-session](08-session/README.md) | [session](../design/08-session/session.md) | +| 9. Общение и романтический пак | 41–47 | [09-social](09-social/README.md) | [social](../design/09-social/social.md) | +| 10. Ремесло | 56–59 | [10-craft](10-craft/README.md) | [craft](../design/10-craft/craft.md) | +| Вне очереди | 48–55 | [off-queue](off-queue/README.md) | [off-queue](../design/off-queue/) | diff --git a/docs/phases/48-lesson-teacher-present.md b/docs/phases/off-queue/48-lesson-teacher-present.md similarity index 96% rename from docs/phases/48-lesson-teacher-present.md rename to docs/phases/off-queue/48-lesson-teacher-present.md index 9c19b42..7e6ca03 100644 --- a/docs/phases/48-lesson-teacher-present.md +++ b/docs/phases/off-queue/48-lesson-teacher-present.md @@ -2,8 +2,8 @@ ## Зависимости -- [Фаза 21](21-decisions.md) -- [Фаза 18](18-presence-walking.md) +- [Фаза 21](../05-ai/21-decisions.md) +- [Фаза 18](../05-ai/18-presence-walking.md) ## Зачем diff --git a/docs/phases/49-skip-stale-build.md b/docs/phases/off-queue/49-skip-stale-build.md similarity index 100% rename from docs/phases/49-skip-stale-build.md rename to docs/phases/off-queue/49-skip-stale-build.md diff --git a/docs/phases/50-teacher-skill-gain.md b/docs/phases/off-queue/50-teacher-skill-gain.md similarity index 97% rename from docs/phases/50-teacher-skill-gain.md rename to docs/phases/off-queue/50-teacher-skill-gain.md index 06ddbc2..bbb31a5 100644 --- a/docs/phases/50-teacher-skill-gain.md +++ b/docs/phases/off-queue/50-teacher-skill-gain.md @@ -3,7 +3,7 @@ ## Зависимости - [Фаза 48](48-lesson-teacher-present.md) -- [Фаза 12](12-hiring-budget.md) +- [Фаза 12](../03-staffing/12-hiring-budget.md) ## Зачем diff --git a/docs/phases/51-warmth-lesson.md b/docs/phases/off-queue/51-warmth-lesson.md similarity index 89% rename from docs/phases/51-warmth-lesson.md rename to docs/phases/off-queue/51-warmth-lesson.md index ac017fb..b750cb0 100644 --- a/docs/phases/51-warmth-lesson.md +++ b/docs/phases/off-queue/51-warmth-lesson.md @@ -3,7 +3,7 @@ ## Зависимости - [Фаза 48](48-lesson-teacher-present.md) -- [Фаза 32](32-weather-warmth.md) +- [Фаза 32](../07-inventory/32-weather-warmth.md) ## Зачем @@ -16,7 +16,7 @@ - [x] Лог `lesson-cold` один раз на предмет в сутки, если на уроке, который иначе учил, тепло ниже `BehaviorDef.needThreshold`; `thingDef` — предмет - [x] `docs/protocol.md` — тип лога -- [x] В [`inventory.md`](../design/inventory.md) убрать «тепло урок не штрафует»: стало рядом +- [x] В [`inventory.md`](../../design/07-inventory/inventory.md) убрать «тепло урок не штрафует»: стало рядом с голодом ## Тесты, без которых фаза не закрыта diff --git a/docs/phases/52-textbook-lesson.md b/docs/phases/off-queue/52-textbook-lesson.md similarity index 97% rename from docs/phases/52-textbook-lesson.md rename to docs/phases/off-queue/52-textbook-lesson.md index 7619250..6d61983 100644 --- a/docs/phases/52-textbook-lesson.md +++ b/docs/phases/off-queue/52-textbook-lesson.md @@ -3,7 +3,7 @@ ## Зависимости - [Фаза 48](48-lesson-teacher-present.md) -- [Фаза 35](35-appropriateness.md) +- [Фаза 35](../07-inventory/35-appropriateness.md) ## Зачем diff --git a/docs/phases/53-weather-commute.md b/docs/phases/off-queue/53-weather-commute.md similarity index 87% rename from docs/phases/53-weather-commute.md rename to docs/phases/off-queue/53-weather-commute.md index ecfc066..7fb7a91 100644 --- a/docs/phases/53-weather-commute.md +++ b/docs/phases/off-queue/53-weather-commute.md @@ -2,8 +2,8 @@ ## Зависимости -- [Фаза 18](18-presence-walking.md) -- [Фаза 32](32-weather-warmth.md) +- [Фаза 18](../05-ai/18-presence-walking.md) +- [Фаза 32](../07-inventory/32-weather-warmth.md) ## Зачем @@ -16,7 +16,7 @@ - [x] `DayPlans.Build` принимает уже посчитанные лишние минуты; `HSchool.Ai` не знает осадков - [x] Симуляция при сборке плана дня подставляет минуты из текущей улицы - [x] Лог на каждого не пишем — опоздание видно местом -- [x] В [`inventory.md`](../design/inventory.md) поправка: погода влияет на дорогу, не только +- [x] В [`inventory.md`](../../design/07-inventory/inventory.md) поправка: погода влияет на дорогу, не только на переодевание ## Тесты, без которых фаза не закрыта diff --git a/docs/phases/54-whats-new.md b/docs/phases/off-queue/54-whats-new.md similarity index 96% rename from docs/phases/54-whats-new.md rename to docs/phases/off-queue/54-whats-new.md index 2531336..c2b7d26 100644 --- a/docs/phases/54-whats-new.md +++ b/docs/phases/off-queue/54-whats-new.md @@ -17,7 +17,7 @@ - [x] Subject `Mark phase N…` в окне нет; в цепочке якоря они остаются - [x] Кука `hschool.seen-rev` (не HttpOnly, не сессионная): первый заход молча запоминает HEAD - [x] Модалка только после `ensureSession` (вход и повторный вход после logout), не с меню школы -- [x] [`protocol.md`](../protocol.md) HTTP, версию сокета не бампить +- [x] [`protocol.md`](../../protocol.md) HTTP, версию сокета не бампить ## Тесты, без которых фаза не закрыта diff --git a/docs/phases/55-view-state.md b/docs/phases/off-queue/55-view-state.md similarity index 97% rename from docs/phases/55-view-state.md rename to docs/phases/off-queue/55-view-state.md index bea6344..289ac00 100644 --- a/docs/phases/55-view-state.md +++ b/docs/phases/off-queue/55-view-state.md @@ -20,7 +20,7 @@ F5 и выход в меню не должны выкидывать игрока последний `mode`/`tab`/выбор — чтобы повторный вход с меню поднял то же место - [x] `history.pushState` только вход и выход; смена вкладки и фильтров — `replaceState`. «Назад» из школы — меню. Протокол, HTTP и сейв не трогать; роутер не добавлять -- [x] Подписи через `t(...)` не нужны: новых строк нет. `docs/design/off-queue.md` и эта фаза — +- [x] Подписи через `t(...)` не нужны: новых строк нет. `docs/design/off-queue/view-state.md` и эта фаза — в том же коммите, что и код ## Тесты, без которых фаза не закрыта diff --git a/docs/phases/off-queue/README.md b/docs/phases/off-queue/README.md new file mode 100644 index 0000000..21cc2e3 --- /dev/null +++ b/docs/phases/off-queue/README.md @@ -0,0 +1,21 @@ +Индекс срезов: [`../README.md`](../README.md). + +## Вне очереди + +Дизайн: [`../design/off-queue.md`](../../design/off-queue/README.md). + +Склейка уже живых систем и мелкий DX, не новый кусок игры. Номера не спорят с именованными +срезами. После каждой игровой фазы школа целая. 50–52 стоят на 48; 53 — на 18 и 32, можно +параллельно с 48. 49, 54 и 55 от них не зависят. Баг в уже сделанном — +[`../bugs/README.md`](../../bugs/README.md). + +| Фаза | Статус | Зачем | +| --- | --- | --- | +| [48. Учитель в кабинете](48-lesson-teacher-present.md) | ✅ | Нет учителя на месте — нет роста навыка | +| [49. Пропуск сборки при запуске](49-skip-stale-build.md) | ✅ | `run-aspire.cmd` не гоняет MSBuild, если dll свежие | +| [50. Навык учителя](50-teacher-skill-gain.md) | ✅ | Соискатель 78 учит лучше, чем 41 | +| [51. Тепло на уроке](51-warmth-lesson.md) | ✅ | Замёрзший учится хуже, как голодный | +| [52. Учебник на уроке](52-textbook-lesson.md) | ✅ | Нет в сумке — половинный рост | +| [53. Погода на дороге](53-weather-commute.md) | ✅ | Снег и дождь добавляют минуты к приходу | +| [54. Что нового](54-whats-new.md) | ✅ | После входа — окно коммитов с прошлого визита | +| [55. Состояние экрана](55-view-state.md) | ✅ | F5 и переходы не сбрасывают школу, вкладки и фильтры | diff --git a/docs/phases/reviewed.md b/docs/phases/reviewed.md index d561c5e..bb6f8bf 100644 --- a/docs/phases/reviewed.md +++ b/docs/phases/reviewed.md @@ -1,16 +1,14 @@ # Проверенные срезы -Журнал ревью. Фаза с ✅ в [`README.md`](README.md) — заявка автора; строка здесь — проверенный -факт: каждое обещание фазы сверено с кодом и тестом, найденное дописано или исправлено. +Журнал ревью по срезам. Фаза с ✅ в индексе среза — заявка автора; строка в `reviewed.md` папки — +проверенный факт. Коммит в «Проверен на» делает журнал самопроверяющимся. Срез перестаёт считаться проверенным, -как только `git log <коммит>..HEAD -- <пути>` перестаёт быть пустым — поэтому «Пути» обязательны. +как только `git log <коммит>..HEAD -- <пути>` перестаёт быть пустым. -Закрытые замечания из прошлых проходов сюда не возвращаются. История перепроверок свёрнута в -строку «Проверен на». Последняя проверка срезов 1–6 была до фаз 29–30 (страна, одежда): дрейф -по путям есть, это очередь следующего прохода, а не задняя датировка. +Агенту: не читай журналы всех срезов. Нужен один срез — его `reviewed.md`. -Оговорки, которые относятся сразу к нескольким срезам (не дублировать ниже): +Оговорки сразу к нескольким срезам (не дублировать ниже): - Хостовый `OpeningASchoolDuringAMathLesson_...` после правки явки не гонялся: Aspire держал DLL (`MSB3021`). Симуляционные тесты явки зелёные. @@ -19,368 +17,17 @@ - `GET /api/catalog` отдаёт каркас дня без `lunchBreaks` (только `longBreakAfter`). Сетке смены не нужны; молча расширять HTTP ревью не стало. -## Срез 1. Оболочка и карта - -- **Фазы:** 0–4 -- **Проверен на:** `418a4ea`, 2026-08-20 -- **Пути:** `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` -- **Итог:** обещания фаз 0–4 целы; дописаны 3 теста; протокол v7 в трёх местах - -Что подтверждено: - -- PixiJS нет; дерево карты, пять секций локации, клиентский фильтр, `localize()`. Оболочка — - вкладки Карта/Люди, не три панели; отказ от ленты событий записан в фазе 1 и в `design/near-term.md`. -- `SchoolWorker` — `LongRunning`, свой `World`, аккумулятор с потолком 5. Супервизор таблицу - держит и `World` не трогает. Сейв на create/shutdown/`SaveIntervalSeconds`; - `ReloadFromDisk_RestoresAPausedClock`, `PausingOneSchool_DoesNotStopAnother`. -- Восемь тестов каталога/карты в `HSchool.Content.Tests`. Ванильная раскладка валидна. - Пропавшая папка мода не стартует школу и файл не трогает. -- `GET /api/mods`, каталог, create с картой и модами, снимок при OpenSchool. `core` в диалоге - создания нельзя снять. Кодек, `protocol.ts` и `docs/protocol.md` описывают один `MapSnapshot`. -- `modal.ts` резолвит промис явно; `.dialog--screen` — `height: fit-content`; - `DefaultStartDate` UTC; `MapSnapshotSize` от сообщения; outbox `DropOldest`; - `WaitToReadAsync` переносится между итерациями; меню патчит `card.update`. -- Родной язык — HTTP/JSON без бампа протокола. Старый сейв без поля берёт первый язык набора, - не бросает кубик (`ReloadWithoutASavedPick_TakesTheFirstLanguageInsteadOfRolling`). - -Дописано: - -- `SchoolApiTests.Catalog_WithAnUnknownMod_IsRejected` -- `SchoolApiTests.ModId_ThatEscapesTheModsFolder_IsRejectedOnBothEndpoints` -- `SkillGrantTests.ReloadWithoutASavedPick_TakesTheFirstLanguageInsteadOfRolling` - -Исправлено: - -- Два поля `speedIndex` в клиентском `School` (`api.ts`) — артефакт мержа фазы 26; vitest не - ловил, `npm run build` падал с TS2300. - -## Срез 2. Люди - -- **Фазы:** 5–9 -- **Проверен на:** `bdb1253`, 2026-08-20 -- **Пути:** `src/HSchool.People`, `src/HSchool.Content/PeopleDefs.cs`, - `src/HSchool.Simulation/RosterSpawner.cs`, `src/HSchool.Simulation/School.cs`, - `src/HSchool.Server/Api/SchoolEndpoints.cs`, `src/HSchool.Server/Game/PersonCardReader.cs`, - `src/HSchool.Server/Game/SchoolWorker.cs`, - `src/HSchool.Client/src/ui/peoplePanel.ts`, `src/HSchool.Client/src/ui/personCard.ts`, - `tests/HSchool.People.Tests`, `tests/HSchool.Content.Tests/PeopleDefTests.cs`, - `tests/HSchool.Simulation.Tests/PeopleInSchoolTests.cs`, - `tests/HSchool.AppHost.Tests/PeopleApiTests.cs`, - `tests/HSchool.AppHost.Tests/SchoolSeedTests.cs` -- **Итог:** обещания фаз 5–9 целы; починены два API-теста, дописан один на reload после набора - -Что подтверждено: - -- Дефы человека: разбор, наследование, патч, неизвестная ссылка, склонение, мод со своим набором. -- Генератор: шесть тестов списка фазы, без Arch/ASP.NET/`DateTime.Now`. Неполные семьи 5–10%. - Сид — поле школы, не `school.Id`; хостовые тесты, которым нужен состав, передают сид явно. -- Одиннадцать кабинетов, сущности в `World`, `PersistPeople` только при смене состава. - Штат при создании пуст — это фаза 11. -- Список с опубликованного снимка, карточка через `TaskCompletionSource`. Фильтры, пейджер, - семейные ссылки, всё через `t(...)`. Вкладка рядом с картой, не нижний ряд — так записано - в фазе 8 и в `design/people.md`. -- Выпуск старшей имеющейся, вместимость, семьи через поколения, reload после набора. - -Дописано: - -- `PeopleApiTests.ReloadAfterTheYearlyIntake_RestoresTheNewComposition` — ждёт 1 сентября и - требует состав **после** набора, отличный от исходного (детерминизм от сида иначе скрыл бы - потерянный файл). - -Исправлено: - -- `Card_IncludesFamilyAndLiveNeeds` брал `People[0]` и требовал двух родителей. Состав зависит - от сида; на чистом клоне первый ученик иногда из неполной семьи. Теперь ищет ученика с матерью - и проверяет диапазон. -- `List_SortsBySurname` требовал ordinal-порядок, сервер ставит Ё на место Е. Совпадали, пока - в `core` не было фамилий на Ё. - -## Срез 3. Штат - -- **Фазы:** 10–13 -- **Проверен на:** `bdb1253`, 2026-08-20 -- **Пути:** `src/HSchool.People/Staffing.cs`, `src/HSchool.People/ApplicantPool.cs`, - `src/HSchool.Content/SubjectDef` и `defs/staffing`, `src/HSchool.Server/Api/SchoolEndpoints.cs` - (staffing/hire/subjects), `src/HSchool.Server/mods/core/defs/{subjects,positions,rooms,staffing,traits}`, - `src/HSchool.Client/src/ui/managementPanel.ts`, `src/HSchool.Client/src/net/api.ts`, - `tests/HSchool.People.Tests/{StaffingTests,ApplicantPoolTests}.cs`, - `tests/HSchool.AppHost.Tests/StaffingApiTests.cs`, - `src/HSchool.Client/src/ui/managementPanel.test.ts` -- **Итог:** обещания фаз 10–13 целы; дописаны 3 теста; `docs/protocol.md` дополнен `?lang=` - -Что подтверждено: - -- `PETeacher` нет; кабинеты без учительской должности; места через `defaultSeats`. - Неизвестный навык в предмете роняет каталог. Стартовая дата 31 марта 2012. -- Пул: детерминизм по сиду и неделе, частичное обновление, устойчивый размер, «сильный просит - больше», пустой штат, соискатель вне ростера до найма. Пул 32. -- Фонд оплаты, деление часов, пол ставки, покрытие. `GET /staffing` читает снимок, наём и - назначение — `GameCommand` с `TaskCompletionSource`. `MaxWeeklyHours` не запрещает назначение, - а оставляет предмет непокрытым. `PrimarySchool` на ванили требует трёх учителей (`teachersShort`). -- Экран: деньги, непокрытые, пул, штат, назначение с карточки, отказ по пределу текстом, - всё через `t(...)`. -- Полностью укомплектованная школа влезает в 100 000 впритык (запас 5–13%). Нанять пул целиком - без назначений предела достигает. Раскладка «каждому все предметы» — самая дешёвая, не - «укомплектованная школа». - -Дописано: - -- `StaffingApiTests.AssigningPastTheCap_IsRejectedWithTheNumbers` — фаза 12 требовала API-тест - на отказ по пределу; единственное место было написано через `if (over.StatusCode == Conflict)` - и проходило при любом исходе. -- `ApplicantPoolTests.ConfidentTraits_AskForMoreAndQuietOnesForLess` -- В `AssignAndUnassign_HonourTheCap_AndReloadKeepsAssignments` — тот же пул с теми же ставками - после перезапуска. - -Исправлено: - -- `docs/protocol.md` не упоминал `?lang=` у hire/subjects, хотя клиент его шлёт. - -## Срез 4. Расписание - -- **Фазы:** 14–17 -- **Проверен на:** `bdb1253`, 2026-08-20 -- **Пути:** `src/HSchool.Schedule`, `src/HSchool.Content` (каркас дня, каникулы, `SubjectDef.room`), - `src/HSchool.Simulation/SchoolTimetables.cs`, `src/HSchool.Server/Api/TimetableEndpoints.cs`, - `src/HSchool.Server/Game/SchoolWorker.cs` (пин, перестроение), - `src/HSchool.Client/src/ui/timetableGrid.ts`, `src/HSchool.Client/src/ui/gameScreen.ts`, - `tests/HSchool.Schedule.Tests`, `tests/HSchool.Content.Tests/CalendarTests.cs`, - `tests/HSchool.Content.Tests/LunchBreakTests.cs`, - `tests/HSchool.AppHost.Tests/TimetableApiTests.cs`, - `src/HSchool.Client/src/ui/timetableGrid.test.ts` -- **Итог:** обещания фаз 14–17 целы; дописан тест на коды отказа пина; две строки дизайна - помечены как отменённые срезом 5 - -Что подтверждено: - -- Календарь: суббота, каникулы и ночь — «вне учебного дня»; 10:20 вторника — третий урок; - шестидневка; зима через 1 января. Физкультура в спортзале. Обеденные смены в каркасе дня - (младшие после 3-го, старшие после 4-го). -- Планировщик: четыре запрета, класс не влезает в меньший кабинет, без Arch/ASP.NET/`DateTime.Now`. -- Перестроение только при `PeopleChanged` + `TimetableDirty`, не по тику. `GET /timetable` - читает снимок, пин — `TaskCompletionSource`. `Reload_RestoresLockedLessons`. -- Сетка, закрепление, тексты отказа (`pin-rejected` / `no-teacher` / `unknown-lesson`), - скобки в дереве из присутствия, личное расписание в карточке. Всё через `t(...)`. -- Занятость узла в протоколе v7 живёт в кадре `Presence`, не в снимке карты — фаза 19. - -Дописано: - -- `TimetableApiTests.PinAndUnpinRefusals_CarryTheDocumentedCodes` — `no-teacher`, `unknown-class`, - `unknown-subject`, `unknown-room`, `unknown-lesson`, `invalid-query`. - -Исправлено: - -- В `design/schedule.md` «панель локации из расписания» и «люди находятся по расписанию» помечены - как отменённые срезом 5. - -## Срез 5. Жизнь школы - -- **Фазы:** 18–21 -- **Проверен на:** `cbe739a`, 2026-08-20 -- **Пути:** `src/HSchool.Ai`, `src/HSchool.Simulation/{PresenceSystem,ActivitySystem,LessonLearningSystem,School}.cs`, - `src/HSchool.Protocol` (кадр `Presence`, пропуск в кадре часов), - `src/HSchool.Server/Game/PresenceFrame.cs`, `src/HSchool.Client/src/ui/gameScreen.ts`, - `src/HSchool.Server/mods/core/defs/{actions,behavior,needs}`, `tests/HSchool.Ai.Tests`, - `tests/HSchool.Simulation.Tests/{PresenceTests,DecisionTests,ActivityTests,NeedDecayTests,LunchTests}.cs`, - `tests/HSchool.AppHost.Tests/GameSocketTests.cs` -- **Итог:** все 32 теста из списков фаз на месте; дописаны 2 теста явки; `SetTimetable` сбрасывает - планы дня. Голод закрыт данными. AppHost после правки не гонялся — Aspire держал DLL. - -Что подтверждено: - -- Ходьба: 13 тестов списка фазы 18 (маршрут, `travelMinutes`, воскресенье/каникулы, звонок, - детерминизм, пропуск пустого времени, лето, промотанная неделя). -- Присутствие: раскладка с обеих сторон, круговой прогон, люди не в снимке карты, пропуск в - учебное время отбрасывается. На паузе кадры **идут** (occupancy + подписи урока); людей в них - может не быть. -- Нужды: декей, обед, стулья, ночь вне школы, неизвестная нужда, детерминизм. - `Hunger` восстанавливается вне школы, в столовой 112 стульев, обед — смена по параллелям. - `SleepAndHunger_ReturnToMaxOffCampus`, `AfterASchoolDay_NobodyIsLeftStarving`. -- Решения: восемь тестов фазы 21, в том числе туалет с урока и возврат, потолок отложенных, - детерминизм за неделю. `HSchool.Ai` без Arch/ASP.NET/`DateTime.Now`. - -Дописано: - -- `HireAndAssignMath_DuringLessonThree_PutsSomeoneOnCampus` -- `AssigningMath_AfterDayPlansWereBuilt_PutsSomeoneOnCampus` - -Исправлено: - -- `School.SetTimetable` обнуляет `PlanDay`. Иначе после назначения предмета без смены численности - `EnsurePlans` считает планы актуальными, `AppearAt` остаётся `null`, кампус пуст. Найм это - маскировал (численность растёт). -- `OpeningASchoolDuringAMathLesson_...` после снятия паузы сразу ждал людей и съедал бюджет на - паузные кадры. Теперь ждёт `clock.Running`, потом людей. - -## Срез 6. Фундамент - -- **Фазы:** 22–28 -- **Проверен на:** `cbe739a`, 2026-08-20 -- **Пути:** `src/HSchool.Content` (`PackManifest`, `PackLoadOrder`, `BehaviorDef`), - `src/HSchool.Server/mods/{core/pack.jsonc,example}`, `src/HSchool.Server/Game/SchoolStore.cs`, - `src/HSchool.Server/Api/DevEndpoints.cs`, `src/HSchool.Server/Game/SchoolDumpReader.cs`, - `src/HSchool.People/RosterJson.cs`, `src/HSchool.Client/src/ui/*.test.ts`, - `tests/HSchool.Content.Tests/{PackIdentityTests,ExamplePackTests,BehaviorDefTests}.cs`, - `tests/HSchool.People.Tests/GoldenRosterTests.cs`, - `tests/HSchool.AppHost.Tests/{ExamplePackTests,GoldenSaveTests,SchoolSeedTests,ServiceabilityTests}.cs` -- **Итог:** все тесты из списков семи фаз на месте; дописан один клиентский; `foundation.md` - приведён к закрытому срезу. Хостовые тесты сверены по коду, не прогоном. - -Что подтверждено: - -- Пак: манифест, `missing-mod`, порядок — выбор игрока плюс топология (не алфавит), цикл — - `mod-cycle`, def без подписи предупреждает и грузится. -- `mods/example` на диске; каталог/create/reload; патч только с паком; ваниль без выбора не меняется. -- Веса в `BehaviorDef`; пак с обедом выше урока уводит класс; отрицательный вес роняет загрузку. -- Золотой ростер и сейв; `PrimarySchool` требует трёх; `teachersShort` в API и в `t(...)`. -- Сид — поле школы. Format новее — школа не стартует, файл нетронут. Дамп через снимок + мейлбокс, - не в `World` с HTTP. `HSchool.Simulation.Tests` без параллельности. -- Клиентские экраны в `happy-dom`: фильтры/пейджер, create, сетка, предел фонда текстом. - -Дописано: - -- `managementPanel.test.ts` — непокрытый предмет показывает `teachersShort`. - -Исправлено: - -- `AGENTS.md` утверждал, что на паузе кадры присутствия не приходят. Работник их шлёт - (occupancy + подписи). Тест, который ждёт *людей* на паузе, зависнет; тест на подпись урока — нет. -- `design/foundation.md`: порядок загрузки — не алфавит; «почему сейчас» про веса в `Decision.cs` - устарел. - -Замечено рядом: - -- `Dump_ReturnsPeopleNodesAndTimetable` принимает `NodeId is not null || Needs.Count > 0`. - Нужды есть у всех, так что узлы на кампусе не проверяются. Усиливать до явки — срез 5. - -## Срез 7. Этап A — данные и гардероб - -- **Фазы:** 29–31 -- **Проверен на:** `d5547b7`, 2026-08-20 -- **Пути:** `src/HSchool.Content/{CountryDef,ApparelDefs,CatalogLoader}.cs`, - `src/HSchool.People/{DressGenerator,CountryClimate,RosterJson}.cs`, - `src/HSchool.Server/{Game/SchoolStore.cs,Api/{ModEndpoints,SchoolEndpoints}.cs,Api/PeopleModels.cs}`, - `src/HSchool.Client/src/ui/{createSchoolDialog.ts,personCard.ts}`, - `tests/HSchool.Content.Tests/{CountryDefTests,ApparelDefTests,VanillaCoreTests}.cs`, - `tests/HSchool.People.Tests/{DressGeneratorTests,GoldenRosterTests}.cs`, - `tests/HSchool.AppHost.Tests/{SchoolApiTests,InventoryApiTests,GoldenSaveTests,ServiceabilityTests}.cs`, - `src/HSchool.Client/src/ui/createSchoolDialog.test.ts` -- **Итог:** все 17 тестов из списков фаз на месте; исправлен дубликат `peopleTabOverview` в - `strings.ts` (ломал `npm run build`); одно замечание рядом про вкладки карточки - -Что подтверждено: - -- **29:** `CountryDef`/`ClimatePresetDef`; `namesets/` убран; ваниль `Russia` с двумя пресетами; - каталог отдаёт `countries`; create принимает `countryId`, `unknown-country` на чужую страну; - пресет от сида (`CreateSchool_WithTheSameSeed_GetsTheSameClimatePreset`); `nameSetId: Slavic` - поднимается через `UpgradeOlderSave` (`GoldenSaveTests`, `legacy-no-native/`); формат без - страны не стартует (`ServiceabilityTests`); диалог — страны и языки (`createSchoolDialog.test.ts`); - золотой ростер обновлён (`GoldenRosterTests`). -- **30:** поля `ThingDef`/`ColorDef`, слои как роли, мебель без `layers`; ванильная палитра и - одежда в `VanillaCoreTests`; локали ru/en (`EveryVanillaLabel_ExistsInBothLocales`); учебник - один def без `subject` на def. -- **31:** `InventoryItem` с `def`/`color`/`condition`/`location`/`subject`; `DressGenerator` - детерминирован (`DressGeneratorTests`, `InventoryApiTests.SameSeed_YieldsTheSameShirt`); слои - без пересечения, платье на два слоя; пол/яркость/учебник/родители/соискатели/масса/Hauling; - карточка отдаёт worn+carried, список — нет (`InventoryApiTests`); старый `people.json` без - items одевается на reload (`PeopleJsonWithoutInventory_IsDressedOnReloadAndRewritten`); - `HSchool.People`/`HSchool.Content` без ASP.NET; протокол не тронут. - -Дописано: - -- (нет новых тестов — списки фаз закрыты существующими) - -Исправлено: - -- Дубликат ключа `peopleTabOverview` в `strings.ts` (ru/en): vitest не ловит TS2300/TS1117, - `npm run build` падал; ключ перенесён, `peopleTabPortrait` рядом с остальными вкладками. -- Повтор `using HSchool.Content` в `SchoolStore.cs` (CS0105). - -Замечено рядом: - -- Карточка уже с вкладками (`personCard.ts`, фаза 31 обещала один свиток — позже по - `design/inventory.md`); портрет/SwarmUI-строки в `strings.ts` — не этап A. -- `ResolveClimatePreset` в `SchoolWorker` при reload без сохранённого пресета берёт первый - пресет страны (`schoolSeed: 0`, `rollIfOmitted: false`) — по дизайну для legacy; create - кидает пресет из `GameLoopService` до spawn. - -## Срез 7. Этап B–C — климат, жизнь одежды и правила - -- **Фазы:** 32–37 -- **Проверен на:** `5dcdee6`, 2026-08-20 -- **Пути:** `src/HSchool.Simulation/{WeatherSampler,WarmthDecay,PlaceClimate,ApparelDresser,ApparelWear,MorningDress,PersonLogBrowser}.cs`, - `src/HSchool.People/LockerAssigner.cs`, `src/HSchool.Content/{Appropriateness,ClimatePreset,DressRules}.cs`, - `src/HSchool.Ai/ApparelGoals.cs`, `src/HSchool.Protocol`, `src/HSchool.Server/Api/{SchoolEndpoints,DressRulesModels}.cs`, - `src/HSchool.Server/Game/{PersonCardReader,SchoolWorker}.cs`, `src/HSchool.Client/src/{ui/dressRulesPanel.ts,ui/managementPanel.ts,format/weather.ts,net/api.ts}`, - `docs/protocol.md`, `tests/HSchool.Simulation.Tests`, `tests/HSchool.People.Tests/LockerAssignerTests.cs`, - `tests/HSchool.Content.Tests/{ClimatePresetTests,AppropriatenessTests,ChangingRoomMapTests}.cs`, - `tests/HSchool.AppHost.Tests/{PersonLogApiTests,DressRulesApiTests}.cs`, `tests/HSchool.Protocol.Tests`, - `src/HSchool.Client/src/ui/{personCard,dressRulesPanel,managementPanel}.test.ts` -- **Итог:** фазы 32–37 подтверждены; дописано 5 тестов симуляции; исправлены 2 бага переодевания и 2 - регрессии тестов (не протокол) - -Что подтверждено: - -- **32–36:** погода детерминирована; skip выставляет утро; кадр часов v8 (27 байт) в трёх местах; - `Warmth` и техник; шкафчики; износ и утренняя замена; уместность и PE; вкладки карточки и HTTP-лог. -- **37:** вкладка «Правила» в «Управлении»; четыре комбобокса; GET/POST `dress-rules` с pending; - `unknown-form`/`unknown-color` → `400`; правило с завтра; `ColorAllowed` для `whiteTopBlackBottom`; - `dressRulesPanel.test.ts` — combobox без API до Apply, ошибка текстом; `ManagementPanel` не ломает штат - и сетку. - -Дописано (32–36, прошлый проход): - -- `AppropriatenessSimulationTests.MorningDress_StagesPeKitForTodaysLesson` -- `AppropriatenessSimulationTests.PeBreak_CompletesChangeBeforeLesson` -- `AppropriatenessSimulationTests.PeLesson_WearsPeThenReturnsToEveryday` -- `AppropriatenessSimulationTests.AfterPeBreak_ModeExpectsEveryday` -- `PresenceTests.PhysicalEducationBeforeLesson_GoesToChangingRoomOnTheBreak` - -Исправлено: - -- `PresenceSystem`: ходьба по duty не обрывает активный `ChangeClothes`. -- `ApparelPresence.ModeAt`: после перемены following PE — режим everyday. -- `managementPanel.test.ts`: панель штата скрывается через `hidden`, не удаляется из DOM. -- `PortraitApiTests.GetSwarmUiSettings_ReturnsPresets`: URL `/api/settings/swarmui` (CS1503). -- `dressRulesPanel.ts`: неиспользуемый импорт `clear` — TS6133 на `npm run build`. - -Замечено рядом: - -- `PhysicalEducationBeforeLesson_…` не гоняет полный цикл «зал с формой» — это в - `AppropriatenessSimulationTests.PeLesson_…`. -- Портреты/SwarmUI — вне среза 7, но `PortraitApiTests` чинился, чтобы AppHost-сборка не падала. - -## Срез 8. Сессия и темп - -- **Фазы:** 38–40 -- **Проверен на:** `1c3ae9a`, 2026-08-20 -- **Пути:** `src/HSchool.Server/{Session,Api/SessionEndpoints.cs,Api/SchoolAccess.cs,Game/SchoolOwnership.cs,Game/UserStore.cs,Net/ClientRegistry.cs,Net/GameSocketHandler.cs,Game/GameLoopService.cs}`, `src/HSchool.Simulation/{ClockSpeed.cs,GameClock.cs,School.cs,SimulationOptions.cs}`, `src/HSchool.Client/src/{ui/sessionGate.ts,ui/mainMenu.ts,ui/gameScreen.ts,ui/schoolCard.ts,net/protocol.ts,net/api.ts,main.ts}`, `docs/protocol.md`, `docs/design/session.md`, `tests/HSchool.AppHost.Tests/{SessionApiTests.cs,SchoolOwnerApiTests.cs,GameSocketTests.cs}`, `tests/HSchool.Simulation.Tests/{ClockTempoTests.cs,GameClockTests.cs}`, `src/HSchool.Client/src/ui/{sessionGate,gameScreen,schoolCard,mainMenu}.test.ts` -- **Итог:** обещания 38–40 на месте; дописано 4 теста; вторая вкладка с той же кукой больше не получает Welcome. Срез 9 (44/45/47) в работе — не ревьюился - -Что подтверждено: - -- **38:** `HSchool:AlphaPassword` (пустой — `ValidateOnStart`); `POST/GET/DELETE /api/session`; кука HttpOnly, `SameSite=Lax`, `Path=/`, `Max-Age` из `SessionCookieDays` (14); `users.json` каноническое имя; повтор другим регистром — та же запись; игровой HTTP без куки — `401`; `/health` и три ручки сессии — без неё; сокет без куки — close `PolicyViolation`, без Welcome; Hello по-прежнему версия + локаль; клиент — сначала сессия, потом сокет; хостовые тесты логинятся в `ResetAsync`/`LoginAsync`. Версия протокола не бампилась (v8). -- **39:** `MaxSchools` = 2 на игрока, `MaxSchoolsTotal` = 16 (в AppHost-тестах Total = 7); create пишет `owner`; `limit-reached` в коде — `school-limit-reached` (как в `protocol.md`); `server-full`; `GET /api/schools` — `schools` / `others`, `mine`, `owner` или `null`; Welcome.`MaxSchools` — слоты игрока; гость: найм `403` `not-owner`, `SetRunning` не меняет `running`; бесхозная в `others`, удаляется вторым; меню «Мои»/«Чужие»; в чужой школе нет «Управление» и кнопок часов. Портретный POST без `RequireManage`. Формат сейва не бампился. -- **40:** база 1 игровая минута/с; таблица `0.5, 1, 2, 5, 10` в `ClockSpeed`, `protocol.ts` и `protocol.md`; 20 импульсов ×1 = 1 минута, индексы 3/4 = 5 и 10 минут; на ×10 тяжёлые 10 раз с квантом 1; на ×5 — 5 раз; индекс 5 игнорируется; воркер по-прежнему 20 Гц и потолок догона 5; пять кнопок на экране. `HSchool.Simulation` без ASP.NET. - -Дописано: - -- `SessionApiTests.Login_SetsHttpOnlyLaxSessionCookie` -- `SessionApiTests.SecondWebSocket_ForAnOnlineName_ClosesWithoutWelcome` -- `ClockTempoTests.AtX5_TwentyImpulses_RunHeavyFiveTimesAndAdvanceFiveGameMinutes` (и календарь +1 на ×1 в `AtX1_…`) -- `mainMenu.test.ts` — чужая карточка без удаления, бесхозная с удалением - -Исправлено: - -- Второй WebSocket того же имени принимался и слал Welcome: `POST` проверял «онлайн», upgrade — нет. Две вкладки с живой кукой обходили `409` `name-online`. Теперь `ClientRegistry.TryClaimUserName` под тем же замком, второй сокет закрывается `PolicyViolation` без Welcome. - -Открыто: - -- `POST /api/session` с другим именем при живой куке перезаписывает сессию без `DELETE`. Дизайн: «Без выхода второе имя с той же куки не взять». Кода ошибки нет — новый HTTP-код молча не вводился. -- `AddDataProtection()` без ключей рядом с `saves/`. Рестарт того же профиля Windows куки, скорее всего, не сбрасывает; новый контейнер — может. -- Текст фазы 39 пишет `limit-reached`; живой код и `protocol.md` — `school-limit-reached` (так было до среза). Не менялось. -- Бесхозная карточка показывает «Хозяин: —», не голый прочерк. `GET /api/schools/{id}` из дизайна нет: `mine`/`owner` живут в списке меню. +Закрытые замечания из прошлых проходов сюда не возвращаются. Последняя проверка срезов 1–6 была +до фаз 29–30 (страна, одежда): дрейф по путям есть, это очередь следующего прохода. + +| Срез | Проверен на | Журнал | +| --- | --- | --- | +| Срез 1. Оболочка и карта | `418a4ea` | [01-shell](01-shell/reviewed.md) | +| Срез 2. Люди | `bdb1253` | [02-people](02-people/reviewed.md) | +| Срез 3. Штат | `bdb1253` | [03-staffing](03-staffing/reviewed.md) | +| Срез 4. Расписание | `bdb1253` | [04-schedule](04-schedule/reviewed.md) | +| Срез 5. Жизнь школы | `cbe739a` | [05-ai](05-ai/reviewed.md) | +| Срез 6. Фундамент | `cbe739a` | [06-foundation](06-foundation/reviewed.md) | +| Срез 7. Этап A — данные и гардероб | `d5547b7` | [07-inventory](07-inventory/reviewed.md) | +| Срез 7. Этап B–C — климат, жизнь одежды и правила | `5dcdee6` | [07-inventory](07-inventory/reviewed.md) | +| Срез 8. Сессия и темп | `1c3ae9a` | [08-session](08-session/reviewed.md) |