fix(pipeline-status): MCP wrapper timeout 60s kills 5min CI poll cycle #67
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Контекст
pipeline-statusMCP tool используется оркестратором для определения фазы pipeline (ISSUE → IMPLEMENT → CI → REVIEW → MERGE → MEMORY) на каждой итерации/run-pipeline.Tool заявляет в description: «Blocks up to 5 min while CI runs (polling CI). Returns DONE on green, NOT_DONE on failure, AMBIGUOUS on timeout/API error.». По факту MCP-обёртка убивает процесс через 60s, и пользователь видит
⚠️ pipeline_status failed (timed out (60s)):вместо verdict'а, пока CI ещё бежит (статус IN_PROGRESS). После завершения CI оракул отрабатывает нормально (<1s).Корневая причина — несоответствие таймаутов:
.opencode/tools/pipeline-status.ts:14— MCP-обёртка жёстко режет процесс черезtimeout: 60000(60s) вspawnSync(..., { timeout: 60000 })..opencode/scripts/pipeline-status.py:61— Python-оракул объявляетCI_WAIT_TIMEOUT = 300(5 минут poll-цикл для CI в IN_PROGRESS)._classify_rollup_with_poll(.opencode/scripts/pipeline-status.py:697,time.sleep(config.poll_interval)на строке 706, цикл до ~300s). MCP через 60s убивает процесс SIGTERM'ом → пользователь видит⚠️ pipeline_status failed (timed out (60s)):.Задача
Согласовать таймауты MCP-обёртки и Python-оракула, чтобы описание tool'а соответствовало реальному поведению. Один из вариантов (на усмотрение исполнителя):
.opencode/tools/pipeline-status.ts:14timeout: 60000→310000(5мин10с, чуть большеCI_WAIT_TIMEOUT=300) — соответствует заявленному description..opencode/scripts/pipeline-status.py:61CI_WAIT_TIMEOUT = 300→ значение, вписывающееся в 60s (минус: длинный CI будет prematurely AMBIGUOUS, нарушается обещание «up to 5 min» в description — потребует правки description).--ci-wait-timeoutаргумент из MCP-обёртки в скрипт, чтобы таймаут управлялся из одного места (MCP-обёртка =timeout + 10000от переданногоCI_WAIT_TIMEOUT).Предпочтительный вариант — первый (поднять MCP-таймаут), т.к. он не нарушает внешний контракт description и не ухудшает UX при длинном CI.
Контракты
.opencode/opencode.json/tool({ description })) НЕ меняется — уже обещает «up to 5 min». При выборе варианта со снижениемCI_WAIT_TIMEOUT— description нужно править под новое поведение.DONE/NOT_DONE/AMBIGUOUS— без изменений.pr_number: number— без изменений.python3 scripts/pipeline-status.py <pr_number>— без изменений (если не выбран гибридный вариант с--ci-wait-timeout, тогда это новое опциональное расширение).Инварианты
DONE/NOT_DONE/AMBIGUOUS) вместо⚠️ pipeline_status failed (timed out (60s)).AMBIGUOUS(не бесконечный poll). Гарантия верхнего предела сохраняется.spawnSyncостаётся синхронным блокирующим вызовом (оракул по дизайну блокирующий — это заявлено в description).Граничные случаи
CI_WAIT_TIMEOUT) — после фикса оракул корректно возвращаетAMBIGUOUS, MCP не убивает процесс раньше.AMBIGUOUSпо контракту (poll-цикл сам выходит поCI_WAIT_TIMEOUT).FORGEJO_RETRY_INTERVAL_S), не этот баг.r.status !== 0, этот путь не затрагивается.Влияние на связанные компоненты
run-pipelineskill — полагается наpipeline-statusна каждой итерации (особенно фаза CI). Сейчас pipeline может стопориться / выдавать ложный негатив, когда CI бежит 1-5 минут: оркестратор видитtimed out (60s)вместоNOT_DONEи не может корректно дождаться зелёного CI./run-pipeline— страдают от этого бага, пока CI бежит дольше 60s. Это особенно актуально для репо с медленным CI (fullstack с Playwright/pytest).pipeline-statustool также используется вручную оркестратором на фазах REVIEW/MERGE для проверки, что CI зелёный — те же симптомы.spec-statustool — НЕ затрагивается (другой скрипт, нет poll-цикла на CI).Вне scope
_classify_rollup,_classify_rollup_with_poll,_classify_rollup_in_progress) — не трогаем. Только таймауты._env_int("FORGEJO_RETRY_INTERVAL_S", ...),CI_NO_RUNS_INTERVAL) — не трогаем.merge-pr,post-review,project-status,spec-status) — не трогаем.DONE/NOT_DONE/AMBIGUOUS+NEXT:) — не трогаем.spawnSyncна асинхронную модель — не трогаем (оракул намеренно блокирующий).Критерии приемки
pipeline-statusотдаёт verdict (DONE/NOT_DONE/AMBIGUOUS) когда CI бежит 1-5 минут, без⚠️ pipeline_status failed (timed out (60s)).tool({ description })и в.opencode/opencode.jsonесли дублирует) соответствует реальному поведению — «up to 5 min» действительно означает до ~5 минут poll, а не 60s.python3 .opencode/scripts/pipeline-status.py <pr>на PR с IN_PROGRESS CI — завершается естественным образом verdict'ом, не SIGTERM'ом от MCP-обёртки.AMBIGUOUSпо контракту (верхний предел сохраняется, не бесконечный poll).pipeline-status(если есть вtests/) — проходят. Если тестов нет — добавить минимальный тест на таймаут-контракт (опционально, на усмотрение исполнителя).timeout>=CI_WAIT_TIMEOUT + buffer(чтобы скрипт успел вернуть verdict до того, как MCP его убьёт).