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.shdelegate_wait_started).
디스패치의 "에이전트가 바쁜가" 판정은 레지스트리(그 세션의 열린 잡)가 아니라 herdr 의 agent_status 다. herdr 상태는 실제와 어긋날 수 있다. 위의 ← 1 agent 사례가 그렇다.
#18·#23 의 원칙은 "생존·전달 판정은 프로토콜(레지스트리) 기준, 화면·herdr 상태는 진단용"이다. 이 두 곳은 아직 그 원칙을 따르지 않는다.
제안 — 코드로 강제
create --onboard 는 온보딩 종결까지 기다린다.
started 확인 뒤에도 레지스트리에서 그 잡이 종결 상태(completed/error/cancelled 등)가 될 때까지 기다린다.
끝나면 결과를 한 줄로 출력하고 종료한다.
상한은 환경변수로 둔다.
worktree_prepare.sh --onboard 도 같은 경로를 탄다. 오케스트레이터는 create 가 끝나는 것으로 온보딩 완료를 안다.
실패·시간 초과 시 세션을 유지한다.
온보딩이 error 로 끝났거나 상한 안에 끝나지 않으면, 살아 있는 에이전트를 죽이지 않는다. 지금의 "started 없음" 처리와 같은 원칙이다.
레지스트리 행에 온보딩 실패를 기록하고, 이름 붙은 0 아닌 종료 코드와 원인을 stderr 에 낸다. 시간 초과와 error 는 서로 다른 코드로 낸다.
잡 디스패치는 레지스트리 기준으로 기다린다.
래퍼가 주입하기 전에, 대상 세션의 이전 잡(온보딩 포함, 그 세션 행의 delegate_job_id 등)이 열려 있는지 레지스트리로 확인한다. 열려 있으면 종결까지 기다린다(상한 있음).
herdr agent_status 는 진단 참고로만 쓰고 취소 판정의 근거로 쓰지 않는다.
루프의 첫 잡이 아직 온보딩 중인 creator 에게 가는 경우, 그리고 resume 뒤나 수동 잡처럼 create 를 거치지 않은 경로도 같이 막힌다.
작업을 시작할 때 먼저 재현한다. ← 1 agent 로 working 이 이어지는 상태에서 실제로 session_busy 취소가 나는지 확인한 뒤 고친다.
동시 생성 테스트를 둔다.
온보딩 대기가 길어져도 여러 에이전트를 동시에 만들면 전체 시간은 가장 느린 온보딩 정도만 늘어나야 한다.
3 개를 동시에 create --onboard 하는 테스트로 레지스트리(이미 flock), git worktree, herdr 서버 첫 기동의 경합이 없는지 확인한다.
테스트는 실제 herdr 서버와 실제 발행 경로로 재현해야 한다. 가짜 상태로 대체하면 herdr 상태와 레지스트리가 어긋나는 이 문제를 잡지 못한다(#23 의 교훈). 실제 운영 세션(multi-agent-mux)과 실제 .mam 은 건드리지 않는다.
## 증상
`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 하위 잡 대기
온보딩 대기: 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·2 (create --onboard 가 온보딩 종결까지 기다림, 실패해도 세션 유지) — 범위가 분명하고 위험이 낮다. 시간 초과와 error 를 서로 다른 종료 코드로, 레지스트리 행에 온보딩 실패 기록. worktree_prepare.sh --onboard 도 같은 경로. 제안 4(3 개 동시 create --onboard) 테스트를 함께 둔다.
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` 은 건드리지 않는다.
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.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
증상
multi-agent-mux-create --onboard(및 이를 부르는worktree_prepare.sh --onboard)는 온보딩 잡의started만 확인하고 끝난다(#23 의 전달 확인). 온보딩이 실제로 끝났는지(completed/error)는 아무도 기다리지 않고, 끝났다는 알림도 없다.completed는 레지스트리에만 남는다.agent_status)로 완료를 짐작하게 되는데, 그 근거가 틀릴 수 있다.같은 원인이 잡 디스패치에도 있다. 래퍼는 잡을 주입하기 전에 대상 에이전트가 쉬는지를 herdr 상태로 판단한다(
wait_for_agent_idle, 기본 120 s). 그 안에working이 풀리지 않으면 잡을cancelled(session_busy)로 닫는다.관측
started, 09:39:05 에completed로 끝났다. 그런데 herdr 는 그 뒤로도 10 분 넘게working을 보고했다. 화면 아래← 1 agent(백그라운드 에이전트) 표시가 남아 있었다. 오케스트레이터는herdr agent wait --until idle로 기다리다 시간 초과가 났다.started직후인 04:29:54 에 끝났고,completed는 04:30:15 에 기록됐다. 그 사이를 기다리는 주체가 없었다.started→completed)은 실측 10–20 s 였다.원인
started까지다(create_session.sh의delegate_wait_started,lib.shdelegate_wait_started).agent_status다. herdr 상태는 실제와 어긋날 수 있다. 위의← 1 agent사례가 그렇다.제안 — 코드로 강제
--onboard는 온보딩 종결까지 기다린다.started확인 뒤에도 레지스트리에서 그 잡이 종결 상태(completed/error/cancelled등)가 될 때까지 기다린다.worktree_prepare.sh --onboard도 같은 경로를 탄다. 오케스트레이터는 create 가 끝나는 것으로 온보딩 완료를 안다.error로 끝났거나 상한 안에 끝나지 않으면, 살아 있는 에이전트를 죽이지 않는다. 지금의 "started 없음" 처리와 같은 원칙이다.error는 서로 다른 코드로 낸다.delegate_job_id등)이 열려 있는지 레지스트리로 확인한다. 열려 있으면 종결까지 기다린다(상한 있음).agent_status는 진단 참고로만 쓰고 취소 판정의 근거로 쓰지 않는다.← 1 agent로working이 이어지는 상태에서 실제로session_busy취소가 나는지 확인한 뒤 고친다.--onboard하는 테스트로 레지스트리(이미 flock),git worktree, herdr 서버 첫 기동의 경합이 없는지 확인한다.테스트는 실제 herdr 서버와 실제 발행 경로로 재현해야 한다. 가짜 상태로 대체하면 herdr 상태와 레지스트리가 어긋나는 이 문제를 잡지 못한다(#23 의 교훈). 실제 운영 세션(
multi-agent-mux)과 실제.mam은 건드리지 않는다.관련
started기준)진행 계획과 작업 전 결정 사항 (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) 은 통과한다.진행 순서 제안 — 두 단계로 나눈다
worktree_prepare.sh --onboard도 같은 경로. 제안 4(3 개 동시 create --onboard) 테스트를 함께 둔다.← 1 agent로 working 이 이어지는 상태에서 실제 session_busy 취소가 나는지, 실제 herdr 서버로). 아래 결정을 내린 뒤 고친다.2단계 전에 정할 것
blocked는 남긴다. 대화창·권한 프롬프트는 화면으로만 알 수 있다. 막힌 창에 키를 넣지 않도록 blocked 판정(rc 2)은 유지하는 편이 맞다. 바꾸는 것은 working → 취소 경로뿐.transport: remote는 지금 herdr 확인을 건너뛴다. 레지스트리 기준 대기로 바꾸면 원격 세션에도 같은 대기가 생겨 오히려 일관된다 — 범위에 넣을지 결정 필요.테스트는 본문대로 실제 herdr 서버와 실제 발행 경로로 재현하고, 운영 세션(multi-agent-mux)과 실제
.mam은 건드리지 않는다.Fixed by PR #56, merged as
8b0b4a0(v4.9.0). Verified on macOS / Python 3.9: full suite at21f0c12passed (1860 passed / 0 failed / 38 skipped) after a unanimous review.