Стать специалистом по машинному обучению
Канал о машинном обучении для людей Учусь разбираться в терминах ML вместе с вами. Для разбора теории приглашаю профессионалов. Подкаст: https://mlpodcast.mave.digital С вопросами и предложениями пишите @kmsint
Show more📈 Analytical overview of Telegram channel Стать специалистом по машинному обучению
Channel Стать специалистом по машинному обучению (@tobeanmlspecialist) in the Russian language segment is an active participant. Currently, the community unites 10 855 subscribers, ranking 11 008 in the Technologies & Applications category and 58 883 in the Russia region.
📊 Audience metrics and dynamics
Since its creation on невідомо, the project has demonstrated rapid growth, gathering an audience of 10 855 subscribers.
According to the latest data from 07 September, 2026, the channel demonstrates stable activity. Although there has been a change in the number of participants by -31 over the last 30 days and by -4 over the last 24 hours, overall reach remains high.
- Verification status: Not verified
- Engagement rate (ER): The average audience engagement rate is 18.40%. Within the first 24 hours after publication, content typically collects 6.31% reactions from the total number of subscribers.
- Post reach: On average, each post receives 1 996 views. Within the first day, a publication typically gains 684 views.
- Reactions and interaction: The audience actively supports content: the average number of reactions per post is 22.
- Thematic interests: Content is focused on key topics such as llm, enum, строка, программист, nats.
📝 Description and content policy
The author describes the resource as a platform for expressing subjective opinions:
“Канал о машинном обучении для людей
Учусь разбираться в терминах ML вместе с вами. Для разбора теории приглашаю профессионалов. Подкаст: https://mlpodcast.mave.digital
С вопросами и предложениями пишите @kmsint”
Thanks to the high frequency of updates (latest data received on 08 September, 2026), the channel maintains relevance and a high level of publication reach. Analytics show that the audience actively interacts with content, making it an important point of influence in the Technologies & Applications category.
--strictPort (это флаг Vite, не Playwright), чтобы занятый порт означал ошибку, а не тихий переход на следующий свободный. Занятый порт должен кричать (сейчас на этом слове вдруг осознал, что понахватался терминологии от агентов, раньше я бы и представить не мог, что закрытый порт может "кричать" :)). Переиспользование при этом осталось, но теперь оно переиспользует только свой собственный сервер на своём собственном порту.
Иронично, что через три недели этот же флаг уронил чужой PR. CI у меня живёт на этой же машине, где работают агенты, на своих раннерах, и в какой-то момент чей-то прогон покраснел с сообщением "порт 5174 уже используется" - порт держал локальный прогон одной из сессий. К коду в том PR это не имело ни малейшего отношения. То есть сначала мы сделали порт строгим, а потом обнаружили, что строгий порт без изоляции сам становится источником конфликтов. Развели порты по ролям - для рабочей сессии один, для CI - другой, между двумя своими worktree - задавать явно.
Вторым выстрелил путь. Контрактные тесты ищут схемы в соседнем репозитории относительно рабочего каталога, а worktree лежит на уровень глубже обычного чекаута - и путь стал указывать в пустоту. Прогон давал 215 упавших тестов вместо 2191 прошедшего. Ошибка, при этом, сообщала и лечение, но две сотни красных строк повергли агента в шок и он начал преживать, что сломал вообще весь код. Починили тем, что поиск стал подниматься по дереву каталогов вверх, пока не найдёт корень нужного репозитория, а маркером сделали сам файл спецификации, а не имя папки. Иначе пустая папка с подходящим именем, оказавшись ближе, выигрывала бы просто по расстоянию.
Третьим выстрелило то, что каталог агента, в котором запускается фоновая команда, - не то состояние, на которое можно рассчитывать в следующей команде. У моего раннера после фоновой задачи каталог переднего плана возвращается туда, где был. Агент запускает длинный прогон в фоне, потом выполняет обычную команду - и правит файлы уже не в своём worktree, а в главном чекауте. Молча, без единой ошибки. Однажды за одну сессию такое случилось трижды. Механика та же, что и в первом инциденте, только виноват уже не сосед, а собственный дрейф. Лечится тем, что каждая команда начинается с перехода в свой worktree, а не только первая в цепочке. И, поскольку такое правило держится исключительно на внимательности, теперь стоит хук, который просто отказывает в записи в главный чекаут репозитория, если у того есть живое второе дерево. Чтение и уборку после мержа он пропускает, всё остальное - нет.
Последнее, о чём хочется рассказать про текущий флоу - то, как открываются пулл-реквесты. Агент у меня не открывает их сам, он готовит ветку, коммит и push, а дальше отдаёт мне ссылку на страницу сравнения веток с уже подставленными заголовком и описанием. Я открываю, при желании правлю прямо в форме и жму кнопку. Смысл в том, что создание PR - это точка, где я могу посмотреть на работу глазами перед тем, как она поедет в мастер. Честно признаюсь, что реально ревью я провожу всё реже и реже, больше полагаясь на выросшие вокруг процесса автоматические проверки. Плюс база всегда мастер - отдельное правило, появившееся после того, как GitHub однажды влил PR в другую feature-ветку, которая к тому моменту отстала, и работа просто не доехала.git status, чтобы не унаследовать чужое. Перед каждым коммитом проверять, на какой ветке стоит HEAD. Правила хорошие, они и сейчас в силе. Но по-настоящему проблему они не решают. Между проверкой и действием всегда есть окно. Агент посмотрел git status, увидел чистое дерево, начал работу, а через двадцать минут соседняя сессия сделала свой checkout. Дисциплина уменьшает вероятность, но не устраняет причину, что дерево одно, а писателей несколько.
Настоящее решение оказалось встроено в Git и называется worktree. Век живи - век учись, как говорится, даже не знал до этого момента о такой фиче гита.
git worktree add _wt/задача-сессии-N -b ветка-сессии-N origin/master
Она создаёт ещё одно рабочее дерево того же репозитория - в подпапке _wt/задача-сессии-N, с полным набором файлов проекта и, главное, со своим собственным HEAD. При этом никакого второго клона не появляется - история, объекты и все ветки остаются общими, физически одними. Новое дерево знает, где лежат эти общие данные, а они держат запись о каждом живом дереве. Занимает это примерно столько, сколько весят файлы проекта, то есть история не дублируется.
При этом переключение ветки в одном дереве больше не может повлиять на другое, потому что HEAD теперь у каждого свой. Каждая сессия сидит в своём каталоге и делает там что хочет. Главный чекаут остаётся стоять на мастере как справочная копия, и его никто не трогает.
Несколько практических нюансов, всплывших в процессе работы. Одну и ту же ветку нельзя чекаутить в двух деревьях сразу - Git прямо откажет и назовёт, какое дерево её держит: fatal: 'feature' is already used by worktree at '…'. Запрет можно снять флагом --force, и я специально попробовал, что тогда будет. Получается так. Коммит из первого дерева двигает ветку, второе дерево оказывается на новом коммите, но с файлами старого состояния, и его git status показывает чужой файл как удалённый. Следующий обычный коммит оттуда этот файл действительно удаляет. То есть без запрета вернулась бы та же гонка, только хуже. В исходной истории работа терялась и её можно было достать из коммита, а здесь она отменяется коммитом, который выглядит совершенно законным. То есть изоляция worktree - это про то, что у каждой ветки ровно один писатель.
Другой нюанс. Дерево после мержа надо убирать командой git worktree remove, а не просто удалять папку, так как иначе Git продолжит держать служебную запись о нём и считать ветку занятой, пока запись не почистить через git worktree prune - сам он до неё может добраться, но нескоро, там свой TTL (время жизни, срок устаревания). И ещё. В новом дереве нет ничего, чего нет в Git - ни .env, ни установленных зависимостей, ни виртуального окружения. Для питона мы обошлись относительным симлинком на окружение основного чекаута, для веба - общей папкой зависимостей. При этом, отсутствие .env неожиданно оказалось полезным - свежее дерево - это то, что видит CI, и однажды именно так мы воспроизвели падение, которое локально не воспроизводилось никак.
После перехода на рабочие деревья класс проблем "коммит ушёл не туда" исчез полностью. Даже не уменьшился, а именно, что исчез. В истории видно, как переключения веток в главном чекауте обрываются в один день, и дальше их просто нет. Мержится по-прежнему несколько десятков PR в день, и нужно понимать, что подход не ускоряет отдельно взятые операции, он делает параллельную работу надёжной, а значит устраняет временные потери на то, чтобы разобраться, почему всё идёт не так, как хочется. Скорость даёт параллельность, а worktree убирает аварии, которыми изначально мы с агентами за эту скорость расплачивалась.
А теперь главный урок всей истории. Worktree изолирует рабочее дерево, но не изолирует окружение. Файлы разъехались, а всё остальное, что сессии делят между собой, осталось общим. И следующие три инцидента пришли именно оттуда.HEAD, стоявший на ветке сессии А, теперь стоит на мастере. Сессия А об этом не узнает никак: она не выполняла команд, ей никто ничего не сообщил, у неё в чате по-прежнему написано "создал ветку feature/…".
Затем сессия А делает git commit. Коммит всегда ложится туда, куда указывает HEAD, а HEAD в этот момент на мастере. Локальный мастер уезжает вперёд на один коммит с чужой работой. Дальше git push -u origin ветка-сессии-А создаёт эту ветку на сервере - и она указывает на тот коммит, с которого была отрезана, потому что локально её никто никуда не двигал. В origin уезжает ветка ровно в том виде, в каком её создали - пустая. Отсюда и "нечего сравнивать".
Такие записи сохранились в reflog (журнале, куда гит пишет каждое перемещение HEAD). Журнал не знает, кто именно выполнял команду, но, в целом, картина восстанавливается однозначно:
12:44:14 checkout: moving from master to feat/the-controls… ← сессия А 13:11:49 checkout: moving from feat/the-controls… to master ← сессия Б 13:11:51 pull -q --ff-only: Fast-forward ← сессия Б 13:23:32 commit: feat(editor): the controls are part of… ← коммит сессии А, на мастере 13:37:26 reset: moving to origin/master ← сессия АДвадцать семь минут нормальной работы между "создал ветку" и "дерево ушло". Ни одна команда за это время не упала. В принципе, чинится это без переключения дерева - чтобы не мешать соседу, который в нём работает. Ветку переставляют на нужный коммит принудительно (
git branch -f), отправляют на сервер с перезаписью и откатывают локальный мастер к серверному состоянию. Перезаписывать опубликованную ветку лучше не голым --force, а --force-with-lease. Он отправит только в том случае, если на сервере с момента вашей последней сверки ничего не изменилось. В обычной работе разница не существенна, а при нескольких параллельных сессиях именно она отделяет починку от затирания чужой работы. И перед всем этим стоит посмотреть git show --stat на спорный коммит - если внутри оказалось что-то чужое, это уже другая история.
Теперь та же проблема в обратную сторону. Незакоммиченные файлы не принадлежат какой-либо ветке. Совсем. Они просто лежат в рабочем дереве, а ветка про них ничего не знает, потому что ветка - это указатель на уже сделанные коммиты. Особенно это касается новых файлов, которые Git ещё не отслеживает. При переключении веток он их не трогает вообще, они остаются на диске как есть.
И вот сессия Б создала в дереве пару новых файлов и ушла на мастер. Сессия А, ничего не проверив, создаёт ветку - и чужие файлы оказываются в её ветке как её собственные. Команда git add -A, которой агенты пользуются постоянно, означает "взять все изменения из рабочего дерева, которые Git не игнорирует", и она честно берёт всё, включая чужое. Дальше сессия Б делает свой коммит, но HEAD ведь уже стоит на ветке сессии А. В origin уезжает коммит с сообщением сессии Б, её работой, чужой поправкой и под именем ветки сессии А. Распутать этот клубок постфактум можно, но агент сталкивается с тем, что не всегда однозначно понимает чью историю имеет право переписывать.
У всех этих случаев одна общая черта - ни одна команда не завершилась ошибкой. Git каждый раз сделал ровно то, что у него просили. Просто это "что просили" складывалось из команд двух независимых процессов, каждый из которых считал дерево своим. Такие вещи не ловятся тестами, не видны в CI и обнаруживаются, только когда я открываю ссылку на PR и вижу, что сравнивать нечего..git. Вторая - рабочее дерево - те самые файлы проекта, которые мы видим в редакторе. И вот ключевое - данных гита хватает на сколько угодно веток одновременно, а рабочее дерево у клона одно. Ветка при этом - это не папка и не копия проекта, а просто указатель на коммит - строчка в файле, если совсем грубо. Ещё есть указатель по имени HEAD, который говорит "мы сейчас вот здесь". Когда мы делаем git checkout другая-ветка, git не создаёт никакую копию, - он переписывает файлы в вашем единственном дереве под состояние той ветки и переставляет HEAD. Пока вы в клоне один, это ровно то, что нужно. Как только в нём двое - начинается то, ради чего этот пост.
expected = calculate_price(order)
assert response.price == expected
Дальше. Недавно я добавлял в проект роль "админ readonly" и обнаружил, что десяток тестов админских страниц всё это время проходили от лица анонимного гостя. Просто потому, что роль до этого дня ни на что не влияла, и разницы не было видно. Тесты были зелёные ровно потому, что проверять было нечего. Как только роль начала что-то значить, они посыпались все разом. Зелёный тест отвечал "работает" на вопрос, не имеющий смысла.
Так у нас появился ритуал, который зафиксирован отдельным правилом. Написал проверку - сломай то, что она охраняет, и посмотри, что она покраснела. Это позволяет отделить реальную проверку от её имитации. По сути, это ручное, но теперь руками агента, мутационное тестирование.
Но вы, надеюсь, не подумали, что на этом проблемы с тестами прекратились? Мутация, оказывается, тоже может не сработать. Буквально на этой неделе Claude сломал правило в файле, прогнал тесты и увидел в выводе "1 error". Обрадовался, что поймал - и чуть не пошёл исправлять. А это была не упавшая проверка, это питон не смог собрать файл. Клод неаккуратно вырезал строку и порвал синтаксис. То есть тесты вообще не запускались, и проверка проверки ничего не проверила.