업데이트와 적용은 다르다: 긴 AI 세션에서 최신 스킬을 읽게 만든 과정

하승진·2일 전

Level Up 개발자

목록 보기
21/30

업데이트된 스킬 문서를 프롬프트를 통해 현재 세션에 연결하는 개념 썸네일

공유 스킬의 규칙을 고쳤다고 팀의 작업이 바로 바뀌지는 않았다. 각자의 로컬 설치본이 갱신돼야 하고, 이미 열어둔 세션은 그 새 내용을 다시 읽어야 한다.

첫 단계로 자동 업데이트를 넣었다. 그런데 받아둔 새 버전은 다음 세션에서야 적용되는 흐름이었다. 긴 작업을 이어가는 동안에는 디스크가 최신이어도 세션은 이전 규칙을 계속 사용할 수 있었다.

공유 스킬의 문서 불일치를 검사한 이야기가 규칙 자체의 정확성을 다뤘다면, 이번에는 그 규칙이 현재 작업에 도달하는 경로를 다뤘다. 이 구현은 Claude Code 플러그인의 훅과 설치 메타데이터를 기준으로 한 사례다. 다른 AI 도구에 같은 파일 구조나 이벤트가 있다고 가정하지는 않는다.

자동으로 실행돼야 한다면 스킬 본문만으로는 부족하다

스킬에 ‘사용 전 최신 버전을 확인하라’고 써두는 것과, 실제로 매번 확인하는 것은 다르다. 모델이 그 스킬을 호출하지 않으면 확인 절차도 시작되지 않는다.

자동 발동 지점은 도구의 실행 환경이 제공하는 훅으로 잡았다. 첫 구현은 SessionStart에서 로컬과 원격 커밋 SHA를 비교하고, 다르면 업데이트 작업을 백그라운드로 분리했다. 수동으로 즉시 갱신할 수 있는 스킬은 별도로 남겼다.

Claude Code 훅 문서는 세션 시작과 프롬프트 제출 등 실행 생명주기의 이벤트를 제공한다. 이번에는 ‘에이전트가 기억하면 수행할 절차’와 ‘실행 환경이 정해진 시점에 호출할 절차’를 구분하는 것이 먼저였다.

업데이트 대상도 고정 목록으로 적지 않았다. 등록된 마켓플레이스의 설치 위치와 설치된 플러그인 목록에서 대상 경로를 찾았다. 사람마다 설치 구성이 달라도 같은 흐름을 사용할 수 있도록 했다.

디스크, 설치 메타데이터, 현재 세션은 따로 움직였다

업데이트 명령은 적용에 재시작이 필요하다고 안내했다. 여기서 무엇이 실제로 바뀌었는지 나눠 확인했다.

  • 새 스킬 문서는 새 SHA의 캐시 디렉터리에 내려받아져 있었다.
  • 이전 버전 디렉터리도 남아 있어 기존 세션의 파일 경로가 사라지지 않았다.
  • 설치 메타데이터는 최신 SHA와 새 설치 경로를 가리켰다.
  • 이미 세션에 들어온 지시문은 이전 버전이었다.
디스크의 파일       : 새 버전 있음
설치 메타데이터     : 새 버전 경로를 가리킴
현재 세션의 지시문  : 아직 이전 버전

이 상태에서 할 수 있는 것과 없는 것을 구분했다. 최신 경로의 SKILL.md와 reference를 직접 읽으면 이번 작업에 최신 절차를 참고할 수 있다. 하지만 파일을 읽었다고 실행 환경의 훅·커맨드 등록까지 바뀌지는 않는다.

따라서 목표는 플러그인 전체를 마술처럼 핫 리로드하는 것이 아니었다. 갱신된 문서의 경로를 지금 세션에 알려주고, 그 절차를 쓰기 전에 실제 파일을 다시 읽게 하는 것이었다. 실행 등록 변경은 여전히 reload나 새 세션이 필요한 별도 범위로 남겼다.

다음 프롬프트를 연결 지점으로 삼았다

UserPromptSubmit 훅을 추가해 세션이 로드한 플러그인 경로의 SHA와 설치 메타데이터의 SHA를 비교했다. 다르면 최신 플러그인의 경로들을 컨텍스트로 전달한다.

안내에는 본문뿐 아니라 상세 reference와 필요한 커맨드 문서도 읽도록 적었다. 요약만 최신이어도 실제 규칙 대부분이 reference에 있다면 작업은 여전히 이전 규칙을 따를 수 있기 때문이다.

컨텍스트 전달 형식은 다음과 같다. ctx에는 버전 차이와 최신 파일 경로 안내가 들어간다.

{
  "continue": true,
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "최신 버전의 스킬 파일 경로와 재확인 안내"
  }
}

동작을 순서로 보면 이렇다.

세션 시작 / 프롬프트 제출
  → 확인 주기가 지났으면 백그라운드 갱신 예약
  → 백그라운드에서 새 파일 설치
  → 다음 프롬프트에서 로컬 SHA 차이 감지
  → 최신 경로를 컨텍스트로 전달
  → 해당 스킬을 쓰기 전에 새 파일 읽기

최신 경로를 알려주는 시점과 파일을 설치하는 시점을 분리했기 때문에, 다운로드 완료를 기다리며 프롬프트 처리를 막을 필요가 없었다. 단, 안내가 들어왔다는 사실만으로 모델이 파일을 읽었다고 증명할 수는 없다. 실제 작업에서 어떤 경로를 읽었는지 확인하는 단계는 여전히 중요하다.

매 프롬프트마다 네트워크를 기다리지 않게 했다

첫 구현에서는 실제 다운로드를 백그라운드로 넘겼지만 원격 SHA 확인은 세션 시작 앞단에서 수행했다. 상한을 두었어도 네트워크 대기가 남아 있었다.

후속 변경에서는 원격 확인까지 전부 백그라운드로 옮겼다. SessionStart는 갱신 작업을 예약하고 반환하며, UserPromptSubmit의 판단은 로컬 메타데이터 비교로 끝낸다. 확인 주기가 지나면 여기서도 백그라운드 작업을 예약할 수 있지만, 원격 응답을 동기적으로 기다리지는 않는다.

기본 확인 간격은 6시간이었다. 세션 시작 때만 확인하면 하루 종일 열린 세션은 다음 갱신을 놓치므로 두 이벤트에서 주기를 확인하게 했다. 반대로 프롬프트마다 원격을 조회하지는 않는다. 실패해도 확인 시각을 기록해 통신이 막힌 환경에서 계속 재시도하지 않도록 했다.

필요한 실행 파일이나 인증이 없을 때는 작업 세션을 중단시키지 않고 통과하도록 했다. 업데이트는 편의를 위한 보조 기능이다. 갱신에 실패했다는 이유로 본래 개발 작업까지 시작할 수 없게 만들고 싶지는 않았다.

알림을 한 번만 보낸다는 말에도 범위가 있다

같은 버전 안내가 매 프롬프트마다 반복되면 컨텍스트를 불필요하게 채운다. 그래서 마지막으로 알린 SHA를 announced 파일에 기록하고 같은 SHA의 안내를 억제했다.

다만 이 표식은 상태 디렉터리에 저장된다. 세션별 고유 키로 분리한 구조가 아니므로, 여러 세션이 같은 상태 디렉터리를 공유하면 한 세션의 알림이 다른 세션의 알림에도 영향을 줄 수 있다. ‘한 번만 알린다’를 ‘모든 열린 세션에 정확히 한 번씩 전달한다’로 확대해서 설명할 수는 없다.

버전 비교에도 당시 마켓플레이스의 플러그인들이 같은 저장소 SHA를 따른다는 전제가 있었다. 플러그인별 설치 버전이 섞이는 구성이 된다면 플러그인마다 로드 버전과 설치 버전을 대조하는 방식으로 다시 나눠야 한다.

또한 새 프롬프트 훅 자체가 아직 등록되지 않은 예전 세션은 이 안내를 자동으로 받을 수 없다. 훅을 처음 설치하거나 실행 등록을 바꿀 때는 reload 또는 세션 시작이 필요하다. 문서 내용의 갱신과 훅 등록의 갱신을 구분한 이유가 여기서도 드러난다.

검증과 측정에서 확인한 범위

임시 설정 디렉터리와 스텁 CLI로 버전 불일치 시 최신 경로가 출력되는지, 이미 최신이거나 동일 버전을 다시 확인할 때 안내가 생략되는지, 비활성화 설정과 확인 주기가 지켜지는지 검증했다. 실제 설치 환경의 인증이나 모든 팀원의 긴 세션을 대신하는 검증은 아니었다.

당시 /usr/bin/time으로 다섯 번 측정한 프롬프트 훅 실행 시간은 0.01~0.02초였다. 세션 시작 훅은 측정 표시상 0초였지만, 이를 모든 환경에서 비용이 0ms라고 해석하지는 않는다. 의미 있는 변경은 정확한 시간값보다 사용자 경로에서 동기 네트워크 대기를 제거했다는 것이다.

회고: 설치 완료 메시지 다음을 확인하기

업데이트 명령을 실행했다는 사실, 최신 파일이 있다는 사실, 현재 작업이 그 내용을 따른다는 사실은 서로 다르다. 이 셋을 하나로 취급하면 자동화했는데도 오래된 규칙을 계속 사용할 수 있다.

이번에는 파일과 세션 사이에 최신 경로 안내를 연결했다. 모든 런타임 상태를 갈아끼우는 대신 문서 적용 경로를 분리한 작은 변경이었다. 그만큼 실행 등록, 다중 세션 알림, 실제 파일 읽기의 확인은 남은 경계로 설명할 수 있었다.

스킬을 운영할 때 이제는 ‘잘 내려받았나’에서 멈추지 않으려 한다. 현재 작업이 무엇을 읽고 있는지까지 확인해야 업데이트가 실제 행동으로 이어진다.

작업 기록

자동 갱신 훅 #15, 긴 세션에 최신 경로 전달 #19에 반영했다. 저장소 링크는 접근 권한이 필요할 수 있다.

profile
기어갈지언정 한 발자국씩이라도 가보자

0개의 댓글