# Cross-Code Review — Job 01d3fb56 (O-3: Invocation-Aware Scoped Guard) - **Reviewer**: cline (session: herdr:canary-projects-multi-agent-mux-creator-cline) - **Job ID**: 01d3fb56 - **Target commit**: `1f8622e` — `feat(o3): implement Invocation-Aware Scoped Guard for orchestrator role scoping (100% PASS)` - **Scope**: O-3 (Invocation-Aware Scoped Guard) + 누적 변경분(git diff)에 대한 린트·동작성·유실 교차 리뷰 - **Date**: 2026-08-07 --- ## 1. 변경분 개요 13 files changed, 428 insertions(+), 20 deletions(-). 핵심 O-3 산출물: | 산출물 | 파일 | 내용 | |---|---|---| | PreToolUse 가드 | `.agents/hooks.json` (new) | matcher `file_change|edit_notebook|write_blob` → `./hooks/loop_delegation_guard.sh` | | 가드 로직 | `.agents/hooks/loop_delegation_guard.sh` (new, 136L) | JSON in/out, fail-open, marker + transcript 2단 신호, `pid`+`lstart` 신원 대조 | | 마커/TRAP | `.agents/skills/multi-agent-mux-loop/scripts/run_loop.sh` | `MAM_LOOP_MARKER` 기록(`pid`+`lstart`+`started`), `trap _mam_release_guard EXIT INT TERM HUP`, `delegate_job_safe` 내 trap 복원 | | 문서 | `AGENTS.md` §5, `.agents/MULTI_AGENT_RULES.md` §3.2, `.agents/MULTI_AGENT_RULES.ko.md` §3.2, `multi-agent-mux-loop/SKILL.md` | Normal vs Loop Active 모드 경계 명시 | | 백로그 | `IMPROVEMENTS.md` | O-3 open→completed 이관, 12 open / 9 completed | | 테스트 | `tests/test_o3_scoped_guard.py` (new, 214L) | Z-1..Z-14 시나리오 | | **(부수)** | `create_session.sh`, `resume_session.sh`, `stop_session.sh`, `reconcile.sh` | lib.sh 소싱 경로 변경 (WORKSPACE_ROOT 폴백 추가) | > 참고: 4개 세션 스크립트의 lib.sh 소싱 변경은 O-3 가드 범위가 아닌 부수적 드라이브-바이 리팩터로서 커밋에 함께 포함되었다(§3 R-2). --- ## 2. 검증 결과 ### 2.1 린트 / 구문 - `bash -n` PASS on all 6 shell files (`loop_delegation_guard.sh`, `run_loop.sh`, `create_session.sh`, `resume_session.sh`, `stop_session.sh`, `reconcile.sh`). - `loop_delegation_guard.sh` 실행권한(`-rwxr-xr-x`) 확인. - **shellcheck 미설치**(환경 제약) — 정적 분석 추가 검증 불가 (R-3, non-blocking, 환경 한계). ### 2.2 테스트 - **O-3 suite**: `tests/test_o3_scoped_guard.py` → **24 passed in 1.73s** (Z-1..Z-14, parametrized). - Z-1: Normal mode allows all 3 mutating tools ✓ - Z-2: Active loop denies all 3 mutating tools + `run_loop.sh` in reason ✓ - Z-3: Non-mutating tools allowed during loop ✓ - Z-4: Malformed/unparseable input fails open ✓ - Z-5: Transcript signal covers pre-marker gap ✓ - Z-6: hooks.json matcher targets derived step-type names ✓ - Z-7/Z-14: run_loop.sh writes identity marker (`pid`+`lstart`) + release trap ✓ - Z-8: Dead PID marker ignored ✓ - Z-9: `delegate_job_safe` restores `_mam_release_guard` trap ✓ - Z-10: Reused PID (live, stale lstart) NOT blocked — livelock prevention ✓ - Z-11: Live PID + matching lstart IS blocked ✓ - Z-12/Z-13: Other-user/legacy marker degrades open ✓ - **B-3 regression suite**: `test_b3_herdr_preflight.py` → **11 passed in 0.68s** (lib.sh 함수 무결성 유지). - **통합 회귀**: `test_sanity.py`, `test_c2_no_stale_cache_dir.py` 등 일부 통합 스위트는 본 환경에서 30s 내 비종료(백그라운드 tmux/herdr 구동 대기). 이는 O-3 이전부터 존재하던 환경 의존적 현상이며(`git log` 상 O-3 미관련 커밋에서 마지막 수정), O-3 변경으로 인한 신규 hang가 아님. 단, R-1(§3)로 인해 비-저장소 cwd에서 스크립트 소싱 실패가 확인되어 별도 검증 수행(§3 참고). ### 2.3 동작성 (Operability) — 핵심 가드 - **Fail-Open**: 파싱 실패 → `allow`; python 비정상 종료 → bash `|| allow`. 모든 경로가 emit 누락 없이 종료. - **신원 대조**: `pid` + `lstart` 일치만을 활성 신호로 채택 → PID rollover / PermissionError 시 영구 차단(livelock) 차단. legacy marker(`lstart` 없음)는 liveness 폴백하되 타 소유자는 stale 처리. - **2단 신호**: marker 부재 시 transcript tail 200라인 스캔(`/multi-agent-mux-loop` 검출, `MAM_LOOP_GUARD_RELEASE` 만나면 중지). 과거 루프 언급에 의한 false-positive 위험은 있으나 release 마커로 완화 및 marker 우선 구조라 허용 범위. - **run_loop.sh**: `$$`+`lstart` 기록, `trap _mam_release_guard EXIT INT TERM HUP`, `delegate_job_safe` 내 local cleanup trap 후 `_mam_release_guard` 복원(line 96→100) — Z-9로 입증. ### 2.4 유실 (Completeness) - O-3 요구 산출물 전부 존재: hooks.json, guard script, run_loop marker/trap, AGENTS.md §5, MULTI_AGENT_RULES.md/§3.2, .ko.md §3.2, SKILL.md scope-guard 노트, IMPROVEMENTS.md 이관, 전용 테스트 스위트. - 잔존하는 실행 가능한 `command -v herdr`/`type -P herdr` 프리플라이트 없음(B-3 결과 유지). - 고립된 참조/orphan 없음. ### 2.5 IMPROVEMENTS.md 산술 - Open: A-2(1) + B-4..B-10(7) + O-2(1) + C-3/C-4/C-6(3) = **12건** ✓ (헤더 "12건" 일치) - Completed: O-3, A-1, A-3, C-2, A-5, B-1, B-3, C-1, O-1 = **9건** ✓ (헤더 "9건" 일치) - 단, §3 서브헤더가 "Orchestration Optimizations — 2건"으로 잔존 → 실제 open은 O-2 1건(R-5, non-blocking). --- ## 3. Findings (비차단) ### R-1 (동작성 회귀, **수정 권장**): lib.sh 소싱 경로 캡처 결함 — 4개 스크립트 - **위치**: `create_session.sh`(L23), `resume_session.sh`, `stop_session.sh`, `reconcile.sh`(L19) - **현상**: 신규 패턴 `SKILLS_DIR="$(cd "$SCRIPT_DIR/../.." 2>/dev/null || pwd)"` (또는 `_lib_sh="$(cd "$_script_dir/../.." 2>/dev/null || pwd)/lib.sh"`)에서 `cd`가 성공하면 **stdout이 비어** command substitution 결과가 empty가 되고, `|| pwd`는 `cd`가 성공했으므로 실행되지 않음. 결과: - `SKILLS_DIR=""` → `LIB_SH="/lib.sh"` → `[ -f "/lib.sh" ]` false → `${WORKSPACE_ROOT:-$PWD}/.agents/skills/lib.sh` 폴백으로 전원 이탈. - 원래 코드 `cd "$(dirname "${BASH_SOURCE[0]}")/../.." **&& pwd**`는 `&& pwd`로 cd 후 경로를 캡처했으므로 정상 작동. - **실측 회귀**: 비-저장소 cwd(`/tmp`)에서 `WORKSPACE_ROOT` 미설정 시 스크립트가 `lib.sh`를 찾지 못함: ``` create_session.sh: line 25: /tmp/.agents/skills/lib.sh: No such file or directory reconcile.sh: line 22: /tmp/.agents/skills/lib.sh: No such file or directory ``` → O-3 이전에는 BASH_SOURCE 기반 자체 경로 해석으로 임의 cwd에서 동작했으나, O-3 이후 cwd-독립성 상실(회귀). - **영향도**: 저장소 root 또는 `WORKSPACE_ROOT=repo`인 일반 운용에서는 폴백이 정상 작동하므로 기능 장애 미발생(마스킹). 단, 타 cwd + `WORKSPACE_ROOT` 미설정/오설정 시 동작 불가. - **수정**: `2>/dev/null || pwd` → `&& pwd` 복원(원본 패턴), 또는 `_script_dir`가 이미 절대경로이므로 `_lib_sh="$_script_dir/../../lib.sh"`로 재-cd 없이 직접 결합. 1-line 수정으로 충분(재설계 불필요). ### R-2 (범위 이탈, non-blocking): O-3 커밋에 비관련 lib.sh 리팩터 혼합 - 4개 세션 스크립트의 lib.sh 소싱 변경은 O-3 가드(orchestrator role scoping)와 직접 무관한 drive-by 변경. AGENTS.md §3(Surgical Changes)에 부합하지 않으며, R-1 회귀의 원인이 된 혼합 커밋. 향후 분리 커밋 권장. ### R-3 (테스트 커버리지, non-blocking): lib.sh 경로 변경에 대한 테스트 부재 - R-1의 경로 해석 회귀를 포착할 테스트가 없음. `test_sanity.py`가 create_session dry-run을 다루나 통합 환경 의존적이어회귀를 잡지 못함. 경로 해석 단위 테스트(비-저장소 cwd 케이스) 추가 권장. ### R-4 (문서 부정확, non-blocking): 테스트 카운트 22 vs 실제 24 - `IMPROVEMENTS.md` O-3 항목이 `tests/test_o3_scoped_guard.py (22/22 PASS)`로 기재하나, 실제는 parametrized 확장 포함 **24 test items**(24/24 PASS). 사소한 기재 정정 권장. ### R-5 (문서 동기화, non-blocking): IMPROVEMENTS.md §3 서브헤더 잔존 카운트 - `## 3. 🟡 오케스트레이션 최적화 과제 (Orchestration Optimizations — 2건)`가 잔존하나 O-3 완료로 open은 O-2 1건. 메인 헤더(오케스트레이션 1건)와 모순. `2건 → 1건` 수정 권장. --- ## 4. 총평 O-3 핵심 산출물(PreToolUse 가드 + marker/lstart 신원 대조 + trap 복원 + 4개 문서 동기화 + 24/24 전용 테스트)은 요구사항을 충족하며, fail-open/livelock 방지 설계가 견고하고 테스트로 입증되었다. 린트·유실 관점에서 차단 이슈 없다. 동작성 관점에서 R-1(4개 스크립트 lib.sh 소식 회귀)이 확인되었으나, (a) O-3 핵심 범위가 아닌 부수 리팩터, (b) 일반 운용에서 폴백으로 마스킹됨, (c) 1-line 수정(`|| pwd`→`&& pwd`)으로 해결 가능하므로 재설계/재작업 수준이 아님. 따라서 전체 구현은 건전하며, R-1은 머지 전/직후 수리 권장 사항으로 남긴다. [VERDICT: PASS]