release(v4.8.2): bump framework and eight skills to 4.8.2 in lockstep; archive the follow-up loop's reports
PR #41/#43 review follow-ups F1-F6, implemented by creator-claude-01 in a
loop and reviewed by claude, agy, grok and codex: round 1 (@95eabc6)
codex NOT PASS (a failed env load cached as loaded), fixed in 75f364e;
round 2 4/4 PASS. Full suite at 75f364e 1380 passed / 0 failed. Reviewer
reports (round 2 with identity sidecars) and the creator's reports go to
.agents/reports/<session>/.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
This commit is contained in:
1 parent
75f364e4b4
commit
3563a6fc17
30 files changed
+927
-26
No files matched your search
@@ -0,0 +1,87 @@
|
||||
# Follow-ups #41/#43 review — b5db3aee
|
||||
|
||||
**NOT PASS**: F1/F3/F4/F5/F6는 요구사항을 충족한다. F2의 첫 호출은 요구한 오류 형식을 반환하지만, 실패를 성공으로 캐시하여 같은 프로세스의 다음 호출이 설정 검사를 우회한다. 아래 R1의 작은 수정과 회귀 테스트가 필요하며 설계 재계획은 필요하지 않다.
|
||||
|
||||
## 검증 identity
|
||||
|
||||
- loop: `loop-20261001T230324Z-b511bf`
|
||||
- base: `0051fd6c3b088f8b265928ccd94ea31053fdde7e`
|
||||
- head: `95eabc693f7346f491592b053487bdcf0acd5f4f`
|
||||
- tree: `46399844fa63d62d97f7b52ee35010f2fa1b7998`
|
||||
- 검증 cwd: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261001T230324Z-b511bf/1`
|
||||
- 인터프리터: `/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/followups/.venv/bin/python`
|
||||
|
||||
브리프 identity와 일치함을 확인했다. 실제 설정·세션·모니터를 조작하지 않았고 `.mam.env`를 읽거나 export하지 않았다. 검증 트리와 creator/main 소스는 수정하지 않았다. 회귀 역검증과 탐침은 별도 `/tmp` archive 사본 또는 독립 Python 프로세스의 메모리에서 수행했다.
|
||||
|
||||
## R1 — [P2] F2: 설정 읽기 실패를 성공으로 캐시하여 재호출이 검사를 우회한다
|
||||
|
||||
**근거:** `.agents/skills/lib_py/delegation/mqtt_common.py:125`는 `_load_dotenv()`가 성공하기 **전**에 `_env_loaded=True`로 설정한다. 이번에 추가한 `load_env_or_report()`(`:135`–`:140`)는 `EnvFileError`를 잡아 `False`를 반환하면서 이 상태를 복구하지 않는다. 따라서 `registry.main()`(`.agents/skills/lib_py/delegation/registry.py:535`–`:540`) 등을 같은 프로세스에서 다시 호출하면 helper가 파일을 읽지 않고 `True`를 반환한다.
|
||||
|
||||
**재현:** 검증 cwd에서 다음 합성 파일을 사용하는 `/tmp/b5db3aee-probe-cache.py`를 실행했다.
|
||||
|
||||
1. 임시 파일에 `b'EXPECTED_CONFIG=from-file\nBAD=\xff\n'`을 쓰고 `MAM_ENV_FILE`로 지정한다. WORKSPACE_ROOT/jobs/logs도 임시 경로에 둔다.
|
||||
2. 하나의 Python 프로세스에서 실제 `registry.main(['--registry-dir', <tmp/jobs>, 'register', '--prompt', 'synthetic retry probe'])`를 두 번 호출한다. 예외를 임의로 삼키거나 제품 함수를 대역으로 바꾸지 않는다.
|
||||
3. 파일을 유효한 `EXPECTED_CONFIG=from-file`로 복구한 뒤 `load_env_or_report()`를 호출한다.
|
||||
|
||||
| 단계 | 기대 | head 실제 결과 |
|
||||
|---|---|---|
|
||||
| 깨진 파일, 첫 register | rc 1, 오류 한 줄, 등록 없음 | rc 1, 등록 0건. 그러나 `_env_loaded=True` |
|
||||
| 같은 깨진 파일, 두 번째 register | 다시 rc 1, 등록 없음 | **rc 0, 잡 1건 등록**. 설정 파일 오류가 출력되지 않음 |
|
||||
| 파일 복구 후 재시도 | 파일을 읽어 EXPECTED_CONFIG 적용 | helper가 True를 반환하지만 EXPECTED_CONFIG는 여전히 미설정 |
|
||||
|
||||
한 번 실행하고 바로 끝나는 CLI 프로세스에서는 드러나지 않는다. 하지만 새 helper는 호출자를 종료시키지 않고 실패값을 반환하며, 실제 `main()` 함수도 일반 함수로 호출된다. 실패 후 재시도·복구가 가능한 동일 프로세스에서 “존재하지만 읽지 못한 파일이면 rc 1” 계약을 깨고 설정 없는 작업을 수행한다. `_env_loaded`의 선행 대입 자체는 기존 코드이나, 이번 F2가 예외를 정상 반환값으로 전환하면서 함께 처리해야 하는 상태 불변식이다.
|
||||
|
||||
**수정 방향:** `_env_loaded=True`를 `_load_dotenv(workspace_dir)`의 **성공 뒤**로 옮겨 성공만 캐시한다. 공통 helper에서 실패 시 캐시를 되돌리는 방법도 가능하지만, 성공 뒤에 기록하는 쪽이 라이브러리 직접 호출까지 일관되게 해결한다. 동일 프로세스에서 (a) 연속 실패 두 번 모두 rc 1, (b) 파일 복구 후 설정 적용, (c) 성공 후에는 기존 once-per-process 동작 유지, (d) 실패 중 registry 생성 없음까지 검증한다.
|
||||
|
||||
**대안 검증:** `/tmp/b5db3aee-probe-cache-fixed-in-memory.py`에서 해당 두 문장의 순서만 메모리 내 함수에 바꾸고 같은 탐침을 실행했다. 결과는 rc **1 → 1**, 등록 **0 → 0**, 실패 중 cache=False, 복구 후 `EXPECTED_CONFIG=from-file`이었다. 검증 트리 파일은 변경하지 않았다. 원본 및 대안 로그는 각각 `/tmp/b5db3aee-probe-cache.log`, `/tmp/b5db3aee-probe-cache-fixed.log`이다.
|
||||
|
||||
## F1~F6 완료 조건 검토
|
||||
|
||||
| 항목 | 판단 | 근거 및 회귀 검증 |
|
||||
|---|---|---|
|
||||
| F1 날짜 의존 테스트 | 충족 | `tests/test_issue21_acceptance.py:268`의 `_marker_lstart`가 공백을 정규화한다. `:259` ps 대역은 날짜에 관계없이 `Oct 1`을 만들며 `:275`는 real/padded 두 경로를 검증한다. 실제 marker 충돌 거부, 미등록, marker 보존 단언이 유지됐다. `loop_lock.sh`의 `mam_lstart`/holder 비교는 변경하지 않았다(F4의 설정 초기화만 변경). 비슷한 패턴 검색 결과 `test_o3_scoped_guard.py:57`은 이미 정규화하고, `test_o2_race_free_lock.py:40`의 raw helper는 호출되지 않는 정의다. 불필요한 정리는 하지 않아도 된다. |
|
||||
| F2 Python 오류 한 줄/rc 1 | **미완료: R1** | 일곱 main 모두 공통 helper를 호출하며 라이브러리가 sys.exit하지 않는다. 새 프로세스에서 권한/UTF-8 오류 14개 조합은 한 줄/rc 1/값 비노출을 검증한다(`tests/test_issue26_envfile.py:184`). job_subscriber의 `RC_ERROR`는 1로 계약과 맞는다. 다만 실패 캐시와 동일 프로세스 재호출을 다루는 테스트가 없다. |
|
||||
| F3 BOM/중복/문서 | 충족, 아래 경미한 문구 권고 | `envfile.py:61`의 `utf-8-sig`가 첫 BOM만 제거한다. key-first-wins와 명시 파일 부재의 no-fallback 동작은 유지된다. `.mam.env.example:24`의 BOM, `:26`의 첫 값 우선 및 `MESSAGING.md:369`의 부재/읽기 실패 구분이 반영됐다. `tests/test_issue26_envfile.py:414` 이후 세 테스트가 실제 두 로더와 문서를 함께 확인한다. |
|
||||
| F4 정수 범위 확장 | 충족 | `runner.py:627`, `:630`, `:762`, `:766`에서 grace/child wait min=0, tree max min=1; invalid는 한 번 경고 후 기본값 300/예산+grace/50. 0은 각각 여유 없음/한 번만 확인으로 의미가 분명하다. Bash helper를 함수만 있는 `lib_int.sh`로 옮겨 standalone lock/diff scripts에도 제공한다. ready/empty streak min=1, idle retries/backoff 및 lock retries min=0, diff caps/poll interval min=1은 소비처 의미와 맞는다. 기본 0.5초인 `SKS_QUIESCENT_INTERVAL`은 원래 소수 의미를 유지하고 표에 이유를 남겼다. 저장소 내 backoff 사용은 0초, poll 사용은 정수이며 이들의 소수 사용 사례는 발견하지 못했다. loop SKILL에 grace와 child 설정이 문서화됐다. |
|
||||
| F5 제품 테스트 훅 제거 | 충족 | `stall_watch.py:225`–`:228`은 실제 adapter 속성만 읽는다. `tests/test_issue30_stall_watch.py:196`의 별도 테스트 프로세스에서 adapter property를 False로 바꾼 뒤 실제 watcher main을 실행한다. S13의 “idle에서는 무발행/무주입, blocked는 발행” 단언을 유지하고 `:475`에서 제품의 MAM_TEST_ 부재를 고정한다. |
|
||||
| F6 문서/두 번째 wait 중 소멸 | 충족 | delegate-job `SKILL.md:53`은 60초를 herdr wait에 한정하고 발행 완료까지 더 늦을 수 있음을 명시한다. `test_issue30_stall_watch_stop.py:429`가 두 번째 wait 내부에서 gone 상태를 바꾸고 `:435`가 실제 두 번의 wait 및 마지막 alive=False를 확인한다. 단순 호출 횟수만으로 wait 전에 종료되는 이전 허점을 막는다. |
|
||||
|
||||
위 표의 짧은 제품 경로는 `.agents/skills/` 아래이며 runner는 `lib_py/loop/runner.py`, envfile은 `lib_py/delegation/envfile.py`이다.
|
||||
|
||||
**경미한 문서 권고:** 새 `MESSAGING.md:369`은 wrapper와 Python이 모두 오류 **한 줄**을 출력한다고 서술한다. Python의 새 형식은 한 줄이지만 wrapper의 기존 `:44`는 parser ERROR 뒤에 `ERROR: cannot load ... (see the line above)`를 한 줄 더 출력한다. 한 줄이라는 표현을 Python 진입점에만 한정하면 정확하다. 이번 반려의 근거는 R1이며 이 문구는 별도 blocker가 아니다.
|
||||
|
||||
## 수정 전 실패 확인 방법
|
||||
|
||||
- base `0051fd6`을 `/tmp/b5db3aee-before`에 `git archive`로 풀고, 현재 회귀 테스트 3개 파일(envfile/ints/stall_watch)만 복사했다. F2의 7×2 오류 조합, F3의 세 항목, F4의 runner/standalone/empty capture, F5의 제품 훅 부재를 선택했다. **44 failed, 17 passed, 118 deselected in 72.16s**. 테스트 수집/누락 모듈 실패가 아니라 traceback, BOM 유실, 문서 불일치, 경고/기본값/정규화 차이 및 제품 훅 잔존을 확인하는 단언에서 실패했다. 정상 값 일부는 원래도 같으므로 통과하는 것이 맞다. 긴 child wait 값은 이전 구현에서 60초 subprocess timeout으로 실패했다. 로그: `/tmp/b5db3aee-before.log`.
|
||||
- F3 중 중복 키와 없는 명시 파일은 동작을 바꾸지 않는 문서 수정이다. 이전 소스에서는 동작 단언은 통과하고 새 문서 단언이 실패했다. 이를 기능 회귀로 과장하지 않는다.
|
||||
- F1은 제품 수정이 아닌 테스트 수정이다. `/tmp/b5db3aee-test-mutations`에서 새 테스트의 marker 정규화만 이전 `.strip()`으로 되돌리면 padded 테스트가 실패한다. 정규화 결과 자체를 확인하는 단언을 임시로 제거하고 한 번 더 실행해도 실제 `already running` 거부 단언에서 실패했다(잘못된 stale 판정으로 controller 초기화까지 진행). 날짜에 무관하게 이전 문제를 재현했다. 로그: `/tmp/b5db3aee-f1-behavior.log`.
|
||||
- F6 역시 제품 동작 변경이 아닌 테스트의 사건 순서 강화다. 같은 임시 사본에서 alive 대역을 이전 `호출 횟수 <= 2` 방식으로 되돌리면 새 `ticks == [1, 2]` 단언이 `[1]`로 실패했다. 새 테스트가 이전의 잘못된 사건 배치를 구별함을 확인했다. F1/F6 mutation 첫 실행은 **2 failed**였다. 로그: `/tmp/b5db3aee-test-mutations.log`.
|
||||
|
||||
## 범위와 검증 결과
|
||||
|
||||
8개 커밋은 모두 지정된 `fix(followups):`/`test(followups):` 접두어와 Co-Authored-By를 갖는다. 항목별 커밋이며 마지막 F2 보완은 subscriber의 symbolic rc 정리다. 공통 `lib_int.sh` 추출에 따라 standalone 테스트 fixture에 파일을 복사하고 `deploy/remove.sh` fallback에 새 파일을 추가한 것은 필요한 부수 변경이다. 설치와 freeze는 skills 디렉터리를 통째로 복사하므로 새 파일도 포함된다. wrapper 종결 처리 Python 이전, #39/#40/#42, 버전 bump나 외부 상태 변경은 없다.
|
||||
|
||||
- 브리프 지정 7개 파일 + 직접 변경된 `test_issue21_loop_b.py`: **303 passed, 1 skipped in 446.40s**, rc 0. skip은 macOS에서 `/proc` 기반 process-tree 테스트를 제외하는 기존 조건이다. 로그: `/tmp/b5db3aee-selected.log`.
|
||||
- 공통 Bash helper/lock/poll 변경의 추가 검증(`test_o2_race_free_lock.py`, `test_issue6_turn_handshake.py`, `test_issue19_subscriber_daemon_lifecycle.py`): **52 passed in 102.02s**, rc 0. 로그: `/tmp/b5db3aee-extra.log`.
|
||||
- 검증 종료 후 `git status --short`/`git diff --quiet`는 clean이며 HEAD/tree도 동일하다.
|
||||
|
||||
지정 검증 명령(위 review cwd):
|
||||
|
||||
```bash
|
||||
AGENT_PYTHON_BIN=/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/followups/.venv/bin/python \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/followups/.venv/bin/python \
|
||||
-m pytest -p no:cacheprovider -q --basetemp=/tmp/b5db3aee-selected \
|
||||
tests/test_issue26_envfile.py tests/test_issue26_ints.py tests/test_issue21_acceptance.py \
|
||||
tests/test_issue30_stall_watch.py tests/test_issue30_stall_watch_stop.py \
|
||||
tests/test_tier1_unit.py tests/test_version_consistency.py tests/test_issue21_loop_b.py
|
||||
```
|
||||
|
||||
추가 검증은 같은 환경/옵션에 별도 `--basetemp=/tmp/b5db3aee-extra`를 사용했다. 실제 `.mam.env` export 없이 local MQTT fixture를 사용했다.
|
||||
|
||||
- `git diff --check 0051fd6 HEAD`: 통과.
|
||||
- 변경된 모든 Python 파일 AST 구문 검사와 shell 파일 `bash -n`: 통과.
|
||||
- 독립 cache probe: 원본에서 R1 재현, 메모리 내 최소 수정에서 해결 확인.
|
||||
- 전체 약 60분 스위트는 지시대로 실행하지 않았다.
|
||||
|
||||
[VERDICT: NOT PASS]
|
||||
Reference in new issue
Block a user