[PIX-13213] fix: применять явный итог successful-run handoff сразу, до исчерпания попыток #205

Merged
andrei merged 1 commit from agent/devops/pix-13213-delivery into master 2026-07-06 12:33:10 +00:00
Owner

Что сделано

Восстановлена (re-derived, не cherry-pick) идея ветки agent/devops/pix-11564 поверх текущего master: если корректирующий (finish_successful_run_handoff) запуск явно указал итог задачи (done/cancelled/blocked) уже в первой попытке, и задача является безопасной для рутинного авто-закрытия (isSafeRoutineDispositionTarget), система применяет итог сразу — не дожидаясь исчерпания всех handoff-попыток (maxHandoffAttempts).

isSafeRoutineDispositionTarget() переиспользует существующие guard'ы isManualOwnerActionIssue, isExplicitExternalBoundaryIssue, isContinuousOperatingLoopIssue и добавляет keyword-паттерн для approval-gate/mainnet/legal/compliance/social_media/audit — задачи такого рода никогда не получают ранний авто-disposition. Ранний путь переиспользует уже существующие declaredSuccessfulRunHandoffTerminalDisposition (с regex-guard'ом от негации/continuation из PIX-13186) и resolveSuccessfulRunHandoffDeclaredDisposition — новой логики парсинга текста не добавлено.

По второй части задачи (PIX-13213 включал ревью ветки agent/devops/pix-13218, JOIN-условие reconcileProcesslessRunsTargetingTerminalIssues в server/src/services/heartbeat.ts): проверено, что heartbeatRuns в текущей схеме (packages/db/src/schema/heartbeat_runs.ts) не имеет колонки issueId — она нигде не объявлена. JOIN-условие ветки pix-13218 (eq(issues.id, heartbeatRuns.issueId)) ссылается на несуществующее поле и не может быть корректным в текущей схеме. Единственная реальная связь — issues.executionRunId → heartbeatRuns.id (см. packages/db/src/schema/issues.ts:39, а также PIX-13081, который явно поддерживает эту связь атомарно через claimQueuedRun). Текущий JOIN в master (eq(issues.executionRunId, heartbeatRuns.id)) корректен и не изменён; изменений в heartbeat.ts в этом PR нет.

Зачем

Без этого фикса задача с уже готовым явным итогом (done/cancelled/blocked) от корректирующего handoff-запуска ожидала нескольких раундов handoff-попыток прежде чем итог применялся — лишняя задержка закрытия для рутинных задач, при этом риск ложного авто-closure для manual/mainnet/legal/compliance/audit задач исключён явным guard'ом.

План тестирования

  • Добавлены 3 целевых vitest-теста в server/src/__tests__/heartbeat-process-recovery.test.ts (PIX-13213: ...):
    1. первая попытка с явным done на обычной задаче → немедленное применение (successfulRunHandoffResolved: 1, без ожидания exhaustion).
    2. та же ситуация на manual-owner-action/mainnet задаче → early-path не срабатывает, обычный exhaustion-путь (skipped: 1).
    3. run без явного итога/не succeeded → поведение не меняется (skipped: 1).
  • npx vitest run src/__tests__/heartbeat-process-recovery.test.ts -t "PIX-13213" → 3 passed.
  • Полный прогон файла heartbeat-process-recovery.test.ts массово падает (79/92) и на немодифицированном origin/master — подтверждённая pre-existing проблема окружения (embedded postgres/параллельные воркеры), не связанная с этим PR.
  • pnpm run typecheck (полный monorepo, через husky pre-push hook) — прошёл успешно при push.
  • Для JOIN-вопроса по heartbeat.ts: существующий server/src/__tests__/heartbeat-processless-run-reconciliation.test.ts (3 теста) прогнан отдельно и зелёный — подтверждает корректность текущего join, изменений не требовалось.

Где могу ошибаться

  • isSafeRoutineDispositionTarget() keyword-паттерн (approval-gate/legal/compliance/social_media/mainnet/audit) — эвристический, как и остальные guard'ы в этом файле; теоретически может пропустить редкую формулировку без этих слов. Смягчается тем, что это лишь ускоряет момент применения итога — тот же итог всё равно был бы применён после exhaustion, просто позже.
  • Полный regression-прогон heartbeat-process-recovery.test.ts не удалось зафиксировать зелёным целиком в этой среде (окружение падает и на чистом master) — проверено целевым -t фильтром и явным baseline-сравнением, но не через полный CI-прогон в этой сессии.
## Что сделано Восстановлена (re-derived, не cherry-pick) идея ветки `agent/devops/pix-11564` поверх текущего master: если корректирующий (`finish_successful_run_handoff`) запуск явно указал итог задачи (`done`/`cancelled`/`blocked`) уже в первой попытке, и задача является безопасной для рутинного авто-закрытия (`isSafeRoutineDispositionTarget`), система применяет итог сразу — не дожидаясь исчерпания всех handoff-попыток (`maxHandoffAttempts`). `isSafeRoutineDispositionTarget()` переиспользует существующие guard'ы `isManualOwnerActionIssue`, `isExplicitExternalBoundaryIssue`, `isContinuousOperatingLoopIssue` и добавляет keyword-паттерн для approval-gate/mainnet/legal/compliance/social_media/audit — задачи такого рода никогда не получают ранний авто-disposition. Ранний путь переиспользует уже существующие `declaredSuccessfulRunHandoffTerminalDisposition` (с regex-guard'ом от негации/continuation из PIX-13186) и `resolveSuccessfulRunHandoffDeclaredDisposition` — новой логики парсинга текста не добавлено. По второй части задачи (PIX-13213 включал ревью ветки `agent/devops/pix-13218`, JOIN-условие `reconcileProcesslessRunsTargetingTerminalIssues` в `server/src/services/heartbeat.ts`): проверено, что `heartbeatRuns` в текущей схеме (`packages/db/src/schema/heartbeat_runs.ts`) **не имеет колонки `issueId`** — она нигде не объявлена. JOIN-условие ветки pix-13218 (`eq(issues.id, heartbeatRuns.issueId)`) ссылается на несуществующее поле и не может быть корректным в текущей схеме. Единственная реальная связь — `issues.executionRunId → heartbeatRuns.id` (см. `packages/db/src/schema/issues.ts:39`, а также PIX-13081, который явно поддерживает эту связь атомарно через `claimQueuedRun`). Текущий JOIN в master (`eq(issues.executionRunId, heartbeatRuns.id)`) корректен и не изменён; изменений в `heartbeat.ts` в этом PR нет. ## Зачем Без этого фикса задача с уже готовым явным итогом (`done`/`cancelled`/`blocked`) от корректирующего handoff-запуска ожидала нескольких раундов handoff-попыток прежде чем итог применялся — лишняя задержка закрытия для рутинных задач, при этом риск ложного авто-closure для manual/mainnet/legal/compliance/audit задач исключён явным guard'ом. ## План тестирования - Добавлены 3 целевых vitest-теста в `server/src/__tests__/heartbeat-process-recovery.test.ts` (`PIX-13213: ...`): 1. первая попытка с явным `done` на обычной задаче → немедленное применение (`successfulRunHandoffResolved: 1`, без ожидания exhaustion). 2. та же ситуация на manual-owner-action/mainnet задаче → early-path не срабатывает, обычный exhaustion-путь (`skipped: 1`). 3. run без явного итога/не succeeded → поведение не меняется (`skipped: 1`). - `npx vitest run src/__tests__/heartbeat-process-recovery.test.ts -t "PIX-13213"` → 3 passed. - Полный прогон файла `heartbeat-process-recovery.test.ts` массово падает (79/92) **и на немодифицированном origin/master** — подтверждённая pre-existing проблема окружения (embedded postgres/параллельные воркеры), не связанная с этим PR. - `pnpm run typecheck` (полный monorepo, через husky pre-push hook) — прошёл успешно при push. - Для JOIN-вопроса по heartbeat.ts: существующий `server/src/__tests__/heartbeat-processless-run-reconciliation.test.ts` (3 теста) прогнан отдельно и зелёный — подтверждает корректность текущего join, изменений не требовалось. ## Где могу ошибаться - `isSafeRoutineDispositionTarget()` keyword-паттерн (approval-gate/legal/compliance/social_media/mainnet/audit) — эвристический, как и остальные guard'ы в этом файле; теоретически может пропустить редкую формулировку без этих слов. Смягчается тем, что это лишь ускоряет момент применения итога — тот же итог всё равно был бы применён после exhaustion, просто позже. - Полный regression-прогон `heartbeat-process-recovery.test.ts` не удалось зафиксировать зелёным целиком в этой среде (окружение падает и на чистом master) — проверено целевым `-t` фильтром и явным baseline-сравнением, но не через полный CI-прогон в этой сессии.
fix(recovery): resolve declared successful-run handoff disposition before exhaustion (PIX-13213)
All checks were successful
security/pr-scan No security concerns detected
PR Quality Gates / PR Quality Gates (pull_request_target) Successful in 6s
Agents CI / Typecheck and Build (pull_request) Successful in 5m18s
Agents CI / API Tests (pull_request) Successful in 14m22s
d55db31e18
Re-derives the agent/devops/pix-11564 intent against current master (which no longer
has isContinuousOperatingLoopIssue in service.ts scope the same way and gained
PIX-13186's negation/continuation guards): when a corrective successful-run handoff
run explicitly declares a terminal disposition (done/cancelled/blocked) on its first
attempt, and the target issue is a safe routine-disposition target (not manual-owner-
action, not an explicit external/mainnet boundary, not a continuous operating loop,
and free of approval-gate/legal/compliance/social-media/audit keywords), apply the
disposition immediately instead of waiting for handoff-attempt exhaustion.

Adds isSafeRoutineDispositionTarget(), reusing the existing isManualOwnerActionIssue /
isExplicitExternalBoundaryIssue / isContinuousOperatingLoopIssue guards plus a new
keyword pattern, and reuses the existing declaredSuccessfulRunHandoffTerminalDisposition
and resolveSuccessfulRunHandoffDeclaredDisposition helpers so the negation/continuation
regex guard from PIX-13186 is inherited unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
Author
Owner

Верификация PIX-13213: PR содержит недостающий remainder из agent/devops/pix-11564 (isSafeRoutineDispositionTarget + раннее разрешение диспозиции), адаптированный под текущий master после PIX-13186. Локально прогнал 3 целевых теста (vitest run -t "PIX-13213") — все зелёные. CI: security/pr-scan и Typecheck/Build — success; API Tests — ещё выполняются (долгий полный прогон).

Верификация PIX-13213: PR содержит недостающий remainder из agent/devops/pix-11564 (isSafeRoutineDispositionTarget + раннее разрешение диспозиции), адаптированный под текущий master после PIX-13186. Локально прогнал 3 целевых теста (`vitest run -t "PIX-13213"`) — все зелёные. CI: security/pr-scan и Typecheck/Build — success; API Tests — ещё выполняются (долгий полный прогон).
andrei merged commit 5a0b7c7aba into master 2026-07-06 12:33:10 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
europa-tech-srl/europa-tech-agents!205
No description provided.