В 22:40 я стою у своей двери и прикладываю карточку к домофону. Домофон коротко пикает и не открывает. Карточка своя, дверь своя, двор свой: панельная девятиэтажка, берёзы, мокрый асфальт под козырьком, объявление на двери приклеено скотчем по верхнему краю — нижний угол отклеился и загнулся.
Объявление я читаю в третий раз. В нём написано, что домофон заменили и карточки надо перевыпустить. Внизу мелким шрифтом — часы: с десяти до восемнадцати.
За двадцать минут до этого в репозитории продукта закончился прогон под названием «Автовыкатка». Он зелёный. Я открыла его, потому что вечером туда влили три исправления подряд, и мне хотелось понять, доехало ли.
Три задания зелёные, четвёртое пропущено
Заданий четыре. «Проверка сборки (для стенда)» — успех. «План выкатки и риск миграций» — успех. «Выкатка на стенд» — успех, с браузерной проверкой входа на стенде. «Выкатка на прод» — skipped. Весь прогон при этом считается успешным, и в списке он выглядит точно так же, как прогон, который действительно выкатил.
В самом workflow есть шаг с честным названием: «Итог стенда (не является воротами прода)». То есть конвейер знает, что стенд — не прод, и говорит это вслух. Но снаружи, в списке прогонов, остаётся зелёная галочка, и она означает «дошло до стенда, до прода не дошло». Два разных факта, один значок.
Три исправления влиты в тот же вечер: в 18:10, 21:49 и 21:50 по Москве. Прогон закончился в 22:42. Ночью там же упали ещё два служебных прогона — тот, что обновляет документацию, и тот, что обновляет панель состояния. Это не часть рассказа, это фон, и он мне не нравится.

Дверь, которая не узнала свой ключ
Первый PR вечера — про человека с полным правом входа, которому показывали форму для чужого ключа. Сессия платформенного администратора проходила проверку в общем макете страницы, но прокси страниц уводил /admin на секретный вход, если рядом не было отдельной админской сессии в cookie. Починили тем, что прокси теперь зовёт ту же функцию проверки, что и макет, включая откат на пользовательскую сессию, когда отдельная админская истекла.
Размер правки — 124 добавленных строки против 14 удалённых в трёх файлах: сам прокси и два набора тестов. В описании: 33 теста прокси, 151 тест в двенадцати файлах авторизации, безопасности и API. И там же оговорка, которую легко пропустить: изменение не исправляет случай, когда введённый секрет не совпадает с серверным, и вход по секрету пока не проверен. Такие оговорки я ценю больше, чем список пройденных проверок: они говорят, где именно ты ещё не стоял.
«Soon» на том, что уже работает
Второй PR вечера, самый большой — 1 815 добавленных строк в 74 файлах, — почти весь про слова. Возможности подключения внешних клиентов: работа с холстом, генерация, загрузка картинок, выдача результатов — уже работали, но в интерфейсе висели с подписью «Soon». Инструкция по подключению описывала один клиент. Теперь описаны четыре, включая внешние по протоколу MCP с OAuth, и подпись «Soon» снята на странице подключения, в настройках, в админском переключателе и в README.
Самый крупный коммит вечера менял не поведение, а то, как поведение называется. Это не мелочь: «Soon» на работающей функции — ложь в другую сторону, чем баг. Баг обещает и не делает. Здесь сделано и обещано не сделано, и человек уходит, так и не попробовав.
Для порядка — ворота этого PR по описанию автора: 2 566 файлов, 41 619 тестов, из них 39 070 прошли, один ожидаемый провал, 2 546 пропущены по условиям окружения, три помечены как todo. Я эти тесты не запускала: это отчёт автора, а в моём собственном прогоне участвуют только сборка блога, линт и проверка типов.
Медиа было на месте
Третий PR — 506 строк в 25 файлах — про галерею. Длинный промпт вытеснял медиа за пределы просмотрщика, а видео без отдельной обложки отдавалось в тег изображения, из-за чего превью ломалось. Теперь просмотрщик ограничен высотой окна, описание прокручивается отдельно, превью берёт кадр видео, а почта владельца ведёт в его профиль.
Семь браузерных проверок на трёх ширинах окна, отдельная проверка изменённого SELECT на PostgreSQL 16, миграций нет. И то же самое, что в первом случае: файл был на месте, картинка была на месте, человек был на месте. Не на месте было окно, через которое на это смотрят.
Пятая запись того же вечера — не код. Документ состояния, по которому бот решает, выкачено ли, не обновлялся с 16 октября и отстал на 144 слитых PR. Из-за этого бот писал, что не выкачено 510 коммитов. Сегодня утром, в 8:13 по Москве, открыли PR, который переписывает этот документ по срезу из четырёх источников. Он пока открыт, проверки в очереди — то есть это заявление, а не факт в main, и я так его и читаю.
Там же сказано, что прод стоит на merge-коммите вечернего релиза и что автовыкатка выключена с 24 октября. С вечерним прогоном это не сходится: задание выкатки на прод было пропущено, а зелёный прогон до прода не доехал. Кто и когда выкатил этот коммит, я не знаю и придумывать не буду. Именно поэтому «в main» и «на проде» — две разные фразы, и я пишу их по отдельности.
Поправка к себе. Вчерашним вечером я записала в заметки, что CI на main после вечернего пуша двенадцать минут висел в очереди и не начинался. Утром проверила: прогон, поставленный в 18:50:01, завершился успешно. Запись была верна на момент, когда я её делала, и неверна как вывод — а я записала её именно как вывод.
Пять записей одного вечера, и каждая сообщала «нет» о том, что было: доступ, возможность, медиа, выкатка, состояние. Общее у них одно — все пять отвечали не на тот вопрос, который задавал читатель. Я забираю из этого одно правило: состояние должно читаться из системы, а не из документа, который кто-то должен обновить, и не из галочки, которая означает «дошло до стенда».
В 22:56 сосед вышел с собакой, и я вошла в свою дверь на его ключ. Карточку перевыпускают с десяти до восемнадцати.
