Привет, Хабр! Меня зовут Андрей Квапил, я основатель Ænix — мы делаем открытую облачную платформу Cozystack и помогаем компаниям строить инфраструктуру. Мы полностью удалённая компания: 15 человек, несколько команд (две reliability-команды, команда разработки, маркетинг, бэкофис и т.д.), живем в разных часовых поясах. Поначалу мы жили в GitHub Projects, но когда начали расти, резко упёрлись в ограничения текущих процессов: задачи размазаны по доскам и чатам, на утреннем синке половина времени уходит на выяснение «а что там у нас вообще в работе», незапланированная работа съедает весь день и нигде отдельно не видна.

Эта статья — история о том, как мы решили эту проблему инструментом aeman, который разработали сами и недавно заопенсорсили. Но начать придётся издалека.

Откуда ноги растут

Когда-то я работал во «Фланте», и там были две внутренние системы с прекрасными именами — Ford и Nixon. Про них коллеги подробно писали на Хабре (ссылки приложил в конце статьи), поэтому тут расскажу кратко: это доски для отслеживания ежедневных задач, на которых инженер видит ровно то, чем он занимается сегодня. Не бэклог на три месяца, не сто колонок канбана — а «вот твой день». Эта система годами позволяла организовывать эффективную работу множества удалённых команд во «Фланте» и обслуживать инфраструктуру многочисленных клиентов.

Скажу честно: процесс, который лёг в основу aeman, — это не совсем моя идея. Я взял работающую систему управления процессами, которую видел во «Фланте», и переложил на наши процессы и задачи. Теперь наша доска заставляет фокусироваться на самом главном — бизнесовых задачах.

Эта модель, в отличие от Trello, изначально исходит из того, что человек в течение дня может решать задачи в разных командах. И при этом дневной план не должен превращаться в «стену задач»: если у инженера запланировано 6–10 задач, хотя реально он успеет выполнить лишь 3–4, у него возникает ощущение постоянного долга, а у менеджмента — ложное впечатление высокой загрузки. Кроме того, человек начинает постоянно переключаться между задачами, а мозг естественным образом выбирает те, которые проще закрыть. В результате сложные задачи могут долго оставаться не начатыми. Так что дневной план должен быть коротким и честным.

Почему не взять готовый инструмент?

Я пытался выстроить подобный процесс на существующих инструментах: Trello, Asana, Notion, GitHub Projects — с разной степенью успеха. Ближе всех к цели оказался Notion, но загонять всю команду ещё в одну систему и платить за неё только лишь ради досок я был не готов. К тому же со временем доска неизбежно разрасталась и переставала помещаться на экран.

Самым жизнеспособным вариантом в итоге стали GitHub Projects. Они бесплатные, предлагают удобный API и вообще содержат всё необходимое: спринты, приоритеты, статусы и другие сущности. Кроме того, они хорошо расширяются. Именно поэтому мы долгое время работали на них.

Но довольно быстро стало понятно, что проблема не в возможностях инструмента, а в UX. Чтобы создать задачу, назначить исполнителя и заполнить необходимые поля, приходится сделать слишком много лишних действий. Кроме того, вокруг GitHub Projects так и не получилось выстроить удобный ежедневный процесс. Например, спринты там есть, но пользоваться ими оказалось слишком неудобно: нельзя одним действием завершить текущий спринт, запустить следующий с переносом незавершенных задач или просто быстро убрать задачу «на потом», чтобы она исчезла из поля зрения и не отнимала внимание инженера.

Со временем проявилась и ещё одна проблема. На каждом синке мы фактически смотрели только в одну колонку — In Progress. Именно там происходила вся работа, а карточки в других колонках постепенно превращались в «кладбище задач»: они продолжали существовать на доске, но переставали участвовать в ежедневном процессе и никуда не двигались. GitHub Projects оказался отличным инструментом для хранения задач, но для управления ежедневным фокусом команды подходил слабо.

Именно поэтому, приступив к разработке aeman, я выбрал GitHub Projects в качестве бэкенда новой системы, и выстроил пользовательский интерфейс и рабочий процесс поверх него.

Ключевая идея: ровно те карточки, которые нужны сегодня

Цель aeman — вывести список актуальных задач на сегодня и сфокусировать инженера на них. В идеальном мире на утреннем синке инженер получает список задач, над которыми он точно будет сегодня работать. Всё остальное уезжает на завтра или на следующую неделю — и физически исчезает с доски, чтобы не мозолить глаза.

Вторая идея — незапланированная работа должна быть видна. У всех бывают дни, время в которых съедается внезапным инцидентом или срочной просьбой соседней команды. Обычно эта работа нигде не фиксируется, и в конце спринта непонятно, куда делось время. В aeman для неё есть отдельная зона: заносишь постфактум одной карточкой — и день перестаёт пропадать бесследно (скриншоты — в следующих разделах). А на ежедневном синке есть возможность рассказать об этом и взять задачу в блок «planned».

Третья идея — многокомандность как свойство модели, а не фильтр. В небольшой компании один инженер может выполнять задачи в разных командах: особенно это касается задач фаундеров, которые до сих пор мультизадачны. В aeman команды — это измерение доски: спринт трекается на команду, у каждой команды свой недельный план, а доска Me собирает на один день карточки из всех команд инженера сразу. Не нужно прыгать между досками, чтобы понять свой день; при этом задача легко переезжает между людьми и командами — она может менять исполнителей изо дня в день, и это нормальный режим работы, инструмент не должен этому мешать.

И четвёртая идея — тимлиду нужен инструмент, где он за минуту понимает статус по каждой задаче, не отвлекая людей лишними вопросами.

По своей философии aeman больше похож на Todoist, чем на классическую канбан-доску. Задачи здесь намеренно короткие: в обычном режиме виден только заголовок, а описание открывается по двойному щелчку. В описание можно в свободной форме кидать ссылки на Pull Request, GitHub Issues, Telegram-чаты и любые другие ресурсы — aeman автоматически распознает их и покажет специальную кнопку для быстрого перехода. Благодаря этому главный экран остаётся чистым, а закрытие очередной задачи приносит то самое приятное чувство, знакомое пользователям хороших таск-менеджеров.

Как это выглядит

Доска Me: твой день

Инженер заходит утром и видит свой день: карточки, сгруппированные в четыре цветные зоны. Это не матрица Эйзенхауэра, градация другая:

  • Краснаяurgent, необходимо сделать уже сегодня;

  • Сераяplanned, обычная запланированная работа (может занимать и больше одного дня);

  • Жёлтаяunplanned, то, что прилетело в течение дня;

  • Зелёнаяnice to have, делать, только если всё остальное готово.

На доску попадают карточки из всех команд инженера — то есть из текущего спринта каждой из них; селектор команд сверху позволяет отфильтровать список до задач одной команды или спрятать с доски все готовые карточки и карточки, которые сейчас заблокированы или находятся на ревью, чтобы показать только то, над чем можно работать прямо здесь и сейчас, когда надо погрузиться в работу. У каждой карточки есть ползунок готовности от 0 до 100 % с шагом 10 % (двигать прогресс-бар в done на удивление приятно), стадия (Review / Locked / Recurrent / Done), которая перекрашивает бар, счётчик дней выполнения и ссылки на issue или PR. Справа — панель заметок: персональный лог дня, куда можно писать что-нибудь по ходу работы. Чтобы на следующем дейли не пришлось вспоминать, а можно было быстро пробежаться по нему и рассказать что сделано. Заметки живут в контексте дня — назавтра доска чистая, но всегда можно вернуться на день назад и перечитать.

Доска Team: команда одним взглядом

Вид для тимлида: сетка «люди × зоны» на выбранный день. Колонки — инженеры с аватарками, строки — те же цветные зоны. Сразу видно, кто чем занят, у кого что горит и кто перегружен. Отсюда же тимлид заводит карточки: пара кликов — задача создана, назначена и приоритизирована. 

В режиме Me тимлид также имеет возможность имперсонализации (кнопка View as) — посмотреть на доску глазами конкретного инженера и при необходимости навести там порядок.

Дневные спринты и carry-over

Спринты у нас короткие — один день — и считаются вперёд, а не назад. Утром на синке команда сначала обсуждает, что сделано вчера, а потом тимлид жмёт Carry over: все незавершённые карточки переезжают в новый спринт (сегодня), завершённые остаются в истории вчерашнего дня. Регулярные задачи пересоздаются автоматически. Это и есть планирование дня — занимает минуты.

Если на планировании решили, что задачей сегодня точно не займёмся, — есть кнопки «+1 день» и «+1 неделя»: карточка исчезает с доски до своего дня, история при этом не теряется. Когда день настанет, она появляется снова.

Недельный план

Здесь мы уже отошли от оригинальной идеи и начали натягивать собственный рабочий флоу на получившийся инструмент.

Раньше мы с фаундерами планировали работу команды на неделю, для этого мы записывали в google sheets новый список задач на неделю и рассылали командам в слак «текущие фокусы на неделю», сейчас этот процесс тоже переехал в aeman.

Под командной сеткой живёт недельный план: бизнес-задачи команды на неделю, разложенные в две полосы — «к среде» и «к пятнице». Фаундеры раз в неделю заносят туда задачи, и они ждут своей очереди. Тимлид перетаскивает карточку плана на инженера — она появляется у него на дневной доске, оставаясь в плане с цветной отметкой. Общий прогресс-бар показывает, как команда идёт по неделе, а недельный Carry over переносит незакрытое на следующую неделю.

Ревью, сабтаски и журнал

Отправка задачи на ревью создаёт связанную карточку у ревьюера — с обратной связью: пока ревью не пройдено, оригинал стоит на стадии Review, а когда ревьюер докручивает свою карточку до 100 %, оригинал автоматически освобождается. Большие задачи дробятся на сабтаски с наследуемым прогрессом родителя. Каждое действие на доске пишется в журнал активности карточки — постфактум можно восстановить всю её историю: кто двигал, когда меняли прогресс и почему она переехала в другой спринт.

Один день с aeman

Как выглядит наш флоу целиком:

  1. Утренний синк. Открываем доску Team на вчерашний день, тимлид шарит свой экран, проходимся по карточкам: что сделано, что застряло и так по каждому, поправляет статусы, если нужно. Нажимает «Carry over» — незавершённое переезжает в сегодня. Обсуждаем новое: тимлид на лету заводит карточки и раскидывает по людям и зонам.

  2. День. Каждый работает по своей доске Me. Прилетело срочное — карточка в жёлтую зону. Что-то заблокировалось — стадия Locked, тимлид увидит. Прогресс двигается по ходу дела; мысли и находки — в заметки дня.

  3. Готово — карточка на 100 %, стадия Done. Требует ревью — Send to review, и она появляется у ревьюера.

  4. Завтра всё повторяется.

Все доски живые: правки коллег и AI-агентов появляются на экранах примерно за секунду, без перезагрузки — как это устроено, расскажу ниже.

Под капотом

Теперь самое интересное для инженерной публики — как это всё работает.

GitHub Projects v2 — единственное хранилище

У aeman нет своей базы данных вообще. Каждая карточка — это item на доске GitHub Projects, все поля (зона, прогресс, спринт, недельный план) — обычные поля проекта. Причём aeman создаёт нужные поля лениво сам: наводишь его на любой пустой проект — и первое же изменение провиженит недостающие поля. Можно сказать, что aeman — это специализированный view для доски GitHub: та же самая доска открывается в родном интерфейсе GitHub, данные всегда ваши, и уйти с aeman можно в любой момент, ничего не мигрируя.

Kubernetes-подобный API

API aeman намеренно сделан в духе Kubernetes — паттерн, который отлично зарекомендовал себя для синхронизации распределённого состояния. Каждая сущность — это ресурс с kind, metadata и spec (Card, Sprint, Ordering, Presence), а клиент работает по знакомой схеме list + watch: сначала забирает срез доски, потом держит WebSocket-стрим и получает события ADDED / MODIFIED / DELETED плюс закрывающий Sync-фрейм после полной перезагрузки. По сути, каждая открытая вкладка — это informer с локальным кэшем: правки коллег (и AI-агентов) появляются на всех экранах примерно за секунду, а собственные изменения не «эхо-каются» обратно отправителю. GET /api/v1 отдаёт машиночитаемый каталог всех эндпоинтов.

API aeman намеренно построен по тем же принципам, что и API Kubernetes — это паттерн, который отлично зарекомендовал себя для синхронизации распределённого состояния. Каждая сущность представлена отдельным ресурсом (Card, Sprint, Ordering, Presence) с привычными kind, metadata и spec.

Клиент работает по знакомой схеме list + watch: сначала получает полный снимок состояния доски, затем открывает WebSocket и начинает получать поток событий ADDED, MODIFIED и DELETED. После полной пересинхронизации сервер отправляет специальный Sync-фрейм, который сигнализирует, что локальное состояние полностью актуально.

По сути, каждая открытая вкладка браузера работает как Kubernetes informer с собственным локальным кэшем. Любые изменения, внесённые другими пользователями или AI-агентами, появляются на всех открытых экранах примерно за секунду. GET /api/v1 отдаёт машиночитаемый каталог всех доступных ресурсов и эндпоинтов, благодаря чему клиентам и AI-агентам не требуется заранее знать структуру API.

GitHub API медленный. Что с этим делать

Главная инженерная проблема проекта — GraphQL-мутации GitHub занимают сотни миллисекунд, а то и секунды, на burst-нагрузку прилетают secondary rate limits, и — сюрприз — реплики чтения GitHub отстают от записи на несколько секунд. Если писать «в лоб», UI превращается в слайд-шоу: подвинул слайдер — жди, перетащил карточку — жди.

Поэтому запись в aeman работает по схеме write-behind: пользователь никогда не ждёт GitHub. Любое изменение мгновенно применяется к серверному кэшу, сразу становится видимым всем открытым доскам и лишь затем асинхронно отправляется в GitHub. Запись выполняется через фоновую очередь с ограничением скорости и автоматическими повторами при временных ошибках, чтобы не упираться в GitHub API rate limits.

Очередь умеет объединять последовательные изменения по аналогии с Kubernetes DeltaFIFO. Если пользователь несколько раз подряд меняет одно и то же поле карточки, в GitHub отправляется только его итоговое значение. Для содержимого карточек действует другое правило: текст не теряется. Если пользователь быстро редактирует описание или добавляет заметки, очередь объединяет их в одну финальную версию и отправляет её одним запросом. До подтверждения записи новые заметки существуют с временными идентификаторами, которые затем автоматически заменяются настоящими.

Если запись после всех повторных попыток всё же не удалась, сервер уведомляет все подключённые клиенты и перечитывает состояние доски из GitHub. Именно GitHub остаётся единственным источником истины. При завершении работы aeman сначала дожидается обработки всей очереди, чтобы уже подтверждённые пользователю изменения не были потеряны.

GitHub не гарантирует, что только что записанное изменение сразу станет доступно для чтения. Иногда сразу после успешной записи API ещё некоторое время возвращает старое состояние. Из-за этого пользователь мог бы увидеть, как только что перемещённая карточка «прыгает» обратно, удалённая — снова появляется, а новая — оказывается не на своём месте. Чтобы этого избежать, aeman на короткое время считает собственные записи более достоверными, чем свежие данные, пришедшие из GitHub. Когда GitHub начинает возвращать уже актуальное состояние, система автоматически переключается обратно на него. Благодаря этому пользователь никогда не видит временных откатов интерфейса.

Комментарии и журнал действий

Заметки и история изменений тоже хранятся в GitHub — без отдельной базы данных. У draft-карточек журнал находится прямо в теле issue после специального маркера <!-- aeman:log -->. Если карточка привязана к существующему Issue или Pull Request, заметки становятся обычными комментариями GitHub и видны всем участникам обсуждения.

Каждое действие на доске — создание карточки, изменение прогресса, перенос между спринтами, отправка на ревью и другие — сервер записывает в виде машиночитаемой строки события. Чтобы не засыпать подписчиков десятками уведомлений, все такие события собираются в один специальный лог-комментарий, который просто дополняется новыми записями. При этом журнал ограничивается последними 200 событиями, а пользовательские заметки никогда не удаляются.

В результате у каждой карточки сохраняется полная история изменений: кто и когда её перемещал, как менялся прогресс и почему она оказалась в том или ином спринте. Этот журнал можно использовать не только для аудита, но и как источник данных для построения статистики и различных метрик.

Расширяемый бэкенд

aeman изначально создавался с расчётом на расширяемый бэкенд. Сегодня карточки хранятся в GitHub Projects, но сама система никак не привязана к GitHub. Всё взаимодействие с хранилищем скрыто за интерфейсом Backend, поэтому поддержка нового бэкенда сводится к реализации одного интерфейса.

На следующих итерациях я хочу добавить git-бэкенд, в котором карточки будут храниться обычными файлами прямо в репозитории. При этом ни пользовательский интерфейс, ни бизнес-логику приложения менять не придётся.

Кроме того, пакеты из pkg/ можно использовать как обычную Go-библиотеку и встроить движок доски в собственный инструмент. Подробнее об этом — в docs/embedding.md.

MCP для AI-агентов

Тот же бинарник умеет работать и как MCP-сервер. Благодаря этому Claude и другие AI-агенты становятся полноценными участниками процесса: создают и перемещают карточки, оставляют заметки, проводят carry-over между спринтами и выполняют другие операции.

При этом для AI нет отдельного API или специальных привилегий. Все действия проходят через тот же доменный слой, что и действия человека, с тем же контрактом и проверками, а изменения мгновенно появляются у всех.

Помимо локального режима, aeman предлагает общедоступный MCP-сервер. Это позволяет инженерам подключать AI-клиенты без дополнительной установки локальных утилит и сразу работать с задачами напрямую из Claude Code и других AI-агентов.

Как попробовать

Самый простой способ попробовать aeman — запустить его локально со своим GitHub-токеном. 

gh auth login # нужны скоупы project и repo

git clone https://github.com/aenix-io/aeman
cd aeman
make build

./aeman serve

Браузер автоматически откроется на http://127.0.0.1:8765. В качестве доски можно указать любой GitHub Project — лучше новый и пустой. Все необходимые поля aeman создаст автоматически при первом изменении.

Для командной работы предусмотрен multi-user режим. В репозитории есть готовый docker-compose.yml, поддерживается вход через GitHub OAuth App, а каждый пользователь работает со своим GitHub-токеном. Подробная инструкция находится в docs/deploy.md.

Что в итоге

Мы живём на aeman всей компанией уже несколько недель. Продуктовые менеджеры и тимлиды быстро оценили новый подход к планированию, хотя, как и с любым новым инструментом, не обошлось без скепсиса. Но главный эффект оказался другим: утром каждый инженер знает, чем будет заниматься сегодня, незапланированная работа перестала быть невидимой, а ежедневные синки стали гораздо более конструктивными и сфокусированными.

Проект открыт под лицензией Apache-2.0 и доступен на GitHub: github.com/aenix-io/aeman.

Если решите попробовать aeman у себя, буду рад любому фидбэку, найденным проблемам и рассказам о том, как у вас устроен процесс планирования.

Референсы

Статьи «Фланта» про Ford, Nixon и устройство их процессов, из которых выросла вся эта история:

Комментарии (1)


  1. gserge
    24.07.2026 08:26

    Спасибо за имплементацию. После того, как я покинул Флант, мне не хватало этого инструментария.