Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
23 KiB
23 KiB
PR #73 (Issue #71) 리뷰 — head 5f60653
- 리뷰어: planner-reviewer-claude-01 (macOS Darwin 25.6,
/bin/bash3.2.57,/usr/bin/python33.9.6, BSD sed/tr) - 검증 경로:
/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261026T133440Z-c8961b/2 - identity 확인 (시작·종료 시 모두):
- HEAD
5f6065344f, tree98d57bdc60 git status --porcelain --ignored비어 있음.mam없음. 추적 파일.mam.env.example만 있다.- main HEAD
29cc4fd그대로.
- HEAD
- 범위: 29cc4fd..5f60653, 커밋 14개, 파일 28개 (+953/−176).
- 읽은 것: 이슈 #71 본문과 댓글("범위 정리" 1–10항, #72 분리), PR #73 본문.
0. 이번 잡(f39ae4ba)에 대해
- 이 잡은 같은 루프의 검증 경로
/2다. head5f60653, tree98d57bdc는 앞 잡 0698ffc5(검증 경로/1) 와 같다. 브리프의 확인 항목도 잡 ID와 경로를 빼면 글자까지 같다. - 그래서 아래 1장의 분석(파일:줄 근거, 리뷰어 시험 p1–p12, 변이 m1–m12, base 비교)은 같은 tree 의
git archive사본에서 얻은 결과를 그대로 쓴다. - 이번 경로
/2에서 새로 확인한 것:- 브리프에 붙은 diff(1920줄)가
git diff 29cc4fd 5f60653와 바이트까지 같다. 커밋은 14개다. /2의lib.sh에서_sanitize_herdr_agent_name을 다시 뽑았다. 정의는 하나이고, 앞서 비교한 정의와 같다./2의lib_py를 기준으로 bash↔Python 비교를 다시 돌렸다. 408건에 불일치는 0이다. python3 와 해시 도구가 모두 없는 5건도 기대대로 동작했다.- 테스트 세 묶음을
/2에서 다시 돌렸다. 결과는 아래 2장 표와 같다.- issue71+issue66: 77 passed
- A: 384 passed, 1 skipped
- B: 543 passed, 3 skipped
- 실제 herdr 서버 두 개(pid 11655, 69309)는 실행 전후로 같다.
- 시작과 끝에
/2의 HEAD·tree 를 확인했다.status --ignored는 비어 있고.mam은 없다. main HEAD 는29cc4fd그대로다.
- 브리프에 붙은 diff(1920줄)가
1. 확인한 것과 근거
1) 완료 조건과 범위
| 항목 | 결과 | 근거 |
|---|---|---|
| 1 attach 형태 | 충족 | 아래 3) |
2 default 도 적음 |
충족 | 아래 3) |
| 3 래퍼 attach | 충족 | 아래 3) |
| 4 두 필드 삭제 | 충족 | 아래 4) |
| 5 원칙(스킬로 시작·종료), 행 없는 stop | 충족 | 아래 4), 5) |
| 6 attach 는 예외 | 충족 | attach 만 herdr 명령을 직접 쓴다 |
| 7 변환 규칙 하나 | 충족 | 아래 6) |
| 8 해시 불가 시 오류 | 충족 | 아래 6) |
| 9 마이그레이션 | 충족 | 아래 7) |
| 10 stall_watch 한 곳 | 충족 | 아래 8) |
- 보강 완료 조건도 모두 충족한다.
- 새 행에 두 필드가 없다.
- 해시 도구가 없으면 python3 로 같은 결과를 내고, python3 도 없으면 오류다.
- 옛 행의 마이그레이션은 멱등이고, YAML 과 DB 가 같다.
- 행 없는 stop 은 레지스트리를 바꾸지 않는다.
- 정리 안내는 stop 스킬 명령이다.
- #72(update 스킬)는 끌어들이지 않았다.
- 진입점
python3 -m lib_py.registry_format만 두었다. - 그 진입점을 부르는 코드는 없다.
- 진입점
- 본문 완료 조건 6(실제 에이전트를 32자 초과 이름으로 띄워 attach)은 수동 확인 항목이다. 브리프 제약(실제 에이전트·실제 herdr 금지)에 따라 하지 않았다.
- 대신 형태가 맞는지는 확인했다. 실제 herdr 0.9.5 의
agent attach --help를 격리 HOME 에서 보면 사용법이herdr agent attach <TARGET> [OPTIONS]이고, 옵션은--takeover다.
2) 회귀 테스트 tests/test_issue71_herdr_names.py (70개)
- head (검증 경로): issue71 70개와 issue66 7개를 함께 돌려 77 passed.
- base:
29cc4fd사본에 head 의 테스트 파일을 넣었다. conftest 는 이 PR 에서 바뀌지 않았다.- 결과: 68 failed, 2 passed. PR 이 말한 숫자와 같다.
- 통과하는 2개:
test_the_names_need_converting_and_the_plain_one_does_not: 변환 전제를 확인한다.test_wrapper_attach_calls_herdr_with_the_herdr_name[PLAIN]: 변환이 필요 없는 이름이라 base 에서도 맞다.
- reconcile 경로 (
test_reconcile_registers_with_the_attach_command, test :126-139) 는 행 등록 경로에 실제로 닿는다.- mock herdr 에
cwd=mam_sandbox,pane_id가 있는 agent 를 넣는다. - 실행 전에 행이 없음을 확인한다 (:132).
- 실행 뒤
last_visible_note == "auto-registered by monitor"인 행이 하나인지 확인한다 (:137-138). - 변이 m10 (reconcile 을 옛 형태로) 이 이 테스트 3개를 실패시킨다.
- mock herdr 에
3) attach_command (lib_py/agents/sanitize.py:34-43)
- 형태:
herdr --session <shlex.quote(server or "default")> agent attach <shlex.quote(sanitize(name))>.- 서버가 None 이나 빈 값이어도
--session default가 된다. - 변환된 이름은
[a-z0-9_-]이므로 quote 를 해도 그대로다. - 서버
srv x는'srv x'로 감싸진다. 내 시험과 test :51 에서 확인했다.
- 서버가 None 이나 빈 값이어도
- 쓰는 곳은 네 곳이고, 모두 이 함수를 쓴다:
- create:
create_session.sh:566 - resume 의 새 행:
update_yaml_resumed.sh:151 - resume 의 서버 변경:
update_yaml_resumed.sh:161 - reconcile:
reconcile.sh:676
- create:
- 마이그레이션도 같은 함수를 쓴다 (
registry_format.py:62). - resume 의 else 분기는 서버가 바뀔 때만 attach_command 를 다시 쓴다 (:159-161).
- 서버가 그대로인 기존 행은 첫 쓰기의 마이그레이션이 이미 새 형태로 바꿔 둔다. 그래서 낡은 값이 남지 않는다.
- lib.sh 래퍼 (
lib.sh:605-613):agent attach <t> ...의 첫 인자를_resolve_herdr_target으로 변환한다. get/read/prompt 와 같은 함수다.- 나머지
"$@"는 그대로 넘긴다. - 테스트 :151-159 가 strict mock 에서
["agent","attach",<herdr 이름>,"--takeover"]를 확인한다. - 변이 m4 (원래 이름을 넘김) 는 LONG·UPPER 에서 잡힌다.
4) kill_command·start_command 삭제
- 코드·문서에는 이 두 키가 남아 있지 않다. 리뷰 트리 전체를 grep 했다.
- 남은 곳: 마이그레이션 상수 (
registry_format.py:37), 테스트의 "옛 행" 픽스처와 부재 단언, 과거 리뷰 보관본.agents/reports/*.
- 남은 곳: 마이그레이션 상수 (
status --json의start_command키는 빠졌다 (status.sh:224-228).- 이 키를 읽는 코드나 테스트는 없다.
- status SKILL.md 의 예시도 같이 고쳤다.
- 래퍼의
new-session/kill-session가짜 명령은 남겨 두는 것이 맞다. 아직 쓰는 곳:- create (
create_session.sh:266-276, :346) - stop (
stop_session.sh:157, :307, :321) - reconcile
- create (
- 사용자에게 보이던 kill-session 안내 두 곳은 stop 스킬 명령으로 바뀌었다:
- create exit 43 (
create_session.sh:364) - id_order (
id_order.py:343-345) - 바뀐 명령
stop_session.sh --session X --herdr-session S는 행이 없어도 동작한다 (아래 5). - exit 43 은
_mark_row_rolled_back "running" "rollback-failed"로 행을 남기므로, 그 경우는 행 있는 stop 경로를 탄다.
- create exit 43 (
5) 행이 없는 stop (stop_session.sh:117-191)
- 행이 없을 때만 그 경로로 간다.
- 행을 찾는 Python 은 행이 없을 때만
sys.exit(7)을 낸다. [ "${MAPPED_RC:-0}" -ne 7 ] || stop_rowless(:189) 로 7 일 때만 row-less 로 간다.- 다른 실패는
cannot read the row로 exit 1 이다 (:190-193). - 변이 m6 (0 이 아니면 모두 row-less) 은
test_stop_does_not_take_an_unreadable_row_for_no_row에 잡힌다.
- 행을 찾는 Python 은 행이 없을 때만
- 레지스트리를 읽지 못할 때는
load_state_json strict(:170, lib.sh:1598-1631) 가 exit 1 로 끝난다.- 해당 경우: YAML 깨짐(DB 없음), DB 손상, sessions 테이블 없음, 테이블 전부 없음.
- 테스트 :514-541 의 4개 매개변수가 이를 확인한다.
- 인자 없는 기존 호출은 동작이 같다. strict 일 때만 raise 하거나 exit 한다.
- 작업 공간 판정 (:142-151) 은
realpath두 개를 비교한다:cwd == root or cwd.startswith(root + os.sep). 내 추가 시험 (head 사본,tests/test_zz_review71.py) 결과:
| 경우 | 결과 |
|---|---|
p1 접두어 혼동 (<ws>-other) |
exit 5, 닫지 않음 |
| p2 밖에서 작업 공간 안으로 가는 심볼릭 링크 | 0, 닫음 |
| p3 작업 공간 안에서 밖으로 가는 심볼릭 링크 | exit 5 |
| p4 후행 슬래시 | 0 |
| p5 WORKSPACE_ROOT 를 심볼릭 링크로 지정 | 0 |
| p7 32자 초과 이름 (strict mock) | 0 |
- 서버 결정 순서 (:107-113, :128-130) 는
--herdr-session→ 환경의HERDR_SESSION_NAME→ 작업 공간 서버다.- 환경 값이 비었거나
default이면 작업 공간 서버로 간다. create 의 :185, :253 과 같은 규칙이다. - p6 (환경 값
default) 에서 모든agent get이 작업 공간 서버로 간 것을 확인했다. - 테스트 :487-499 는 환경 서버를 확인한다.
- 환경 값이 비었거나
- 레지스트리를 바꾸지 않는다. 모든 row-less 테스트가 YAML/DB 해시를 비교한다 (
_registry_bytes). --purge-conversation은 행이 없으면 exit 1 이다 (:124-126, test :544-550).- 종료 코드:
- 0 / 1 / 5 / 6 은 SKILL.md 표 (:58-67) 와 헤더 주석 (:30-36), 코드 (:134, :154, :161, :163) 가 같다.
test_literal_exit_rule_on_base_scripts는{0..6}이다.- skill_code_blocks 의
entry_points도 5·6 을 더했다.
6) 이름 변환 (lib.sh:44-82)
- 정의는 하나다 (
lib.sh:44). test :171 이 확인한다. - shim 은
{ printf shebang; declare -f _sanitize_herdr_agent_name; cat <<'EOF' … } > "$tmp_file"(:179-1393) 으로 같은 정의를 받는다.- bash 3.2 의
declare -f출력을 다시 source 한 사본도 아래 비교에 넣었다. - 이 호스트의
bash는/bin/bash3.2 하나뿐이라, 테스트의 shim 도 3.2 로 돈다.
- bash 3.2 의
- has-session 의
lambda s: s.lower()[:32]대체와os.path.abspath(".agents/skills")import 는 없어졌다.- 대신 lib.sh 가 계산한
TARGET_SAN을 넘긴다 (:633-641).
- 대신 lib.sh 가 계산한
- macOS 실측 비교: 이름 34개를 Python
sanitize_herdr_agent_name과 비교했다.- 이름: ASCII, 대문자, 32/33/40/200자, 숫자·
_·-n·-e·--help로 시작,\n(가운데·앞·뒤·연속),\t,\r,*?[a],\,%s,Ü,İstanbul…,日本語, 이모지,K(U+212A),ß,Dž,Ω…긴 이름, NBSP 등. - 원본 정의와
declare -f사본 2 벌 × 환경 6 개 (LANG=en_US.UTF-8,LC_ALL=C, 로캘 없음,LC_ALL=UTF-8+LC_CTYPE=C, 해시 도구 없이 python3 만 + UTF-8 또는 C) = 408 건, 불일치 0. - ASCII 가 아닌 이름은 바이트 단위 치환을 쓰지 않고 python3 의 문자 단위
str.lower()+치환을 쓰므로 Python 과 같다 (:51-56).
- 이름: ASCII, 대문자, 32/33/40/200자, 숫자·
- 해시 도구별:
/sbin/sha1sum만, openssl(LibreSSL) 만, 깨진 shasum(빈 출력)에서 openssl 로 넘어가는 경우 모두 Python 과 같았다 (mam-wt-issue-71-very-lo-54e536a7). - python3 도 해시 도구도 없을 때:
- 짧은 ASCII 이름(대문자, 끝의
\n포함)은 정상이다. - 32자 초과는
no shasum, sha1sum, openssl or python3오류로 rc 1 이다. - ASCII 가 아닌 이름은
not ASCII and there is no python3오류로 rc 1 이다. 00000000은 어디에도 없다.
- 짧은 ASCII 이름(대문자, 끝의
- 호출하는 곳은 실패를 넘긴다:
|| return 1,|| exit 1,wait_for_agent_idle은|| return 3, delegate-job 은 진단만 건너뛴다.- spawn 의
agent_name계산은 루프 앞 (:688) 으로 옮겨졌다. 그래서 이름을 못 구하면 spawn 하지 않는다. test :267 이 확인한다.
- spawn 의
7) 레지스트리 형식 2 (registry_format.py, atomic_yaml.py:118-137, :192)
- 순서: 잠금 →
BEGIN IMMEDIATE(:79-87) → DB 또는 YAML 읽기 → 마이그레이션 (:121-126) → 호출자 변경 (:144-149) → 검증 → DB 쓰기 → commit → YAML 쓰기.- 마이그레이션은 잠금 안에서, 호출자 변경 전에 돈다.
- 모든 행을 바꾼다.
migrate는 이름이 있는 모든 행에서 두 키를 지우고 attach_command 를 다시 만든다.- stopped·terminated 도 포함한다. 변이 m8 (running 만) 은 6 failed 로 잡힌다.
- YAML 도 반드시 다시 쓴다.
force_yaml = True(:126) 이고, 변이 m2 는 6 failed 로 잡힌다. - 멱등이다. test :327-338 이 확인하고, 진입점을 두 번 부르면
2 -> 2, rows changed: 0에 바이트도 같다 (test :342-362). - YAML 교체가 실패한 뒤의 복구: DB 가 2 이고 YAML 이 2 미만이면 다음 쓰기에서
force_yaml이 된다 (:127-137).- 진입점으로 하든 아무 쓰기로 하든 복구된다 (test :379-411).
- 변이 m3 은 2 failed 로 잡힌다.
- 다른 쓰기 경로: 레지스트리 YAML/DB 를 쓰는 코드는
atomic_yaml하나뿐이다.- grep 결과
safe_dump,INTO state/sessions는 이 파일에만 있다. hermes.py 의 DELETE 는 에이전트 자신의 DB 다. - orc_onboard 와 autopin 도
atomic_dump_yaml(_main)을 거친다. - 호출자 변경 가운데
d를 통째로 새로 만드는 것은 없다. 만들어도 다음 쓰기가 다시 마이그레이션하므로 형식 키를 잃지 않는다.
- grep 결과
- 읽기 경로:
load_state_json과lib_py.state는 바꾸지 않았다. strict 인자만 더했다. - 미래 형식 (p8):
registry_format: 3이면 진입점은3 -> 3, rows changed: 0이다. 쓰기 뒤에도 키는 3 으로 남고, 행(옛 키 포함)도 그대로다. 내려 쓰지 않는다. - 비정상 값 (p9):
"2",True,None은 1 로 보고 2 로 바꾼다.0은0 -> 2로 출력된다. - 진입점 계약:
- 성공은 stdout 한 줄
registry-format: <from> -> <to>, rows changed: <n>에 exit 0 이다. - 레지스트리가 없으면 exit 1 이다 (test :423).
- DB 손상 (p10) 은 stderr
registry-format: error: file is not a database에 exit 1 이고 stdout 은 비어 있다. - 검증 거부 (p11) 는
registry-format: error: VALIDATE: … bad status에 exit 1 이다. - 모두 docstring 과 같다.
- 성공은 stdout 한 줄
- macOS persistent WAL: YAML/DB 바이트 비교 테스트(진입점 재실행, row-less stop)가 이 호스트에서 모두 통과했다.
8) stall_watch
StallWatch.__init__이self.herdr_name을 한 번 구한다 (:229).- 그 값을
HerdrSource(..., name=self.herdr_name)(:230) 와wait_once(:332) 에 같이 쓴다. HerdrSource는 keyword-onlyname이 필수다. 원래 이름으로 대신 쓰는 경로는 없다.- 다른 호출자는
test_issue60_linux_fixes.py:99하나이고,name="t"를 넘기도록 바뀌었다. tests/test_issue66_stall_watch_sanitized_name.py는 diff 가 없고, 7 passed 다.
9) 바뀐 기존 테스트
test_tier2_component:HERDR_SESSION_NAME=포함 여부 대신 새 attach 형태 전체가 같은지와 두 키가 없는지를 본다.- 서버 전파와 라벨이 새지 않는지를 보는 원래 의도는 유지되고, 단언은 더 엄격해졌다.
test_issue50_pane_lock:173: create 가 쓴 행이라는 증거를start_command대신session_id_source로 본다.- reconcile 의 행 (
reconcile.sh:666-680) 에는 이 키가 없다. 그래서 증거로서 같은 구분을 한다.
- reconcile 의 행 (
test_issue21_worktree_prereqs:584: 제어 루트 경로가 새지 않는지를 attach_command 에서 본다. 의도가 같다.test_tier1_unit/test_tier3_integration:- "agent 를 추론할 수 없으면 exit 2" 를 이제 행이 있는 세션으로 확인한다. 행이 없으면 row-less 경로로 가기 때문이다.
- 의도가 유지된다.
test_issue63,test_issue23_24S9: 안내 문구를 새 명령으로 바꿨다.test_layout,test_issue42: heredoc 시작 위치와 종료 코드 집합을 따라 고쳤다.tests/data/skill_code_blocks.json:- 블록 33개 가운데 sha 가 바뀐 것은 문서 내용이 바뀐 두 블록이다: create :134 (YAML 행 예시), status :74 (JSON 예시).
- 나머지는 줄 이동과 evidence 줄 번호다.
entry_points의 stop 종료 코드에 5·6 을 더했다.- 문서 diff 와 맞다.
- S11 (
test_issue23_24_startup_path.py:294-316):- 비교 기준을
0fc162e로 두었다.0fc162e..HEAD사이 lib.sh 변경은5f60653의load_state_json하나뿐이고, shim heredoc 과 sanitize 함수는 바뀌지 않았다. - merge commit 으로 병합하면
0fc162e가 main 이력에 남으므로 계속 맞다. - 기준을 찾지 못하면
pytest.skip한다. 이는 base(525df2b) 때부터의 동작이다. git 이 없는 설치본이나 squash 병합에서 건너뛰는 것은 기존 설계 그대로라고 본다. - 검증 경로(git worktree)에서는 skip 이 아니라 통과했다 (
-k s11: 1 passed). - squash 하면 이 확인이 사라지므로 PR 본문의 "merge commit 필수" 를 지켜야 한다.
- 비교 기준을
10) macOS 호환
- 바뀐 Python 6개 파일이
/usr/bin/python33.9.6 에서 compile 된다. attach_command와migrate가 3.9 에서 실행된다.- 바뀐 셸 줄에
declare -A,mapfile,${x,,},sed -i,stat -c,readlink -f,local -n이 없다. - 이름 변환은 BSD tr/sed 와 bash 3.2 에서 위 6) 처럼 확인했다.
2. 실행한 테스트
- 실행 조건:
- 모두
env -i PATH=/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin, 임시 HOME/XDG_DATA_HOME 에서 돌렸다. PYTHONDONTWRITEBYTECODE=1 -p no:cacheprovider --basetemp=<스크래치>를 썼다..mam.env는 export 하지 않았다.
- 모두
- 실제 herdr 서버 두 개는 테스트 전후로 같다: pid 11655
herdr server, 69309herdr --session multi-agent-mux server, 시작 시각도 같다.
| 대상 | 결과 |
|---|---|
| 검증 경로: test_issue71_herdr_names + test_issue66_stall_watch_sanitized_name | 77 passed (/1, /2 모두) |
base 29cc4fd 사본 + head 의 issue71 테스트 |
68 failed, 2 passed |
| 검증 경로 A: tier1_unit, tier2_component, tier3_integration, issue23_24_startup_path, issue50_pane_lock, issue21_worktree_prereqs, issue63_same_cwd_ids, issue60_linux_fixes, layout, issue42_skill_blocks | 384 passed, 1 skipped (/1, /2 모두; skip: issue42 의 .claude/skills is untracked; absent in a worktree, 환경 요인) |
| 검증 경로 B (stop·reconcile·create·resume 인접): b4_session_created, b19_headless_reconcile_fixes, c1_tui_readiness, issue15_registry_isolation, issue17_workspace_root_isolation, issue31_purge_residual, issue24_resume_dialogs, issue40_auto_pin, issue45_restore, issue6_fail_closed, issue9_server_env, issue64_ro_connect, uuid_target, workspace_scope, issue57_logs_follow_registry, issue30_stall_watch, issue30_stall_watch_stop, issue50_dispatch_herdr, issue60_opencode_v2, issue36_prompt_transport, a4_adapter_contract, issue26_ints | 543 passed, 3 skipped (/1, /2 모두) |
| S11 단독 (검증 경로) | 1 passed (skip 아님) |
| 리뷰어 추가 시험 (head 사본, p1–p12) | 14 passed. 1개는 내 기대값 오류다 (형식 0 → "0 -> 2" 출력, 동작은 정상) |
| bash↔Python 이름 변환 비교 | 408 건 불일치 0, 도구 없음 5 건 모두 기대대로 (/2 의 lib.sh·lib_py 로 다시 실행) |
변이 (head 사본, issue71 70개):
| # | 변이 | 결과 |
|---|---|---|
| m1 | 작업 공간 판정을 startswith(root) 로 (접두어 혼동) |
살아남음 (리뷰어 p1 은 잡음) |
| m2 | 마이그레이션 뒤 YAML 강제 쓰기 끔 | 6 failed |
| m3 | YAML 복구 분기 끔 | 2 failed |
| m4 | 래퍼 attach 에 원래 이름 | 2 failed |
| m5 | 줄바꿈 → - 제거 |
6 failed |
| m6 | rc≠0 이면 모두 row-less | 1 failed |
| m7 | strict 에서 state 테이블 오류만 무시 | 살아남음 (아래 N3) |
| m8 | running 행만 마이그레이션 | 6 failed |
| m9 | 서버 없으면 --session '' |
3 failed |
| m10 | reconcile 을 옛 attach 형태로 | 3 failed |
| m11 | 행 없는 stop 이 환경 서버를 무시 | 1 failed |
| m12 | 해시 실패 시 00000000 |
4 failed |
3. 차단 결함
없음.
4. 비차단
N1: 행 없는 stop 이 제어 루트 밖 워크트리의 세션은 닫지 못한다
- 상황:
worktree_prepare.sh:229-236은 creator 를WORKSPACE_ROOT=$MAIN과--workspace $WT로 만든다.$WT는--path로 받은 임의의 경로이고, 보통 제어 루트 밖이다. 루프의mam-wt-*나$TMPDIR/mam-review/…가 그 예다.
- 결과:
- 그런 create 가 행을 등록하기 전에 죽으면, id_order 의 안내 (
id_order.py:343-345) 는 stop 명령을 준다. - 그 stop 은
realpath(cwd)가WORKSPACE_ROOT밖이므로 exit 5 로 거부한다 (stop_session.sh:151-154). 다른 작업 공간 테스트 (test :467) 와 같은 경로다. - 거부는 안전하다(아무것도 바꾸지 않음). 하지만 이 경우에는 안내된 스킬 명령으로 정리할 수 없다.
- 그런 create 가 행을 등록하기 전에 죽으면, id_order 의 안내 (
- 권고: 둘 중 하나.
- (a) row-less 판정에 "이 제어 루트에 묶인 워크트리(
git worktree list)" 도 넣는다. - (b) exit 5 메시지에 그 경우의 정리 방법을 적는다.
- (a) row-less 판정에 "이 제어 루트에 묶인 워크트리(
N2: 접두어 혼동을 고정하는 테스트가 없다
- m1 이 PR 테스트에서 살아남는다.
<ws>-other를 cwd 로 하는 한 줄짜리 시험을 더하기를 권한다. 내 p1 이 그 형태다.
N3: strict 읽기에서 state 테이블만 없는 DB 는 걸리지 않는다
- m7 이 살아남은 이유는 테스트의
no-tables가 sessions 테이블 오류로도 잡히기 때문이다. - state 만 없는 DB 에서도 stop 은 sessions 테이블로 행을 정확히 찾으므로 영향은 없다.
N4: 이 PR 이 건드리지 않은 attach 안내가 남아 있다 (모두 base 부터 있던 것)
create_session.sh:770:Attach: herdr session attach $SESSION_NAME. 서버를 적지 않고, 이름도 변환하지 않는다. 행의attach_command를 출력하기를 권한다.multi-agent-mux-delegate-job:1021:HERDR_SESSION_NAME=… herdr agent attach $sess (lib.sh를 source한 셸에서). 래퍼가 이제 이름을 변환하므로 동작은 하지만, 새 형태와 다르다.deploy/install_mam.sh:330:HERDR_SERVER_NAME=… herdr agent attach. 없는 환경 변수 이름이다.
N5: 래퍼 attach 는 대상이 첫 인자라고 가정한다
herdr agent attach --takeover <name>순서로 쓰면 래퍼는--takeover를 대상으로 찾다가 exit 1 을 낸다.- herdr 의 사용법(
<TARGET> [OPTIONS])과 문서의 순서대로라면 문제가 없다.
N6: YAML 이 아직 없는 작업 공간에서는 행 없는 stop 이 동작하지 않는다
- 처음 만든 작업 공간에서 첫 create 가 행 등록 전에 죽어 YAML 이 아직 없으면, stop 은 행 없는 경로 전에
YAML not found로 exit 1 한다 (:101). - 드문 경우다.
N7: 정리할 만한 작은 것들
create_session.sh:519의server_opt는 쓰는 곳이 없다. base 에서도 그랬다.- shim 내용이 바뀌었으므로, 같은 제어 루트를 쓰는 옛 버전 체크아웃과 새 버전 체크아웃이 섞이면
.mam/shim/herdr를 서로 덮어쓴다. #45 때와 같은 전환기 비용이다.
[VERDICT: PASS]