[PIX-13312] Возвращать 409 вместо 500 при повторном чекауте тем же executionRunId #219
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "agent/fullstack/pix-13312"
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?
Что сделано
checkout()вserver/src/services/issues.tsтеперь ловит нарушение уникального индексаissues_active_execution_run_uq(один активный executionRunId — одна активная задача) и превращает его в понятный409 Conflictс телом{"error": "execution_run_already_active", "activeIssueId": "..."}вместо сырого Postgres-исключения, которое раньше утекало наверх как необработанный500.Drizzle оборачивает ошибку postgres в
DrizzleQueryErrorс.cause, поэтому проверкаisActiveExecutionRunConflict()смотрит и на верхний объект ошибки, и наerror.cause(там реальный код23505иconstraint_name).Зачем
Когда один и тот же прогон агента (
executionRunId), уже владеющий одной активной задачей, пытается зачекаутить вторую задачу (например, потрогать смежную задачу в рамках того же heartbeat), запрос падал сырым500вместо ожидаемого409. Это замусоривало боевые логи стектрейсами на штатном сценарии и не давало агентам отличить "issue занята другим агентом" (обычный 409) от "нельзя чекаутить вторую задачу из этого же run".План тестирования
server/src/__tests__/issues-service.test.ts(returns 409 instead of a raw 500 when the same executionRunId tries to check out a second issue) на embedded Postgres: чекаутит issue A текущим runId, затем чекаутит issue B тем же runId и ожидает409сactiveIssueIdissue A вместо необработанного исключения; проверяет, что issue B осталась нетронутой (todo, безcheckoutRunId/executionRunId).pnpm exec vitest run src/__tests__/issues-service.test.ts— 99/100 зелёных (единственное падениеselects primary workspace when multiple workspaces exist and one is primary— предсуществующее, не связано с этим изменением, воспроизводится и на чистомmaster).pnpm exec vitest run src/__tests__/heartbeat-execution-run-one-to-one-linkage.test.ts(тесты PIX-13106 для самого констрейнта) — все 3 зелёные, констрейнт продолжает работать как раньше.pnpm typecheckпо всему монорепо прошёл в рамках pre-push хука перед пушем.Где могу ошибаться
issues_active_execution_run_uq; если у Postgres-драйвера в другой версии/конфигурации имя поля будет неconstraint_name, а что-то ещё — проверка не сработает и упадёт обратно в общийthrow error, то есть в худшем случае вернётся прежнее поведение (500), а не хуже.activeIssueIdв теле 409 ищется отдельнымSELECTпосле перехвата ошибки — теоретически междуUPDATEи этимSELECTактивная задача могла уже освободиться, тогдаactiveIssueIdбудетnull; это не ломает контракт (409 всё равно возвращается), просто поле будет пустым в редком гоночном случае.checkout() let the issues_active_execution_run_uq unique-violation escape as a raw Postgres error when the same executionRunId tried to check out a second issue while already holding one active issue. Catch that specific constraint violation (top-level or wrapped under DrizzleQueryError.cause) and translate it into a 409 with { error: "execution_run_already_active", activeIssueId } so callers can distinguish it from an ordinary "issue taken by another agent" 409 without a Postgres stack trace hitting the logs.