Update AGENTS.md to correct the behavior of presence frames during school pauses, clarify the design documentation regarding mod load order, and add a new test for uncovered teaching positions in the management panel. Enhance reviewed phase details for accuracy and completeness.
This commit is contained in:
@@ -176,6 +176,7 @@ say so explicitly in the change description.
|
||||
one machine and red on a clean clone. Assert a property, not the first pupil of school 1.
|
||||
- **Host tests share one server and one save folder.** Start each test by clearing the list
|
||||
through the API (`SchoolApiTests.ResetAsync`).
|
||||
- **Presence frames do not arrive at all while a school is paused.** They are broadcast only
|
||||
after a tick step, and a paused clock produces none. Opening still sends one snapshot; a test
|
||||
that waits for presence on pause will hang.
|
||||
- **A paused school still emits presence frames.** The worker loop ticks the mailbox even when
|
||||
the clock does not advance, so occupancy (often empty) and lesson labels from the timetable
|
||||
keep going out. Opening sends one extra snapshot. A test that waits for *people* on pause
|
||||
will hang; a test that waits for a lesson label will not.
|
||||
|
||||
+10
-18
@@ -1,11 +1,7 @@
|
||||
# Фундамент: паки, числа, сид, версия и золотые файлы
|
||||
|
||||
Договорённость на срез после жизни школы. Не текущий код — то, что решено сделать.
|
||||
|
||||
Срез не добавляет игроку ни одной новой кнопки. Он берёт то, что ревью нашло дешёвым и заметным,
|
||||
и доводит до конца: мод становится настоящим объектом, числа поведения уезжают из кода в данные,
|
||||
сид перестаёт быть идентификатором школы, у сейва появляется читаемая версия, а регрессии, которые
|
||||
сейчас ловятся только глазами, начинают ловиться тестом.
|
||||
Договорённость на срез после жизни школы. Срез закрыт фазами 22–28; таблица внизу — решения.
|
||||
Абзацы «почему сейчас» ниже описывают дыры, которые срез закрыл, а не текущий код.
|
||||
|
||||
## Почему сейчас
|
||||
|
||||
@@ -16,17 +12,13 @@
|
||||
маленький скучный пак на диске, чтобы last-wins, патчи и дорога «игрок выбрал мод» проверялись
|
||||
настоящей папкой, а не документами в памяти.
|
||||
|
||||
**Числа поведения живут в коде вопреки [`ai.md`](ai.md).** Там записано: «Числа поведения —
|
||||
отдельный деф правил, как `StaffingDef` у штата». В `BehaviorDef` уехали порог нужды, скорость
|
||||
обучения и разброс на дорогу, а веса целей остались константами в `Decision.cs`. Пока они там, мод
|
||||
не может перебалансировать поведение, не написав кода, — а это ровно то обещание, ради которого
|
||||
заводились дефы.
|
||||
**Числа поведения уехали в `BehaviorDef`.** Порог нужды, обучение, разброс на дорогу и веса целей
|
||||
(урок, переход, нужда на нуле, обед) — данные. Каталог без дефа падает на константы кода, а не
|
||||
ломается. Пак может поднять `lunchWeight` выше урока без форка.
|
||||
|
||||
**Две регрессии ловятся руками.** Генератор людей переписывали дважды за одну сессию ревью, а
|
||||
тест на детерминизм сравнивает два прогона *в одном процессе*: он не заметит, если поменяется сам
|
||||
порядок бросков. [`people.md`](people.md) называет отпечаток «единственным способом поймать
|
||||
регрессию в генераторе». Так же с сейвом: поле `nativeLanguage` добавили без бампа формата, и то,
|
||||
что старые сейвы грузятся, держится на комментарии в коде.
|
||||
**Золотые файлы ловят то, что раньше ловили глазами.** Отпечаток ростера — файл в фикстурах.
|
||||
Сейв текущего формата и сейв без `nativeLanguage` грузятся тестом хоста. Формат новее своего
|
||||
школу не стартует и файл не трогает.
|
||||
|
||||
## Удостоверение пака
|
||||
|
||||
@@ -152,8 +144,8 @@
|
||||
- Не даёт менять набор модов у живой школы. Каталог по-прежнему замерзает на работнике.
|
||||
- Не кладёт `defName` комнаты в снимок карты. Клиент смог бы стилизовать типы помещений от модов,
|
||||
но это версия протокола и правка в трёх местах — не «дёшево», значит не сюда.
|
||||
- Не заводит UI для порядка модов. Порядок — алфавит плюс зависимости; ручная перестановка ждёт
|
||||
того дня, когда паков станет больше трёх.
|
||||
- Не заводит UI для порядка модов. Порядок — выбор игрока плюс устойчивая топологическая
|
||||
сортировка по `requires`; ручная перестановка ждёт того дня, когда паков станет больше трёх.
|
||||
|
||||
## Зафиксировано этим разговором
|
||||
|
||||
|
||||
+66
-1
@@ -519,7 +519,7 @@ Calendar+обеды 20, планировщик 9, TimetableApi 6.
|
||||
## Срез 5. Жизнь школы
|
||||
|
||||
- **Фазы:** 18–21
|
||||
- **Проверен на:** `450495f`, 2026-08-20 (первичная проверка — `21d79cb`; предыдущая перепроверка — `5a00ad7`)
|
||||
- **Проверен на:** `cbe739a`, 2026-08-20 (первичная проверка — `21d79cb`; предыдущая перепроверка — `5a00ad7`; дерево до коммита правок — `450495f`)
|
||||
- **Пути:** `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`,
|
||||
@@ -653,6 +653,71 @@ Calendar+обеды 20, планировщик 9, TimetableApi 6.
|
||||
|
||||
Ai 29, Presence+Decision+Activity+NeedDecay+Lunch 31, Presence_RoundTrips 1.
|
||||
|
||||
## Срез 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`
|
||||
- **Итог:** все тесты из списков семи фаз на месте; дописан один клиентский; исправлен ложный
|
||||
gotcha про паузу; `foundation.md` приведён к закрытому срезу. Хостовые тесты сверены по коду,
|
||||
не прогоном — Aspire держит DLL.
|
||||
|
||||
Что подтверждено:
|
||||
|
||||
- **Фаза 22.** Пак без `pack.jsonc` (id вместо названия), подписи `?lang=`, `core` с манифестом,
|
||||
`400 missing-mod` с id недостающего, зависимость грузится раньше зависимого, цикл — `mod-cycle`,
|
||||
def без подписи предупреждает и грузится. Порядок — устойчивая топология поверх выбора игрока,
|
||||
не алфавит; разрешённый список в ответе создания и в сейве.
|
||||
- **Фаза 23.** `mods/example` на диске (`pack.jsonc`, локали, черты, набор имён, патч, комната,
|
||||
README). Копируется в вывод тестов как `core`. `GET /api/catalog?mods=example`, create+reload,
|
||||
патч только с паком, last-wins, карта пака валидна. Ваниль без выбора не меняется.
|
||||
- **Фаза 24.** Веса в `BehaviorDef` с комментариями, что перевешивают. Таблица ванильных решений,
|
||||
пак с обедом выше урока уводит класс, каталог без дефа на константах, отрицательный вес роняет
|
||||
загрузку.
|
||||
- **Фаза 25.** `golden/roster.txt` и первая несовпавшая строка; правило в README рядом. Сейв
|
||||
текущего формата и сейв без `nativeLanguage`. Кривая голода `Hunger_FollowsTheSchoolDayCurve`.
|
||||
`PrimarySchool` требует трёх, второй учитель не закрывает. `teachersShort` в API и в `t(...)`.
|
||||
- **Фаза 26.** Сид — поле школы, не id. Тот же сид — те же люди; без сида после reload тот же
|
||||
состав; золотой сейв `seed == id` грузится; переданный сид в списке. На экране школы и в create.
|
||||
- **Фаза 27.** Format новее — школа не стартует, файл нетронут. Текущий грузится. Дамп через
|
||||
снимок + мейлбокс (`DumpSchool` → работник), не в `World` с HTTP. `404 unknown-school`.
|
||||
`HSchool.Simulation.Tests` без параллельности. Дамп в `docs/protocol.md` за тем же
|
||||
переключателем, что reload.
|
||||
- **Фаза 28.** `happy-dom` на экранах; фильтры/пейджер, create (`core` нельзя снять, сброс карты,
|
||||
busy submit), сетка (pin-rejected / no-teacher / unknown-lesson), предел фонда текстом.
|
||||
Политика в `AGENTS.md`. `npm test` — 46 тестов, ~5 с.
|
||||
- **Инварианты.** `Simulation` без `DateTime.Now`. Протокол не бампили. Сид в хостовых тестах,
|
||||
которым нужен состав, передаётся явно.
|
||||
|
||||
Дописано:
|
||||
|
||||
- `managementPanel.test.ts` — непокрытый предмет показывает, скольких учителей не хватает
|
||||
(`staffUncoveredShort` с `n: 3`). Задача фазы 25 была в коде без теста экрана.
|
||||
|
||||
Исправлено:
|
||||
|
||||
- `AGENTS.md` утверждал, что на паузе кадры присутствия не приходят вовсе. Это ложь: работник
|
||||
крутит цикл и шлёт occupancy + подписи урока из таблицы. Фаза 27 записала заблуждение ревью.
|
||||
Строка приведена к факту (тест, который ждёт *людей* на паузе, зависнет; тест на подпись урока —
|
||||
нет).
|
||||
- В `design/foundation.md` «порядок — алфавит плюс зависимости» расходился с кодом (порядок
|
||||
игрока + топология). «Почему сейчас» про веса в `Decision.cs` устарел — срез это закрыл.
|
||||
|
||||
Замечено рядом:
|
||||
|
||||
- `Dump_ReturnsPeopleNodesAndTimetable` принимает `NodeId is not null || Needs.Count > 0`.
|
||||
Нужды есть у всех, так что узлы на кампусе не проверяются. Дамп поле `nodeId` отдаёт; критерий
|
||||
«кто где стоит» руками. Усиливать до явки — соседний срез 5, не здесь.
|
||||
- Срез 7 (одежда) в работе: фазы 29–30 🔄, 31–37 ⬜. В журнал как закрытый не писать.
|
||||
|
||||
Content 17, People 2, Ai 3, Hunger curve 1, клиент 46/46. AppHost не гонялся.
|
||||
|
||||
## Что из журнала закрыто, 2026-08-19
|
||||
|
||||
Ревью нашло семь пунктов; шесть закрыты в этот же день, седьмой снят как несуществующий.
|
||||
|
||||
@@ -165,3 +165,42 @@ describe('ManagementPanel payroll cap', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ManagementPanel uncovered', () => {
|
||||
beforeEach(() => {
|
||||
setLocale('en');
|
||||
vi.mocked(fetchStaffing).mockReset();
|
||||
vi.mocked(fetchTimetable).mockReset();
|
||||
vi.mocked(fetchStaffing).mockResolvedValue({
|
||||
...staffing(),
|
||||
applicants: [],
|
||||
uncovered: [
|
||||
{
|
||||
defName: 'PrimarySchool',
|
||||
label: 'Primary',
|
||||
gradeMin: 1,
|
||||
gradeMax: 4,
|
||||
hoursPerWeek: 80,
|
||||
teachersShort: 3,
|
||||
},
|
||||
],
|
||||
});
|
||||
vi.mocked(fetchTimetable).mockResolvedValue(timetable());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
document.body.replaceChildren();
|
||||
setLocale(initialLocale);
|
||||
});
|
||||
|
||||
it('shows how many teachers a subject still needs', async () => {
|
||||
const panel = new ManagementPanel();
|
||||
document.body.append(panel.listElement, panel.cardElement);
|
||||
panel.show(2);
|
||||
await vi.waitFor(() =>
|
||||
expect(panel.listElement.textContent).toContain(
|
||||
t('staffUncoveredShort', { label: 'Primary', min: 1, max: 4, n: 3 }),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user