Понятие · подробно

Спека (ТЗ)

В этом проекте спека (ТЗ) — это не документ в чате, а артефакт системы: у неё есть тип, статус, жизненный цикл, и она путешествует по понятным каналам. Здесь — что это, откуда приходит и как доходит до DEV-AGENT.

Репозиторий: meeseeks/mr_meeseeks · на главную · разбор workflows.py · про ЧЕЛОВЕК · про Gate

Что такое спека (ТЗ) внутри системы

Внутри системы спека (ТЗ) называется «спека» (от англ. specification; привычное «техническое задание», ТЗ, — тот же термин), а в коде — артефакт с типом kind=spec. Технически это запись в базе artifacts-service с телом (текст задания) и набором полей:

ПолеЧто значит
kind = specэто именно спека (ТЗ), а не задача, результат и т.п.
statusгде спека (ТЗ) в жизни: черновик → на проверке → одобрено / отклонено
bodyсам текст спеки (ТЗ) — что нужно сделать
base_shaверсия кода, от которой «пляшет» работа (конкретный коммит) — подробно ниже
parent_idиз какой задачи эта спека (ТЗ) родилась
Простая аналогия: спека (ТЗ) — это «карточка заказа» в системе. У карточки есть статус, автор, текст заказа и «от какого коммита работать». Пока карточка не одобрена — никто по ней не работает.

base_sha — от какой версии кода «отталкиваемся»

base_sha — это конкретный коммит репозитория, на который опирается спека (ТЗ). Смысл простой: код меняется каждый день, и спека (ТЗ) всегда написана под какую-то конкретную версию кода. Чтобы работа была предсказуемой, запоминают именно этот коммит — и DEV-AGENT стартует от него, а не от «самого свежего состояния на сейчас».

«Отталкиваться от коммита» на практике значит: перед работой DEV-AGENT берёт снимок репозитория ровно на этом коммите — и весь его труд (чтение файлов, правки, коммиты) происходит относительно этой версии. То есть база, от которой DEV-AGENT пляшет, зафиксирована и не «уплывает» из-под него.

Зачем это нужно — пример

Представьте репозиторий, где код постоянно меняется:

понедельник:  коммит aaa1111  — SPEC-AGENT читает код и пишет спеку (ТЗ):
               «добавь функцию export_report в модуль reports.py»,
               запоминает base_sha = aaa1111
вторник:     в main влили чужой рефакторинг → коммит bbb2222,
               файл reports.py переименован в reporting.py
среда:       запускается DEV-AGENT

Если бы base_sha не было, DEV-AGENT в среду взял бы свежий код (bbb2222): файла reports.py там уже нет, задание «добавь функцию в reports.py» не сходится с реальностью, DEV-AGENT буксует или делает не то.

С base_sha = aaa1111 DEV-AGENT стартует со снимка понедельника: файл на месте, задание выполнимо ровно так, как его задумал автор спеки (ТЗ).

Поэтому в коде нет «молчаливого» поведения: если у спеки (ТЗ) нет base_sha или он не похож на коммит, пайплайн останавливается с ошибкой, а не берёт «что-нибудь свежее» наугад.

Откуда спека (ТЗ) берётся

Есть два пути, и какой сработает — зависит от того, с чем пришла задача в workflow:

Путь 1. Спека (ТЗ) уже готова

В workflow приходит сразу spec_id — номер готовой одобренной спеки (ТЗ). Она уже прошла проверку, и дальше сразу запускается DEV-AGENT (агент-разработчик, программа, а не ЧЕЛОВЕК): он начинает работу по готовой спеке (ТЗ).

Путь 2. Спеку (ТЗ) надо сделать

В workflow приходит task_id (номер задачи). Тогда система сама запускает SPEC-AGENT, чтобы он написал спеку (ТЗ) с нуля.

Как SPEC-AGENT пишет спеку (ТЗ) — путь 2

  1. Workflow получает task_id и видит: «спеки (ТЗ) ещё нет, надо сделать».
  2. Workflow запускает шаг SPEC_STEP — диспатчит SPEC-AGENT в Nomad.
  3. SPEC-AGENT получает пакет задачи (историю, подзадачи, срезы, тесткейсы — всё, что уже есть о задаче).
  4. SPEC-AGENT через MCP-инструмент artifacts создаёт артефакт spec в статусе draft (черновик).
  5. В тело ложатся версии источников; отдельно записывается base_sha — коммит, от которого работаем.
  6. SPEC-AGENT пишет в свой результат ссылку на созданную спеку (spec_id) и завершается.
  7. Workflow читает spec_id из результата и отправляет спеку (ТЗ) на проверку ЧЕЛОВЕК.
# workflows.py — первый шаг пути «надо сделать спеку (ТЗ)»
run(spec_id, task_id=""):
    if task_id:                       # спеки (ТЗ) ещё нет
        результат = запустить_шаг(SPEC_STEP)     # SPEC-AGENT пишет черновик
        spec_id = вытащить_id_из(результат)
        пройти_Gate_спеки(spec_id)               # человек проверяет (см. ниже)

Как спека (ТЗ) «живёт»: статусы

У спеки (ТЗ) конечный набор статусов, и переходы строго контролируются:

  1. draft — черновик. Только что его создал SPEC-AGENT.
  2. pending_review — отправлена на проверку ЧЕЛОВЕК (workflow делает submit от имени SPEC-AGENT).
  3. approvedЧЕЛОВЕК одобрил. Только теперь по спеке (ТЗ) можно работать.
  4. rejectedЧЕЛОВЕК вернул с замечаниями. Спека (ТЗ) отправляется на переделку, рождается новая версия.
Важная деталь: при возврате старая версия не переписывается — она остаётся в статусе rejected как есть. SPEC-AGENT создаёт новый артефакт-черновик. Так всегда видно историю: какая версия была отклонена и почему.

Кто и как проверяет спеку (ТЗ)

Проверяет ЧЕЛОВЕК. Механика — две вещи, которые работают вместе:

1. Решение — через artifacts-service

Человек жмёт «одобрить» / «вернуть» в интерфейсе, и система меняет статус спеки (ТЗ) через REST: POST /status (decide_transition, роль human).

2. «Стук в дверь» — сигнал Temporal

Когда статус поменялся, workflow «будят» сигналом human_verdict. Вместе с возвратом ЧЕЛОВЕК шлёт текст замечаний (feedback).

Код не верит сигналу на слово: после «стука» он сам перечитывает реальный статус спеки (ТЗ) через artifacts-service. Так решение никогда не расходится с тем, что реально хранится в базе.

человек → POST /status (approved/rejected)   # решает artifacts-service
человек → сигнал human_verdict(feedback)      # будит workflow
workflow → перечитывает статус спеки (ТЗ)     # проверяет сам, не на слово

Что происходит после вердикта

Одобрено ✅

  1. Workflow запоминает одобренную спеку (ТЗ).
  2. Узнаёт base_sha — с какого коммита работать.
  3. Запускает DEV-AGENT: тело спеки (ТЗ) попадает в его задание как основной текст.
  4. Дальше — обычные шаги разработки (см. разбор workflows.py).

Вернули с замечаниями 🔁

  1. Workflow берёт текст замечаний и добавляет его в задание SPEC-AGENT: «Ревьюер вернул спеку (ТЗ): …».
  2. SPEC-AGENT переделывает и создаёт новый черновик.
  3. Новый черновик снова отправляется ЧЕЛОВЕК на проверку. Цикл повторяется.

Слишком много возвратов ⛔

Всему есть предел — MAX_REWORK = 2. После двух возвратов workflow больше не гоняет SPEC-AGENT по кругу, а помечает исход как escalated («нужен ЧЕЛОВЕК»): система честно говорит «сама не справляюсь, реши руками», а не молча крутит цикл вечно.

if rework_count >= MAX_REWORK:      # MAX_REWORK = 2
    return {"status": "escalated", "failed_step": "spec-review",
            "reason": "spec rework ceiling reached: max_rework=2"}

Каналы: откуда и куда

Если собрать всё путешествие спеки (ТЗ) по каналам, получается такая карта:

КаналКто говоритЧто передаёт
Task-пакетсистема → SPEC-AGENTисходники задачи: история, подзадачи, срезы, тесткейсы
MCP artifactsSPEC-AGENT → artifacts-serviceсоздание артефакта spec (draft, body, base_sha)
result.jsonSPEC-AGENTworkflowссылка на созданную спеку (spec_id)
activity submitworkflow → artifacts-servicedraft → pending_review
REST POST /statusЧЕЛОВЕК → artifacts-serviceapproved / rejected (решение)
signal human_verdictсистема → workflow«проснись», плюс текст замечаний при возврате
REST чтение статусаworkflow → artifacts-serviceпроверка «а что там реально лежит»
Task-промпт DEV-AGENTworkflowDEV-AGENTтело одобренной спеки (ТЗ) + base_sha для работы

Итог: спека (ТЗ) в одной картинке

  1. Пришла задача без спеки (ТЗ) → workflow запускает SPEC-AGENT.
  2. SPEC-AGENT создаёт черновик (артефакт spec) через MCP artifacts.
  3. Черновик уходит ЧЕЛОВЕК на проверку (pending_review).
  4. Одобрил → спека (ТЗ) едет к DEV-AGENT (тело в промпт, base_sha — точка старта).
  5. ВернулSPEC-AGENT переделывает с учётом замечаний; максимум 2 раза, дальше — эскалация ЧЕЛОВЕК.
Главное: спека (ТЗ) — это живой артефакт со статусом, а не текст в переписке. Одобренная спека (ТЗ) — единственный легальный вход в разработку.