[PIX-13286] Реконсилировать recovery/pix-13213-heartbeat-fixes с master #215

Merged
andrei merged 16 commits from agent/devops/pix-13286 into master 2026-07-07 11:12:55 +00:00
Owner

Что сделано

Реконсилирована ветка recovery/pix-13213-heartbeat-fixes (live control-plane checkout, 11 коммитов) с origin/master (17 коммитов, включая фикс PIX-13257 — повторная проверка terminal status issue перед стартом adapter).

  • Смержен origin/master в ветку, конфликты в server/src/services/recovery/service.ts и server/src/services/heartbeat.ts разрешены в пользу master-версии с точечным восстановлением уникальных recovery-хотфиксов (hasActiveRoutineContinuation gate, title/description operating-loop markers).
  • Дополнительно устранена ошибка типов, обнаруженная pre-push tsc-gate после мержа: тип SuccessfulRunHandoffRecoveryEvidence был приватным дублем в service.ts, а тест импортировал его из successful-run-handoff.ts — теперь тип экспортируется из единого места и переиспользуется.

Зачем

PR #211 (PIX-13257) уже в master, но живой PM2-процесс europatech-agents работает со старой ветки recovery/pix-13213-heartbeat-fixes, отставшей от master на 17 коммитов. Это значит корневой фикс инцидента с повторным запуском heartbeat run по уже done issue ещё не в проде.

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

  • npx vitest run по затронутым файлам (successful-run-handoff.test.ts, fleet-usage-limit.test.ts, heartbeat-static-regression.test.ts, ecosystem.config.test.mjs) — 122 теста прошли.
  • python3 -m pytest scripts/daemons/tests/ — 300 тестов прошли.
  • npx tsc --noEmit в server/ — чисто.
  • Полный pre-push CI gate (typecheck всех пакетов + husky no-git-push check) прошёл при push.

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

  • После мержа PR в master нужно передеплоить PM2-процесс europatech-agents на новый коммит и сделать graceful restart — это отдельный деплой-шаг вне PR, требующий осознанного окна (процесс обслуживает live agent-сессии, включая текущую).
## Что сделано Реконсилирована ветка `recovery/pix-13213-heartbeat-fixes` (live control-plane checkout, 11 коммитов) с `origin/master` (17 коммитов, включая фикс PIX-13257 — повторная проверка terminal status issue перед стартом adapter). - Смержен `origin/master` в ветку, конфликты в `server/src/services/recovery/service.ts` и `server/src/services/heartbeat.ts` разрешены в пользу master-версии с точечным восстановлением уникальных recovery-хотфиксов (`hasActiveRoutineContinuation` gate, title/description operating-loop markers). - Дополнительно устранена ошибка типов, обнаруженная pre-push tsc-gate после мержа: тип `SuccessfulRunHandoffRecoveryEvidence` был приватным дублем в `service.ts`, а тест импортировал его из `successful-run-handoff.ts` — теперь тип экспортируется из единого места и переиспользуется. ## Зачем PR #211 (PIX-13257) уже в master, но живой PM2-процесс `europatech-agents` работает со старой ветки `recovery/pix-13213-heartbeat-fixes`, отставшей от master на 17 коммитов. Это значит корневой фикс инцидента с повторным запуском heartbeat run по уже done issue ещё не в проде. ## План тестирования - `npx vitest run` по затронутым файлам (`successful-run-handoff.test.ts`, `fleet-usage-limit.test.ts`, `heartbeat-static-regression.test.ts`, `ecosystem.config.test.mjs`) — 122 теста прошли. - `python3 -m pytest scripts/daemons/tests/` — 300 тестов прошли. - `npx tsc --noEmit` в `server/` — чисто. - Полный pre-push CI gate (typecheck всех пакетов + husky no-git-push check) прошёл при push. ## Где могу ошибаться - После мержа PR в master нужно передеплоить PM2-процесс `europatech-agents` на новый коммит и сделать graceful restart — это отдельный деплой-шаг вне PR, требующий осознанного окна (процесс обслуживает live agent-сессии, включая текущую).
Добавлен детерминированный путь для разрешения успешных status-only handoff runs
с явно declared disposition на routine/snapshot задачах. Ранее система создавала
повторяющиеся recovery циклы для PIX-11464/PIX-11466 и PIX-11544/PIX-11546, где
source run имел GREEN доказательства и объявил done, но остался заблокирован.

1. **Early disposition check** (service.ts:3733-3748)
   - Если declared disposition и safe routine target и status "succeeded"
   - Применить disposition БЕЗ ОЖИДАНИЯ exhausted attempts
   - Прерывает loop для задач с explicit GREEN evidence

2. **Safety boundaries** (service.ts:3107-3121)
   - isSafeRoutineDispositionTarget(): проверяет что это routine task
   - Исключает: manual owner actions, continuous loops, external approvals
   - Защищает: mainnet, legal, social, compliance задачи от auto-close

3. **Export declared disposition logic** (successful-run-handoff.ts)
   - declaredSuccessfulRunHandoffTerminalDisposition()
   - extractDeclaredHandoffTerminalDisposition()
   - DeclaredHandoffTerminalDisposition type
   - Более тестируемо и переиспользуемо

4. **Regression tests** (successful-run-handoff.test.ts)
   - Парсирование done/cancelled/blocked disposition
   - Null return для undeclared disposition
   - Single-attempt exhaustion для routine tasks

- Regex patterns для disposition могут не покрыть все format variants
  → Добавлены patterns для русского и английского синтаксиса
- originKind может быть null или неполный
  → Используется fallback на description text search
- successfulRunHandoffRecoveryEvidence вызывается до exhausted check
  → Проверено: первый check не требует exhausted, second требует
- Safety check может быть недостаточно консервативным
  → Явно исключены все external approval категории по требованию issue

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Implement safe reconciliation for running runs when their target issue reaches
terminal status (done/cancelled) but the process is no longer alive.

Changes:
- Add reconcileProcesslessRunsTargetingTerminalIssues() to detect and cancel
  running runs targeting terminal issues that have no active process (PID dead)
- Integrate into heartbeatService.tickTimers() for automatic periodic detection
- Use POSIX kill(pid, 0) for reliable process liveness check
- Properly release execution locks and promote next queued runs
- Export function for testing

Fixes: live-run 886d86ef targeting done issue with missing PID 190091
Scope: Paperclip control-plane heartbeat reconciliation only

Test: Add heartbeat-processless-run-reconciliation.test.ts with 3 cases
- Detects and cancels processless run on terminal issue
- Preserves running run when issue is not terminal
- Skips run when process is still alive

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Paperclip <noreply@paperclip.ing>
# Conflicts:
#	scripts/daemons/telegram_approval_bot.py
recovery/pix-13213-heartbeat-fixes commit 7ac1e0283 corrupted
server/src/services/heartbeat.ts (net -3579 lines) despite its
commit message describing a small targeted addition. Subsequent
commit 8bd1b7c0c patched only part of that corrupted state back.
Auto-merge silently took the corrupted recovery side (13814 lines
vs master's 16601), dropping master content including PIX-13257
(scheduler terminal-status recheck) internals.

Master's providerUsageLimitHold path already implements
per-model-class gating (claudeUsageLimitModelClass, hold.modelClass)
independently of the recovery-branch approach, so no functional
regression from discarding the corrupted lineage. The one genuinely
new piece not yet in master -- making the fleet-wide weekly-cap
circuit breaker (isClaudeFleetUsageLimited) model-class-aware --
is tracked in a follow-up issue for clean reimplementation against
current master instead of reapplying a patch built on corrupted
history.
Same corruption pattern as heartbeat.ts: recovery-branch commit
9fef8fc57 ("stop recurring successful-run handoff loops for routine
snapshots") shrank this file by ~440 lines net despite its message
describing a small additive early-disposition check. Master's
recovery/service.ts is byte-identical to the pre-corruption
merge-base and already contains the exact same
declaredSuccessfulRunHandoffTerminalDisposition /
isSafeRoutineDispositionTarget /
resolveSuccessfulRunHandoffDeclaredDisposition logic (landed via
PIX-13213 PR #205), so keeping master is a pure no-op for that
feature and only discards the corrupted lineage.
Recovery-branch commit 9fef8fc57 accidentally deleted the
hasActiveRoutineContinuation input field/check and the title-
and description-based isContinuousOperatingLoopIssue markers
from successful-run-handoff.ts while leaving their regression
tests in place and the caller in heartbeat.ts (server/src/services/heartbeat.ts:6295-6309)
still passing hasActiveRoutineContinuation. Restored both from
the pre-9fef8fc57 revision, plus the missing test import for
declaredSuccessfulRunHandoffTerminalDisposition. All 24 tests in
successful-run-handoff.test.ts pass.
fix(recovery): export SuccessfulRunHandoffRecoveryEvidence type shared between service.ts and successful-run-handoff.ts
Some checks failed
security/pr-scan No security concerns detected
PR Quality Gates / PR Quality Gates (pull_request_target) Successful in 5s
Agents CI / Typecheck and Build (pull_request) Successful in 6m26s
Agents CI / API Tests (pull_request) Failing after 10m39s
f4154393eb
Merge reconciliation of recovery/pix-13213-heartbeat-fixes with master left service.ts with a private
duplicate of this type while the test imports it from successful-run-handoff.ts, breaking tsc --noEmit.
andrei merged commit d1311403b6 into master 2026-07-07 11:12:55 +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!215
No description provided.