Дайджесты новостей
Диспетчерский пульт с экранами контроля автономного конвейера кода

Cole Medin запустил диспетчерскую Mission Control для автономных фабрик софта

Когда в начале года индустрия заговорила об автономных фабриках программного обеспечения (AI Software Factory), идея казалась сказочной: скармливаешь нейросети подробный PRD, ложишься спать, а утром принимаешь готовый микросервис с тестами и чистой историей коммитов. Мы подробно исследовали этот концепт, когда разбирали архитектуру «темной фабрики» кода. Но реальность продакшена быстро сбила спесь даже с отчаянных энтузиастов.

Коул Медин (Cole Medin), создатель открытого оркестратора Archon, убедился на практике: полностью «темная» фабрика (Dark Factory) без участия человека неизбежно деградирует. Когда агенты штампуют десятки pull request в сутки вслепую, кодовая база страдает от архитектурного дрейфа. Модели уверенно подменяют контекст, ходят по кругу в попытках исправить нерелевантные тесты и застревают на неоднозначностях спецификации, сжигая сотни долларов на бесполезный инференс.

Ответом на этот кризис стал запуск открытого проекта factory-mission-control — командного пульта диспетчера, построенного поверх обновленного конвейера ai-software-factory. Я изучил эту связку и готов показать, как гибридный подход Human-in-the-Loop позволяет небольшой команде безопасно выпускать сотни проверенных PR в неделю.

Почему «темная фабрика» без человека заходит в тупик

Автономный CLI-агент вроде Claude Code или Aider чертовски хорош на коротких дистанциях. Ты ставишь локальную задачу, он правит функцию, прогоняет линтер и выдает аккуратный diff. Но стоит связать агентов в сквозной конвейер, как вылезает главная проблема: отсутствие детерминированного слоя управления.

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

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

Архитектура Mission Control: от вебхуков к реактивной базе

Чтобы не утонуть в сотнях параллельных задач, фабрике нужен прозрачный слой состояния (Control Plane). Медин отказался от хранения статусов в файлах репозитория или комментариях GitHub. Фундаментом диспетчерской стала связка Neon Postgres и механизма живых подписок Neon Realtime.

Сквозная цепочка событий:

  • Шина событий: любое действие в GitHub (создание issue, коммит, открытие PR) генерирует Webhook.
  • Бессерверный инжектор: легковесные функции Neon Functions перехватывают вебхук и записывают события в таблицы (laps, decisions, audit_logs).
  • Реактивный интерфейс: дашборд Mission Control подписывается на живые SQL-запросы через WebSocket. Любое изменение в базе мгновенно обновляет карточку задачи на экране без перезагрузки страницы.
  • Ветвление данных (Neon Branching): мгновенное клонирование состояния базы позволяет проводить чистое A/B-тестирование разных версий самой фабрики и промптов.

Сам дашборд Mission Control разрабатывается мета-способом: фабрика сама пишет и развивает свой интерфейс под надзором автора.

Как устроен рабочий цикл: Git worktrees и узел Human Gate

Сердцем фабрики остается оркестратор Archon, разворачивающий задачу в направленный граф (DAG) на YAML. Чтобы параллельные запуски (laps) не конфликтовали, Archon изолирует каждый запуск через git worktree в отдельную директорию.

Вот фрагмент сценария Archon с точкой эскалации human_decision_gate:

version: "1.0"
name: "feature-pipeline"

nodes:
  - id: create_worktree
    type: shell
    action: |
      WORKTREE="/tmp/worktrees/lap-${ARCHON_LAP_ID}"
      git worktree add -b "feature/${ARCHON_ISSUE_KEY}" "${WORKTREE}" origin/main

  - id: analyze_spec
    type: agent
    depends_on: [create_worktree]
    model: "claude-3-7-sonnet"
    harness:
      cwd: "${create_worktree.WORKTREE}"
    prompt: |
      Проанализируй тикет ${ARCHON_ISSUE_KEY}. При архитектурных развилках
      вызови инструмент escalate_to_owner.

  - id: human_decision_gate
    type: gate
    depends_on: [analyze_spec]
    condition: "${analyze_spec.needs_owner_decision == true}"
    on_wait:
      notify_webhook: "https://my-neon-func.neon.tech/api/escalate"
      payload:
        lap_id: "${ARCHON_LAP_ID}"
        issue: "${ARCHON_ISSUE_KEY}"

  - id: code_generation
    type: agent
    depends_on: [human_decision_gate]
    model: "claude-3-7-sonnet"
    harness:
      cwd: "${create_worktree.WORKTREE}"
      tools: ["fs_read", "fs_write", "bash", "test_runner"]
    prompt: "Реализуй требования и добейся прохождения тестов."

  - id: independent_review
    type: agent
    depends_on: [code_generation]
    model: "claude-3-7-sonnet"
    prompt: "Проведи независимый аудит безопасности git diff."

Узел independent_review принципиален: проверяющий агент изолирован от кодера, видит только итоговый git diff и не подвержен чужим галлюцинациям.

Бессерверная реактивность и живой пульт управления

Преобразование событий GitHub в реактивный интерфейс диспетчера решено через серверлесс-функции и клиентские хуки.

Обработчик вебхуков на стороне Neon Serverless Functions:

import { neon } from '@neondatabase/serverless';

export default async function handler(req: Request) {
  const sql = neon(process.env.DATABASE_URL!);
  const { action, pull_request, repository } = await req.json();

  if (req.headers.get('x-github-event') === 'pull_request') {
    await sql`
      INSERT INTO factory_events (event_type, repo_name, pr_number, status, title)
      VALUES (${action}, ${repository.full_name}, ${pull_request.number}, ${pull_request.state}, ${pull_request.title})
      ON CONFLICT (pr_number, repo_name) DO UPDATE SET status = ${pull_request.state};
    `;
    return new Response(JSON.stringify({ ok: true }));
  }
  return new Response(JSON.stringify({ ignored: true }));
}

Клиентский компонент Mission Control с подпиской Neon Realtime:

import { useRealtimeQuery } from '@neondatabase/react-realtime';

export function ActiveLapsWidget({ factoryId }: { factoryId: string }) {
  const { data: laps, loading } = useRealtimeQuery(
    `SELECT id, issue_key, status, current_step, owner_blocked 
     FROM factory_laps WHERE factory_id = $1 AND status != 'completed'`,
    [factoryId]
  );

  if (loading) return <div>Синхронизация с пультом фабрики...</div>;

  return (
    <div className="laps-grid">
      {laps.map(lap => (
        <div key={lap.id} className={`lap-card ${lap.owner_blocked ? 'blocked' : 'active'}`}>
          <h4>#{lap.id}: {lap.issue_key} ({lap.current_step})</h4>
          {lap.owner_blocked && <span className="warning">Требуется решение</span>}
        </div>
      ))}
    </div>
  );
}

Все задачи с флагом owner_blocked попадают во вкладку Owner Tab. Инженер раз в несколько часов изучает развилки, делает пару кликов, и агенты продолжают сборку.

Экономика масштабирования и границы применимости

Практические метрики Коула Медина впечатляют: система отгружает сотни PR в неделю. Анализ отказов (Failure Analysis) в дашборде показал:

  • Из сотен прогонов лишь 2 сбоя произошли из-за лимитов провайдеров LLM.
  • 21 сбой потребовал подстройки шагов и условий в самих графах Archon.
  • Ни один дефектный PR не попал в основную ветку благодаря автоматическому ревьюеру и тестам.
Совет по дисковому пространству

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

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