quad/.claude/session/2026-08-16-02-audit-tooling-verification-and-first-real-run.md
qwreey 01c4abf727
docs: 감사 4라운드 반영 + const는 HUMAN_TODO로 이관(사용자 결정)
사용자 결정 — const 바인딩 재검토는 에이전트가 정할 문제가 아님. 논거:
pesde의 타입 추출(d.ts처럼 types emit)류 툴링이 미성숙해 const를 제공 못
하고 언제 가능해지는지도 불명확하며, 이는 사용자가 확인된 정보로 제공하는
게 맞다는 것. question.md에서 닫고 archive로 옮긴 뒤 HUMAN_TODO.md 8번
신설 — 사용자가 시점을 파악하거나 가능해질 때 에이전트에 알림. 그때까지
에이전트는 "일단 local로" 원칙 유지. architecture.md에 날짜와 논거 반영.

감사 4라운드(설계 코퍼스 각도):
- todos.md의 개수 하드코딩 2건 제거("체크리스트 8개", "11개 전원 완료")
  — 이 프로젝트 자신의 "개수는 소스 하나만" 규칙 위반이던 기존 항목
- 코퍼스 확실 발견 0건(설계 문서 쪽은 깨끗)

주입 경로 실측 둘:
- 에이전트 정의는 이번엔 HEAD보다 1커밋 전 — 뒤처지는 폭이 실행마다 다름을
  관측표에 4번째 행으로 추가
- CLAUDE.md @import 컨텍스트는 세션 시작 시점에 고정됨(감사자가 받은 것이
  세션 시작 커밋과 일치, 그 사이 7커밋). 메인 세션도 동일해 옛 conventions.md
  를 들고 있었음. 동작이 명확한 별개 경로라 conventions.md에 규칙으로 명시 —
  @import 파일을 고친 세션은 기억이 아니라 파일을 직접 Read해서 따를 것

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 04:01:29 +09:00

30 KiB

2026-08-16 두 번째 세션 — 감사 툴링 재시작 검증 + 핸드오버 감사 첫 실동

전 세션(2026-08-16-01-subagent-audit-and-claudemd-split.md)이 남긴 "재시작해야만 확인 가능한 것 3개"를 닫고, quad-handover-audit 워크플로를 처음으로 실제로 감사가 도는 상태에서 돌린 세션.

1. 재시작 검증 3건 — 전부 확인

항목 결과 근거
@import 3개 로드 conventions.md/project-context.md/todos.md 내용이 실제 컨텍스트에 들어옴
quad-doc-auditor 등록 에이전트 목록에 잡힘. 전 세션 전멸(agentType not found)은 .claude/agents/가 세션 도중 생긴 디렉토리였던 탓
frontmatter model: sonnet 반영 서브에이전트 트랜스크립트에 "model":"claude-sonnet-5" 기록. 워크플로에 opts.model 명시 불필요

모델 확인은 처음에 "네 모델과 도구를 보고해라"는 프로브 에이전트로 시도했다가 에이전트가 프롬프트 인젝션으로 판단해 거부했다(원래 과업인 감사를 버리고 시스템 프롬프트를 덤프하라는 지시였으니 정당한 거부). 자기 보고 대신 트랜스크립트 파일을 직접 grep하는 쪽이 1차 증거라 더 낫다.

2. 워크플로는 name으로 부르면 스냅샷, scriptPath로 부르면 실시간

[읽는 순서 주의] 이 절의 "에이전트 정의" 관련 결론은 뒤에서 두 번 정정됐다 — 최신 상태는 이 문서 끝쪽(§10, §12)과 .claude/agents/quad-doc-auditor.md 상단 배너를 볼 것. 여기 구체적인 결론을 다시 적지 않는 건, 그 결론이 이미 두 번 뒤집혔기 때문이다. 워크플로 쪽(name vs scriptPath) 관측은 그대로 유효하다.

전 세션 감사 라운드들이 "캐시냐 frontmatter 무시냐"로 길게 갈라놨던 것을 좁히는 증거가 나왔다. 처음엔 이걸 "정의 파일은 세션 시작 스냅샷으로 고정된다(에이전트·워크플로 공통)"로 일반화해서 적었는데, 그건 증거보다 센 주장이었다scriptPath 경로를 테스트한 적이 없었다. 실제로 갈렸다.

  • Workflow({name}) = 세션 시작 시점 스냅샷. 이 세션에서 .claude/workflows/quad-handover-audit.js를 편집한 뒤 이름으로 돌렸는데, 런타임이 실제로 실행한 스크립트가 세션 시작 시점 상태(= 당시 HEAD, 작업트리가 깨끗했음)와 바이트 단위로 동일했다. 편집은 하나도 안 실렸다.
  • Workflow({scriptPath}) = 디스크에서 실시간. 세션 시작 후 새로 만든 프로브 스크립트(에이전트 0개)가 정상 실행됐고 — 스냅샷이라면 존재할 수 없다 — 그 파일을 고쳐 다시 부르니 고친 값이 그대로 돌아왔다.

실무 규칙: 워크플로 정의를 고쳤으면 재시작하지 말고 scriptPath로 호출할 것. 이름으로 부르면 옛 정의로 돌면서 새 정의로 돈 것처럼 보인다 — 감사 도구에선 이게 조용한 거짓 초록불이다.

에이전트 정의 쪽은 같은 등급의 증거가 없다 — 감사 에이전트들이 "받은 지시문에 그 세션에 추가된 배너가 없었다"고 보고했을 뿐이고 그건 자기 보고다(아래 Grep/Glob과 같은 등급). 에이전트엔 scriptPath 같은 우회가 없으니 확인 전까지는 보수적으로 고쳤으면 재시작 뒤에 감사를 따른다.

미해소로 남은 건 읽기 전용인데 Write/Edit이 주어지는 원인 하나. memory: project를 뺐지만 그 뒤 재시작 없이 관찰한 것뿐이라 진단 미확정.

전 세션 감사가 남긴 "Grep/Glob이 안 주어졌다"는 서술은 두 소스가 어긋나는 미해결 불일치다 — 호출 세션이 보는 에이전트 등록 목록엔 Grep/Glob이 포함돼 있는데, 실행된 에이전트는 자기 도구를 Read/Bash/Write/Edit으로 보고했다. (처음엔 "자기 보고라 근거가 약하다"고 깎으려 했으나, 에이전트는 자기 함수 목록을 직접 볼 수 있으므로 그건 과한 판단이었다.) 어느 쪽도 확정하지 말고 Write/Edit 원인과 함께 재시작 직후 실행에서 볼 것.

3. "존재하지 않는 원칙 인용" 3건 — 재분류 결과 2건은 인용 오류였음

전 세션은 셋 다 같은 성격(가짜 원칙)으로 넘겼는데, 코퍼스 grep + git log -S로 다시 보니 갈렸다.

  • research/v1-compat-plan.md:50 — 원칙 자체는 실재. 실제 소스는 base/component-composition-plan.md §1(자동 store 생성/자동 흡수 매직 폐기, 사용자 확정)과 base/store-plan.md "Store 값 설정 문법" 절 (__newindex 자동 위임 폐기). → 재조준.
  • research/pre-implementation-audit.md:434 — 역시 실재. 실제 소스는 루트 ROADMAP.md의 quad-debug 백로그 항목 + question.md. 다만 research/debug-tooling-plan.md 쪽은 더 약한 표현("존재 가능성만 인지")이라 그 온도차도 같이 적었다. → 재조준.
  • base/modifier-plan.md:536 — 이것만 진짜로 출처가 없다. 코퍼스 전체에도, git log -S로 본 CLAUDE.md 전 히스토리에도 그런 서술이 존재한 적 없다. 임의로 정하지 않고 날짜 붙인 대기 배너 + question.md 3번 항목(선택지 (a) conventions.md에 명문화 / (b) 지역 논거로만 유지)로 올려 사용자 판단 대기.

4. quad-handover-audit 첫 실동 — 6라운드, 수렴 실패

에이전트 67개 / 라운드별 새 발견 28 → 15 → 16 → 7 → 11 → 6. 최대 라운드(6)를 소진할 때까지 새 발견이 마르지 않아 converged:false로 끝났다. 과거 수동 감사가 4~6라운드에 수렴했던 것과 다른 양상.

  • 라운드 5에서 발견이 7→11로 다시 늘었다 — 감사가 코퍼스를 훑는 순서/ 관점이 라운드마다 달라서 생기는 것으로 보이고, 단조 감소를 전제한 MAX_ROUNDS=6이 이 코퍼스엔 부족하다는 신호.
  • 이번 실행은 위 2번 때문에 옛 스크립트로 돌았다(이름으로 호출) — 즉 이 결과 자체가 그 함정의 사례다. 다음 실동은 scriptPath로.
  • 마지막 라운드(6)의 새 발견 6건은 반영만 되고 재감사되지 않았다. 수렴 조건을 못 채우고 최대 라운드로 끊겼기 때문이다. 그 6건을 포함한 diff 전체를 이 세션이 손으로 검토하고 핵심 주장(절 제목·archive 파일 존재·spikes 실제 개수)은 1차 근거로 확인했지만, dry 라운드 하나가 주는 확인과 같지는 않다 — 다음 실동의 첫 임무가 이걸 재확인하는 것.

수정 품질 자체는 높았다. 실제로 잡힌 것 중 값진 것:

  • base/slot-plan.md:218의 정정 배너가 "폐기로 정정됨"에서 멈춰 있었는데, 그 "폐기"가 2026-08-13에 다시 뒤집혀 지금 정본은 retract=언마운트다. 배너만 보고 지나가면 구현자가 옛 결론을 믿을 자리였다.
  • spikes 44개류 하드코딩 개수가 여러 문서에 흩어져 있었는데 실제로는 48개(0043 + 21b21e)였다. 전부 "폴더가 소스"로 바꿈 — conventions.md의 "개수·목록·상태는 소스를 하나만" 규칙 적용.
  • tools/doc-check.py docstring이 1·2번 검사 심각도를 실제 코드와 다르게 적어놨던 것(둘 다 ERROR라 했지만 코드는 그렇게 동작한 적 없음). 검사 로직은 안 건드리고 서술만 정정.

5. 워크플로 스크립트 개선(다음 실행부터 적용됨)

  • 반환값에 findings 배열 추가. 기존엔 roundLog에 파일명·개수만 있어 커밋 전 diff 리뷰 때 "이 수정이 왜 들어갔는지"를 알 방법이 journal.jsonl 고고학뿐이었다.
  • totalFindingsFixedfindingsSentToFix로 개명. "의심"으로 표시돼 반영 에이전트가 확인 후 건드리지 않은 것도 "고쳐짐"으로 세던 과대계상.
  • 반영 에이전트에 model: 'sonnet' 명시(감사 패스는 frontmatter로 이미 sonnet, 반영 쪽만 메인 모델을 상속하고 있었음).

남은 것

  • MAX_ROUNDS 재검토 + 미재감사분 확인 — 수렴 실패가 재현되는지 scriptPath 호출로 재실동. 단조 감소 전제가 안 맞으니 "연속 dry 2회" 조건 자체도 같이 볼 것. 라운드 6의 미재감사 6건도 이때 걸러진다.
  • (d) Write/Edit 원인 — 다음 세션 시작 직후 확인.
  • .claude/agent-memory/ — 감사 에이전트가 스스로 쓰는 영속 메모리가 생겼고 지금 untracked. 커밋할지 .gitignore로 뺄지는 사용자 판단.
  • modifier-plan.md:536 원칙question.md 3번, 사용자 판단 대기.

6. [사용자 결정] 감사 루프 재설계 — 워크플로 폐기, 메인 일괄 처리

첫 실동 결과(에이전트 67개 / 4.6M 토큰 / 수렴 실패)를 보고 사용자가 구조 자체를 바꾸기로 함. 사용자 논거 그대로:

"토큰이 미친듯이 갈린다는거야. 차라리 감사 두개, 끝나면 한명이 일괄처리 (보니까, 지금은 처리를 전부 분리해서 함. 이로 인해서 또 다시 부정확 문서가 생성될 수도 있고, 파일 겹침을 막기 위해서 너무 많은 처리를 수행함). 가 맞다고 생각해. 정확히는, 문제 사항을 처음 편집을 했던 최상위 에이전트가 받고, 검토 한 다음 처리하고 다시 2개 정도의 감사 에이전트를 돌리고, 마치 code-review 처럼 되는게 맞는듯."

"이런 구조로 인해서 만일 유저의 판단이 필요해보이더라도, 에이전트들이 유저에게 입력 요구하기가 까다로움. 메인 에이전트다 하면 뭔가 애매하다 싶으면 바로 유저에게 보고하게 하는게 맞아보임"

정리하면 폐기 근거 셋 — (1) 토큰 과다, (2) 파일별 픽스 에이전트가 또 부정확한 서술을 새로 생산하고 파일 충돌 회피에만 처리를 낭비, (3) 서브에이전트는 사용자에게 못 물어서 판단이 필요한 항목이 임의 처리됨.

메커니즘은 사용자가 Agent 직접 호출을 골랐다(워크플로 유지안과 비교 제시 → "기계가 가장 적고 name/scriptPath 스냅샷 함정이 아예 사라짐" 쪽). 그래서 .claude/workflows/quad-handover-audit.js삭제됐고 절차는 conventions.md "작업 방식"이 소스가 됐다:

  1. quad-doc-auditor 2개를 한 메시지에 병렬 호출
  2. 메인이 두 리포트를 합쳐 검토하고 수정도 메인이 일괄로 수행
  3. 애매하면 임의 처리 말고 즉시 사용자에게 보고
  4. 새 발견 없는 라운드 2연속까지 반복, 수렴 못 하면 사용자에게 보고하고 멈춤
  5. doc-check.py ERROR 0 확인 후 커밋

감사 에이전트 정의에도 출력 형식에 사용자 판단 등급을 추가했다 — 의심에 묻지 말고 따로 빼라는 것(메인이 사용자에게 물을 수 있다는 게 이 구조의 핵심인데, 감사자가 애매한 걸 "이렇게 고치면 됨"으로 넘기면 그 강점이 죽는다).

7. [사용자 결정] 출처 없던 원칙 — (a) 명문화 + 새 인용 관례

modifier-plan.md:536이 인용해온 "드문 오용/가상 미래 요구까지 방어/최적화하려고 구조를 복잡하게 만들지 않는다"는 선택지 (a) 채택으로 conventions.md에 "설계 원칙" 절을 신설해 명문화했다.

사용자 논거: "(a) 하면 될것 같아. 그리고 추측하건데, 내가 세션 중 했던 말을 옮겨적지 않은 경우로 보이기도 함." — 즉 원칙 자체는 사용자가 실제로 세웠는데 문서화가 누락됐다는 판단.

여기서 파생된 요구로 새 관례conventions.md에 추가: 사용자 발언을 근거로 인용할 때 결론("사용자 확정")만 적지 말고 무슨 논거로 그렇게 말했는지까지 남길 것. 논거 원문은 session/에, 라이브 문서엔 결론 + 짧은 논거 + 세션 파일 포인터. 본보기는 base/component-composition-plan.md §1의 **사용자 확정**("마법 안쓴다 그것도 동의함") 패턴. 원칙·규칙을 세우는 발언이면 그 자리에서 명문화할 것 — 안 그러면 이번처럼 출처 없는 원칙을 후속 문서들이 인용하게 된다.

8. [실측] "감사자가 opus로 돈다"는 관찰 — 감사자가 아니라 픽스 에이전트였음

사용자가 "quad-doc-auditor가 opus로 돌고 있더라"고 지적해서 폐기된 워크플로 실행의 트랜스크립트를 전수 집계함(각 턴을 실제로 생성한 모델 = 최상위 message.model 기준):

에이전트 종류 모델 어시스턴트 턴 수
quad-doc-auditor claude-sonnet-5 1550
general-purpose(픽스) claude-opus-5 797

감사자는 한 턴도 opus로 돈 적이 없다 — frontmatter model: sonnet은 정상 반영된다. opus로 돈 건 픽스 에이전트 49개였고, 그게 4.6M 토큰의 주범이다. 픽스 쪽에 model: 'sonnet'을 명시하는 편집을 해뒀었지만 그 편집이 실린 스크립트가 실행되지 않았다 — 이름 호출 스냅샷 문제(§2)를 그대로 밟은 것. 지금은 픽스 에이전트 자체가 없어져 이 경로가 사라졌다.

단, 감사자 트랜스크립트에도 claude-opus-5 문자열이 실제로 섞여 있다 — 위치가 message.usage.iterations[].model이고 감사자 18개 중 14개에 각각 딱 한 번씩만 나온다(그 메시지의 최상위 message.model은 전부 sonnet). 사용량 회계 필드지 생성 모델이 아니다. 모델을 확인할 때 문자열 grep만 하면 여기서 오독한다 — 반드시 message.model로 집계할 것. 이 세션도 처음엔 grep -o '"model":"..."'로 확인했었고, 그 방법이 이 함정에 그대로 노출돼 있었다.

9. [사용자 결정] 감사 패스 수와 감사자 모델

  • 패스 수: "최소 2에, 변경 사항이 많아진다면 batch 수가 늘어나는게 맞다고 봄" — 고정 2가 아니라 변경 규모에 따라 스케일. 사용자 논거는 구조 설명 쪽이었음: "애초에 빠르게 전체를 훑는거고, 의견에 대한 사실검증은 상위자가 하니까. 빠르게 여러패스가 훑어지는게 유리한 구조임."
  • 감사자 모델: "haiku 는 쓰지 않는편이 나아보임" — sonnet 유지. 사용자가 별도 논거를 붙이지는 않았음(추측해 적지 않는다). 이 세션이 관측한 방증만 덧붙이면, 이번 감사가 잡아온 발견들은 문맥 추론이 꽤 들어간 것들이었다.

둘 다 conventions.md의 감사 루프 절차에 반영됨.

10. [재정정] §2의 "세션 시작 스냅샷"은 틀렸다 — 커밋된 HEAD가 맞다

새 절차의 첫 감사 라운드에서 한 패스가 자기가 받은 지시문이 blob 92b9484, 즉 그 시점 HEAD 커밋의 버전과 바이트 단위로 같다고 보고했고, 메인 세션이 git rev-parse HEAD:.claude/agents/quad-doc-auditor.md로 독립 확인했다. 그 HEAD는 세션 도중에 만든 커밋(1343796)이지 세션 시작 시점(8aeec76)이 아니다. 따라서:

  • 정의 파일은 커밋될 때마다 갱신되고, 커밋 안 된 워킹트리 편집만 안 보인다. §2가 관측한 워크플로 사례도 그때 HEAD가 곧 세션 시작 상태였을 뿐이라 이 설명과 모순되지 않는다(그 관측이 틀린 게 아니라 일반화가 틀렸음).
  • 실무 규칙이 "재시작"에서 "커밋"으로 싸졌다. 워크플로엔 scriptPath 우회가 따로 있지만, 지금은 워크플로 자체를 안 쓰므로 규칙은 하나다 — 정의를 고쳤으면 커밋한 뒤 감사를 돌린다.

이 정정이 (d)도 같이 풀었다. "memory: project를 뺐는데도 Write/Edit이 그대로 주어진다"며 미확정으로 남겨뒀던 건, 그 제거가 아직 커밋 안 돼서 반영이 안 됐던 것이다. 실제로 옵션이 빠진 정의가 커밋된 뒤 돈 감사자는 Write/Edit이 없었고 .claude/agent-memory/에 쓰기도 없었다(파일 mtime이 전부 그 이전 시각). 진단은 처음 것이 맞았다.

남은 미해결은 tools: 필드다 — frontmatter에 적힌 Grep/Glob이 실제로는 안 주어지고, 적지 않은 advisor가 주어진 라운드가 있었다. 그래서 감사자의 읽기 전용은 계속 행동 규약으로 지킨다.

교훈 하나: 이 정정은 감사자의 자기 보고에서 출발했다. 앞서 이 세션은 자기 보고를 "근거가 약하다"고 깎았다가 다시 "미해결 불일치"로 올렸는데, 이번엔 그 자기 보고가 검증 가능한 형태(blob 해시)로 왔기 때문에 메인이 1차 근거로 승격시킬 수 있었다. 자기 보고 자체가 약한 게 아니라 대조 가능한 형태로 오는가가 갈림점이다.

11. [사용자 결정] .claude/agent-memory/는 커밋해서 추적한다

사용자 논거 원문:

"agent-memory 는 커밋해도 될것 같음. 실 기록이고 디펜던시도 아니고, 어차피 SAFETY.md 에 따라 구현 시점에는 컨테이너에서 개발되며 다른 프라이빗 git 에 올라가고 검토 후 머징되는거라, 문제되는 메모리 있으면 사람이 감사처리 마지막으로 함.(환경 노출 등?) 결국 프로젝트 사이드 기록이고 같이 올려지는게 맞는게, 개발 환경이 다수라서 필요해보임."

요지 세 가지 — (1) 개발 환경이 여러 개라 에이전트 메모리가 레포를 따라다녀야 함, (2) 실 기록이고 빌드 디펜던시가 아니라 커밋 비용이 낮음, (3) 환경 노출 같은 위험은 SAFETY.md가 규정한 파이프라인(컨테이너 개발 → 프라이빗 git → 사람 검토 후 머징)의 마지막 사람 감사가 최종 방어선.

메인 세션이 커밋 전에 절대경로/이메일/키/토큰 패턴을 스캔했고 노출 소지는 없었다(히트는 전부 frontmatter name:과 마크다운 링크). 커밋 전에 그 안의 낡은 결론 하나도 고쳤다 — project_agent_prompt_caching_bug_reproduced.md가 아직 "캐시 가설"로 서술돼 있었는데 §10에서 이미 뒤집힌 것이라, 커밋된 HEAD 모델로 다시 쓰고 MEMORY.md 색인 줄도 같이 갱신(파일 이름은 그대로 두되 frontmatter name/description을 바꿈). 이 대응 자체가 README가 경고하던 "에이전트가 자기 메모리에 stale한 결론을 남긴다"의 실사례다.

12. [재재정정] §10의 "커밋된 HEAD"도 틀렸다 — 지금은 "모른다"가 정답

새 절차의 2라운드에서 감사자 둘 다 독립적으로 같은 반증을 가져왔다. 그들이 받은 정의 지시문은 어느 커밋과도 일치하지 않는 하이브리드였다: 상단 배너는 구버전(1343796 계열)인데 "출력 형식"의 사용자 판단 문단은 신버전(07c77e5에만 있음)이었다. 메인 세션이 독립 확인:

  • git log --all -S'당시 있던' -- .claude/agents/quad-doc-auditor.md커밋 없음
  • 07c77e5에는 사용자 판단 있고 당시 있던 없음 / 1343796에는 둘 다 없음

그들이 받은 텍스트는 메인 세션이 이 파일을 여러 번에 나눠 편집하던 중간의 워킹트리 상태(출력 형식 추가는 했고 배너 재작성은 아직 안 한 시점)와 일치하고, 그 상태는 커밋된 적이 없다.

실행 실제로 받은 정의
폐기된 워크플로 세션 시작 시점 상태
감사 1라운드 그 시점 HEAD 커밋(1343796)과 바이트 동일
감사 2라운드 어느 커밋과도 불일치 — 커밋된 적 없는 중간 워킹트리 상태

즉 이 세션은 같은 문제에 세 번 결론을 냈고 앞의 둘이 다 틀렸다(§2 "세션 시작 스냅샷", §10 "커밋된 HEAD"). 세 번째 가설을 세우지 않는다. 남기는 건 관측표와 실무 규칙 하나뿐: 정의를 고쳐도 반영됐다고 가정하지 말고, 중요하면 마커 문구를 넣어 감사자에게 물어 확인할 것 — 이 반증이 정확히 그 방법으로 나왔다(2라운드 프롬프트에 "출력 형식에 사용자 판단 문단이 있나"를 끼워 물었고, 그 답이 하이브리드를 드러냈다).

교훈: §10에 "자기 보고도 대조 가능한 형태면 1차 근거로 승격된다"고 적었는데, 이번엔 그 승격된 근거로 세운 결론이 또 틀렸다. 대조 가능한 자기 보고 하나는 그 실행이 무엇을 받았는지를 말해줄 뿐 메커니즘이 무엇인지를 말해주지 않는다 — 관측 하나에서 규칙을 일반화한 게 두 번 연속 실패의 공통 원인이다.

(d) 결론은 살아남는다. memory: project → Write/Edit 상관관계는 제거 이후의 후보 텍스트가 전부 그 옵션을 안 가지므로, 어느 텍스트가 실렸든 영향을 안 받는다(제거 이후 세 라운드 전부 Write/Edit 없음 + 메모리 주입 없음). 미해결로 남는 건 tools: 필드 미반영뿐.

13. [실측 재확인] 감사자 모델 — 다섯 실행 전부 sonnet

사용자가 "여전히 opus로 돈다"고 재지적해 2라운드 감사자 둘을 같은 방법으로 다시 집계했다. 최상위 message.model 기준:

실행 sonnet 턴 opus 턴
워크플로 시절 감사자 18개 1550 0
2라운드 A 80 0
2라운드 B 58 0

감사자 턴은 전부 sonnet이다. 다만 감사자 실행마다 message.usage.iterations[] 안에 claude-opus-5 항목이 정확히 1개씩 붙는다(그 메시지 자신의 message.model은 sonnet, 그 턴의 도구는 Bash). 감사자가 advisor를 부른 것도 아니다 — 도구 사용은 Bash/Read뿐이었다. 이게 정확히 무엇인지는 모른다(회계 항목으로 보임). 모델을 표시하거나 집계할 때 이 항목을 세면 "감사자가 opus"로 보인다.

캐싱으로는 설명이 안 된다 — model: sonnet은 이 파일 최초 커밋(a1c0e44) 부터 있었으므로, 아무리 낡은 버전이 실려도 sonnet이다.

14. [해소] "감사자가 opus로 돈다"는 관측은 뷰잉 이슈였다

사용자가 서브에이전트 뷰에서 위로 스크롤하면 모델명이 opus로 보인다고 두 번 지적했고, 트랜스크립트 실측(다섯 실행 전부 sonnet)과 계속 어긋났다. 사용자가 직접 그 화면을 확인해 원인을 특정함 — 맨 위에 보이는 건 Claude Code 세션 헤더다:

 ▐▛███▜▌   Claude Code v2.1.233
▝▜█████▛▘  Opus 5 · Claude Max
  ▘▘ ▝▝    ~/Desktop/quad

이건 메인 세션의 모델이지 서브에이전트의 모델이 아니다. 서브에이전트 뷰에서도 최상단엔 같은 세션 헤더가 나오므로, 위로 스크롤하면 무조건 상위 모델이 보인다. 사용자 표현대로 "이건 상위 네 모델을 의미".

이제 세 소스가 전부 일치한다 — 트랜스크립트 message.model(sonnet), 결과 출력 필드(sonnet), 세션 헤더(메인 모델, 서브에이전트와 무관). frontmatter model: sonnet은 정상 동작하며, 감사자는 sonnet으로 돈다.

교훈 — 이 조사에서 모델을 잘못 읽을 자리가 세 군데였다:

  1. "model" 문자열 grep → message.usage.iterations[]의 opus 항목에 낚임 (감사자 실행마다 1개씩 붙음, 그 메시지 자신은 sonnet, 정체는 미상)
  2. 화면 최상단 배너 → 세션 헤더(메인 모델)라 서브에이전트와 무관
  3. 폐기된 워크플로의 general-purpose 픽스 에이전트 49개는 진짜로 opus였음 — 같은 실행 안에 opus/sonnet이 섞여 있어 "어떤 건 sonnet으로 나온다"는 관측과 헷갈리기 쉬움

신뢰할 소스는 하나: 트랜스크립트의 최상위 message.model(원하면 attributionAgent로 어느 에이전트 몫인지까지 확인).

15. 감사 3라운드 — agent-memory가 정확히 사각지대였음

두 감사자가 독립적으로 같은 확실 발견을 가져왔다: agent-memory/project_agent_prompt_caching_bug_reproduced.md가 "커밋된 HEAD에서 읽힌다"를 여전히 확정 사실로 서술하고 있었다. §12에서 그 결론을 반증해놓고, 그 정정을 담은 커밋(1935dd4)이 agent-memory/ 아래를 하나도 안 건드린 것이다(git show --stat으로 감사자가 확인).

이건 이 감사 체계가 설계상 노리던 사각지대의 교과서적 사례다 — conventions.md가 말하는 "변경한 세션 자신은 자기가 뭘 안 건드렸는지 모른다". 게다가 이 파일은 같은 세션에 두 번 연속으로 stale이 됐다: 캐시 가설로 썼다가 → "커밋된 HEAD"로 고쳤는데 → 바로 다음 커밋이 그걸 뒤집었는데 또 안 따라옴. agent-memory/가 2026-08-16에 막 추적 대상이 돼서 아직 "이것도 코퍼스"라는 습관이 안 잡힌 탓이다.

고치면서 그 메모리가 결론을 복제하지 않고 배너를 가리키게만 바꿨다 — 같은 사실이 두 곳에 적혀 있으니 두 번 갈라진 것이라, 근본 원인을 없애는 쪽으로. 3라운드 A는 그 외에 todos.md의 매달린 포인터도 잡았다("아래 부수 확정 참고"인데 그 헤딩이 직전 라운드에 "미해결 1/2"로 개명됨).

마커 확인 결과 — 관측표에 새 행: 3라운드 감사자 둘 다 배너의 "재재정정" 낱말과 관측표를 받았고 디스크 현재 내용과 바이트 동일이라고 보고했다. 즉 일관되게 낡은 게 아니다 — 어떤 실행은 최신을 받고 어떤 실행은 안 받는다. "모른다"를 유지할 근거가 하나 더 늘었고, 동시에 마커 확인 방식이 실제로 작동한다는 것도 재확인됐다.

부수적으로 둘 다 자기 모델을 "You are powered by the model named Sonnet 5. The exact model ID is claude-sonnet-5."로 보고 — §13/§14의 트랜스크립트 실측과 일치.

16. 감사 4라운드 — 코퍼스는 깨끗, 대신 주입 경로 실측이 나옴

한 패스는 툴링, 다른 패스는 설계 코퍼스(base//research//ROADMAP.md)를 맡겼다. 이 세션이 툴링만 만졌으니 설계 쪽은 오래 안 훑인 영역이었다.

설계 코퍼스 각도(B) — 이 세션과 무관한 기존 항목 2건:

  • todos.md가 "Handler 작성 체크리스트 8개", "우선순위1 11개 전원 완료"로 개수를 두 번째 장소에 하드코딩. 이 프로젝트 자신의 "개수는 소스 하나만" 규칙 위반이라 개수를 빼고 소스를 가리키게 함.
  • base/architecture.mdconst 미채택 사유에 날짜 없음 — doc-check.py의 TEMPORAL 정규식이 못 잡는 자연어 변형("지금은 ~ 못해서"). 감사 절차 7번이 노리는 유형이 실제로 걸린 사례. → 아래 §17.

감사가 확인만 하고 넘어간 것도 값졌다: doc-check.pydispatch-core-plan.md 인용 WARN 클러스터 11곳을 직접 파본 결과 전부 본문에 굵은 글씨 인라인으로 실존했다(헤딩이 아니라 스크립트가 못 찾을 뿐). 그 문서가 최근 분리된 파일이라 "분리 후 인용자 안 따라옴" 패턴이 의심됐던 자리인데 콘텐츠 유실이 없음이 확인됐다.

툴링 각도(A) — 코퍼스 확실 발견 0건. 대신 주입 경로 관측 둘:

  • 이번 라운드가 받은 에이전트 정의는 HEAD보다 정확히 1커밋 전이었다 (2라운드 같은 하이브리드는 아니었음). 관측표 4번째 행.

  • CLAUDE.md @import 컨텍스트는 세션 시작 시점에 고정된다 — 감사자가 받은 conventions.md/todos.md/project-context.md가 세션 시작 커밋(8aeec76)과 정확히 일치했고 그 사이 7커밋이 있었다. 감사자가 "메인 세션 컨텍스트도 그런지 확인해달라"고 요청해 메인이 자기 컨텍스트를 직접 확인 — 그렇다. 이 세션의 메인은 삭제된 워크플로를 "먼저 돌릴 것" 이라 지시하는 옛 conventions.md와, 항목 6에서 끝나는 옛 todos.md를 컨텍스트에 들고 작업해왔다(실제로는 파일을 직접 읽고 고치며 새 절차를 따랐으므로 행동에는 영향이 없었다).

    이건 에이전트 정의 쪽의 "모르는 현상"과 달리 경로가 다르고 동작이 명확한 것이라 conventions.md에 규칙으로 적었다: @import 파일을 고친 세션은 자기가 방금 쓴 규칙을 컨텍스트에서 못 보므로, 기억에 의존하지 말고 파일을 직접 Read해서 따를 것.

17. [사용자 결정] const 미채택 재검토 — 에이전트가 정할 문제가 아니었음

감사가 "날짜만 붙일지, 구체적 트리거를 명시할지"를 사용자 판단으로 올렸는데, 사용자가 전제 자체를 정정했다.

사용자 논거 원문:

"const 건은 생각보다 더 컴플랙스함. 예를 들어 pesde 의 타입추출화 해서 d.ts 같이 types emit 하는 류가 있음. 아직 툴링이 미성숙한 상태라 const 를 제공 못하는 상황이고, 언제 다시 사용가능해지는지 명확치가 않음. 따라서 이는 사용자가 확인된 정보로 제공하는게 맞다고 보고 있음. 필요한 경우 Human todo 에 해당 부분의 사용 가능해지는 시점을 파악하거나, 사용 가능해질 때 에이전트에 알려야할 필요를 명시해둬"

즉 "구체적 트리거"를 에이전트가 적어둘 수 없는 종류다 — pesde의 types emit 같은 툴링 체인의 지원 시점은 외부 사정이고 에이전트에 관측 수단이 없다. 그래서 question.md에서 닫고(→ archive/question-resolved.md) HUMAN_TODO.md 8번으로 옮겼다: 사용자가 시점을 파악하거나, 사용 가능해질 때 에이전트에 알린다. 알려주기 전까지 에이전트는 스스로 판단하지 않고 "일단 local로" 원칙을 유지한다.