새 스킬 multi-agent-mux-reset: reset.sh [--yes] [--dry-run].
제어 루트에서 실행 중인 상주 에이전트를 purge 한 뒤, 같은 이름·에이전트·역할·herdr 서버·워크스페이스 라벨로 다시 만든다. 그래서 컨텍스트가 빈 상태로 돌아간다.
로직은 lib_py/reset.py 에 있고, reset.sh 는 진입점·잠금·확인만 맡는다.
대상
status: running 이고 pane.cwd 가 제어 루트인 행만 대상이다.
worktree creator, stopped·terminated 행, orchestrator_uuids 는 대상이 아니다. --dry-run 이 이 행들을 사유와 함께 보여 준다.
순서
한 번에 한 에이전트씩 처리한다.
먼저 일반 stop 을 한다. 이때 확인된 대화 ID 가 행에 기록되고, 행은 stopped 가 된다.
그다음 행에 기록된 ID 로 --purge-conversation --yes 를 한다. 명령줄에 uuid 를 넣지 않는다(#63).
마지막으로 create 를 한다. onboarding 은 create 의 기본대로 켜진다.
create 를 병렬로 돌리지 않는다.
사전 검사
다음 중 하나라도 해당하면 아무것도 바꾸지 않고 거부하며, 각각 어떻게 풀면 되는지 출력한다.
열린 잡이 있다.
loop marker 가 살아 있다.
update 가 진행 중이다.
대상 행에 에이전트나 역할이 없다.
호출자 자신이 대상이다.
update 의 "실행 중인 행이 있으면 거부" 검사는 쓰지 않는다. 실행 중인 행이 reset 의 대상이기 때문이다.
잠금
update admission(mam_update_admit)을 잡은 채로 loop lock(.mam/loop-guard-active)을 얻는다.
그다음 admission 을 놓는다.
reset 이 도는 동안 run_loop.sh, update, 다른 reset 은 모두 거부된다.
확인
--yes 가 없으면 터미널에서는 [y/N] 으로 묻고, 터미널이 아니면 exit 3 으로 거부한다.
--dry-run 은 아무것도 바꾸지 않는다.
force 옵션은 없다.
spec 파일과 재실행
첫 stop 전에 .mam/reset-spec.json 을 원자적으로 쓴다. 항목마다 이름·에이전트·역할·서버·라벨·이전 ID 가 들어간다.
이전 ID 가 비어 있던 에이전트는, stop 이 확인한 ID 를 purge 전에 spec 에 기록한다.
재실행하면 spec 을 읽고 끝나지 않은 에이전트부터 이어 간다. 끝났다는 판정은 다음 두 가지가 모두 맞을 때다.
같은 생성 인스턴스의 행에 기록된 ID 가 있다.
그 ID 가 이전 ID 와 다르다.
전부 끝나면 spec 을 지운다.
spec 이 손상됐거나, 알 수 없는 에이전트가 들어 있으면 거부한다.
SIGINT, SIGTERM, SIGHUP 을 받으면 spec 을 남기고 끝난다. 이때 loop lock 은 자식 프로세스가 끝날 때까지 잡고 있는다.
purge 가 거부될 때 (stop exit 4)
그 에이전트를 건너뛰고 다음 에이전트로 넘어간다. 끝나면 exit 4 로 끝난다.
건너뛴 에이전트는 이미 일반 stop 을 거쳤으므로 stopped(resumable) 로 남는다. 대화 파일은 그대로다.
파일을 잡고 있는 프로세스를 출력하되, 외부 프로세스는 종료하지 않는다.
재실행하면 그 에이전트를 다시 시도한다.
exit 5
아무것도 건너뛰거나 실패하지 않았지만, 새로 만든 post-discovered 에이전트(agy, codex, hermes, opencode)의 ID 가 아직 기록되지 않았다는 뜻이다.
spec 은 남는다. 재실행하면 purge 없이 확인만 다시 한다.
결과 표: 에이전트마다 이름·에이전트·이전 ID·새 ID·기록 여부·이전 ID 와 다른지를 보여 준다.
in_use_hints 가 lsof 로 찾은 holder 를 보여 준다.
대화 파일을 연 프로세스의 pid 와 명령줄을 보여 준다.
conversation_in_use 와 같은 경로 목록(in_use_paths)을 쓴다.
기존 ps | grep 힌트는 그대로 둔다.
opencode 는 사용자별 백그라운드 서비스(serve --service)를 holder 에서 뺀다.
codex purge_artifacts
codex delete 가 남기던 ~/.codex/tui-thread-reference-capabilities/<uuid> 도 지운다.
대체 경로에서 rollout 파일을 지우다 실패해도 purge 전체가 멈추지 않는다.
패키징
multi-agent-mux-update 를 추가할 때와 같은 자리에 새 스킬을 넣었다: deploy/install.sh, deploy/remove.sh, deploy/gitea-ci.yml(shellcheck), README 두 개.
tests/data/skill_code_blocks.json 에 새 스킬 핀을 넣었고, test_issue42_skill_blocks.py 의 목록에 새 스킬을 추가했다.
main(v4.13.0)으로 rebase 한 뒤, 새 SKILL.md 의 version 을 main 과 같은 4.13.0 으로 맞췄다.
테스트
새 테스트 파일은 두 개다.
tests/test_issue78_reset.py: 대상 선택, 사전 검사별 거부, --yes 없음, --dry-run, spec 을 쓰는 순서, 중간 실패 뒤 재실행, purge 거부(exit 4)와 재시도, exit 5, 결과 표, reset 중 루프 시작 거부, 시그널을 다룬다.
tests/test_issue78_hints_codex.py: 에이전트별 lsof holder 힌트, opencode 서비스 제외, stop 의 거부 출력, codex 잔여 파일 삭제를 다룬다.
base b9c828f 에서는 새 테스트 25건 중 24건이 실패한다. 나머지 1건은 lsof 결과가 없으면 힌트가 예전과 같은지 확인하는 테스트라 base 에서도 통과하는 게 맞다.
Linux, rebase 전 0a902b9: 전체 스위트 2330 passed, 20 skipped, 2 failed (73분).
실패 2건은 test_issue34_job_tree.py::test_t5b_…[True], [False] 이고 #80 의 경합이다. 하위 잡 사유가 deadline_timeout 이 아니라 - 로 나온다.
이 브랜치는 lib_py/delegation, lib_py/loop 를 건드리지 않는다.
base 에서도 같은 증상으로 실패한다.
Linux, rebase 후 f90a705: 대상 테스트 (새 테스트 두 파일과 위 변경이 닿는 12개 파일, test_issue68_onboard_default.py 포함) 414 passed, 1 skipped.
테스트는 mock herdr, 임시 HOME, 자체 .mam 을 가진 임시 작업 공간을 쓴다. 실제 레지스트리·모니터·herdr 서버·에이전트 홈은 건드리지 않는다.
macOS(bash 3.2, Python 3.9)에서는 실행하지 않았다.
리뷰
리뷰어 4명(claude, agy, grok, codex)이 2회차에서 모두 PASS 를 냈다.
1회차에서 고친 것:
이전 ID 가 비어 있을 때 stop 이 확인한 ID 를 spec 에 남기지 않았다. 그래서 건너뛴 에이전트를 resume 한 뒤 재실행하면, 그 에이전트가 끝난 것으로 잘못 판정됐다.
INT trap 이 종료하지 않았다.
spec 에 알 수 없는 에이전트가 들어 있어도 받아들였다.
grok 의 지적 하나는 creator 의 반론을 grok 이 받아들여 철회됐다.
f90a705(version 한 줄)는 리뷰 이후 커밋이다.
알려진 한계
multi-agent-mux-delegate-job submit 은 loop lock 을 보지 않는다. reset 중에 잡을 제출하지 말라는 주의만 SKILL.md 에 있다.
같은 종류의 post-discovered 에이전트를 여럿 reset 하면, 두 번째 create 가 첫 번째 ID 가 기록될 때까지 기다린다(MAM_ID_ORDER_WAIT_S). 시간이 넘으면 재실행해야 한다.
범위 밖
grok 메모리(~/.grok/memory-v2/) 삭제.
병렬 create.
worktree creator reset.
버전 번호와 VERSIONS.md.
릴리스 노트에 적을 것
새 스킬 multi-agent-mux-reset: 제어 루트의 상주 에이전트를 purge 하고 같은 설정으로 다시 만든다.
purge 가 거부되면 대화 파일을 잡고 있는 프로세스(pid, 명령줄)를 보여 준다.
codex purge 가 tui-thread-reference-capabilities/<uuid> 도 지운다.
Refs #78
## 변경
- **새 스킬 `multi-agent-mux-reset`**: `reset.sh [--yes] [--dry-run]`.
- 제어 루트에서 실행 중인 상주 에이전트를 purge 한 뒤, 같은 이름·에이전트·역할·herdr 서버·워크스페이스 라벨로 다시 만든다. 그래서 컨텍스트가 빈 상태로 돌아간다.
- 로직은 `lib_py/reset.py` 에 있고, `reset.sh` 는 진입점·잠금·확인만 맡는다.
- **대상**
- `status: running` 이고 `pane.cwd` 가 제어 루트인 행만 대상이다.
- worktree creator, `stopped`·`terminated` 행, `orchestrator_uuids` 는 대상이 아니다. `--dry-run` 이 이 행들을 사유와 함께 보여 준다.
- **순서**
- 한 번에 한 에이전트씩 처리한다.
- 먼저 일반 stop 을 한다. 이때 확인된 대화 ID 가 행에 기록되고, 행은 `stopped` 가 된다.
- 그다음 행에 기록된 ID 로 `--purge-conversation --yes` 를 한다. 명령줄에 uuid 를 넣지 않는다(#63).
- 마지막으로 create 를 한다. onboarding 은 create 의 기본대로 켜진다.
- create 를 병렬로 돌리지 않는다.
- **사전 검사**
- 다음 중 하나라도 해당하면 아무것도 바꾸지 않고 거부하며, 각각 어떻게 풀면 되는지 출력한다.
- 열린 잡이 있다.
- loop marker 가 살아 있다.
- update 가 진행 중이다.
- 대상 행에 에이전트나 역할이 없다.
- 호출자 자신이 대상이다.
- update 의 "실행 중인 행이 있으면 거부" 검사는 쓰지 않는다. 실행 중인 행이 reset 의 대상이기 때문이다.
- **잠금**
- update admission(`mam_update_admit`)을 잡은 채로 loop lock(`.mam/loop-guard-active`)을 얻는다.
- 그다음 admission 을 놓는다.
- reset 이 도는 동안 `run_loop.sh`, update, 다른 reset 은 모두 거부된다.
- **확인**
- `--yes` 가 없으면 터미널에서는 `[y/N]` 으로 묻고, 터미널이 아니면 exit 3 으로 거부한다.
- `--dry-run` 은 아무것도 바꾸지 않는다.
- force 옵션은 없다.
- **spec 파일과 재실행**
- 첫 stop 전에 `.mam/reset-spec.json` 을 원자적으로 쓴다. 항목마다 이름·에이전트·역할·서버·라벨·이전 ID 가 들어간다.
- 이전 ID 가 비어 있던 에이전트는, stop 이 확인한 ID 를 purge 전에 spec 에 기록한다.
- 재실행하면 spec 을 읽고 끝나지 않은 에이전트부터 이어 간다. 끝났다는 판정은 다음 두 가지가 모두 맞을 때다.
- 같은 생성 인스턴스의 행에 기록된 ID 가 있다.
- 그 ID 가 이전 ID 와 다르다.
- 전부 끝나면 spec 을 지운다.
- spec 이 손상됐거나, 알 수 없는 에이전트가 들어 있으면 거부한다.
- SIGINT, SIGTERM, SIGHUP 을 받으면 spec 을 남기고 끝난다. 이때 loop lock 은 자식 프로세스가 끝날 때까지 잡고 있는다.
- **purge 가 거부될 때 (stop exit 4)**
- 그 에이전트를 건너뛰고 다음 에이전트로 넘어간다. 끝나면 exit 4 로 끝난다.
- 건너뛴 에이전트는 이미 일반 stop 을 거쳤으므로 `stopped`(resumable) 로 남는다. 대화 파일은 그대로다.
- 파일을 잡고 있는 프로세스를 출력하되, 외부 프로세스는 종료하지 않는다.
- 재실행하면 그 에이전트를 다시 시도한다.
- **exit 5**
- 아무것도 건너뛰거나 실패하지 않았지만, 새로 만든 post-discovered 에이전트(agy, codex, hermes, opencode)의 ID 가 아직 기록되지 않았다는 뜻이다.
- spec 은 남는다. 재실행하면 purge 없이 확인만 다시 한다.
- **결과 표**: 에이전트마다 이름·에이전트·이전 ID·새 ID·기록 여부·이전 ID 와 다른지를 보여 준다.
- **`in_use_hints` 가 lsof 로 찾은 holder 를 보여 준다.**
- 대화 파일을 연 프로세스의 pid 와 명령줄을 보여 준다.
- `conversation_in_use` 와 같은 경로 목록(`in_use_paths`)을 쓴다.
- 기존 `ps | grep` 힌트는 그대로 둔다.
- opencode 는 사용자별 백그라운드 서비스(`serve --service`)를 holder 에서 뺀다.
- **codex `purge_artifacts`**
- `codex delete` 가 남기던 `~/.codex/tui-thread-reference-capabilities/<uuid>` 도 지운다.
- 대체 경로에서 rollout 파일을 지우다 실패해도 purge 전체가 멈추지 않는다.
- **패키징**
- `multi-agent-mux-update` 를 추가할 때와 같은 자리에 새 스킬을 넣었다: `deploy/install.sh`, `deploy/remove.sh`, `deploy/gitea-ci.yml`(shellcheck), README 두 개.
- `tests/data/skill_code_blocks.json` 에 새 스킬 핀을 넣었고, `test_issue42_skill_blocks.py` 의 목록에 새 스킬을 추가했다.
- main(v4.13.0)으로 rebase 한 뒤, 새 SKILL.md 의 version 을 main 과 같은 4.13.0 으로 맞췄다.
## 테스트
- 새 테스트 파일은 두 개다.
- `tests/test_issue78_reset.py`: 대상 선택, 사전 검사별 거부, `--yes` 없음, `--dry-run`, spec 을 쓰는 순서, 중간 실패 뒤 재실행, purge 거부(exit 4)와 재시도, exit 5, 결과 표, reset 중 루프 시작 거부, 시그널을 다룬다.
- `tests/test_issue78_hints_codex.py`: 에이전트별 lsof holder 힌트, opencode 서비스 제외, stop 의 거부 출력, codex 잔여 파일 삭제를 다룬다.
- base `b9c828f` 에서는 새 테스트 25건 중 24건이 실패한다. 나머지 1건은 lsof 결과가 없으면 힌트가 예전과 같은지 확인하는 테스트라 base 에서도 통과하는 게 맞다.
- Linux, rebase 전 `0a902b9`: 전체 스위트 2330 passed, 20 skipped, 2 failed (73분).
- 실패 2건은 `test_issue34_job_tree.py::test_t5b_…[True]`, `[False]` 이고 #80 의 경합이다. 하위 잡 사유가 `deadline_timeout` 이 아니라 `-` 로 나온다.
- 이 브랜치는 `lib_py/delegation`, `lib_py/loop` 를 건드리지 않는다.
- base 에서도 같은 증상으로 실패한다.
- Linux, rebase 후 `f90a705`: 대상 테스트 (새 테스트 두 파일과 위 변경이 닿는 12개 파일, `test_issue68_onboard_default.py` 포함) 414 passed, 1 skipped.
- 테스트는 mock herdr, 임시 HOME, 자체 `.mam` 을 가진 임시 작업 공간을 쓴다. 실제 레지스트리·모니터·herdr 서버·에이전트 홈은 건드리지 않는다.
- macOS(bash 3.2, Python 3.9)에서는 실행하지 않았다.
## 리뷰
- 리뷰어 4명(claude, agy, grok, codex)이 2회차에서 모두 PASS 를 냈다.
- 1회차에서 고친 것:
- 이전 ID 가 비어 있을 때 stop 이 확인한 ID 를 spec 에 남기지 않았다. 그래서 건너뛴 에이전트를 resume 한 뒤 재실행하면, 그 에이전트가 끝난 것으로 잘못 판정됐다.
- INT trap 이 종료하지 않았다.
- spec 에 알 수 없는 에이전트가 들어 있어도 받아들였다.
- grok 의 지적 하나는 creator 의 반론을 grok 이 받아들여 철회됐다.
- `f90a705`(version 한 줄)는 리뷰 이후 커밋이다.
## 알려진 한계
- `multi-agent-mux-delegate-job submit` 은 loop lock 을 보지 않는다. reset 중에 잡을 제출하지 말라는 주의만 SKILL.md 에 있다.
- 같은 종류의 post-discovered 에이전트를 여럿 reset 하면, 두 번째 create 가 첫 번째 ID 가 기록될 때까지 기다린다(`MAM_ID_ORDER_WAIT_S`). 시간이 넘으면 재실행해야 한다.
## 범위 밖
- grok 메모리(`~/.grok/memory-v2/`) 삭제.
- 병렬 create.
- worktree creator reset.
- 버전 번호와 VERSIONS.md.
## 릴리스 노트에 적을 것
- 새 스킬 `multi-agent-mux-reset`: 제어 루트의 상주 에이전트를 purge 하고 같은 설정으로 다시 만든다.
- purge 가 거부되면 대화 파일을 잡고 있는 프로세스(pid, 명령줄)를 보여 준다.
- codex purge 가 `tui-thread-reference-capabilities/<uuid>` 도 지운다.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.
Refs #78
변경
multi-agent-mux-reset:reset.sh [--yes] [--dry-run].lib_py/reset.py에 있고,reset.sh는 진입점·잠금·확인만 맡는다.status: running이고pane.cwd가 제어 루트인 행만 대상이다.stopped·terminated행,orchestrator_uuids는 대상이 아니다.--dry-run이 이 행들을 사유와 함께 보여 준다.stopped가 된다.--purge-conversation --yes를 한다. 명령줄에 uuid 를 넣지 않는다(#63).mam_update_admit)을 잡은 채로 loop lock(.mam/loop-guard-active)을 얻는다.run_loop.sh, update, 다른 reset 은 모두 거부된다.--yes가 없으면 터미널에서는[y/N]으로 묻고, 터미널이 아니면 exit 3 으로 거부한다.--dry-run은 아무것도 바꾸지 않는다..mam/reset-spec.json을 원자적으로 쓴다. 항목마다 이름·에이전트·역할·서버·라벨·이전 ID 가 들어간다.stopped(resumable) 로 남는다. 대화 파일은 그대로다.in_use_hints가 lsof 로 찾은 holder 를 보여 준다.conversation_in_use와 같은 경로 목록(in_use_paths)을 쓴다.ps | grep힌트는 그대로 둔다.serve --service)를 holder 에서 뺀다.purge_artifactscodex delete가 남기던~/.codex/tui-thread-reference-capabilities/<uuid>도 지운다.multi-agent-mux-update를 추가할 때와 같은 자리에 새 스킬을 넣었다:deploy/install.sh,deploy/remove.sh,deploy/gitea-ci.yml(shellcheck), README 두 개.tests/data/skill_code_blocks.json에 새 스킬 핀을 넣었고,test_issue42_skill_blocks.py의 목록에 새 스킬을 추가했다.테스트
tests/test_issue78_reset.py: 대상 선택, 사전 검사별 거부,--yes없음,--dry-run, spec 을 쓰는 순서, 중간 실패 뒤 재실행, purge 거부(exit 4)와 재시도, exit 5, 결과 표, reset 중 루프 시작 거부, 시그널을 다룬다.tests/test_issue78_hints_codex.py: 에이전트별 lsof holder 힌트, opencode 서비스 제외, stop 의 거부 출력, codex 잔여 파일 삭제를 다룬다.b9c828f에서는 새 테스트 25건 중 24건이 실패한다. 나머지 1건은 lsof 결과가 없으면 힌트가 예전과 같은지 확인하는 테스트라 base 에서도 통과하는 게 맞다.0a902b9: 전체 스위트 2330 passed, 20 skipped, 2 failed (73분).test_issue34_job_tree.py::test_t5b_…[True],[False]이고 #80 의 경합이다. 하위 잡 사유가deadline_timeout이 아니라-로 나온다.lib_py/delegation,lib_py/loop를 건드리지 않는다.f90a705: 대상 테스트 (새 테스트 두 파일과 위 변경이 닿는 12개 파일,test_issue68_onboard_default.py포함) 414 passed, 1 skipped..mam을 가진 임시 작업 공간을 쓴다. 실제 레지스트리·모니터·herdr 서버·에이전트 홈은 건드리지 않는다.리뷰
f90a705(version 한 줄)는 리뷰 이후 커밋이다.알려진 한계
multi-agent-mux-delegate-job submit은 loop lock 을 보지 않는다. reset 중에 잡을 제출하지 말라는 주의만 SKILL.md 에 있다.MAM_ID_ORDER_WAIT_S). 시간이 넘으면 재실행해야 한다.범위 밖
~/.grok/memory-v2/) 삭제.릴리스 노트에 적을 것
multi-agent-mux-reset: 제어 루트의 상주 에이전트를 purge 하고 같은 설정으로 다시 만든다.tui-thread-reference-capabilities/<uuid>도 지운다.🤖 Generated with Claude Code
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.