Files
multi-agent-mux/.agents/reports/canary-projects-multi-agent-mux-creator-claude/plan-00334786.md
T
Godopu 40576c44ab refactor(uuid): resolve B-10 by deprecating agent_identities and removing PyYAML dependency
- Remove dead agent_identities read path and PyYAML import from workspace_uuid.py
- Defer eager PyYAML import in verify_session.py to lazy YAML fallback branch
- Simplify UUID resolution to 2-tier model (tier-1 own row ID -> tier-2 adapter scan)
- Clean up unused Drift D and ghost cache clearing in reconcile.sh and stop_session.sh
- Add 3 regression guards in tests/test_tier1_unit.py (266/266 PASS)
- Update IMPROVEMENTS.md, VERSIONS.md, and SKILL.md files
2026-08-17 10:59:03 +09:00

25 KiB
Raw Blame History

📐 구현 계획서 Rev.2 — B-10 (P3-2): agent_identities tier-3 신원 캐시 완전 제거 (Option A)

  • Job ID: 104b94c8 (Rev.1 = 00334786)
  • Planner: claude (session: herdr:canary-projects-multi-agent-mux-creator-claude)
  • Role: Planner (MULTI_AGENT_RULES.md §1 — 본 작업에서 저장소 코드 0건 수정)
  • 반영 대상 Challenge: 26f5d224 (agy, Worker / Plan Reviewer) — [VERDICT: PASS WITH CHALLENGE]
  • 기준 커밋: 7e21077 (refactor, 작업 트리 clean)

0. 요약

Challenge 2건 모두 타당합니다. 전면 수용합니다. 격리 클론에서 Rev.1 의 가드 코드를 원문 그대로 실행해 두 결함을 재현했습니다.

그리고 챌린저의 권고안 #1 을 실제로 구현해 보는 과정에서 생산 코드 결함 1건을 새로 발견했습니다. 이것이 이번 Rev.2 의 가장 중요한 산출입니다.

신규 발견: verify_session.py:10import os, sys, json, sqlite3, yamlyaml 을 즉시 import 합니다. 이 함수(mam_orchestrator_uuids)는 find_workspace_uuid_main():38 에서 tier 로직보다 먼저 호출합니다. 따라서 tier-3 을 제거해도 UUID 해결 경로는 여전히 PyYAML 을 요구합니다. 브리프의 목표("remove PyYAML dependency from workspace_uuid.py")는 파일 단위로는 달성되지만 실행 경로 단위로는 달성되지 않습니다.

이는 B-10 항목 (b) 가 원래 lib.sh/load_state_json 에 대해 서술했던 바로 그 결함 패턴이 다른 파일에 미수정 상태로 남아 있던 것입니다. state.py:34 가 이미 올바른 선례(분기 내부 import)를 제공하므로 1줄로 교정됩니다. 단계 4 로 추가했습니다.

항목 Rev.1 Rev.2
C2 pathlib NameError 존재 수정
C1 가드 공허성 import lib_py.workspace_uuid — 베이스라인에서도 통과 AST 검사 + 실행 검사 2종으로 교체
가드 수 2 3
verify_session.py:10 즉시 yaml import 미인지 단계 4 신설
뮤테이션 검증 계획만 제시(M1~M3) 5종 실측 완료(M1·M2·M3a·M3b·M4)
제거 단계 자체의 실행 검증 미실시 클론에 선적용 후 구문·가드·전체 회귀 확인

1. Challenge 판정 — 2건 모두 수용 (실행으로 재현)

1.1 C2 — pathlib NameError (확인)

Rev.1 §5.1 의 두 번째 테스트는 지역 import 가 subprocess, sys, os 뿐인데 pathlib.Path 를 씁니다. 첫 번째 테스트가 pathlib 을 import 하지만 그것은 자기 함수 스코프이고, tests/test_tier1_unit.py:1-8 에도 최상위 import pathlib 이 없습니다(확인).

Rev.1 가드를 클론에 원문 그대로 붙여 실행:

>       skills = str(pathlib.Path(__file__).resolve().parent.parent / ".agents" / "skills")
E       NameError: name 'pathlib' is not defined
tests/test_tier1_unit.py:370: NameError

챌린저가 예측한 그 줄에서 정확히 재현되었습니다. 단순 누락이며 제 실수입니다.

1.2 C1 — 가드가 베이스라인에서 통과(공허) (확인)

pathlib 만 고치고 tier-3 이 그대로 살아 있는 미수정 베이스라인에서 다시 실행:

tests/test_tier1_unit.py::test_b10_no_agent_identities_reader_in_production  FAILED   ← 정상 (offender 9건 열거)
tests/test_tier1_unit.py::test_b10_workspace_uuid_needs_no_pyyaml            PASSED   ← 공허
1 failed, 1 passed

가드 1 은 제 역할을 합니다(제거 전이므로 실패). 가드 2 는 제거가 일어나지 않았는데도 통과합니다 — 챌린저 지적대로 import lib_py.workspace_uuidfind_workspace_uuid_main() 을 실행하지 않으므로 :100 의 지연 import 에 도달하지 못합니다.

추가 실측 — 공허성의 정확한 범위: Rev.1 이 제안했던 뮤테이션 M3(최상위 import yaml 추가)은 실제로는 잡습니다. 잡지 못하는 것은 이 저장소에 실제로 존재했던 형태, 즉 함수 내부 지연 import 입니다.

뮤테이션 Rev.1 가드 2
M3a — 최상위 import yaml FAIL 잡음
M3b — 함수 내부 지연 import yaml (C1 이 지목한 형태) PASS 못 잡음

즉 제가 설계한 가드는 제가 상상한 결함 형태만 방어하고 실제로 있었던 형태는 놓칩니다. 챌린저 지적이 정확합니다.


2. 챌린저 권고안 평가

챌린저는 두 가지를 권고했습니다.

2.1 권고 #2 (AST/텍스트 검사) — 채택, AST 로 정밀화

텍스트 부분 문자열 검사("yaml" not in source)는 YAML_PATH 같은 정당한 식별자에 걸려 향후 오탐을 냅니다. AST 로 Import/ImportFrom 노드만 검사하면 중첩 깊이와 무관하게 정확히 잡습니다.

2.2 권고 #1 (mock env 로 find_workspace_uuid_main() 실행) — 채택, 단 그대로는 오탐

방향은 옳습니다. 그러나 명세된 형태로 구현하면 완벽한 B-10 구현 위에서도 실패합니다. 챌린저가 제시한 4개 환경변수(WS_ABS, AGENT, MAM_STATE_JSON, YAML_PATH)를 갖추고 noyaml 스텁 하에서 실행한 결과:

AssertionError: resolution path still needs PyYAML:
  File ".../lib_py/workspace_uuid.py", line 38, in find_workspace_uuid_main
    orchestrator_ids = set(mam_orchestrator_uuids())
  File ".../lib_py/verify_session.py", line 10, in mam_orchestrator_uuids
ImportError: PyYAML absent (stub)

실패 원인은 workspace_uuid.py 가 아니라 verify_session.py 입니다 — §3 의 신규 발견으로 이어집니다. 권고 #1 은 그 결함을 함께 고친 뒤에야 의미 있는 가드가 됩니다. 이 계획은 둘 다 반영합니다.


3. 🆕 신규 발견 — verify_session.py:10 의 즉시 yaml import

3.1 결함

# verify_session.py:6-11
def mam_orchestrator_uuids():
    global _MAM_ORC_CACHE
    if _MAM_ORC_CACHE is not None:
        return _MAM_ORC_CACHE
    import os, sys, json, sqlite3, yaml      # ← :10  yaml 을 무조건 import
    override = os.environ.get("MAM_ORCHESTRATOR_UUIDS")

yaml 은 이 함수 안에서 실제로 쓰입니다:48yaml.safe_load(f) (YAML 폴백). 문제는 import 위치입니다. :10 은 함수 진입 즉시 실행되므로:

  • DB 분기만 타도 PyYAML 필요
  • MAM_ORCHESTRATOR_UUIDS 환경변수로 조기 반환해도 필요 (import 가 :10, 오버라이드 검사가 :11)

실측 — 오버라이드를 빈 문자열로 주어 즉시 반환시켜도:

$ PYTHONPATH=<noyaml>:... MAM_ORCHESTRATOR_UUIDS="" python -c "…mam_orchestrator_uuids()"
ImportError: PyYAML absent (stub)

3.2 왜 B-10 범위인가

find_workspace_uuid_main():38 에서 mam_orchestrator_uuids() 를 호출합니다 — tier-1 보다도 먼저입니다. 따라서 tier-3 을 지워도 UUID 해결 경로 전체는 PyYAML 을 요구한 채 남습니다. 브리프의 목표를 실행 경로 기준으로 달성하려면 이 한 줄이 필요합니다.

또한 이것은 B-10 항목 (b) 가 서술한 것과 동일한 결함 패턴입니다. (b) 는 lib.sh/load_state_json 에 대해 제기되었고 state.py 이관 과정에서 해소되었는데(Rev.1 §1.2), 같은 패턴이 verify_session.py 에 남아 있었습니다. B-10 을 "PyYAML 의존 완화" 과제로 닫으면서 이걸 남기면 항목이 절반만 닫힙니다.

3.3 교정 — state.py:34 선례를 그대로 따름

    import os, sys, json, sqlite3                      # :10  — yaml 제거
    ...
            if (d_obj is None or "orchestrator_uuids" not in d_obj) and os.path.exists(yaml_p):
                try:
                    import yaml                        # ← YAML 폴백 분기 안으로
                    with open(yaml_p) as f:
                        d_obj = yaml.safe_load(f) or {}

실측 확인: 이 교정 후 §4 의 실행 가드가 통과합니다(교정 전 FAIL → 교정 후 PASS).

3.4 lib_pyyaml import 전수 조사

위치 판정
atomic_yaml.py:6 (모듈 최상단) 정당 — 모듈의 존재 이유가 YAML 직렬화이고, 이중 인터프리터 전략상 시스템 python3(PyYAML 보유)에서만 실행됨
state.py:34 (분기 내부) 이미 올바름 — 이번 교정의 선례
workspace_uuid.py:100 (tier-3 내부) B-10 단계 1 에서 제거
verify_session.py:10 (함수 즉시) 단계 4 신설

교정 후 lib_py 의 무조건적 PyYAML 요구는 atomic_yaml.py 하나로 수렴합니다.


4. 확정 회귀 가드 — 3종, 뮤테이션 5종 실측 완료

4.1 확정 코드 — tests/test_tier1_unit.py 에 추가

def test_b10_no_agent_identities_reader_in_production():
    """B-10: agent_identities has no writer; no production code may read it."""
    import pathlib
    root = pathlib.Path(__file__).resolve().parent.parent
    targets = [
        root / ".agents" / "skills" / "lib_py" / "workspace_uuid.py",
        root / ".agents" / "skills" / "multi-agent-mux-monitor" / "scripts" / "reconcile.sh",
        root / ".agents" / "skills" / "multi-agent-mux-stop" / "scripts" / "stop_session.sh",
        root / ".agents" / "skills" / "lib.sh",
    ]
    offenders = []
    for f in targets:
        for i, line in enumerate(f.read_text().splitlines(), 1):
            if "agent_identities" not in line:
                continue
            if line.lstrip().startswith("#"):     # 금지 규약을 서술하는 주석은 허용
                continue
            offenders.append(f"{f.name}:{i}: {line.strip()}")
    assert not offenders, "agent_identities read path resurrected:\n" + "\n".join(offenders)


def test_b10_workspace_uuid_has_no_yaml_import():
    """B-10: no `import yaml` anywhere in workspace_uuid.py — top-level OR lazy."""
    import ast, pathlib
    src = (pathlib.Path(__file__).resolve().parent.parent
           / ".agents" / "skills" / "lib_py" / "workspace_uuid.py")
    tree = ast.parse(src.read_text())
    offenders = []
    for node in ast.walk(tree):                       # ast.walk → 중첩 깊이 무관
        if isinstance(node, ast.Import):
            for a in node.names:
                if a.name.split(".")[0] == "yaml":
                    offenders.append(f"line {node.lineno}: import {a.name}")
        elif isinstance(node, ast.ImportFrom):
            if (node.module or "").split(".")[0] == "yaml":
                offenders.append(f"line {node.lineno}: from {node.module} import ...")
    assert not offenders, "PyYAML dependency reintroduced:\n" + "\n".join(offenders)


def test_b10_find_workspace_uuid_runs_without_pyyaml(tmp_path):
    """B-10: the executed resolution path must not need PyYAML."""
    import subprocess, sys, os, json, pathlib
    stub = tmp_path / "noyaml"
    (stub / "yaml").mkdir(parents=True)
    (stub / "yaml" / "__init__.py").write_text('raise ImportError("PyYAML absent (stub)")\n')
    skills = str(pathlib.Path(__file__).resolve().parent.parent / ".agents" / "skills")
    ws = tmp_path / "ws"; ws.mkdir()
    env = os.environ.copy()
    env["PYTHONPATH"] = f"{stub}:{skills}"
    env["WS_ABS"] = str(ws)
    env["AGENT"] = "claude"
    env["MAM_STATE_JSON"] = json.dumps({"herdr_sessions": []})
    env["YAML_PATH"] = str(tmp_path / "agent-sessions.yaml")
    env["HOME_DIR"] = str(tmp_path)
    env["CLAUDE_PROJECT_DIR"] = str(tmp_path / "projects")
    r = subprocess.run(
        [sys.executable, "-c",
         "from lib_py.workspace_uuid import find_workspace_uuid_main; find_workspace_uuid_main()"],
        capture_output=True, text=True, env=env)
    assert r.returncode == 0, f"resolution path still needs PyYAML: {r.stderr}"
    assert "yaml" not in r.stderr.lower(), f"PyYAML touched at runtime: {r.stderr}"

env 를 명시 구성하므로 앰비언트 PYTHONPATH 에 의존하지 않습니다(직전 라운드 N1 재발 방지). 지역 import 에 pathlib 을 포함시켜 C2 를 해소했습니다.

4.2 뮤테이션 매트릭스 — Rev.2 에서 실측

클론에 §5 단계 1~4 를 선적용한 뒤 측정했습니다.

# 뮤테이션 기대 실측
baseline (제거 + 교정 적용) PASS 3 passed
M1 workspace_uuid.pyagent_identities 읽기 복원 가드 1 FAIL 1 failed
M2 reconcile.sh 에 drift D 읽기 복원 가드 1 FAIL 1 failed
M3a 최상위 import yaml 가드 2 FAIL 2 failed (실행 가드도 동반 실패)
M3b 함수 내부 지연 import yaml (C1 형태) 가드 2 FAIL 1 failed ← Rev.1 이 놓쳤던 형태
M4 verify_session.py yaml 지연 교정 되돌림 가드 3 FAIL 1 failed

M3b 가 Rev.2 의 핵심 개선입니다 — Rev.1 가드에서는 이 뮤테이션이 통과했습니다.


5. 구현 계획

5.1 단계 1 — workspace_uuid.py tier-3 제거

ai = d.get('agent_identities') … 부터 print('') 직전까지 28줄 삭제, import 를 import os, sys, json 으로 축소(sqlite3 은 tier-3 외 사용처 0건).

클론 실측: 삭제 후 ast.parse OK, 전체 회귀 §8-9 참조.

5.2 단계 2 — reconcile.sh drift D 제거

# === drift D: stale UUID … === 부터 result = { 직전까지 35줄 삭제. bash -n OK 확인.

주의: glob/sqlite3 import 는 다른 분기에서도 쓰이므로 제거하지 마십시오(클론 실측에서 삭제 없이 정상 동작).

5.3 단계 3 — stop_session.sh 캐시 소거 제거

# agent_identities 는 cache — … 블록 6줄 삭제. :164 주석을 tier-1(row) -> tier-2(workspace-scoped disk scan) 로 정정. bash -n OK 확인.

5.4 🆕 단계 4 — verify_session.py:10 yaml 지연화 (§3)

-    import os, sys, json, sqlite3, yaml
+    import os, sys, json, sqlite3

그리고 yaml.safe_load 를 쓰는 YAML 폴백 try: 블록 첫 줄에 import yaml 을 삽입합니다. 1줄 이동이며 state.py:34 와 동일한 형태입니다.

5.5 단계 5 — lib.sh 주석 정정

# Resolution order:
#   1) herdr_sessions[] row whose pane.cwd == this workspace -> per-row own id
#      (claude_session_id_own / agy_conversation_id_own)
#   2) on-disk scan scoped to this workspace, via the agent adapter's discover()
# Prints the UUID on stdout (empty line if none). Always exits 0.

:13263-tier2-tier, … -> cwd-matched cache 제거.

5.6 단계 6 — 스킬 문서

  • status/SKILL.md:108 drift D 행 삭제 (A/B/C 3종만)
  • monitor/SKILL.md:143 예시 출력의 agent_identities.* 줄 삭제
  • resume/SKILL.md:50-58 해결 순서 교체 — 기존 서술이 이미 오류입니다. agent_identities 를 1·2순위 primary 로 안내하고 있으나 P0-C 가 이를 cache 로 강등했습니다(update_yaml_resumed.sh:5 가 명시). 실제 순서로 교체:
1. herdr_sessions[] 행의 per-row own id (claude_session_id_own / agy_conversation_id_own)
   — multi-agent-mux-stop 이 종료 직전 확정 기록한 값 (tier-1, race-free)
2. 워크스페이스로 스코프된 온디스크 스캔 (어댑터 discover())
둘 다 비면 → 이 워크스페이스에는 아직 대화가 없음. multi-agent-mux-create 로.

보존: resolve_session_id.sh:7# P0-C: 전역 agent_identities 를 즉시 반환하지 않는다금지 규약 서술이므로 유지합니다(tier-3 제거로 오히려 더 정확해짐). 가드 1 의 주석 허용 규칙이 이를 통과시킵니다.


6. adapter.identity_cache_fields — Option A 확정

Rev.1 §4 에서 판단을 요청했고 챌린저가 §3 표에서 "Adopt Option A" 로 동의했으므로 확정합니다.

단계 3 이 stop_session.sh 의 유일한 생산 소비자를 제거하므로, base.py:57 에 근거 주석을 반드시 남깁니다.

    @property
    def identity_cache_fields(self) -> tuple:
        """agent_identities 캐시의 에이전트별 필드명.
        B-10(Option A)로 캐시 읽기 경로가 제거되어 현재 생산 소비자는 0건이지만,
        캐시 쓰기 경로가 도입되면 즉시 필요한 유일한 스키마 기술이므로 존치한다.
        임의 삭제 금지 — 삭제 시 4개 어댑터에 필드명을 다시 흩뿌려야 한다."""
        raise NotImplementedError

근거 없는 미사용 속성은 다음 정리 라운드에서 "쉬운 삭제 대상"으로 오인됩니다 — C-4 가 _HERDR_SHIM_DIR_PATTERN 에서 정확히 그 사례였습니다.


7. 문서 동기화

7.1 IMPROVEMENTS.md — 7곳

현재 변경 후
:3 최종 갱신일 2026-08-17 (…, C-6 완료, 263/263) B-10 완료 및 266/266 반영
:5 미해결 4건 (아키 1, 엣지 3, 오케 0, 레거시 0) 미해결 3건 (아키 1, 엣지 2, 오케 0, 레거시 0)
:6 완료 21건 완료 22건, 목록에 B-10 추가
:70 ## 2. … (Edge-case Bugs — 3건) … (Edge-case Bugs — 2건)
:79-80 B-10 항목 삭제 (§5 로 이동)
:92 ## 5. … (Completed Tasks — 21건) … (Completed Tasks — 22건)
:241 | **P3-2** | **B-10** | tier-3 신원 캐시 존치/제거 결정 + PyYAML 의존 완화 | 중 | A-4 M2 | … tier-3 신원 캐시 완전 제거 (Option A) **(✅ 완료 — 전체 266/266 PASS)** |

§5 신규 항목:

### **B-10 (P3-2): `agent_identities` tier-3 신원 캐시 완전 제거 (Option A)** — ✅ 완료
- 저장소 전체에 `agent_identities` 쓰기 코드가 0건임을 재확인하고(라이브 `.db` 최상위 키에도 부재),
  구조적으로 히트 불가였던 읽기 경로 3곳을 제거했습니다 — `workspace_uuid.py` tier-3 폴백(28줄),
  `reconcile.sh` drift D 진단(35줄), `stop_session.sh` purge 시 캐시 소거(6줄), 관련 주석 3곳.
  UUID 해결은 tier-1(per-row own id) → tier-2(어댑터 `discover()`) 2단계로 단순화되었습니다.
- **PyYAML 의존 — 실행 경로 기준으로 해소**: `verify_session.py::mam_orchestrator_uuids``yaml` 을 함수 진입 즉시 import 하고 있어(`:10`), tier-3 을 지워도 UUID 해결 경로는 PyYAML 을
  요구했습니다. `state.py` 의 기존 선례대로 YAML 폴백 분기 안으로 이동시켜 교정했습니다.
- **정정**: 원 항목이 서술했던 "`lib.sh` 의 PyYAML 하드 의존" 은 `load_state_json``state.py` 로
  이관되며 **이미 해소된 상태**였습니다. 한편 `atomic_yaml.py` 는 모듈 존재 이유상 앞으로도
  최상단에서 import 하므로 **저장소 차원의 PyYAML 요구와 설치 게이트는 유지**됩니다.
- 회귀 가드 3종을 신설하고 뮤테이션 5종(M1·M2·M3a·M3b·M4)으로 방어력을 검증했습니다.

주의: :5 의 엣지케이스 카운트와 :70 §2 헤더는 반드시 함께 바꿉니다.

7.2 VERSIONS.md

### 🚀 v2.0.0 changelog 에 #### 7 추가:

#### 7. `agent_identities` tier-3 신원 캐시 완전 제거 및 UUID 해결 경로 PyYAML 탈의존 (B-10 / Option A)
- 쓰기 경로가 존재하지 않아 구조적으로 히트 불가였던 tier-3 폴백과 부속 소비자
  (`workspace_uuid.py`, `reconcile.sh` drift D, `stop_session.sh` 캐시 소거)를 전면 삭제.
- UUID 해결 경로를 **tier-1(per-row own id) → tier-2(어댑터 `discover()`)** 2단계로 단순화.
- `verify_session.py::mam_orchestrator_uuids` 의 즉시 `yaml` import 를 YAML 폴백 분기로 이동,
  UUID 해결 경로가 PyYAML 없이 완주함을 실행 가드로 고정
  (`atomic_yaml.py` 의 시스템 PyYAML 요구는 설계상 유지).
- 회귀 가드 3종 신설 — 읽기 경로 부활 차단, `import yaml` AST 검사(지연 import 포함), 실행 경로 검증.

:44 의 A-4 인터페이스 나열에서 identity_cache_fields 는 §6 Option A 에 따라 유지합니다.


8. 검증 절차

# 명령 / 확인 기대
1 bash -nlib.sh, reconcile.sh, stop_session.sh 3/3 OK (클론 실측 완료)
2 python -c "import ast; ast.parse(open('workspace_uuid.py').read())" OK (클론 실측 완료)
3 grep -rn "agent_identities" .agents/skills/ 주석 외 0건
4 grep -rn "tier-3|3-tier" .agents/skills/ 0건
5 grep -n sqlite3 lib_py/workspace_uuid.py 0건
6 grep -n "yaml" lib_py/verify_session.py 폴백 분기 내부 1건만
7 라이브 워크스페이스에서 find_workspace_uuid <ws> claude 변경 전과 동일 출력
8 reconcile.sh 1회 실행 후 drifts 클래스 집합 D 미출현, A/B/C 정상
9 뮤테이션 M1·M2·M3a·M3b·M4 각각 해당 가드 FAIL (§4.2 재현)
10 pytest tests/ -q 266 passed (263 실측 + 가드 3건)
11 env -u PYTHONPATH pytest tests/test_tier1_unit.py -q 전부 통과 (환경 비의존)
12 IMPROVEMENTS.md :5:70 대조 엣지 카운트 일치
13 IMPROVEMENTS.md :6:92 대조 둘 다 22건

7번이 동작 동일성 핵심 검증입니다 — tier-3 이 히트 불가였다는 주장이 맞다면 출력이 바뀌어서는 안 됩니다.

10번은 약 6분 30초 소요됩니다. 백그라운드 실행 권장.


9. 규모 및 리스크

파일 변경
lib_py/workspace_uuid.py 28줄, sqlite3 import 제거
lib_py/verify_session.py 🆕 yaml import 1줄 이동
reconcile.sh 35줄 (import 는 보존)
stop_session.sh 6줄 + 주석 1곳
lib.sh 주석 2곳
base.py identity_cache_fields 근거 docstring
SKILL.md 3종 drift D 행·예시 1줄·해결 순서
IMPROVEMENTS.md / VERSIONS.md 카운트·항목 이동 + changelog
tests/test_tier1_unit.py 가드 3건
테스트 총계 263 (실측) → 266
리스크 평가
동작 회귀 낮음. 제거 대상 전부 생산자 0인 데이터를 읽습니다. 클론 전체 회귀로 확인(§10)
단계 4 부작용 낮음. import 위치만 이동하며 yaml 사용 지점은 그대로. state.py 에 동일 선례 존재
레거시 상태 파일 ⚠️ 구버전 agent_identities 가 남은 .db/.yaml 이 있어도 tier-1·tier-2 가 동일 UUID 를 찾습니다. tier-3 은 앞 두 단계가 모두 실패해야 도달하던 경로이고, 스키마를 지우는 게 아니라 읽기를 멈추는 것이므로 데이터 파괴 없음
drift D 진단 상실 영향 없음. 생산자 0이므로 한 번도 발화한 적 없음
identity_cache_fields 고아화 §6 Option A + 근거 docstring 으로 차단
가드 무력화 §4.2 뮤테이션 5종으로 차단

권장 커밋 분할

  1. refactor(uuid): drop the dead agent_identities tier-3 fallback (B-10) — 단계 1~3
  2. fix(verify): defer the yaml import so UUID resolution runs without PyYAML (B-10) — 단계 4
  3. test(b10): guard the read path, the yaml import, and the executed resolution path — §4
  4. docs: sync comments, SKILL.md resolution order, IMPROVEMENTS.md and VERSIONS.md for B-10 — 단계 5~6 + §7

3번을 1·2번 뒤에 두면 가드가 앞 커밋 없이 실패하고 함께는 통과함을 커밋 순서로 증명할 수 있습니다. 2번을 분리하는 이유는 이것이 읽기 경로 제거와 독립된 생산 코드 수정이기 때문입니다 — 되돌릴 일이 생기면 따로 되돌릴 수 있어야 합니다.


10. 한계

  • 본 계획은 Planner 산출물이며 저장소 파일을 수정하지 않았습니다(작업 트리 계획 전후 clean). 모든 실측은 격리 클론(git clone --local --no-hardlinks)에서 수행했고 클론은 삭제했습니다.
  • 단계 14 를 클론에 선적용해 구문 검사·가드·뮤테이션·전체 회귀를 실측했습니다. 단계 56(주석·문서)은 실행 대상이 아니므로 적용하지 않았습니다.
  • 클론 전체 회귀 실측 완료: 단계 1~4 + 가드 3종을 적용한 클론에서 266 passed in 408.18s (0:06:48) — 기준 베이스라인 7e21077 의 263 passed in 408.21s 대비 정확히 가드 3건 순증, 회귀 0건입니다. 즉 §8-10 의 266 목표치는 예측이 아니라 실측값입니다. 다만 이는 클론 측정이므로 구현 후 저장소에서 재확인하십시오.
  • 제안 가드 3종의 함수 수가 3이므로 266 을 목표치로 잡았습니다. 구현 시 함수를 병합하면 수치를 그에 맞춰 조정하십시오.
  • 사용자 환경의 레거시 .db/.yaml 에 실제로 agent_identities 가 남아 있는지는 이 워크스페이스 한 곳만 확인했습니다(부재).
  • verify_session.pyyaml 지연화는 mam_orchestrator_uuids 한 함수만 대상으로 했습니다. 같은 파일의 다른 함수는 감사하지 않았습니다(grepyaml 참조는 이 함수에만 존재).