지난 글은 M5 계획이었다. 계획을 병합하고 첫 슬라이스로 가기 전에 열린 이슈 둘을 다시 봤다. 실제 모델로 추천 배치를 재는 #4와, 실제 ChatGPT 호스트에서 전체 흐름을 확인하는 #5. 둘 다 완료 기준이 하나도 체크되지 않았으니 닫을 근거는 없었다. 대신 #5는 지금 해야 할 이유가 있었다.
이번 PR #21은 그 확인의 실행 순서를 적은 문서다. 런타임 변경은 없다. 그리고 호스트에서 실제로 확인한 항목도 아직 없다. 이 글은 확인 결과가 아니라, 확인을 미뤄 온 과정과 절차를 만들며 리뷰에서 배운 것의 기록이다.
여섯 번 바뀐 주소, ChatGPT에서 한 번 본 화면
처음 글에서 Secure MCP Tunnel로 ChatGPT에 연결해 상태 카드 하나를 띄웠다. 실제 호스트에서 확인한 것은 지금까지 그것뿐이다.
그 뒤 위젯 주소는 M1에서 widget-v1이 됐고, 마일스톤과 계약이 바뀔 때마다 올라가 지금은 widget-v6이다. 도구는 상태 하나에서 아홉 개가 됐다. 시리즈의 글마다 “실제 ChatGPT 호스트에서 확인한 것은 아니다”라는 문장이 들어갔고, M3 글에서는 다음 우선순위가 기능 추가가 아니라 실제 모델과 호스트에서의 확인이라고 적었다. 그게 10일이었다. 나흘이 지났고 그 사이 위젯 주소는 v3에서 v6이 됐다.
미뤄진 이유는 순서대로 이렇다. 처음에는 OpenAI 크레딧이 없어 실제 모델 경로가 막혔다. 그다음 무료 제공자를 붙였고, 사용 확인은 스크립트로 돌렸다. 스크립트는 같은 입력으로 다시 돌릴 수 있고 결과가 파일로 남는다. ChatGPT 경로는 터널을 열고 대화를 진행하면서 도구 결과와 화면 상태를 따로 기록해야 한다. 이번에는 재현하기 쉬운 스크립트 경로를 먼저 택했고, 일반 채팅과의 직접 비교는 하지 않았다. 그래서 사용 확인의 세 질문 가운데 “일반 채팅보다 편한가”는 답하지 못했다. 그 비교는 일반 채팅이 한 탭 옆에 있는 ChatGPT 안에서 하기 쉽다.
이슈의 대상이 움직였다
#5는 M3 때 적은 이슈라 기준이 M3의 도구와 v3 위젯이다. 체크리스트의 항목은 지금도 유효하지만 대상은 바뀌었다. 도구 아홉 개, 위젯 v6, 프롬프트 버전 milestone-4b2-v2. 이슈를 닫고 새로 여는 대신, 현재 트리 기준으로 다시 잡은 실행 순서를 문서로 만들었다.
첫 라운드의 범위는 M5-0 기준선 페이지에 필요한 만큼으로 좁혔다. 상태 → 프로필 → 공고 추출 셋 → 판정 셋 → 지원 저장 → 파이프라인 → 상태 갱신. 그리고 그 주변의 호스트 동작인 카드 렌더링, 재진입과 상태 복원, 오류 표시, 작은 화면, 키보드, 링크. 입력은 지난 라운드와 같은 합성 이력서와 공고 세 개, 모델은 같은 OpenRouter 무료 모델이고, 전송 승인은 라운드마다 다시 받는다.
뺀 것도 있다. 공고 검색에서 추천 배치로 이어지는 M3 흐름은 #4의 지연 문제다. 지난 사용 확인에서 답한 모델은 판정 호출 하나에 67~81초가 걸렸다. 후보 다섯 개 배치는 서버의 90초 예산과 아직 확인하지 않은 호스트 제한을 함께 고려해야 한다. 이번에는 배치를 제외하고, 시간 제한에 따른 실패 원인은 별도 측정으로 구분하기로 했다. 인증과 격리는 #6에 있다.
지금 해야 하는 이유는 M5-B다. 그 슬라이스가 위젯을 v7로 올리고 도구 출력을 바꾼다. v6 기준의 호스트 동작을 한 번도 보지 않은 채 v7로 가면, 그 뒤에 호스트에서 무엇이 깨졌을 때 M5 탓인지 원래 그랬는지 가를 수 없다.
문서 PR인데 리뷰가 세 가지를 잡았다
실행 순서를 올리기 전에 로컬에서 확인한 것이 있다. 키를 비운 채 서버를 띄우고 MCP 클라이언트로 조회하니 도구 목록은 아홉 개, 리소스는 widget-v6.html 하나였고, 프로필 도구를 부르면 고정된 설정 오류 문구만 돌아오고 스택이나 요청 본문은 없었다. 절차의 전제 셋은 맞았다. 그런데 리뷰는 다른 곳에서 셋을 잡았다.
삭제 명령이 다른 파일을 가리켰다. 절차에는 앱 데이터베이스와 섞이지 않도록 환경 변수로 별도 파일을 쓰고, 끝나면 지우라고 적었다. 그런데 중간 단계에서 합성 이력서를 출력하려고 cd server를 한 뒤, 마지막 삭제 명령은 상대 경로였다. 서버는 데이터베이스 경로를 현재 디렉터리가 아니라 저장소 루트 기준으로 해석하므로, 절차를 그대로 따르면 삭제 명령은 존재하지 않는 server/data/ 아래의 파일을 겨냥하고 -f 때문에 조용히 성공한다. 확인에 쓴 프로필과 공고와 판정은 남고, 다음 라운드와 섞인다. 리뷰어는 두 경로를 실제로 계산해 보였다. 출력 명령은 서브셸로 바꿔 디렉터리가 바뀌지 않게 했고, 정리는 서버와 터널을 먼저 멈춘 뒤 루트에서 본체와 WAL 모드의 보조 파일까지 지우는 순서로 다시 적었다. 서버가 열어 둔 파일을 지우지 않는 것도 순서에 넣었다.
카드가 그리지 않는 값을 보라고 했다. 판정 단계의 확인 항목에 정수 점수와 요건 ID를 적어 두었다. 지난 라운드의 수정 항목이라 자연스럽게 들어갔다. 그런데 v6 카드는 점수를 그리지 않고, 요건 ID는 목록의 키로만 쓴다. 점수는 선택값이고, 서류 검토 맥락은 없을 수도 있고 정상적으로 평가됐을 수도 있는데, 절차는 모든 결과에서 uncertain을 찾으라고 했다. 이대로 실행하면 정상인 v6 응답을 호스트 회귀로 오판한다. 항목을 둘로 나눴다. 카드에서는 실제로 그려지는 것만 본다. 고용주 줄, 제목, 판정과 확신도와 왜곡도, 근거와 부족분과 결격 사유, 검토 맥락의 표시. 점수와 ID와 선택 필드는 개발자 모드가 보여 주는 구조화 결과에서 따로 본다. OpenAI의 연결 테스트 안내도 UI가 있는 도구는 컴포넌트와 모델이 읽는 결과를 둘 다 시험하라고 적고 있다.
“모델 호출 1회”가 아니었다. 프로필 생성 단계에 모델 호출 한 번이라고 적었다. 사용 확인 스크립트는 SDK 재시도를 0으로 고정하지만, pnpm dev로 뜨는 서버는 기본 경로라 재시도를 고정하지 않는다. 논리적으로는 추출 한 번이어도 HTTP 시도는 여러 번일 수 있다. 호스트 라운드에서는 시도 수를 세지 않는다고 고쳤다. 횟수를 세는 것은 하네스와 스크립트의 일이다.
셋 다 문장을 읽는 것만으로는 놓치기 쉬웠다. 명령이 가리키는 경로를 계산하고, 화면과 호출 경로의 코드를 대조하자 드러났다. 나는 절차의 전제를 서버에 물어봐서 확인했지만, 절차의 명령이 가리키는 파일과 절차가 보라고 한 화면은 확인하지 않았다. 지난 글에서 계획서의 수식과 옵션 이름을 실행해 봐야 한다고 적었는데, 이번에는 경로와 화면이 같은 자리에 있었다.
확인한 것과 남은 것
확인한 것은 절차의 전제뿐이다. 도구 아홉 개, 위젯 리소스 v6, 설정 오류의 고정 문구. 모두 모델 호출 없이 로컬에서 봤다.
남은 것은 실행이다. 문서의 기록 표는 “not run yet”이고, 실행은 내가 터널을 열고 ChatGPT에서 표의 단계를 순서대로 눌러야 한다. 통과한 항목은 그 제공자와 모델과 위젯 주소에 한해 live-verified가 되고, 나머지는 not verified로 남는다. #5는 그때까지 열려 있다. 대상이 바뀐 이슈를 현재 기준으로 다시 잡은 것과 그 이슈를 끝낸 것은 다른 일이다.
실행 순서 전문은 저장소의 HOST_CHECK.md에 있다. 다음 글은 그 표가 채워진 뒤의 기록이다.