개인 작업 환경에 Claude Code, Codex, Hermes, MCP, 스킬과 RAG를 하나씩 붙여 쓰다 보니 어느 순간 설치보다 점검이 먼저라는 생각이 들었다.
처음에는 아무것도 없는 환경에 공통 규칙과 스킬, MCP 설정을 한 번에 넣는 패키지를 만들고 싶었다. 하지만 실제 개발 환경은 비어 있지 않다. 이미 여러 에이전트와 설정 파일이 있고, 오래전에 허용한 권한이나 사용하지 않는 도구도 남아 있다. 이런 환경에 새 패키지를 바로 설치하면 편해지기보다 충돌과 관리 대상만 늘어날 수 있다.
그래서 설치기보다 먼저 동작할 도구를 따로 만들기 시작했다. 이름은 AX Doctor로 정했다.
프로젝트 저장소: github.com/ksungz/ax-doctor
설치기가 먼저일 수는 없었다
새 도구를 넣기 전에 적어도 다음 질문에는 답할 수 있어야 했다.
- 현재 어떤 AI 클라이언트와 공용 자산이 있는가?
- 새로 넣을 파일과 기존 파일의 경로가 겹치지는 않는가?
- 조직이나 개인이 정한 네트워크, 파일 접근, 비밀정보 보관 기준에 맞는가?
- 확인하지 못한 영역은 어디인가?
- 지금 설치해도 되는지, 보류해야 하는지 판단한 근거는 무엇인가?
단순한 설치 전 체크리스트로는 부족했다. 검사 결과가 실행할 때마다 달라지거나, 확인하지 못한 항목을 통과로 간주하면 오히려 위험한 결정을 만들 수 있기 때문이다.
AX Doctor의 역할은 설치를 대신하는 것이 아니다. 현재 상태와 도입 대상을 비교하고, 확인한 근거와 확인하지 못한 범위를 함께 남기는 것으로 범위를 제한했다.
AX Doctor의 판단 흐름
입력과 결과의 계약부터 정했다
바로 파일 탐색 코드를 만들지 않고 먼저 입력과 결과 형식을 정했다.
입력은 세 가지로 나눴다.
- Profile: 허용할 클라이언트와 파일 접근, 네트워크, 비밀정보 보관 기준
- Target manifest: 새 패키지가 만들거나 바꾸려는 파일과 설정
- Observation: 승인된 범위 안에서 읽은 현재 환경의 상태
결과에는 판정만 넣지 않았다. 어떤 관찰 결과를 근거로 삼았는지, 어느 경로를 검사하려 했고 실제로 어디까지 확인했는지, 미지원이거나 접근할 수 없어 남은 공백이 무엇인지 함께 기록하도록 했다.
JSON Schema와 canonical JSON 규칙도 이때 정했다. 같은 입력으로 만든 결과가 같은 의미와 해시를 가져야 나중에 보고서를 비교하거나 다른 도구가 이어받을 수 있기 때문이다.
확인하지 못한 것은 통과로 바꾸지 않았다
이 도구에서 가장 신경 쓴 부분은 READY를 만드는 일이 아니었다. 확인하지 못한 상태를 정확히 남기는 일이었다.
지원하지 않는 클라이언트나 정책 값이 들어왔을 때 조용히 무시하면 결과가 실제보다 안전하게 보일 수 있다. 그래서 현재 구현 범위를 벗어나는 의미는 입력 오류로 중단한다. 파일을 읽을 수 없거나 검사 범위가 충분하지 않으면 UNKNOWN과 coverage 공백으로 남기고, 통과나 일치로 추정하지 않는다.
판정도 다음처럼 나눴다.
| 판정 | 의미 |
|---|---|
READY | 필수 조건과 검사 범위가 모두 충족됨 |
READY_WITH_CONDITIONS | 진행할 수 있지만 확인하거나 조치할 조건이 남음 |
NOT_READY | 명확한 충돌이나 정책 위반으로 도입 보류가 필요함 |
INCOMPLETE | 필수 범위를 충분히 확인하지 못해 판단할 수 없음 |
현재 합성 시나리오에서는 위험한 도입 대상을 NOT_READY, 비교적 안전한 대상을 READY_WITH_CONDITIONS로 구분한다. 두 결과 모두 근거와 미확인 항목을 그대로 보존한다.
읽기 전용은 파일을 쓰지 않는 것보다 넓었다
처음에는 읽기 전용이면 단순히 설치나 파일 수정을 하지 않으면 된다고 생각했다. 실제로는 읽는 과정과 결과를 저장하는 과정에도 별도 기준이 필요했다.
- 발견한 실행 파일이나 스크립트를 실행하지 않는다.
- 네트워크, telemetry, sudo를 사용하지 않는다.
- 승인된 로컬 범위 밖으로 탐색을 넓히지 않는다.
- 심볼릭 링크와 특수 파일을 자동으로 따라가지 않는다.
- 원본 프롬프트, 인증정보, 전체 경로를 외부 보고서에 남기지 않는다.
- 기존 결과 파일을 덮어쓰지 않고, 최종 저장 직전에도 출력 경로와 권한을 다시 확인한다.
검사 대상의 Node.js나 Python 환경에 기대지 않기 위해 구현 언어는 Go를 선택했다. 단일 실행 파일로 배포하기 쉽고, 파일 디스크립터와 경로 검증을 표준 라이브러리 중심으로 다룰 수 있다는 점도 이유였다. 이 프로젝트를 진행하며 Go의 타입과 테스트 구조를 익혔지만, 언어 자체보다 검사 대상과 독립된 실행 환경을 만드는 것이 우선이었다.
완료 보고 뒤에 한 번 더 검토했다
합성 환경에서 처음 scope → scan → report 흐름이 동작했을 때 바로 공개하지 않았다. 별도 검토를 거치니 겉으로는 정상 동작하지만 공개 도구로 두기에는 위험한 부분이 나왔다.
- 지원하지 않는 입력 값 일부가 판정에서 빠질 수 있는 문제
- 충돌 계산이 큰 입력에서 불필요하게 늘어날 수 있는 문제
- 보고서 스키마와 실제 출력 배열 상한의 차이
- 출력 디렉터리 검사 뒤 경로가 바뀌는 경우를 충분히 묶지 못한 문제
- JSON 보고서와 manifest의 참조가 실제 공개 파일에서도 모두 이어지는지 확인이 부족한 문제
이 항목들을 공개 전 보강 작업으로 따로 묶었다. 미지원 값은 fail-closed로 바꾸고, 입력과 결과 크기에 상한을 뒀다. 중복과 충돌은 그룹 단위로 계산했고, 출력 경로와 권한 검증은 실제 파일 디스크립터 체인에 결박했다. 마지막에는 실제로 저장된 JSON과 manifest를 다시 열어 schema, canonical form, payload hash와 근거 참조를 확인하도록 했다.
기능을 더 넣는 것보다 거짓으로 안전해 보이는 결과를 만들지 않는 것이 먼저였다.
지금 재현할 수 있는 범위
현재는 실제 사용자 환경 대신 저장소 안의 합성 데이터로만 제품 흐름을 검증하고 있다.
- Codex, Claude와 공용 스킬·플러그인 자산을 가정한 합성 검사 환경
- 도입 대상의
CREATE,REPLACE변경 비교 - 위험한 대상과 비교적 안전한 대상의 판정 차이
- JSON, Markdown, inspection manifest의 동일 근거 유지
- schema, canonical JSON, hash와 참조 무결성 검증
- race detector, 정적 검사, CGO 비활성 테스트 통과
실행 가능한 합성 데모
같은 입력으로 판정 차이를 직접 확인할 수 있도록 safe, risky 두 가지 시나리오를 별도 명령으로 만들었다.
demo_root="$(cd "$(mktemp -d)" && pwd -P)"
go run ./cmd/ax-doctor-demo --scenario safe --output "${demo_root}/demo-safe"
go run ./cmd/ax-doctor-demo --scenario risky --output "${demo_root}/demo-risky"
| 시나리오 | 판정 | 바이너리 종료 코드 |
|---|---|---|
safe | READY_WITH_CONDITIONS | 0 |
risky | NOT_READY | 2 |
각 출력 경로에는 report.json, report.md, inspection-manifest.json 세 파일만 생성된다. go run으로 risky 시나리오를 실행하면 Go wrapper가 exit status 2를 함께 표시한다.
데모에 필요한 profile, target과 관찰 데이터는 실행 파일 안에 합성 값으로 포함했다. 실제 HOME, AI 도구 설정, 인증정보와 실행 중인 process는 읽지 않는다.
2026년 7월 27일에는 새 임시 출력 폴더에서 두 시나리오와 전체 품질 게이트를 다시 실행했다. 이때 macOS의 /var/... 임시 경로를 물리 경로로 정규화하지 않고 전달하자 결과 파일을 만들기 전에 AXD-OUTPUT-UNSAFE로 중단됐다. README처럼 pwd -P로 실제 경로를 확인한 뒤에만 완료됐다. 성공 시나리오뿐 아니라 모호한 출력 경로에서 추측하지 않고 멈추는 동작도 함께 확인한 셈이다.
검증 환경과 실행 결과, 아직 확인하지 않은 범위는 공개 자체 검증 기록에 정리하고, 실제 환경 진단판과 구분하기 위해 v0.1.0-alpha.1 합성 데모 소스 릴리스로 공개했다.
반대로 아직 하지 않은 일도 분명하다.
- 실제 HOME과 사용자 설정 스캔
- 자동 설치, 수정, 복구
- 조직 단위 배포와 중앙 정책 관리
- 배포 파일 서명과 신뢰 체계
- Gemini, Hermes, Ollama, 로컬 RAG 정식 지원
실제 사용자 환경을 읽는 기능이 연결되기 전까지 일반 실행 명령은 의도적으로 실패하도록 막아뒀다. 지금 단계의 결과를 완성된 기업용 제품처럼 보이게 하고 싶지 않았기 때문이다.
AI 에이전트와 역할을 나눈 방식
설계와 구현에는 Codex와 Claude를 함께 사용했다. 요구사항 한 줄로 전체 코드를 만들게 두기보다 제품 범위와 비목표, schema, 위협 모델, 단계별 완료 조건을 먼저 문서로 고정했다. 기능과 테스트는 같은 단위로 진행했고, 한 에이전트가 완료했다고 보고한 결과는 다른 관점의 검토와 실제 명령 결과로 다시 확인했다.
AI를 많이 쓰는 프로젝트일수록 사람이 맡아야 할 일은 줄지 않았다. 무엇을 지원하지 않을지 정하고, 어떤 상태를 성공으로 보지 않을지 결정하고, 완료 보고를 그대로 믿지 않는 일이 오히려 더 중요했다.
다음 단계
실행 가능한 합성 데모와 함께 저장소를 오픈소스로 공개했다. 다음에는 지원 대상을 하나씩 늘리고, 각 대상마다 개인정보 노출과 무변경 조건을 검증하는 합성 데이터를 함께 추가할 생각이다.
조직 배포 기능을 서둘러 붙이기보다, 도입 전에 믿고 실행할 수 있는 점검 도구부터 만드는 것이 현재 목표다.
관련 글: 모델은 바뀌었는데, 하네스는 그대로였다 · 개인 AI 에이전트 워크스페이스 설계 · Obsidian RAG — 에이전트가 내 문서를 찾아보게 하기