[Bug] 격리 herdr 서버가 재기동되면 저장 레이아웃 복원으로 에이전트가 권한 플래그 없이 뜨고, 지운 에이전트까지 되살아난다 #45

Closed
opened 2026-10-03 13:10:23 +00:00 by Godopu · 2 comments
Owner

증상

격리 herdr 서버(multi-agent-mux)가 에이전트를 정지하지 않은 채 꺼진 뒤(재부팅 등) 다시 뜨면, herdr 가 저장해 둔 레이아웃을 복원하면서 에이전트를 herdr 자신의 재개 명령으로 다시 실행한다. MAM 의 등록부와 무관하게 일어나며, 결과가 두 가지다.

  1. 권한 플래그 없이 에이전트가 뜬다. herdr 의 재개 명령은 claude --resume <id>, grok --resume <id>, agy --conversation <id> 처럼 MAM 이 붙이는 --dangerously-skip-permissions / --permission-mode bypassPermissions 가 없다. 이대로 잡을 받으면 권한 확인 창에 멈춘다(#30 의 blocked).
  2. MAM 이 지운 에이전트가 되살아난다. 서버가 꺼진 상태에서 지운 에이전트의 pane 은 저장 레이아웃에 남아 있어, 서버가 뜰 때 이미 지운 대화 ID 로 다시 실행된다.

여기에 MAM 쪽 동작이 겹쳐 상황이 가려진다.

  • resume_session.sh 는 복원된 에이전트를 보고 "already running" 으로 처리해, 실제 프로세스와 다른 자기 cmd_full 로 행을 running 으로 갱신하고 실행을 건너뛴다. 행과 실제 프로세스가 어긋난다.
  • 복원되지 않은 에이전트(관측상 codex)는 재개가 다른 에이전트가 차지한 pane 을 쓰려다 실패하기도 한다(agent_pane_busy … is not an available shell).
  • 복원된 에이전트가 herdr 이름 없이 뜨면 stop_session.sh 가 그 프로세스를 찾지 못한 채 행만 갱신하고 대화 파일을 지운다(프로세스는 계속 실행).

관측

2026-09-23 (herdr 업그레이드 뒤 서버 재시작): 재개를 시작하자 claude 두 개, grok, agy 가 플래그 없이 복원됐고 codex 는 복원되지 않아 재개가 agent_pane_busy 로 실패했다. 네 개를 정지하고 다시 재개해 바로잡았다.

2026-10-03 (재부팅, 에이전트 정지 없이): 서버가 꺼진 상태에서 오케스트레이터가 creator-claude-issue26 을 --purge-conversation 으로 지웠다. stop_session.sh 는 herdr already dead, just updating YAML 로 행과 대화 파일만 지웠다. 이어 재개가 서버를 띄우자:

13:05:09 WARN herdr::persist::restore: saved pane cwd does not exist, falling back to HOME cwd=…/multi-agent-mux-wt/issue-26
13:05:09 INFO session restore evaluated event="persist.restore" outcome="ok" workspaces=3
  • 팀 리더 셋(claude 플래너, agy, grok)이 플래그 없이 복원됐다.
  • 지운 creator 가 홈 디렉터리에서 삭제된 대화 ID 로 claude --resume edda5fdc… 를 실행하고 "이 폴더를 신뢰하는가" 창에서 blocked 로 멈췄다. 워크트리는 이미 지워져 있었다.
  • 재개는 셋에 대해 "already running" 을 냈고, codex 는 첫 재개가 실패했다.

원인

  • herdr 는 named session 의 레이아웃을 ~/.config/herdr/sessions/<name>/session.json 에 저장한다. pane 마다 cwd 와 agent_session(에이전트 종류, 대화 ID)이 들어 있고, 서버 기동 시 persist::restore 가 이를 되살린다.
  • MAM 이 서버가 떠 있을 때 정지하면 pane 이 닫혀 저장 레이아웃에서도 빠진다. 그래서 정상 정지 뒤에는 문제가 없다.
  • 서버가 꺼진 상태에서는 MAM 이 저장 레이아웃을 건드릴 수 없다. 정지·삭제는 등록부와 대화 파일만 바꾸고, 다음 서버 기동 때 herdr 가 옛 pane 을 그대로 되살린다.
  • herdr 0.9.1 의 CLI 도움말과 설정에서 복원을 끄는 옵션은 찾지 못했다(확인 필요).

제안 — 코드로 강제

사람이 "재부팅 전에 정지하라"는 절차를 지키는 데 기대지 않는다. 재부팅은 예고 없이 일어난다.

  1. 서버 기동 직후 복원 결과를 등록부와 대조한다. MAM 이 격리 서버를 띄우는 모든 경로(lib.sh 의 headless bootstrap)에서, 기동 직후 herdr agent list·pane process-info 로 복원된 pane 을 읽어 등록부와 맞춘다.
    • 등록부에 없거나 terminated·stopped 인 에이전트의 pane 은 닫는다(지운 에이전트가 되살아나지 않게).
    • 등록부에 running 인데 실제 명령줄이 MAM 의 cmd_full(권한 플래그 포함)과 다르면, 그 pane 을 닫고 MAM 의 재개 경로로 다시 띄운다.
  2. resume_session.sh 의 "already running" 판단에 명령줄 검증을 넣는다. 이름만 보고 실행 중이라 판단하지 말고, 실제 프로세스의 명령줄이 행의 cmd_full 과 같은지 확인한다. 다르면 위 1 과 같이 처리한다.
  3. 서버가 꺼진 상태의 정지·삭제가 저장 레이아웃을 남기지 않게 한다. 방법은 계획에서 정한다: herdr 가 복원을 끄는 수단을 제공하면 MAM 의 격리 서버에서 끈다 / 정지·삭제 시 서버를 복원 없이 잠깐 띄워 pane 을 닫는다 / 다음 기동 때 1 의 대조가 처리하게 둔다 등. session.json 을 직접 고치는 방식은 herdr 내부 형식에 기대므로 피한다.
  4. 이름 없이 뜬 에이전트도 정지 대상에 잡히게 한다. 등록부의 대화 ID 와 프로세스 명령줄로 찾는다.

테스트는 실제 herdr 0.9.1 서버로, 저장 레이아웃이 남은 상태에서 서버를 띄워 복원이 일어나는 경로를 그대로 재현해야 한다. 가짜 herdr 로 대체하면 이 문제를 잡지 못한다.

관련

  • #30 멈춘 에이전트 알림 (플래그 없이 복원된 에이전트가 권한 창에 멈추는 경우)
  • #24 시작 다이얼로그 처리 (되살아난 에이전트가 신뢰 창에 멈춘 경우)
  • #21 워크트리 운영 모델 (지운 워크트리 경로의 pane 이 홈 디렉터리로 복원된 경우)
## 증상 격리 herdr 서버(`multi-agent-mux`)가 에이전트를 정지하지 않은 채 꺼진 뒤(재부팅 등) 다시 뜨면, herdr 가 저장해 둔 레이아웃을 복원하면서 에이전트를 **herdr 자신의 재개 명령으로** 다시 실행한다. MAM 의 등록부와 무관하게 일어나며, 결과가 두 가지다. 1. **권한 플래그 없이 에이전트가 뜬다.** herdr 의 재개 명령은 `claude --resume <id>`, `grok --resume <id>`, `agy --conversation <id>` 처럼 MAM 이 붙이는 `--dangerously-skip-permissions` / `--permission-mode bypassPermissions` 가 없다. 이대로 잡을 받으면 권한 확인 창에 멈춘다(#30 의 `blocked`). 2. **MAM 이 지운 에이전트가 되살아난다.** 서버가 꺼진 상태에서 지운 에이전트의 pane 은 저장 레이아웃에 남아 있어, 서버가 뜰 때 이미 지운 대화 ID 로 다시 실행된다. 여기에 MAM 쪽 동작이 겹쳐 상황이 가려진다. - `resume_session.sh` 는 복원된 에이전트를 보고 "already running" 으로 처리해, 실제 프로세스와 다른 자기 `cmd_full` 로 행을 `running` 으로 갱신하고 실행을 건너뛴다. 행과 실제 프로세스가 어긋난다. - 복원되지 않은 에이전트(관측상 codex)는 재개가 다른 에이전트가 차지한 pane 을 쓰려다 실패하기도 한다(`agent_pane_busy … is not an available shell`). - 복원된 에이전트가 herdr 이름 없이 뜨면 `stop_session.sh` 가 그 프로세스를 찾지 못한 채 행만 갱신하고 대화 파일을 지운다(프로세스는 계속 실행). ## 관측 **2026-09-23** (herdr 업그레이드 뒤 서버 재시작): 재개를 시작하자 claude 두 개, grok, agy 가 플래그 없이 복원됐고 codex 는 복원되지 않아 재개가 `agent_pane_busy` 로 실패했다. 네 개를 정지하고 다시 재개해 바로잡았다. **2026-10-03** (재부팅, 에이전트 정지 없이): 서버가 꺼진 상태에서 오케스트레이터가 `creator-claude-issue26` 을 `--purge-conversation` 으로 지웠다. `stop_session.sh` 는 `herdr already dead, just updating YAML` 로 행과 대화 파일만 지웠다. 이어 재개가 서버를 띄우자: ``` 13:05:09 WARN herdr::persist::restore: saved pane cwd does not exist, falling back to HOME cwd=…/multi-agent-mux-wt/issue-26 13:05:09 INFO session restore evaluated event="persist.restore" outcome="ok" workspaces=3 ``` - 팀 리더 셋(claude 플래너, agy, grok)이 플래그 없이 복원됐다. - 지운 creator 가 홈 디렉터리에서 **삭제된 대화 ID 로** `claude --resume edda5fdc…` 를 실행하고 "이 폴더를 신뢰하는가" 창에서 `blocked` 로 멈췄다. 워크트리는 이미 지워져 있었다. - 재개는 셋에 대해 "already running" 을 냈고, codex 는 첫 재개가 실패했다. ## 원인 - herdr 는 named session 의 레이아웃을 `~/.config/herdr/sessions/<name>/session.json` 에 저장한다. pane 마다 `cwd` 와 `agent_session`(에이전트 종류, 대화 ID)이 들어 있고, 서버 기동 시 `persist::restore` 가 이를 되살린다. - MAM 이 서버가 떠 있을 때 정지하면 pane 이 닫혀 저장 레이아웃에서도 빠진다. 그래서 정상 정지 뒤에는 문제가 없다. - 서버가 꺼진 상태에서는 MAM 이 저장 레이아웃을 건드릴 수 없다. 정지·삭제는 등록부와 대화 파일만 바꾸고, 다음 서버 기동 때 herdr 가 옛 pane 을 그대로 되살린다. - herdr 0.9.1 의 CLI 도움말과 설정에서 복원을 끄는 옵션은 찾지 못했다(확인 필요). ## 제안 — 코드로 강제 사람이 "재부팅 전에 정지하라"는 절차를 지키는 데 기대지 않는다. 재부팅은 예고 없이 일어난다. 1. **서버 기동 직후 복원 결과를 등록부와 대조한다.** MAM 이 격리 서버를 띄우는 모든 경로(lib.sh 의 headless bootstrap)에서, 기동 직후 `herdr agent list`·`pane process-info` 로 복원된 pane 을 읽어 등록부와 맞춘다. - 등록부에 없거나 `terminated`·`stopped` 인 에이전트의 pane 은 닫는다(지운 에이전트가 되살아나지 않게). - 등록부에 `running` 인데 실제 명령줄이 MAM 의 `cmd_full`(권한 플래그 포함)과 다르면, 그 pane 을 닫고 MAM 의 재개 경로로 다시 띄운다. 2. **`resume_session.sh` 의 "already running" 판단에 명령줄 검증을 넣는다.** 이름만 보고 실행 중이라 판단하지 말고, 실제 프로세스의 명령줄이 행의 `cmd_full` 과 같은지 확인한다. 다르면 위 1 과 같이 처리한다. 3. **서버가 꺼진 상태의 정지·삭제가 저장 레이아웃을 남기지 않게 한다.** 방법은 계획에서 정한다: herdr 가 복원을 끄는 수단을 제공하면 MAM 의 격리 서버에서 끈다 / 정지·삭제 시 서버를 복원 없이 잠깐 띄워 pane 을 닫는다 / 다음 기동 때 1 의 대조가 처리하게 둔다 등. `session.json` 을 직접 고치는 방식은 herdr 내부 형식에 기대므로 피한다. 4. **이름 없이 뜬 에이전트도 정지 대상에 잡히게 한다.** 등록부의 대화 ID 와 프로세스 명령줄로 찾는다. 테스트는 실제 herdr 0.9.1 서버로, 저장 레이아웃이 남은 상태에서 서버를 띄워 복원이 일어나는 경로를 그대로 재현해야 한다. 가짜 herdr 로 대체하면 이 문제를 잡지 못한다. ## 관련 - #30 멈춘 에이전트 알림 (플래그 없이 복원된 에이전트가 권한 창에 멈추는 경우) - #24 시작 다이얼로그 처리 (되살아난 에이전트가 신뢰 창에 멈춘 경우) - #21 워크트리 운영 모델 (지운 워크트리 경로의 pane 이 홈 디렉터리로 복원된 경우)
Author
Owner

PR #48(v4.8.5, 7209750)로 닫힙니다. 합의한 범위 A+B:

  • A: MAM 이 격리 herdr 서버를 띄울 때 사용자 herdr 설정에 [session] resume_agents_on_restore = false 만 더한 설정을 HERDR_CONFIG_PATH 로 넘긴다. 서버가 재기동돼도 저장된 에이전트를 권한 플래그 없이 다시 띄우지 않는다.
  • B: 복원된 pane 은 빈 셸로 돌아오는데, resume 이 기동 직전에 자기 세션의 그 빈 셸만 닫는다.

주의: 설정 병합에는 PATH 의 python3 에 tomllib(3.11+)이 필요하다. macOS 기본 3.9 에서는 복원은 꺼지지만 MAM 서버가 사용자의 다른 herdr 설정 없이 뜬다(경고 표시). 사용자가 직접 attach 해 띄운 서버는 복원이 켜진 채 남으므로, 재부팅 뒤에는 attach 전에 /multi-agent-mux-resume 을 먼저 실행한다.

범위 밖으로 남은 제안: 2(resume 의 "already running" 판단에 명령줄 검증), 4(이름 없이 뜬 에이전트 정지). 필요하면 새 이슈로 다룬다. 리뷰 경과는 PR #48 코멘트에 있습니다.

PR #48(v4.8.5, 7209750)로 닫힙니다. 합의한 범위 A+B: - **A**: MAM 이 격리 herdr 서버를 띄울 때 사용자 herdr 설정에 `[session] resume_agents_on_restore = false` 만 더한 설정을 `HERDR_CONFIG_PATH` 로 넘긴다. 서버가 재기동돼도 저장된 에이전트를 권한 플래그 없이 다시 띄우지 않는다. - **B**: 복원된 pane 은 빈 셸로 돌아오는데, resume 이 기동 직전에 자기 세션의 그 빈 셸만 닫는다. 주의: 설정 병합에는 PATH 의 `python3` 에 `tomllib`(3.11+)이 필요하다. macOS 기본 3.9 에서는 복원은 꺼지지만 MAM 서버가 사용자의 다른 herdr 설정 없이 뜬다(경고 표시). 사용자가 직접 attach 해 띄운 서버는 복원이 켜진 채 남으므로, 재부팅 뒤에는 attach 전에 `/multi-agent-mux-resume` 을 먼저 실행한다. 범위 밖으로 남은 제안: 2(resume 의 "already running" 판단에 명령줄 검증), 4(이름 없이 뜬 에이전트 정지). 필요하면 새 이슈로 다룬다. 리뷰 경과는 PR #48 코멘트에 있습니다.
Author
Owner

#45 의 범위 밖 제안 2·4 는 #49 로 옮겼습니다(서비스 차원의 훅이 생길 때까지 보류, herdr 이벤트 확인을 첫 단계로).

#45 의 범위 밖 제안 2·4 는 #49 로 옮겼습니다(서비스 차원의 훅이 생길 때까지 보류, herdr 이벤트 확인을 첫 단계로).
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#45