[Enhancement] create --onboard 가 온보딩 완료까지 기다리지 않고, 디스패치의 바쁨 판정이 herdr 상태에 기대 실제와 어긋난다 #50

Closed
opened 2026-10-06 04:41:00 +00:00 by Godopu · 2 comments
Owner

증상

multi-agent-mux-create --onboard(및 이를 부르는 worktree_prepare.sh --onboard)는 온보딩 잡의 started 만 확인하고 끝난다(#23 의 전달 확인). 온보딩이 실제로 끝났는지(completed/error)는 아무도 기다리지 않고, 끝났다는 알림도 없다.

  • 오케스트레이터는 MQTT 이벤트를 직접 받지 않는다. create 가 끝난 뒤의 completed 는 레지스트리에만 남는다.
  • 그래서 오케스트레이터가 다른 근거(herdr 의 agent_status)로 완료를 짐작하게 되는데, 그 근거가 틀릴 수 있다.

같은 원인이 잡 디스패치에도 있다. 래퍼는 잡을 주입하기 전에 대상 에이전트가 쉬는지를 herdr 상태로 판단한다(wait_for_agent_idle, 기본 120 s). 그 안에 working 이 풀리지 않으면 잡을 cancelled(session_busy)로 닫는다.

관측

  • 2026-10-04 (#45, creator-claude-issue45). 온보딩은 09:38:55 에 started, 09:39:05 에 completed 로 끝났다. 그런데 herdr 는 그 뒤로도 10 분 넘게 working 을 보고했다. 화면 아래 ← 1 agent(백그라운드 에이전트) 표시가 남아 있었다. 오케스트레이터는 herdr agent wait --until idle 로 기다리다 시간 초과가 났다.
  • 2026-10-06 (creator-claude-issue17). 잡이 04:29:45 에 생성됐다. create 는 started 직후인 04:29:54 에 끝났고, completed 는 04:30:15 에 기록됐다. 그 사이를 기다리는 주체가 없었다.
  • 온보딩 시간(started → completed)은 실측 10–20 s 였다.

원인

  • create 의 대기는 started 까지다(create_session.sh 의 delegate_wait_started, lib.sh delegate_wait_started).
  • 디스패치의 "에이전트가 바쁜가" 판정은 레지스트리(그 세션의 열린 잡)가 아니라 herdr 의 agent_status 다. herdr 상태는 실제와 어긋날 수 있다. 위의 ← 1 agent 사례가 그렇다.
  • #18·#23 의 원칙은 "생존·전달 판정은 프로토콜(레지스트리) 기준, 화면·herdr 상태는 진단용"이다. 이 두 곳은 아직 그 원칙을 따르지 않는다.

제안 — 코드로 강제

  1. create --onboard 는 온보딩 종결까지 기다린다.
    • started 확인 뒤에도 레지스트리에서 그 잡이 종결 상태(completed/error/cancelled 등)가 될 때까지 기다린다.
    • 끝나면 결과를 한 줄로 출력하고 종료한다.
    • 상한은 환경변수로 둔다.
    • worktree_prepare.sh --onboard 도 같은 경로를 탄다. 오케스트레이터는 create 가 끝나는 것으로 온보딩 완료를 안다.
  2. 실패·시간 초과 시 세션을 유지한다.
    • 온보딩이 error 로 끝났거나 상한 안에 끝나지 않으면, 살아 있는 에이전트를 죽이지 않는다. 지금의 "started 없음" 처리와 같은 원칙이다.
    • 레지스트리 행에 온보딩 실패를 기록하고, 이름 붙은 0 아닌 종료 코드와 원인을 stderr 에 낸다. 시간 초과와 error 는 서로 다른 코드로 낸다.
  3. 잡 디스패치는 레지스트리 기준으로 기다린다.
    • 래퍼가 주입하기 전에, 대상 세션의 이전 잡(온보딩 포함, 그 세션 행의 delegate_job_id 등)이 열려 있는지 레지스트리로 확인한다. 열려 있으면 종결까지 기다린다(상한 있음).
    • herdr agent_status 는 진단 참고로만 쓰고 취소 판정의 근거로 쓰지 않는다.
    • 루프의 첫 잡이 아직 온보딩 중인 creator 에게 가는 경우, 그리고 resume 뒤나 수동 잡처럼 create 를 거치지 않은 경로도 같이 막힌다.
    • 작업을 시작할 때 먼저 재현한다. ← 1 agent 로 working 이 이어지는 상태에서 실제로 session_busy 취소가 나는지 확인한 뒤 고친다.
  4. 동시 생성 테스트를 둔다.
    • 온보딩 대기가 길어져도 여러 에이전트를 동시에 만들면 전체 시간은 가장 느린 온보딩 정도만 늘어나야 한다.
    • 3 개를 동시에 create --onboard 하는 테스트로 레지스트리(이미 flock), git worktree, herdr 서버 첫 기동의 경합이 없는지 확인한다.

테스트는 실제 herdr 서버와 실제 발행 경로로 재현해야 한다. 가짜 상태로 대체하면 herdr 상태와 레지스트리가 어긋나는 이 문제를 잡지 못한다(#23 의 교훈). 실제 운영 세션(multi-agent-mux)과 실제 .mam 은 건드리지 않는다.

관련

  • #23 온보딩 전달 확인(started 기준)
  • #18 생존 계약(프로토콜 기준 판정)
  • #30 멈춘 에이전트 알림
  • #34 하위 잡 대기
## 증상 `multi-agent-mux-create --onboard`(및 이를 부르는 `worktree_prepare.sh --onboard`)는 온보딩 잡의 `started` 만 확인하고 끝난다(#23 의 전달 확인). 온보딩이 실제로 끝났는지(`completed`/`error`)는 아무도 기다리지 않고, 끝났다는 알림도 없다. - 오케스트레이터는 MQTT 이벤트를 직접 받지 않는다. create 가 끝난 뒤의 `completed` 는 레지스트리에만 남는다. - 그래서 오케스트레이터가 다른 근거(herdr 의 `agent_status`)로 완료를 짐작하게 되는데, 그 근거가 틀릴 수 있다. 같은 원인이 잡 디스패치에도 있다. 래퍼는 잡을 주입하기 전에 대상 에이전트가 쉬는지를 **herdr 상태**로 판단한다(`wait_for_agent_idle`, 기본 120 s). 그 안에 `working` 이 풀리지 않으면 잡을 `cancelled`(`session_busy`)로 닫는다. ## 관측 - **2026-10-04 (#45, creator-claude-issue45).** 온보딩은 09:38:55 에 `started`, 09:39:05 에 `completed` 로 끝났다. 그런데 herdr 는 그 뒤로도 10 분 넘게 `working` 을 보고했다. 화면 아래 `← 1 agent`(백그라운드 에이전트) 표시가 남아 있었다. 오케스트레이터는 `herdr agent wait --until idle` 로 기다리다 시간 초과가 났다. - **2026-10-06 (creator-claude-issue17).** 잡이 04:29:45 에 생성됐다. create 는 `started` 직후인 04:29:54 에 끝났고, `completed` 는 04:30:15 에 기록됐다. 그 사이를 기다리는 주체가 없었다. - 온보딩 시간(`started` → `completed`)은 실측 10–20 s 였다. ## 원인 - create 의 대기는 `started` 까지다(`create_session.sh` 의 `delegate_wait_started`, `lib.sh` `delegate_wait_started`). - 디스패치의 "에이전트가 바쁜가" 판정은 레지스트리(그 세션의 열린 잡)가 아니라 herdr 의 `agent_status` 다. herdr 상태는 실제와 어긋날 수 있다. 위의 `← 1 agent` 사례가 그렇다. - #18·#23 의 원칙은 "생존·전달 판정은 프로토콜(레지스트리) 기준, 화면·herdr 상태는 진단용"이다. 이 두 곳은 아직 그 원칙을 따르지 않는다. ## 제안 — 코드로 강제 1. **create `--onboard` 는 온보딩 종결까지 기다린다.** - `started` 확인 뒤에도 레지스트리에서 그 잡이 종결 상태(`completed`/`error`/`cancelled` 등)가 될 때까지 기다린다. - 끝나면 결과를 한 줄로 출력하고 종료한다. - 상한은 환경변수로 둔다. - `worktree_prepare.sh --onboard` 도 같은 경로를 탄다. 오케스트레이터는 create 가 끝나는 것으로 온보딩 완료를 안다. 2. **실패·시간 초과 시 세션을 유지한다.** - 온보딩이 `error` 로 끝났거나 상한 안에 끝나지 않으면, 살아 있는 에이전트를 죽이지 않는다. 지금의 "started 없음" 처리와 같은 원칙이다. - 레지스트리 행에 온보딩 실패를 기록하고, **이름 붙은 0 아닌 종료 코드**와 원인을 stderr 에 낸다. 시간 초과와 `error` 는 서로 다른 코드로 낸다. 3. **잡 디스패치는 레지스트리 기준으로 기다린다.** - 래퍼가 주입하기 전에, 대상 세션의 이전 잡(온보딩 포함, 그 세션 행의 `delegate_job_id` 등)이 열려 있는지 레지스트리로 확인한다. 열려 있으면 종결까지 기다린다(상한 있음). - herdr `agent_status` 는 진단 참고로만 쓰고 취소 판정의 근거로 쓰지 않는다. - 루프의 첫 잡이 아직 온보딩 중인 creator 에게 가는 경우, 그리고 resume 뒤나 수동 잡처럼 create 를 거치지 않은 경로도 같이 막힌다. - 작업을 시작할 때 먼저 재현한다. `← 1 agent` 로 `working` 이 이어지는 상태에서 실제로 `session_busy` 취소가 나는지 확인한 뒤 고친다. 4. **동시 생성 테스트를 둔다.** - 온보딩 대기가 길어져도 여러 에이전트를 동시에 만들면 전체 시간은 가장 느린 온보딩 정도만 늘어나야 한다. - 3 개를 동시에 create `--onboard` 하는 테스트로 레지스트리(이미 flock), `git worktree`, herdr 서버 첫 기동의 경합이 없는지 확인한다. 테스트는 실제 herdr 서버와 실제 발행 경로로 재현해야 한다. 가짜 상태로 대체하면 herdr 상태와 레지스트리가 어긋나는 이 문제를 잡지 못한다(#23 의 교훈). 실제 운영 세션(`multi-agent-mux`)과 실제 `.mam` 은 건드리지 않는다. ## 관련 - #23 온보딩 전달 확인(`started` 기준) - #18 생존 계약(프로토콜 기준 판정) - #30 멈춘 에이전트 알림 - #34 하위 잡 대기
Author
Owner

진행 계획과 작업 전 결정 사항 (2026-10-10, main 42e5173 기준)

코드 위치 확인

  • 온보딩 대기: create_session.sh:675 의 delegate_wait_started 는 started 까지만 기다린다(상한 MAM_ONBOARD_ACK_S → MAM_INJECT_ACK_GRACE_S → DELEGATE_JOB_ACK_S, 기본 180 s).
  • 바쁨 판정: 래퍼 multi-agent-mux-delegate-job:360(direct)·:523(loop/discuss) 가 wait_for_agent_idle(lib.sh:2664) 을 부른다. 이것은 herdr agent get 의 agent_status 만 보며, 상한 SKS_IDLE_TIMEOUT_MS(기본 120000) 안에 working 이 풀리지 않거나(rc 1) blocked 이면(rc 2) 잡을 닫고 exit 5 로 끝난다. unknown(rc 3) 은 통과한다.

진행 순서 제안 — 두 단계로 나눈다

  1. 1단계: 제안 1·2 (create --onboard 가 온보딩 종결까지 기다림, 실패해도 세션 유지) — 범위가 분명하고 위험이 낮다. 시간 초과와 error 를 서로 다른 종료 코드로, 레지스트리 행에 온보딩 실패 기록. worktree_prepare.sh --onboard 도 같은 경로. 제안 4(3 개 동시 create --onboard) 테스트를 함께 둔다.
  2. 2단계: 제안 3 (디스패치 바쁨 판정을 레지스트리 기준으로) — 먼저 재현한다(← 1 agent 로 working 이 이어지는 상태에서 실제 session_busy 취소가 나는지, 실제 herdr 서버로). 아래 결정을 내린 뒤 고친다.

2단계 전에 정할 것

  • 레지스트리가 모르는 바쁨. 레지스트리는 MAM 잡만 안다. 사용자가 에이전트 창에 직접 입력해 일을 시키는 중이면 열린 잡이 없으므로, herdr 상태를 완전히 버리면 그 위에 프롬프트를 주입할 수 있다. 안: (a) 열린 잡이 있으면 레지스트리로 종결까지 대기, (b) 열린 잡이 없는데 herdr 가 working 이면 경고만 남기고 주입(또는 짧은 상한 뒤 주입) — 취소 근거로는 쓰지 않는다.
  • blocked 는 남긴다. 대화창·권한 프롬프트는 화면으로만 알 수 있다. 막힌 창에 키를 넣지 않도록 blocked 판정(rc 2)은 유지하는 편이 맞다. 바꾸는 것은 working → 취소 경로뿐.
  • 원격 세션. transport: remote 는 지금 herdr 확인을 건너뛴다. 레지스트리 기준 대기로 바꾸면 원격 세션에도 같은 대기가 생겨 오히려 일관된다 — 범위에 넣을지 결정 필요.

테스트는 본문대로 실제 herdr 서버와 실제 발행 경로로 재현하고, 운영 세션(multi-agent-mux)과 실제 .mam 은 건드리지 않는다.

## 진행 계획과 작업 전 결정 사항 (2026-10-10, main 42e5173 기준) ### 코드 위치 확인 - 온보딩 대기: `create_session.sh:675` 의 `delegate_wait_started` 는 `started` 까지만 기다린다(상한 `MAM_ONBOARD_ACK_S` → `MAM_INJECT_ACK_GRACE_S` → `DELEGATE_JOB_ACK_S`, 기본 180 s). - 바쁨 판정: 래퍼 `multi-agent-mux-delegate-job:360`(direct)·`:523`(loop/discuss) 가 `wait_for_agent_idle`(`lib.sh:2664`) 을 부른다. 이것은 `herdr agent get` 의 `agent_status` 만 보며, 상한 `SKS_IDLE_TIMEOUT_MS`(기본 120000) 안에 working 이 풀리지 않거나(rc 1) blocked 이면(rc 2) 잡을 닫고 exit 5 로 끝난다. unknown(rc 3) 은 통과한다. ### 진행 순서 제안 — 두 단계로 나눈다 1. **1단계: 제안 1·2 (create --onboard 가 온보딩 종결까지 기다림, 실패해도 세션 유지)** — 범위가 분명하고 위험이 낮다. 시간 초과와 error 를 서로 다른 종료 코드로, 레지스트리 행에 온보딩 실패 기록. `worktree_prepare.sh --onboard` 도 같은 경로. 제안 4(3 개 동시 create --onboard) 테스트를 함께 둔다. 2. **2단계: 제안 3 (디스패치 바쁨 판정을 레지스트리 기준으로)** — 먼저 재현한다(`← 1 agent` 로 working 이 이어지는 상태에서 실제 session_busy 취소가 나는지, 실제 herdr 서버로). 아래 결정을 내린 뒤 고친다. ### 2단계 전에 정할 것 - **레지스트리가 모르는 바쁨.** 레지스트리는 MAM 잡만 안다. 사용자가 에이전트 창에 직접 입력해 일을 시키는 중이면 열린 잡이 없으므로, herdr 상태를 완전히 버리면 그 위에 프롬프트를 주입할 수 있다. 안: (a) 열린 잡이 있으면 레지스트리로 종결까지 대기, (b) 열린 잡이 없는데 herdr 가 working 이면 경고만 남기고 주입(또는 짧은 상한 뒤 주입) — 취소 근거로는 쓰지 않는다. - **`blocked` 는 남긴다.** 대화창·권한 프롬프트는 화면으로만 알 수 있다. 막힌 창에 키를 넣지 않도록 blocked 판정(rc 2)은 유지하는 편이 맞다. 바꾸는 것은 working → 취소 경로뿐. - **원격 세션.** `transport: remote` 는 지금 herdr 확인을 건너뛴다. 레지스트리 기준 대기로 바꾸면 원격 세션에도 같은 대기가 생겨 오히려 일관된다 — 범위에 넣을지 결정 필요. 테스트는 본문대로 실제 herdr 서버와 실제 발행 경로로 재현하고, 운영 세션(multi-agent-mux)과 실제 `.mam` 은 건드리지 않는다.
Author
Owner

Fixed by PR #56, merged as 8b0b4a0 (v4.9.0). Verified on macOS / Python 3.9: full suite at 21f0c12 passed (1860 passed / 0 failed / 38 skipped) after a unanimous review.

Fixed by PR #56, merged as 8b0b4a0 (v4.9.0). Verified on macOS / Python 3.9: full suite at 21f0c12 passed (1860 passed / 0 failed / 38 skipped) after a unanimous review.
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: tmpl/multi-agent-mux#50