<!-- task=control_plane_phase_exit_followups/01_heartbeat_timeout_runtime plan=1 tag=REVIEW_CLOUD_G06 -->

# Follow-up Plan - REVIEW_CLOUD_G06

## Metadata

- date: 2026-06-10
- task: `control_plane_phase_exit_followups/01_heartbeat_timeout_runtime`
- plan: 1
- tag: `REVIEW_CLOUD_G06`
- parent plan log: `plan_cloud_G06_0.log`
- parent review log: `code_review_cloud_G06_0.log`
- verdict source: FAIL follow-up

## Problem

The heartbeat timeout runtime loop implementation exists and `cd services/core && go test -count=1 ./...` passed during review, but the active review artifact was still marked `Status: pending implementation` and contained no implementation checklist or verification output. That makes the task unusable as phase-exit evidence.

The review also found two narrow hardening items:

- `Server.Start` and `StartListener` start the timeout loop before entering the blocking HTTP serving call, so an immediate serving failure can leave the loop running unless the caller also shuts the server down.
- `TestServerTimeoutLoopTransitionsRunnerOffline` relies on one fixed sleep instead of polling until the runtime status actually changes.

## Scope

- Keep the existing heartbeat timeout runtime behavior and public HTTP routes intact.
- Do not change runner protocol messages, registration compatibility, or CICD job/execution state semantics.
- Fix only the review artifact gap and the two narrow lifecycle/test-stability issues.

## Implementer Instructions

Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation. If a user-only decision, user-owned external prerequisite, or scope conflict blocks the work, fill the `사용자 리뷰 요청` section in `CODE_REVIEW-cloud-G06.md`, leave the active files in place, and stop for code review.

Evidence gaps that can be closed by rerunning commands or collecting output are normal implementation work, not user-review blockers.

## 구현 항목

### REVIEW_CLOUD_G06-1 - Restore review evidence

Problem: The previous active `CODE_REVIEW-cloud-G06.md` still said `Status: pending implementation` and had no filled implementation checklist or verification output.

Solution:

- Fill the new `CODE_REVIEW-cloud-G06.md` implementation-owned sections after code changes.
- Record changed files, implementation notes, and exact verification command output.
- Check the final mandatory implementation checklist item only after the review file has the required evidence.

Checklist:

- [ ] `CODE_REVIEW-cloud-G06.md` has no stale pending-implementation wording.
- [ ] `검증 결과` includes `cd services/core && go test -count=1 ./...` with actual output.
- [ ] `사용자 리뷰 요청` remains `없음` unless a valid user-only blocker exists.

### REVIEW_CLOUD_G06-2 - Tighten timeout loop lifecycle on serve failure

Problem: `services/core/internal/httpserver/server.go` starts the timeout loop before `ListenAndServe`/`Serve` returns control. If serving fails immediately, the timeout loop has already started and is not stopped by that failure path.

Solution:

- Keep the loop server-owned and started from `Start`/`StartListener`.
- Add an idempotent stop helper or equivalent lifecycle cleanup so serving failure stops the loop.
- Preserve normal shutdown behavior: `Shutdown` must still stop the loop and then shut down the HTTP server cleanly.
- Avoid changing production defaults (`90s` timeout, `30s` scan interval).

Checklist:

- [ ] Immediate `Serve`/`ListenAndServe` failure does not leave the timeout loop running.
- [ ] `Shutdown` remains safe when called after `Start`/`StartListener`.
- [ ] nil registry and non-positive scan interval still do not run timeout work.

### REVIEW_CLOUD_G06-3 - Stabilize runtime timeout test

Problem: `TestServerTimeoutLoopTransitionsRunnerOffline` uses a single `time.Sleep(120 * time.Millisecond)` before checking runner status. That proves the happy path locally but can be flaky under scheduler delay.

Solution:

- Replace the fixed sleep with a small polling helper that waits until the runner reaches `runnerregistry.StatusHeartbeatTimeout` or a bounded deadline expires.
- Keep the test as a server-level/runtime test using HTTP register and heartbeat routes.
- Keep the shutdown test focused on clean loop termination.

Checklist:

- [ ] Timeout transition test polls observed state until success or deadline.
- [ ] Failure output includes the last observed runner status.
- [ ] Test still proves runtime enforcement rather than calling `Registry.CheckTimeouts` directly.

## 구현 체크리스트

- [ ] [REVIEW_CLOUD_G06-1] active review 증적을 완성한다.
- [ ] [REVIEW_CLOUD_G06-2] timeout loop lifecycle 실패 경로를 정리한다.
- [ ] [REVIEW_CLOUD_G06-3] timeout runtime 테스트를 polling 기반으로 안정화한다.
- [ ] `CODE_REVIEW-cloud-G06.md`에 구현 결과와 검증 출력, 사용자 리뷰 요청 상태를 기록한다.

## Verification

- Required: `cd services/core && go test -count=1 ./...`

## Handoff Notes

- The previous reviewer already ran `cd services/core && go test -count=1 ./...` and it passed, but that output is not enough to close this follow-up because the active implementation artifact must be filled by the implementation pass.
- Keep the follow-up narrow; do not expand into timeout policy, runner revival semantics, or job state mutation.
