# Ремесло: слои, страховка тестов, горячие точки Договорённость на срез после одежды, сессии и склейки урока, не текущий код. Фундамент: [`foundation.md`](foundation.md). Проекты: [`projects.md`](projects.md). Вне очереди: [`off-queue.md`](off-queue.md). Общение (не ждём 44/46): [`social.md`](social.md). Игра уже живая: люди ходят, одеваются, учатся, болтают. Рядом лежат четыре копии одного запрета на ссылки, `npm test` зелёный при красном `tsc`, работник школы на тысячу строк и дамп, который не проверяет явку. Срез это **подметает**. Новой кнопки нет. Срез отвечает на вопросы «сломал ли я слой», «прошёл ли клиентский tsc» и «можно ли разрезать работника, не трогая поток». Оценок, протокола +1 и охоты на всё SOLID нет. ## Что увидит игрок Ничего обязательного на экране. Школа после каждой фазы та же, что вчера. Игрок может заметить только то, что агент и CI ловят дубликат ключа и чужой `using` до мержа, а не после. ## На какие вопросы отвечает - Слои из [`projects.md`](projects.md) и инвариант 4 — тест в одном месте, не четыре копипасты. - `npm test` клиента = typecheck + vitest; дубликат в `strings.ts` красный без `npm run build`. - `SchoolWorker` и сборка карточки режутся по швам, поведение то же. ## Чего нет - Новых экранов, полей сейва, байтов сокета. - Фаз 44 и 46, портретов/SwarmUI как фичи, `GET /api/catalog.lunchBreaks`. - Багов сессии (второе имя при живой куке, ключи DataProtection) — [`../bugs/README.md`](../bugs/README.md). - Инверсии Game → Api DTO и «пройти SOLID по всему дереву». ## Что меняется в уже принятом | Было | Стало | Почему | | --- | --- | --- | | Запрет Arch/ASP.NET/`DateTime.Now` скопирован в People, Ai, Schedule, Simulation | Один проект `HSchool.Architecture.Tests`; Content и Protocol в том же списке | Четыре теста расходятся; Content и Protocol молчат | | `npm test` = только vitest | `npm test` = `tsc -b` и vitest | Журнал трижды ловил дубликат ключа, зелёный в vitest | | `SchoolWorker` и `PersonCardReader` — по одному файлу на всё | `partial` / файлы по шву; один тип, один поток, один `Read` | SRP без нового инварианта | | Дамп «люди с узлом **или** нуждами» | После найма и хода часов — кто-то с `NodeId` | Нужды есть у всех; явка не проверялась | Протокол, HTTP меню и формат сейва не меняются. Версию сокета не бампить. ## Этап A — страховка Слои и клиентский tsc. После этапа школа целая, даже если работник ещё тысяча строк. ### Слои одной пачкой Новый тестовый проект ссылается на библиотеки, не на AppHost. Пакета NetArchTest нет: как сейчас, `GetReferencedAssemblies` и чтение `*.cs`. Копии `Assembly_DoesNotReference*` и `Sources_DoNotUseWallClock` из People/Ai/Schedule/Simulation уезжают сюда. Список (как [`projects.md`](projects.md)): | Сборка | Не ссылается | Не содержит в исходниках | | --- | --- | --- | | Protocol | Arch, ASP.NET, Sockets, игровые проекты | `DateTime.Now` / `UtcNow` | | Content | Arch, ASP.NET, Sockets, Protocol, Simulation | то же | | People, Schedule, Ai | Arch, ASP.NET, Sockets | то же | | Simulation | ASP.NET, Sockets | то же; Arch — да | `HSchool.Server` в этот список не входит: он и есть хост. ### tsc в `npm test` Скрипт `test` гоняет `typecheck` (`tsc -b`), потом vitest. Петля `"hschool-client": "file:"` в `package.json` уходит, если без неё `npm test` и `npm run build` зелёные. ## Этап B — горячие точки Именованный рефактор, не аудит всего дерева. После этапа поведение то же: карточка, пауза, снимок, дамп. 1. **`SchoolWorker`** (~1200 строк) — `partial`: почта, тик, запись. Один тип, тот же `LongRunning`-поток, без замка вокруг `School`/`World`. 2. **`PersonCardReader`** (~600) — сборка по вкладкам в том же классе или соседних `internal` helper; публичный вход по-прежнему `Read(School, personId, locale)`. 3. **`personCard.ts`** (~850) — модули по вкладкам; `personCardHost.ts` по-прежнему ходит в HTTP. Game-слой по-прежнему может зависеть от DTO в `Api` — инверсия не этот срез. ## Этап C — дамп не врёт `Dump_ReturnsPeopleNodesAndTimetable` после найма **запускает** часы и ждёт человека с `NodeId`, не `Needs.Count > 0`. На паузе не ждать людей (кадры присутствия всё равно идут). ## Где живёт код Новой игровой библиотеки нет. | Что | Куда | | --- | --- | | Запреты слоёв и wall-clock | `tests/HSchool.Architecture.Tests` | | Скрипт `npm test` | `src/HSchool.Client/package.json` | | Работник | `src/HSchool.Server/Game/SchoolWorker*.cs` | | Карточка HTTP | `PersonCardReader` + клиент `ui/personCard*.ts` | | Дамп | уже `DevEndpoints` / `SchoolDumpReader`; меняется тест | ## Советы, которые стоит принять сразу - Не тащить NetArchTest ради четырёх `Assert.DoesNotContain`. - Не резать `SchoolWorker` на несколько типов с разными потоками. - Не инвертировать Game → Api «заодно»: это другой срез или вне очереди. - Не бампить протокол и не расширять catalog, чтобы «заодно» отдать `lunchBreaks`. - Фаза 55 занята веткой `phase/55-view-state` — номера среза с 56. ## Заведомо не сейчас - Срез 9: ссора/драка, политика речи и дом. - Баги сессии и DataProtection. - `GET /api/catalog` + `lunchBreaks`. - Портреты как контент; SwarmUI не выкидывать. - README «школа ещё пустая» как отдельная кампания — одна строка в фазе 56, не рерайт. - Новый именованный кусок игры (оценки, приказы, магазины). ## Зафиксировано этим разговором | Тема | Решение | | --- | --- | | Куда | Срез 10, не «Вне очереди» | | SOLID | Arch-тесты слоёв + три именованные точки, не всё дерево | | Список | Журнал ревью + ложь AGENTS; без инвентаризации-фазы | | Баги сессии | Не брать, `/bug-work` | | Протокол | Не бампить | | HTTP | Не расширять catalog | | Сейв | Не трогать | | Срез 9 | Не ждать 44/46, не писать в них | | Стоп | После A школа целая; после B — тоже | | Ваниль | Без пака | | Номера | 56–59; 55 занята |