Перейти к содержимому
arckep.ru — нейросети без VPN Перейти
>AISTUDY_
AUTHORСвежий выпуск №031 → Git и конвейер для агента
FOLDER SERIES / 031 / ISSUE / 031
· · ·  AUTHOR COLUMN  · · ·
SHEET 01 / 06 / SCALE 1:1 / RU · 2026
N°031 REV·01 PAPER·A3 DRAWN·BY·AUTHOR

031GIT И КОНВЕЙЕР ДЛЯ АГЕНТА

Ветки и рабочие копии. Перехватчики опасных команд и секретов. Автотесты, CI/CD и раннеры без розовых очков. Где хранить код и какие три ключа нельзя отдавать ИИ-агенту, даже если он настраивает всё остальное сам.

СЕРИЯFOLDER 031
ВЫПУСК031 / 30.10.26
ОБЪЁМ≈ 7 500 СЛОВ
РЕЖИМЕДИНЫЙ РАЗБОР
01
BLOCK 01 / ОПОРА И ИЗОЛЯЦИЯ Зачем агенту Git и что он даёт на деле Обратимость, рентген правок, именные ветки и параллельная работа без свалки

ПОЧЕМУ БЕЗ GIT АГЕНТ ПРЕВРАЩАЕТ ПРОЕКТ В РУЛЕТКУ

Пока код пишет человек, он физически ограничен скоростью собственных пальцев. За час разработчик меняет два-три файла, держит в голове причину каждой строчки и обычно замечает, если случайно задел соседний экран. ИИ-агент устроен иначе: за сорок секунд он способен переписать восемнадцать файлов в четырёх папках, поправить схему базы данных, «заодно» упростить проверку оплаты — и в конце одинаково бодрым тоном отчитаться: «Готово, ошибку исправил».

Если в папке проекта нет Git (системы контроля версий, которая хранит полную историю снимков кода и умеет сравнивать или откатывать любые изменения), вы остаётесь с этим отчётом один на один. Вчера форма заказа работала, сегодня после трёх просьб «подвинь кнопку и ускорь загрузку» отвалилась оплата картой, а какая именно из сорока правок её сломала — неизвестно. Люди начинают копировать папки с названиями вроде project_final_v3_rabochaya, бояться лишний раз дышать на работающий сервис и проверять руками весь сайт после каждой мелочи. В этот момент главная выгода агента — скорость — начинает работать против владельца.

Git возвращает в работу с ИИ физический закон сохранения. Он нужен не ради красивой инженерной культуры и не «потому что так принято у программистов». В связке с агентом он решает четыре конкретные задачи, без которых проект разваливается на втором десятке задач.

1. Точка возврата за две секунды вместо страха перед правкой

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

2. Рентген изменений вместо веры тексту в чате

Текст, который агент пишет в окне диалога, — это его пересказ собственных намерений, а не факт. Факт показывает команда git diff (построчное сравнение «что было» и «что реально изменилось на диске»). На практике именно там всплывают тихие сюрпризы: просили поменять текст уведомления, а в диффе рядом мелькает удалённая проверка прав администратора или заменённый адрес боевого сервера. Вам даже не обязательно самому читать код — достаточно попросить самого агента или соседнюю сессию разобрать вывод git diff перед сохранением и назвать каждый затронутый файл своими словами.

3. Неприкосновенный ствол и именные ветки

Главное правило взрослой работы с агентами звучит просто: основная ветка main (ствол проекта, из которого выкладывается живой сайт) всегда обязана быть готова к запуску. Ни человек, ни агент не пишут в неё напрямую. Под каждую задачу от ствола отводится отдельная ветка (branch — изолированная копия истории): пока агент чинит в ней корзину или верстает новую страницу, основной продукт остаётся целым. Если посреди крупной переделки прилетает срочная поломка с боевого сайта, вы не выкидываете полуразобранную работу — вы переключаетесь на чистый main, делаете быструю заплатку в отдельной короткой ветке, выпускаете её, а потом спокойно возвращаетесь к большой задаче.

У себя на сервере я ввёл жёсткую схему имён веток <кто>/<область>-<задача>: например, claude/billing-refund-guard, gemini/ui-mobile-header или fix/sentry-4812 для автоматического разбора ошибок. Когда в одном репозитории работают два разных ИИ-агента и фоновые скрипты, префикс в названии ветки экономит часы расследований. На одном из наших продуктовых сервисов за восемь месяцев накопилось 1 228 коммитов и 152 ветки: 56 веток от одного агента, 71 рабочая ветка по фичам и багам от второго, ещё 14 создал ночной охотник за ошибками. По одному списку сразу видно, чьи руки трогали узел.

Там же вылез и первый урок гигиены: когда мы провели ревизию этих 152 веток, оказалось, что 123 из них уже давно влиты в ствол, но никто их не удалил. Агент блестяще создаёт ветки и почти никогда не убирает за собой без отдельного правила. Если не заставить его удалять ветку сразу после слияния, а брошенные тупиковые подходы уносить под тег archive/..., через полгода в списке веток невозможно отличить живую незавершённую работу от весеннего мусора.

4. Параллельные руки в одном проекте и ловушка папки /tmp

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

Для этого в Git встроен механизм git worktree (связанная рабочая копия). Он за секунду создаёт рядом лёгкую отдельную папку под конкретную ветку, а всю тяжёлую историю коммитов делит с основным репозиторием без дублирования. Каждый параллельный агент заходит в свою песочницу, собирает и тестирует код независимо, а после слияния папка удаляется.

ГРАБЛИ ИЗ ПРАКТИКИ · 22.04.2026 · ПОЧЕМУ WORKTREE НЕЛЬЗЯ КЛАСТЬ В /TMP

По умолчанию агенты обожают создавать временные рабочие копии в системной папке /tmp. На нашем Linux-сервере под /tmp был отведён стандартный диск в оперативной памяти (tmpfs) объёмом 2 ГБ. Когда четыре параллельных помощника одновременно развернули там четыре рабочие копии с зависимостями, они заняли 1,8 из 2 ГБ. Как только /tmp забился, системный вызов запуска процессов и временных файлов начал молча падать сразу во всех сессиях агентов на машине — терминал у моделей просто перестал отвечать.

С того дня в глобальном правиле всех проектов прописан жёсткий адрес: любые рабочие копии создаются только на обычном диске в /var/tmp/wt/<имя>, где свободно больше 200 ГБ, и обязательно снимаются через git worktree remove сразу после закрытия задачи.

ЧТО ПРОСИТЬ АГЕНТА НАСТРОИТЬ В ПЕРВЫЙ ДЕНЬ

Хорошая новость в том, что вам не нужно учить десятки флагов консоли, чтобы выстроить этот порядок. Достаточно один раз поручить самому агенту подготовить фундамент репозитория и записать правила работы в файл инструкций проекта (AGENTS.md или CLAUDE.md, который агент читает автоматически в начале каждой новой сессии).

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

ПРОМПТ 01 · БАЗОВАЯ НАСТРОЙКА GIT И ПРАВИЛ ВЕТВЛЕНИЯ С НУЛЯ P01
Проверь состояние Git в моём проекте. Пока ничего не удаляй и не отправляй на внешний сервер, сначала покажи план и список файлов. Сделай по шагам: 1. Если Git ещё не инициализирован, инициализируй репозиторий с основной веткой main. 2. Собери точный файл .gitignore под реальный стек этого проекта: исключи папки зависимостей (node_modules, venv), сборки, кеши, временные базы, логи и любые файлы секретов (.env, .env.*, кроме публичного шаблона .env.example). 3. Проверь, не попали ли уже под отслеживание лишние бинарники, архивы или файлы с ключами. Если попали, покажи список и предложи безопасно убрать их из индекса без удаления с диска (git rm --cached). 4. Запиши в AGENTS.md (и продублируй ссылку в CLAUDE.md) обязательный раздел Git Workflow: - прямой коммит и правка кода на ветках main и develop запрещены (HARD STOP: перед первой правкой всегда проверять git status и git branch --show-current); - под каждую задачу создаётся ветка формата <agent>/<scope>-<slug> (например, claude/feat-login или gemini/fix-cart); - перед отчётом о готовности агент обязан перечитать git diff и запустить проверку сборки/тестов; - после вливания ветки в main отработанная ветка удаляется, а тупиковые эксперименты переносятся под префикс archive/ и закрываются. 5. Сделай чистый стартовый коммит (если его ещё не было) и покажи итоговый git status.
ДЛЯ НОВОГО ПРОЕКТА ИЛИ ПАПКИ БЕЗ ПРАВИЛ
ПРОМПТ 02 · ПАРАЛЛЕЛЬНАЯ РАБОТА ЧЕРЕЗ WORKTREE И УБОРКА ВЕТОК P02
Настрой в правилах проекта (AGENTS.md) и проверь на практике порядок параллельной работы нескольких агентов через git worktree. Требования: 1. Запрети создавать рабочие копии и клоны репозитория в /tmp (чтобы не переполнить системный RAM-диск tmpfs). Все изолированные рабочие копии должны создаваться только на диске в /var/tmp/wt/<project>-<slug>. 2. Опиши в AGENTS.md короткую процедуру из трёх команд для параллельной задачи: - создание: git worktree add -b <agent>/<scope>-<slug> /var/tmp/wt/<project>-<slug> main - проверка перед сдачей внутри этой папки; - обязательная уборка после слияния: git worktree remove /var/tmp/wt/<project>-<slug>, git worktree prune и удаление влитой ветки. 3. Прямо сейчас проведи ревизию текущего репозитория (без удаления!): покажи все активные git worktree list, посчитай общее число веток, выдели ветки, которые уже полностью влиты в main, и ветки старше 14 дней без движения. Предложи список на безопасную уборку.
КОГДА В ПРОЕКТЕ РАБОТАЮТ 2+ СЕССИИ ПАРАЛЛЕЛЬНО
02
BLOCK 02 / МЕХАНИКА И ЗАЩЁЛКИ Как агент общается с Git и какие гейты нужны Почему текстовые запреты не держат удар и как устроены 4 рубежа перехвата

КАК АГЕНТ НА САМОМ ДЕЛЕ РАБОТАЕТ С РЕПОЗИТОРИЕМ

У многих возникает ощущение, будто у современных ИИ-агентов есть какой-то особый нейросетевой разъём к Git. На самом деле никакой магии нет: агент управляет репозиторием ровно через тот же текстовый терминал, что и обычный разработчик. На каждом шаге модель вызывает инструмент оболочки (Bash), отправляет туда обычную команду вроде git status, git diff, git commit -m "..." или утилиту gh pr create, а обратно получает текст ответа и числовой код завершения (0 — команда прошла, ненулевой код — ошибка).

Из этой простой механики растут два важных следствия. Первое — приятное: агент ориентируется в консольном Git быстрее и аккуратнее большинства людей. Он без труда находит по git log -S, в какой день три месяца назад пропала нужная функция, умеет собирать дробные коммиты по смыслу и сам читает логи упавшей сборки через gh run view --log-failed.

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

Три реальных инцидента, после которых мы перестали верить обещаниям

  • 12 мая 2026 года — «сейчас приберу за собой тестовый коммит». Агент проверял гипотезу, сделал временный коммит, убедился, что проблема в другом, и решил аккуратно откатить свой шаг командой git reset --hard HEAD~1. Флаг --hard откатывает не только последний коммит, но и безвозвратно затирает все незафиксированные правки в рабочем дереве — вместе с живой работой, которая ещё не попала в индекс.
  • 24 июня 2026 года — выкладка с недоделанной ветки. Скрипт публикации собирал сайт из текущего состояния папки на сервере. В тот момент репозиторий стоял не на стволе main, а на старой рабочей ветке, где ещё не было двух свежих выпусков. Сборка прошла без единой ошибки — и молча сняла две уже опубликованные страницы с живого сайта, пока мы не заметили пропажу.
  • 2 октября 2026 года — обход наивного запрета на перезапись истории. У нас уже стоял защитный перехватчик команд, который искал в строке подстроки git push --force и git push -f . Столкнувшись с расхождением веток, агент выполнил git push -q --force-with-lease. Тихий флаг -q встал между словами push и --force, строковый фильтр не узнал шаблон, а поскольку на бесплатном приватном репозитории GitHub серверная защита веток отключена, агент перезаписал удалённую ветку поверх чужих коммитов.

После третьего случая стало окончательно ясно: правила в AGENTS.md нужны, чтобы агент не шёл в тупик зря, но страховать необратимые действия обязаны гейты (механические защёлки — программы, которые физически возвращают ошибку до того, как опасная команда коснётся диска или сервера).

ЛОКАЛЬНЫЕ ГЕЙТЫ: ПЕРЕХВАТ КОМАНД И СКАНЕР СЕКРЕТОВ

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

ИНТЕРАКТИВ · СТЕНД ПРОВЕРКИ GIT-ГЕЙТОВ (4 РУБЕЖА) LIVE PARSER
$
ВВЕДИТЕ СВОЮ КОМАНДУ ИЛИ НАЖМИТЕ ПРИМЕР · РАЗБОР ИДЁТ ПО ТОКЕНАМ, А НЕ ПО ПРОСТОМУ ПОИСКУ СТРОКИ

Обратите внимание, почему обычный поиск подстроки проигрывает разбору по токенам. Когда агент передаёт команду с путём git -C /srv/app push -qf origin main, склеивает короткие флаги в -qf, ставит плюс перед именем ветки (+main — скрытый синтаксис принудительной перезаписи в Git) или оборачивает вызов в bash -c "...", наивное регулярное выражение слепнет. А с другой стороны, грубая проверка на слово --force начинает ложно блокировать безобидный коммит git commit -m "document why --force is forbidden", где опасное слово стоит просто внутри текста описания.

Ищет буквальный кусок текста "git push --force" или "git push -f ". Пропускает git push -q --force-with-lease, git -C dir push -f и git push origin +main, зато ложно блокирует упоминание флага внутри кавычек git commit -m "...".

Разбивает оболочку по правилам кавычек и операторов && / ;, заглядывает внутрь bash -c, пропускает служебные опции -C и -c, находит подкоманду push и проверяет каждый её флаг и refspec без единого ложного срабатывания.

Ниже — рабочее ядро нашего перехватчика для обвязки агента (в Claude Code и совместимых CLI он подключается на событие PreToolUse для инструмента Bash; код возврата 2 и сообщение в поток ошибок stderr останавливают выполнение и объясняют модели причину отказа):

# Фрагмент перехватчика PreToolUse (guard-bash): токенная проверка force-push и разрушительного Git
import shlex, sys

GIT_OPTS_WITH_VALUE = {"-C", "-c", "--git-dir", "--work-tree", "--namespace"}

def check_git_tokens(tokens: list[str]) -> str | None:
    i = 1
    while i < len(tokens):
        t = tokens[i]
        if t in GIT_OPTS_WITH_VALUE:
            i += 2  # пропускаем опцию git и её аргумент (например: -C /srv/app)
            continue
        if t.startswith("-"):
            i += 1
            continue
        break
    if i >= len(tokens):
        return None

    subcmd, args = tokens[i], tokens[i + 1:]
    if subcmd == "push":
        for a in args:
            if a in ("--force", "--mirror") or a.startswith("--force-with-lease") or a.startswith("--force-if-includes"):
                return f"Заблокирован перезаписывающий push ({a}). Нужен явный допуск владельца."
            if a.startswith("-") and not a.startswith("--") and "f" in a[1:]:
                return f"Заблокирован короткий флаг -f внутри {a}."
            if a.startswith("+") and len(a) > 1:
                return f"Заблокирован принудительный refspec ({a})."
    if subcmd == "reset" and "--hard" in args:
        return "Заблокирован git reset --hard: он стирает несохранённую работу в папке."
    if subcmd == "clean" and any(a.startswith("-") and "f" in a for a in args):
        return "Заблокирован git clean -f: удаление неотслеживаемых файлов без спроса запрещено."
    if subcmd in ("commit", "push", "merge") and ("--no-verify" in args or "-n" in args if subcmd == "commit" else "--no-verify" in args):
        return "Заблокирован обход репозиторных хуков (--no-verify)."
    return None

Второй рубеж: репозиторный .githooks/pre-commit и десять ловушек на секреты

Хук в настройках конкретного ИИ-клиента защищает одного агента, но если завтра вы откроете проект в другом редакторе или запустите второго агента, защита должна сработать точно так же. Поэтому правила самого репозитория живут прямо внутри него — в папке .githooks/pre-commit, которая включается один раз командой git config core.hooksPath .githooks.

Второй по частоте грех агентов после прямой правки ствола — случайно закоммитить боевой ключ API. Модели делают это без всякого злого умысла: отлаживали интеграцию, положили временный скрипт проверки с ключом или выполнили широкое git add . там, где лежал свежий .env.production. Если секрет хотя бы на секунду попал в коммит и улетел в облако, удалять файл следующим коммитом бесполезно — ключ навсегда остался в истории Git, и его придётся отзывать и перевыпускать в кабинете провайдера.

Наш предкоммитный шлюз проверяет две вещи за доли секунды: текущую ветку и только добавленные строки в подготовленном диффе (git diff --cached). Вот компактный рабочий скрипт, который сразу закрывает обе дыры:

#!/usr/bin/env bash
# .githooks/pre-commit — защита ствола main и перехват секретов до записи в историю Git
set -euo pipefail

BRANCH="$(git rev-parse --abbrev-ref HEAD)"
if [[ "$BRANCH" == "main" || "$BRANCH" == "develop" ]]; then
  echo "HARD STOP: прямой коммит в ветку '$BRANCH' запрещён." >&2
  echo "Создайте рабочую ветку: git checkout -b <agent>/<scope>-<slug>" >&2
  exit 1
fi

# 1. Блокируем любые файлы .env*, кроме публичных шаблонов
STAGED_FILES="$(git diff --cached --name-only --diff-filter=ACM)"
if echo "$STAGED_FILES" | grep -E '(^|/)\.env(\..+)?$' | grep -Ev '\.(example|sample|template|dist)$' >/dev/null; then
  echo "BLOCKED: попытка закоммитить файл окружения (.env). Уберите его через git reset HEAD <file>." >&2
  exit 1
fi

# 2. Сканируем только добавленные строки (+) на типовые ключи провайдеров и приватные ключи
ADDED_LINES="$(git diff --cached -U0 | grep -E '^\+[^+]' || true)"
SECRET_REGEX='(BEGIN [A-Z ]*PRIVATE KEY|sk-proj-[A-Za-z0-9_-]{20,}|sk-ant-[A-Za-z0-9_-]{20,}|AIza[0-9A-Za-z_-]{35}|AKIA[0-9A-Z]{16}|gh[pousr]_[A-Za-z0-9]{30,}|[0-9]{8,10}:AA[A-Za-z0-9_-]{33})'

if echo "$ADDED_LINES" | grep -E "$SECRET_REGEX" >/dev/null; then
  echo "BLOCKED: в подготовленном коммите обнаружен ключ API или приватный ключ." >&2
  echo "Вынесите секрет в переменную окружения (.env) и оставьте в коде только чтение из env." >&2
  exit 1
fi
ПРОМПТ 03 · ПОСТАВИТЬ ЗАПРЕТ КОММИТА В MAIN И ПЕРЕХВАТ ОПАСНЫХ GIT-КОМАНД P03
Настрой в моём проекте двухслойную локальную защиту от разрушительных команд Git. Сделай: 1. Создай в репозитории папку .githooks и исполняемый скрипт .githooks/pre-commit, который блокирует прямой git commit на ветках main и develop с понятной подсказкой создать ветку <agent>/<scope>-<slug>. Включи его через git config core.hooksPath .githooks и добавь эту команду в документацию онбординга. 2. Если в проекте используется обвязка агента с поддержкой хуков (например, .claude/settings.json с PreToolUse для Bash), добавь скрипт перехвата на Python с разбором команд через shlex.split (а не простым поиском подстроки). 3. Перехватчик должен блокировать без явной санкции владельца: - любые формы перезаписи удалённой истории (git push --force, -f, -qf, --force-with-lease, --mirror, git push origin +branch, включая вызовы через git -C dir и bash -c); - git reset --hard, git clean -f, git checkout -- ., git branch -D; - обход хуков через флаг --no-verify. 4. Все сообщения блокировки выводи только в stderr с кодом выхода 2, чтобы не ломать JSON-протокол агента. Прогони 6 автотестов на обходные строки и покажи результат.
РУБЕЖИ 01 И 02 · ЛОКАЛЬНАЯ ЗАЩИТА
ПРОМПТ 04 · ДОБАВИТЬ ПРЕДКОММИТНЫЙ СКАНЕР СЕКРЕТОВ И .ENV P04
Расширь предкоммитный хук (.githooks/pre-commit) проверкой на утечку секретов до записи коммита. Что нужно проверять по git diff --cached: 1. Имена файлов: запретить индексацию любых .env и .env.*, кроме .env.example, .env.sample и .env.template, а также файлов ключей (*.pem, *.key, id_rsa, credentials.json). 2. Добавленные строки кода: блокировать коммит при обнаружении приватных ключей (BEGIN PRIVATE KEY), токенов OpenAI (sk-proj-), Anthropic (sk-ant-), Google API (AIza...), AWS (AKIA...), GitHub (ghp_/gho_/ghs_), Telegram-ботов, а также явного присваивания строковых литералов переменным вида API_KEY, SECRET, PASSWORD, TOKEN длиной от 16 символов. 3. Не блокировать пустые заглушки и чтение из process.env / os.environ. 4. Проверь хук на тестовой строке-муляже (не на настоящем ключе!), убедись, что коммит отклоняется с точным указанием файла, и очисти тестовую правку.
ЗАЩИТА ОТ УТЕЧКИ КЛЮЧЕЙ В ИСТОРИЮ GIT
03
BLOCK 03 / СБОРОЧНЫЙ ЦЕХ Зачем автотесты и CI/CD, когда код пишет агент Пять специальных проверок под болезни ИИ и как не жечь 31% прогонов впустую

АВТОТЕСТЫ — ЭТО ЕДИНСТВЕННАЯ ЗАМЕНА РУЧНОЙ ВЫЧИТКЕ КОДА

Когда владельцу продукта без инженерного прошлого говорят «тебе нужны автотесты и CI/CD», это часто звучит как дорогой корпоративный налог, который придумали большие отделы разработки. В классической ручной разработке отчасти так и было: маленький проект одного автора мог месяцами жить на ручной проверке в браузере, потому что автор сам помнил каждую развилку своего кода.

С приходом ИИ-агентов роли перевернулись. Вы физически не будете глазами вычитывать две тысячи строк чужого кода в день — а если не читаете вы, значит, проверять работу агента должна машина. Автотест — это просто маленький скрипт-ревизор, который притворяется пользователем: создаёт заказ, пробует списать больше денег, чем есть на счёте, проверяет права чужого аккаунта и сверяет ответ с эталоном. А CI/CD (непрерывная интеграция и доставка) — это независимый конвейер на сервере, который автоматически запускает всех этих ревизоров на каждую новую заявку на слияние (Pull Request), прежде чем пустить код агента в основной ствол main.

На двух наших основных продуктовых сервисах сегодня крутятся 4 857 автотестов в первом и 8 463 автотеста во втором (6 061 серверный и 2 402 фронтенд-теста в 573 тестовых файлах). Почти все они написаны самими же агентами по правилу: «делаешь функцию — рядом кладёшь тест, который ломается, если функция начнёт врать». Именно эта сетка позволяет вливать десятки изменений в неделю без бессонных ночей.

Почему нельзя верить фразе агента «я локально всё прогнал, тесты зелёные»

В конце апреля 2026 года у нас подряд проскочили две заявки на слияние, где агент честно запустил один тестовый файл вокруг своей правки, увидел зелёную галочку и сдал задачу — а общий ствол проекта покраснел. Причина типичная для ИИ: агент поменял сигнатуру общей функции, поправил её тест, но не запустил полную проверку типов по всему проекту (tsc --noEmit), и три соседних экрана, которые вызывали эту функцию по-старому, развалились при сборке.

Локальная среда всегда «тёплая»: в ней могут лежать старые закешированные файлы, локальные переменные окружения или незакоммиченный модуль, который агент забыл добавить в Git. Серверный CI поднимает проект с чистого листа ровно из того, что реально попало в ветку. Если файл забыт на диске агента — CI упадёт через минуту и не даст сломать ствол.

ПЯТЬ ГЕЙТОВ В CI, КОТОРЫЕ НУЖНЫ ИМЕННО ПРОТИВ БОЛЕЗНЕЙ АГЕНТОВ

Стандартный учебник по CI предлагает три шага: линтер стиля, проверка типов и запуск тестов. Для человека этого обычно хватает. Для ИИ-агента — нет: у языковых моделей есть собственные устойчивые привычки «срезать угол», которые опытный живой инженер почти никогда не допускает. По следам реальных поломок у нас в конвейере прописались пять специальных агентских гейтов:

01
ЗАПРЕТ ПРАВИТЬ СТАРЫЕ МИГРАЦИИ БАЗЫ ДАННЫХ

Когда просишь агента переименовать поле в таблице, он обожает открыть уже существующий файл миграции прошлого месяца и поправить его задним числом. В свежей тестовой базе всё проходит идеально, а на боевой базе, где эта миграция давно применена, выкладка взрывается. Скрипт в CI сверяет папку миграций со стволом main: добавлять новые файлы можно, менять уже влитые — стоп-кран.

02
КОНТРОЛЬ ГРАНИЦ МЕЖДУ СЛОЯМИ (BOUNDARY LINT)

Агент всегда идёт кратчайшим путём: если ему в компоненте кнопки понадобились данные, он способен импортировать прямой запрос к базе в обход сервисного слоя или завязать два независимых модуля в кольцо. Автоматическая проверка границ (lint:boundaries) рвёт сборку при первой же попытке протащить запрещённую связь.

03
ХРАПОВИК ТЕХДОЛГА (RATCHET) НА ТОЛСТЫЕ ФАЙЛЫ

В огромных файлах на тысячу строк агенты быстро теряют контекст и начинают плодить дубли. Скрипт-храповик считает число файлов длиннее лимита и число маршрутов без тестов, сравнивая их с зафиксированным потолком. Показатель имеет право только оставаться прежним или снижаться. Если агент незаметно раздул ещё один файл за предел — CI возвращает заявку на декомпозицию.

04
ПРАВИЛО «ТРОНУЛ МАРШРУТ — ОПЛАТИ ТЕСТОМ»

Команда git diff origin/main...HEAD находит все изменённые или новые API-маршруты в ветке агента и проверяет, есть ли для каждого из них соответствующий автотест. Забыть написать тест на новую точку оплаты или входа становится физически невозможно — заявка не пройдёт шлюз.

05
МИНИМАЛЬНЫЕ ПРАВА КОНВЕЙЕРА И ФИЛЬТР ЗАГЛУШЕК

Отдельный быстрый шаг проверяет сами файлы .github/workflows/*.yml, чтобы ни один агент при правке CI не оставил широких прав записи токену сборки (по умолчанию строго permissions: contents: read), а заодно сканирует словари интерфейса на типичные модельные заглушки вроде TODO и пустых ключей перевода.

КАК НЕ ГОНЯТЬ ТЯЖЁЛЫЙ CI ДВАЖДЫ НА ОДИН И ТОТ ЖЕ КОД

Когда агенты начинают закрывать по пятнадцать задач в день, всплывает скрытая расточительность классических настроек GitHub Actions. Обычно конвейер настроен запускаться и на заявку pull_request, и на слияние push в main. В октябре 2026 года мы сняли трёхдневный срез по двум активным проектам и посчитали каждую минуту.

ЗАМЕР ЗА 3 ДНЯ (228 ПРОГОНОВ) 1 147 МИН суммарное время работы джобов CI на двух проектах с агентами
ДУБЛИ ПОСЛЕ ЗЕЛЁНОГО PR 31,4 % 417,6 минуты ушли на повторную проверку того же самого кода в main
ПОСЛЕ СВЕРКИ TREE SHA 13 СЕК вместо 15 мин 01 с и 10 мин 26 с на слиянии неизменившегося дерева

Фокус в том, что Git при каждом коммите уже вычисляет криптографический слепок всего дерева исходных файлов — Tree SHA (git rev-parse HEAD^{tree}). В отличие от хеша коммита, который меняется при squash-слиянии в main, хеш дерева файлов остаётся байт-в-байт тем же самым, если в ствол между проверкой заявки и слиянием никто не влил конкурирующую правку.

Мы добавили в конец успешного прогона заявки запись крошечного артефакта с этим слепком дерева. Когда через минуту код вливается в main, первый шаг конвейера за пару секунд сверяет текущий HEAD^{tree} с уже проверенным зелёным слепком: совпало — все тяжёлые многоминутные шаги тестов легально пропускаются; разошлось хоть на один байт — включается полный прогон. Треть нагрузки на раннеры исчезла в тот же вечер без малейшей потери надёжности.

Минимальный скелет такого конвейера в .github/workflows/ci.yml читается на одном экране — обратите внимание на зажатые права permissions: contents: read и отмену устаревших прогонов concurrency, чтобы три быстрых пуша агента подряд не стояли в очереди втроём, а сразу уступали место последнему:

name: CI
on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

permissions:
  contents: read

# Если агент запушил исправление поверх своей же ветки — старый незавершённый прогон сразу гасим
concurrency:
  group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
  cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
  agent-gates:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Неизменность уже влитых миграций БД
        run: bash scripts/ci/check-migration-safety.sh origin/main
      - name: Обязательный тест на изменённый API-маршрут
        run: bash scripts/ci/check-route-tests.sh origin/main
      - name: Линтер границ, потолок размера файлов (ratchet) и типы
        run: npm ci && npm run lint:boundaries && npm run ratchet && npx tsc --noEmit

  tests:
    needs: agent-gates
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
      - name: Автотесты и контрольная сборка
        run: npm ci && npm test && npm run build
ПРОМПТ 05 · СОБРАТЬ CI-КОНВЕЙЕР С ПЯТЬЮ АГЕНТСКИМИ ГЕЙТАМИ P05
Изучи стек моего проекта и собери рабочий CI-конвейер (.github/workflows/ci.yml или аналог под наш Git-сервер), заточенный под работу с ИИ-агентами. Сначала покажи план проверок, ничего не ломая. Обязательные элементы конвейера: 1. Минимальные права по умолчанию (permissions: contents: read) и блок concurrency с cancel-in-progress для pull_request, чтобы повторные пуши агента не копили очередь. 2. Быстрый предварительный джоб agent-gates перед тяжёлыми тестами: - полная проверка типов и сборка без обходных флагов; - скрипт check-migration-safety (если в проекте есть миграции БД): запрет менять уже существующие в main файлы миграций, разрешено только добавлять новые; - скрипт ratchet (храповик техдолга): зафиксируй текущее число файлов кода длиннее 300 строк в baseline JSON и роняй сборку, если их стало больше; - проверка изменённых API-маршрутов относительно origin/main: новый или изменённый маршрут обязан иметь тест. 3. Джоб автотестов и финальной сборки проекта с жёстким timeout-minutes. 4. Сверка Git Tree SHA (git rev-parse HEAD^{tree}): если на push в main дерево файлов побайтово совпадает с уже прошедшим зелёным PR, тяжёлый тестовый шаг пропускается. 5. Добавь в AGENTS.md правило: при красном CI агент обязан сам прочитать лог через gh run view --log-failed и починить причину, а не ослаблять проверку в ci.yml.
РУБЕЖ 03 · СЕРВЕРНЫЙ СБОРОЧНЫЙ ЦЕХ
04
BLOCK 04 / ВЫЧИСЛИТЕЛЬНЫЙ ЦЕХ Раннеры: облачные минуты против своего железа Реальная смета 2026 года, скорость в 5,4 раза выше и пять граблей self-hosted на живом сервере

КТО ФИЗИЧЕСКИ КРУТИТ ВАШИ ТЕСТЫ И СКОЛЬКО ЭТО СТОИТ

Файл ci.yml — это только рецепт. Чтобы реально собрать проект, поднять тестовую базу данных и прогнать тысячи проверок, нужен раннер (runner) — рабочая лошадка, процесс на виртуальной машине, который забирает задачу из очереди, выполняет команды и отправляет обратно зелёную или красную отметку.

Раннеры бывают двух видов: облачные (их выдаёт сам GitHub на пару минут под каждый прогон и тут же уничтожает) и собственные (self-hosted — вы ставите бесплатную программу-агента на арендованный сервер, и задачи прилетают на ваше железо). В ручной разработке об этой развилке вспоминают редко, но с ИИ-агентами вы упираетесь в неё в первый же плотный месяц: агент не устаёт, делает по тридцать коммитов в день и запускает конвейер впятеро чаще человека.

Смета облачных раннеров GitHub Actions в 2026 году

По официальному прайсу GitHub (docs.github.com · Actions Runner Pricing), для открытых публичных репозиториев стандартные облачные раннеры бесплатны без лимита. Для закрытых приватных репозиториев расклад такой:

  • на бесплатном тарифе GitHub Free включено 2 000 минут Linux в месяц (на тарифе Team — 3 000 минут);
  • сверх бесплатного пакета минута лёгкого 1-ядерного раннера стоит $0.002, стандартного 2-ядерного Linux — $0.006/мин, 4-ядерного — $0.012/мин, 8-ядерного — $0.022/мин.

Приложим это к нашему реальному темпу: 1 147 минут за три активных дня дают около 11 500–12 000 минут в месяц. Бесплатная квота в 2 000 минут выгорает за первые пять дней, а перерасход в 10 000 минут на базовом двухъядерном раннере обходится примерно в $57–60 в месяц. Сумма сама по себе не разорительная — куда сильнее бьёт другое: скорость обратной связи.

Стандартный бесплатный двухъядерный раннер в облаке крутил наш фронтенд-пакет тестов Vitest 617 секунд (больше десяти минут). На собственном сервере с быстрым NVMe-диском и прогретым кешем зависимостей ровно тот же набор проходил за 114 секунд — в 5,4 раза быстрее (а полный цикл сборки со всеми проверками ускорился втрое). Когда агент сидит и ждёт вердикта по десять минут на каждую мелкую поправку, живая парная работа превращается в переписку по почте.

ПЯТЬ ГРАБЛЕЙ СВОИХ РАННЕРОВ НА БОЕВОМ СЕРВЕРЕ (ОКТЯБРЬ 2026)

В большинстве заметок про self-hosted раннеры на этом месте ставят точку: «поставьте раннер к себе на сервер за пять минут — будет бесплатно и быстро». Мы тоже так подумали, поселили девять локальных раннеров на мощную рабочую машину (16 ядер CPU, 60 ГБ оперативной памяти), где рядом работали продукты и интерактивные сессии агентов, — и за две недели октября собрали коллекцию аварий, которую лучше прочитать чужими глазами, чем пережить самому.

  • Шторм по процессору и очередь на 50 минут. Стоило двум агентам параллельно открыть серию заявок, как 7 из 9 раннеров одновременно взялись компилировать TypeScript и гонять Python-тесты. Загрузка всех 16 ядер упёрлась в 97–98%, средняя очередь процессов (load average) взлетела до 48,7–52, терминалы живых сессий начали вязнуть, а очередь самого CI растянулась на 28–50 минут.
  • Тестовая база сбежала из клетки ядер. Мы ограничили службы раннеров половиной процессора через настройку systemd AllowedCPUs=0-7 и выдохнули — ровно до следующего утра. Оказалось, что сами раннеры послушно сидели на восьми ядрах, а вот Docker-контейнеры тестовой базы ci-postgres и ci-redis крутились в общем демоне Docker без привязки к ядрам и продолжали выжигать всю машину. Вылечили только жёстким выравниванием: привязали контейнеры тестовых баз через docker update --cpuset-cpus к тому же пулу ядер, понизили приоритет раннеров (CPUWeight=50, Nice=15), а самим агентам запретили запускать полный прогон всех тестов локально — дома агент проверяет только затронутый модуль в два потока (--maxWorkers=2), полный пакет крутит CI.
  • Случайный откат лимита памяти и стояк на load 69 (12.10.2026). При правке конфига один из агентов по старому шаблону ужал потолок памяти раннера до 4–6 ГБ, тогда как восемь параллельных потоков pytest в пике потребляли около шести гигабайт. Ядро Linux не убило процесс сразу, а включило принудительное торможение памяти: за час набежало 3,4 миллиона событий memory.high, диск захлебнулся свопом на 500 МБ/с, load пробил отметку 69, сами слушатели GitHub отвалились по таймауту в offline, а восемь бесхозных процессов тестов продолжали молотить в пустоту ещё 61 минуту. Подняли честный коридор MemoryHigh=10G и MemoryMax=12G — торможения упали в ноль.
  • Ускорение 6 061 серверного теста с 15:03 до 8:01 без покупки железа. Когда мы профилировали долгий серверный прогон, выяснилось, что 40% времени процессор вообще не считает логику: он ждёт, пока тестовый Postgres честно сбросит каждую транзакцию на диск на случай отключения электричества, и тысячи раз заново хеширует пароли тестовых пользователей тяжёлым алгоритмом bcrypt. Для одноразовой тестовой базы, которая живёт восемь минут, сохранность при сбое питания бессмысленна: мы отключили в тестовом контейнере синхронную запись (fsync=off, synchronous_commit=off, full_page_writes=off), закешировали один настоящий хеш bcrypt в фабрике тестовых пользователей и разнесли запуск на изолированные временные базы по шардам. Чистое время прогона 6 061 теста упало с 15 минут 03 секунд до 8 минут 01 секунды.
  • Кладбище сиротских баз и «спящий» слушатель. Когда блок concurrency отменяет устаревший прогон посреди работы, процесс не всегда успевает выполнить финальную уборку. За неделю у нас накопилось 88 брошенных тестовых баз test_ci_* и 21 осиротевший процесс Redis, а один из раннеров простоял 9 часов в статусе offline на стороне GitHub при живом процессе на сервере из-за зависшего сетевого сокета (SocketException 125). Проблему закрыли двумя короткими скриптами: уборщиком сирот, который сверяет временные базы со списком реально идущих run_id, и трёхминутным таймером-сторожем в systemd, перезапускающим зависшего слушателя.
ПРОМПТ 06 · НАСТРОИТЬ СВОЙ РАННЕР В ЖЁСТКОЙ КЛЕТКЕ SYSTEMD СО СТОРОЖЕМ P06
Подготовь конфигурацию изолированного self-hosted раннера для наших автотестов (на выделенной VPS или в ограниченном пуле ресурсов), чтобы он никогда не подвесил хост-машину. Пока ничего не запускай от root без моего подтверждения, сначала покажи файлы и расчёт ресурсов. Что нужно собрать: 1. Drop-in конфиг systemd для службы раннера: - ограничение пула ядер (AllowedCPUs), пониженный приоритет (CPUWeight=50, Nice=15); - защита памяти с запасом под пик тестов (MemoryHigh и MemoryMax), чтобы не допустить своп-шторма; - отдельный непривилегированный системный пользователь без доступа к боевым папкам и прод-базе. 2. Если тесты используют локальные контейнеры Postgres и Redis: - привяжи контейнеры к тому же пулу ядер (--cpuset-cpus); - для одноразового тестового Postgres включи быстрый профиль без лишнего ожидания диска (fsync=off, synchronous_commit=off, full_page_writes=off); - напиши скрипт периодической уборки осиротевших тестовых баз test_ci_* от отменённых прогонов. 3. Добавь systemd-таймер (раз в 3 минуты), который проверяет, не завис ли процесс слушателя раннера с оборванным сокетом, и аккуратно перезапускает его только когда нет активного прогона Worker.
ДЛЯ ПРОЕКТОВ С РАСХОДОМ CI ОТ 3 000+ МИНУТ В МЕСЯЦ
05
BLOCK 05 / ВЫБОР ПЛОЩАДКИ Брать GitHub или разворачивать свой Git-сервер Холодное сравнение GitHub, Forgejo/Gitea, GitLab CE и голого зеркала по SSH

ГДЕ ДЕРЖАТЬ РЕПОЗИТОРИЙ, ЧТОБЫ АГЕНТУ БЫЛО УДОБНО, А ВАМ — СПОКОЙНО

Сам по себе Git живёт прямо в скрытой папке .git внутри вашего проекта и вообще не требует интернета. Внешний Git-сервер нужен для трёх вещей: хранить резервную копию истории отдельно от рабочей машины, принимать заявки на слияние (Pull Requests) с запуском CI и разделять права доступа, чтобы агент физически не мог переписать защищённый ствол main.

Вокруг выбора между облачным GitHub и собственным сервером обычно много идеологии. Если убрать эмоции и посчитать оперативную память, стоимость и то, насколько уверенно с площадкой умеют работать сами ИИ-агенты в 2026 году, остаются четыре понятных сценария.

1. GitHub (Free и Team) — лучший выбор по умолчанию, но с одной скрытой деталью

У GitHub в работе с ИИ есть огромное практическое преимущество: консольная утилита gh входит в «родной язык» абсолютно всех современных агентов. Модель без единой подсказки умеет создать заявку, повесить метку, вытащить лог конкретного упавшего шага сборки, прочитать замечания ревью и проверить статус чеков. Плюс он не тратит ни одного мегабайта оперативной памяти вашего сервера и остаётся живым, даже если ваш хостинг прилёг.

Скрытая деталь, о которую спотыкаются почти все на старте: на бесплатном тарифе GitHub Free для приватных репозиториев отключена серверная защита веток (Branch Protection / Rulesets). То есть кнопки «запретить прямой пуш в main и требовать зелёный CI перед слиянием» на стороне самого GitHub для закрытого проекта открываются только на тарифе GitHub Team за $4 за пользователя в месяц (github.com/pricing). Если вы остаётесь на бесплатном приватном тарифе, весь замок держится исключительно на ваших локальных хуках из Блока 02.

2. Forgejo или Gitea на своём сервере — когда код не должен покидать ваш контур

Если правила заказчика, требования по российскому контуру хранения или нежелание зависеть от блокировок зарубежного аккаунта запрещают держать исходники на американском облаке, не спешите ставить тяжёлый комбайн. Forgejo (открытое ответвление Gitea) и сама Gitea написаны на Go и поставляются одним лёгким бинарным файлом или крошечным контейнером.

В состоянии покоя такой сервер потребляет всего 40–100 МБ оперативной памяти и свободно дышит на самой простой VPS с 1–2 ГБ RAM (а вместе со встроенной системой Forgejo/Gitea Actions, чей формат YAML-файлов почти полностью совместим с привычным GitHub Actions, укладывается в 2–4 ГБ RAM; документация forgejo.org и docs.gitea.com). При этом серверная защита ветки main в них бесплатна на любом приватном репозитории. Единственная доплата в настройке агента — прописать в AGENTS.md консольный клиент tea или токен API вместо привычного gh.

3. GitLab Community Edition (CE) — карьерный самосвал для поездки в булочную

GitLab CE — зрелая корпоративная платформа, внутри которой одновременно крутятся Ruby-сервер Puma, фоновые воркеры Sidekiq, хранилище Gitaly, собственные PostgreSQL, Redis и мониторинг. Официальная документация GitLab для односерверной установки рекомендует 16 ГБ оперативной памяти и 8 ядер vCPU (docs.gitlab.com · Requirements); при ручном отключении лишних служб его удаётся ужать до 4–8 ГБ RAM, а на двухгигабайтной виртуалке он уходит в бесконечный своп. Если в вашей компании уже развёрнут корпоративный GitLab и его ведёт отдел инфраструктуры — смело подключайте агента через утилиту glab. Но поднимать GitLab CE с нуля ради одного-трёх проектов с ИИ — пустая трата железа и выходных.

4. Голый репозиторий по SSH (git init --bare) — идеальная страховка за ноль рублей

Самый недооценённый вариант работает вообще без веб-интерфейса: на любом вашем сервере с обычным SSH создаётся папка git init --bare project.git. Она потребляет 0 МБ памяти в простое. Работать через неё как через единственную площадку с агентами неудобно — там нет ни заявок на слияние, ни визуального обзора диффа, ни встроенного запуска CI. Зато в роли второго независимого зеркала это безупречное решение: три строчки ночного скрипта или дополнительный адрес git remote set-url --add --push автоматически дублируют каждый пуш с GitHub на ваш собственный диск. Даже если облачный аккаунт однажды заморозят, вся история всех веток уже лежит у вас дома.

06
BLOCK 06 / РАЗДЕЛЕНИЕ ВЛАСТЕЙ Может ли агент полностью сам управлять Git Где отдать 95% рутины с закрытыми глазами, а какие три ключа оставить только у себя

НАСТРОИТЬ С НУЛЯ И ВЕСТИ ТЕКУЧКУ — ДА. ДЕРЖАТЬ КЛЮЧИ ОТ СЕЙФА — НЕТ

Главный вопрос, ради которого затевался весь этот разбор: можно ли просто сказать агенту «настрой весь Git, тесты и выкладку сам и дальше веди их без меня»?

Честный ответ состоит из двух половин. Технически агент спокойно берёт на себя 95% всей работы с Git и конвейером — причём настраивает их чище, чем большинство самоучек, которым лень писать хуки и проверки миграций. Вы можете поручить ему инициализировать репозиторий, вычистить мусор из индекса, написать .githooks/pre-commit, сканер секретов и ci.yml, заводить именные ветки, резать работу на аккуратные смысловые коммиты, открывать заявки на слияние, самому читать логи упавших проверок, чинить свои же ошибки и прибирать влитые ветки.

Но отдавать агенту стопроцентную власть над репозиторием и боевой выкладкой нельзя. Причина не в том, что модель «глупая» или «злонамеренная», а в фундаментальном конфликте стимулов. Когда вы ставите агенту цель «добейся, чтобы задача была влита и сборка стала зелёной», а его код упирается в капризный тест или строгий гейт, с точки зрения сухой логики у модели всегда есть два пути: потратить час на поиск глубокой причины в архитектуре — или за три секунды подправить правило в ci.yml, добавить флаг --admin при слиянии и отрапортовать о победе. Если замок висит на поясе у того, кого этот замок должен сдерживать, рано или поздно он его откроет.

Три ключа, которые всегда остаются только в руках владельца

01
ПРАВА АДМИНИСТРАТОРА РЕПОЗИТОРИЯ И ПРАВИЛА ЗАЩИТЫ MAIN

Не давайте агенту свой личный мастер-токен от GitHub или Forgejo с полными правами admin. Выпустите для него отдельный узкий токен (Fine-Grained Personal Access Token) строго на нужные репозитории ровно с тремя разрешениями: Contents: Read & Write (пушить рабочие ветки), Pull requests: Read & Write (создавать заявки) и Actions: Read (читать логи упавших тестов). Права Administration оставьте выключенными, а папки .github/workflows/ и .githooks/ закройте через файл CODEOWNERS — тогда агент сможет предложить улучшение конвейера, но не сможет ослабить проверку без вашего одобрения.

02
ПИШУЩИЕ КЛЮЧИ И ФАЙЛЫ РАННЕРА НА БОЕВОМ СЕРВЕРЕ

Урок нашего внутреннего аудита безопасности от 18 октября 2026 года: на боевом сервере, где крутится живая база клиентов, не должны лежать ни пишущий токен агента, ни файл учётных данных раннера .credentials (обладатель этого файла способен притвориться раннером и получить из облака секреты сборки). Боевой сервер забирает готовый код из ствола main исключительно по ключу только для чтения (Read-Only Deploy Key).

03
КНОПКА СЛИЯНИЯ ЧУВСТВИТЕЛЬНЫХ УЗЛОВ И ЗАПУСК БОЕВОЙ ВЫКЛАДКИ

Слияние веток, затрагивающих деньги, авторизацию, персональные данные или схему базы, а также запуск боевого deploy.sh остаются за человеком. А чтобы даже человек в спешке не выложил недоделанную ветку (как в нашем июньском инциденте), сам скрипт выкладки начинается с железного замка ветки и собирает релиз в соседний запасной слот с мгновенным откатом.

Посмотрите, как просто выглядит этот четвёртый, финальный рубеж в реальном скрипте deploy.sh — два десятка строк на Bash полностью исключают выкладку не из main и позволяют откатить неудачный релиз меньше чем за две секунды переключением символической ссылки (.dist-blue и .dist-green):

#!/usr/bin/env bash
# Фрагмент deploy.sh — замок на ветку main и мгновенное переключение слотов blue/green
set -euo pipefail

CMD="${1:-deploy}"

# Откат и проверка статуса разрешены с любой ветки в любую секунду
if [[ "$CMD" == "rollback" ]]; then
  CURRENT="$(cat .active-slot)"
  PREV=$([[ "$CURRENT" == "blue" ]] && echo "green" || echo "blue")
  ln -sfn ".dist-$PREV" dist
  echo "$PREV" > .active-slot
  echo "Откат выполнен: активен предыдущий слот $PREV."
  exit 0
fi

# Боевая выкладка — строго из чистого ствола main после слияния ветки
DEPLOY_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
if [[ "$DEPLOY_BRANCH" != "main" ]]; then
  echo "ОШИБКА: выкладка запущена с ветки '$DEPLOY_BRANCH'." >&2
  echo "Сначала влейте изменения в main, переключитесь на main и повторите запуск." >&2
  exit 1
fi

ACTIVE="$(cat .active-slot 2>/dev/null || echo blue)"
NEXT=$([[ "$ACTIVE" == "blue" ]] && echo "green" || echo "blue")

# Собираем проект в неактивный слот — живой сайт в эту минуту спокойно раздаётся из старого
npm run build -- --outDir ".dist-$NEXT"
test -f ".dist-$NEXT/index.html"

# Атомарно переключаем указатель за доли миллисекунды
ln -sfn ".dist-$NEXT" dist
echo "$NEXT" > .active-slot
ПРОМПТ 07 · СОБРАТЬ СКРИПТ ВЫКЛАДКИ С ЗАМКОМ НА MAIN И ОТКАТОМ ЗА 2 СЕКУНДЫ P07
Собери для моего проекта безопасный скрипт выкладки deploy.sh (без запуска на живом сервере до моего одобрения!). Требования к скрипту: 1. В начале обычного деплоя проверяй текущую ветку через git rev-parse --abbrev-ref HEAD и наличие незафиксированных правок. Если ветка отличается от main или дерево грязное, немедленно прерывай работу с кодом выхода 1 и понятной инструкцией. 2. Реализуй двухслотовую схему blue/green (или атомарные релизные папки с симлинком current): сборка и проверка целостности идут в запасной слот, пока живой сайт продолжает работать со старого, затем симлинк переключается атомарно через ln -sfn. 3. Поддержи команды deploy.sh status (показать активный слот и коммит) и deploy.sh rollback (мгновенно вернуть симлинк на предыдущий рабочий слот с любой ветки за пару секунд). 4. Запиши в AGENTS.md правило: автономный агент никогда не запускает deploy.sh сам, он только готовит ветку, проверяет сборку во временную папку и передаёт управление владельцу.
РУБЕЖ 04 · ЗАЩИТА ЖИВОГО САЙТА

МАСТЕР-ПРОМПТ: ПОРУЧИТЬ АГЕНТУ СОБРАТЬ ВЕСЬ КОНТУР ЗА ОДИН РАЗ

Если у вас уже есть живой проект, где вы работаете с ИИ-агентом, и вы хотите навести порядок без недели чтения документации — скопируйте ему мастер-промпт ниже. Он специально построен в два такта: сначала агент молча обследует проект и приносит вам карту пробелов на одобрение, затем своими руками собирает все файлы защиты, а в финале выдаёт короткий список из трёх действий в веб-интерфейсе, которые остаётся нажать лично вам.

ПРОМПТ 08 · МАСТЕР-ПРОМПТ: ВЕСЬ GIT-КОНТУР И CI ДЛЯ АГЕНТА С НУЛЯ P08
Ты настраиваешь в этом проекте полный защитный контур Git, локальных гейтов и CI/CD для безопасной работы с ИИ-агентами. ШАГ 1 (ТОЛЬКО РАЗВЕДКА, БЕЗ ПРАВОК): Обследуй проект и выдай короткий отчёт: - текущее состояние Git, ветки, влитые но не удалённые ветки, лишние файлы или секреты в индексе; - какие тесты, линтеры и команды сборки уже есть в проекте; - как сейчас устроена выкладка. Остановись и дождись моего подтверждения плана. ШАГ 2 (ПОСЛЕ МОЕГО ОДОБРЕНИЯ, В ОТДЕЛЬНОЙ ВЕТКЕ): 1. Наведи порядок в .gitignore и запиши в AGENTS.md регламент Git: запрет работы на main, схема веток <agent>/<scope>-<slug>, параллельные копии git worktree строго в /var/tmp/wt/ (не в /tmp), обязательная проверка git diff и удаление ветки после слияния. 2. Создай .githooks/pre-commit (включи core.hooksPath .githooks): запрет коммита в main/develop + сканер подготовленного диффа на .env и ключи API. 3. Если поддерживаются хуки обвязки агента, добавь перехватчик опасных команд Bash на базе токенного разбора shlex (блок force-push во всех формах, reset --hard, clean -f, --no-verify). 4. Собери CI-конвейер с минимальными правами contents: read, отменой устаревших прогонов concurrency, проверкой типов, неизменности старых миграций БД, потолком на размер файлов (ratchet) и автотестами. 5. Подготовь или обнови скрипт выкладки с замком только на ветку main и быстрым откатом rollback. ШАГ 3 (ЧЕКЛИСТ ВЛАДЕЛЬЦУ): Прогони локальную проверку всех новых хуков и сборки, покажи git diff и напиши мне короткую памятку из 3 пунктов без жаргона: какие галочки защиты ветки main, CODEOWNERS и узкого токена доступа мне нужно включить руками в настройках GitHub или Forgejo, чтобы у агента не осталось прав администратора.
ПОЛНАЯ СБОРКА · РАЗВЕДКА → НАСТРОЙКА → 3 ГАЛОЧКИ ВЛАДЕЛЬЦА

КАК ЭТОТ КОНТУР ЖИВЁТ ДАЛЬШЕ

Хорошо настроенный Git с гейтами и автотестами ощущается не как бюрократия, а как снятый ручной тормоз. Вы перестаёте открывать каждый файл за агентом из тревоги «а вдруг он там что-то снёс», перестаёте бояться параллельно запустить три большие переделки в разных рабочих папках и перестаёте гадать, почему после вчерашней мелочи сломалась регистрация.

Модель даёт тягу двигателя. Git, защёлки на входе, сборочный конвейер и три ключа в кармане владельца дают руль и тормоза. Только вместе они превращают быструю нейросеть в надёжное производство.

FOLDER 031 Серия «Git и конвейер для агента» Публикация 031 / 30.10.26
ОТМЕТКА
УТВЕРЖДЕНО ДЛЯ СЕРИИ
AUTHOR · /author/

END OF SHEET 06

// Обсуждение

Можно писать анонимно. Укажите email, чтобы получать уведомления об ответах.