Merge branch 'phase/54-whats-new'
ci / server (push) Failing after 3m45s
ci / client (push) Failing after 21s

This commit is contained in:
Leonid Pershin
2026-08-20 11:45:48 +03:00
20 changed files with 752 additions and 2 deletions
+25
View File
@@ -118,3 +118,28 @@
Голый `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 из бегущего процесса.
Бамп версии сокета. Окно внутри открытой школы.
+42
View File
@@ -0,0 +1,42 @@
# Фаза 54. Что нового
## Зависимости
Нет. Сессия (фаза 38) уже ставит куку входа; это отдельная кука и отдельное окно.
## Зачем
После наката игрок не обязан читать git. Окно один раз показывает, что изменилось с прошлого
захода в этот браузер.
## Задачи
- [x] Сборка Server вшивает SHA и `git log --first-parent` (не вызов git в рантайме)
- [x] `GET /api/changelog?since=` с сессией: текущий SHA и коммиты строго после `since`
- [x] Нет `since`, неизвестный SHA, `since` = текущий — `commits` пустой
- [x] Subject `Mark phase N…` в окне нет; в цепочке якоря они остаются
- [x] Кука `hschool.seen-rev` (не HttpOnly, не сессионная): первый заход молча запоминает HEAD
- [x] Модалка только после `ensureSession` (вход и повторный вход после logout), не с меню школы
- [x] [`protocol.md`](../protocol.md) HTTP, версию сокета не бампить
## Тесты, без которых фаза не закрыта
- [x] Срез после известного SHA — видимые коммиты старше→новее, без `Mark phase`
- [x] Якорь на `Mark phase` — следующие видимые всё равно отдаются
- [x] Нет `since` / неизвестный SHA / `since` = текущий — пустой список
- [x] `GET /api/changelog` без сессии — `401`
- [x] С сессией без `since` — 40 hex в `current`, `commits` пустой
- [x] Нет куки — запрос без `since`, кука = `current`, диалога нет
- [x] Кука старше, сервер вернул коммиты — диалог, по закрытию кука = `current`
## Критерий готовности
- Первый заход после деплоя с новой сборкой Server показывает окно; повторный заход в том же
браузере — нет
- `run-aspire` без `--rebuild` после коммита только в доках может показать старый SHA: вшито
на сборке, не HEAD рабочего дерева
## Стоп
Не писать last-seen в `users.json`. Не бампить сокет. Не вызывать git из бегущего сервера.
Не показывать окно при возврате из школы на меню.
+2 -2
View File
@@ -231,7 +231,7 @@
Склейка уже живых систем и мелкий DX, не новый кусок игры. Номера не спорят со срезом 10.
После каждой игровой фазы школа целая. 50–52 стоят на 48; 53 — на 18 и 32, можно параллельно
с 48. 49 от них не зависит. Баг в уже сделанном — [`../bugs/README.md`](../bugs/README.md).
с 48. 49 и 54 от них не зависят. Баг в уже сделанном — [`../bugs/README.md`](../bugs/README.md).
| Фаза | Статус | Зачем |
| --- | --- | --- |
@@ -241,4 +241,4 @@
| [51. Тепло на уроке](51-warmth-lesson.md) | ✅ | Замёрзший учится хуже, как голодный |
| [52. Учебник на уроке](52-textbook-lesson.md) | ✅ | Нет в сумке — половинный рост |
| [53. Погода на дороге](53-weather-commute.md) | ✅ | Снег и дождь добавляют минуты к приходу |
| [54. Что нового](54-whats-new.md) | 🔄 | После входа — окно коммитов с прошлого визита |
| [54. Что нового](54-whats-new.md) | | После входа — окно коммитов с прошлого визита |
+20
View File
@@ -68,6 +68,26 @@ closes the connection with a policy violation and never sends Welcome. A second
that already has a live connection is closed the same way; `POST /api/session` for that name
returns `409` `name-online`. Hello is unchanged (version + locale only).
### `GET /api/changelog`
What landed in the running Server build since an earlier commit. Requires a session cookie.
`current` is the SHA baked at compile time (`git rev-parse HEAD` during that build), not the live
working tree. `commits` is `git log --first-parent` after `since`, oldest first, subjects that
match `Mark phase <digits>` omitted. Without `since`, with an unknown SHA, or when `since` equals
`current`, `commits` is empty — a first visit must not dump the whole history.
```json
{
"current": "0123456789abcdef0123456789abcdef01234567",
"commits": [
{ "sha": "89abcdef0123456789abcdef0123456789abcdef", "date": "2026-08-20T08:21:00+00:00", "subject": "Merge branch 'phase/53-weather-commute'" }
]
}
```
Optional `?since=` is a 40-character hex SHA from the client's `hschool.seen-rev` cookie. That
cookie is not HttpOnly and is not the session cookie. Protocol version is unchanged.
### `GET /api/schools`
Everything the main menu needs in one request. `schoolWeekDays` is 57 working days counted