빌드가 더는 대들지 않게 된 토요일
스크린샷이 되지 않는 수정들이야말로 당신의 런타임이 배포되는지 여부를 결정하는 것들이다. RakuAI는 다음 여덟 달의 빌드 작업이 결코 대들지 않도록 무른 지점들을 없애는 데 한 주를 썼다.
스크린샷을 만들어내지 않는 종류의 작업이 있다. 그것은 예전에 빨갛게 만들던 머신에서 초록빛이 되는 빌드를 만들어낸다. 더 이상 타임아웃되지 않는 CI 실행을 만들어낸다. 원인이었던 널 포인터가 이제 경계에서 잡히기 때문에 더는 나타나지 않는 스택 트레이스를 만들어낸다. 이번 토요일에 끝난 그 주는 바로 그런 종류의 주였다.
Linux에서 초록빛이 된 열여덟 개의 DLL
이 런타임은 열여덟 개의 네이티브 DLL로 배포된다. 2주 전까지만 해도, 그 DLL들은 Windows에서 깨끗하게 빌드되고 macOS에서 깨끗하게 빌드되었지만 Linux에서는 심볼 가시성 에러로 깨졌다. 그 수정은 4월 11일 PR #1463으로 들어왔다.
이 이야기는 화려하지 않은 종류다. GCC는 기본적으로 공유 라이브러리의 모든 심볼을 보이게 만든다. MSVC는 기본적으로 익스포트되지 않는 한 모든 심볼을 숨긴다. 런타임 코드는 MSVC 기본값을 가정하고 작성되어, DLL 경계를 넘어야 하는 심볼에 __declspec(dllexport)를 두었다. Linux에서는 그 선언들이 아무 효과가 없는데, 이는 모든 심볼이 보이게 된다는 뜻이고, 이는 링커가 올바른 DLL 내부 바인딩을 알아낼 수 없다는 뜻이며, 이는 열여덟 개 중 여럿이 빌드되지 않는다는 뜻이었다.
수정은 GCC 빌드에 컴파일러 플래그로 -fvisibility=hidden을 두고, 익스포트되어야 하는 심볼에는 명시적인 가시성 속성을 두는 것이다. diff는 작다. 파급 범위는 크다. Linux 빌드는 이제 일급 타깃이다. 런타임 변경을 배포하는 에이전트는 이제 CI 매트릭스에 의해 자동으로 Linux에서 테스트받을 수 있다. 서버 배포, 컨테이너 이미지, 클라우드 빌드 팜, 그 모든 것이 이제 손이 닿는 곳에 있다.
이것은 세금 공제처럼 느껴지는 종류의 수정이다. 제품을 앞으로 나아가게 하지는 않는다. 하지만 더는 그것을 막지도 않는다. 그거면 충분하다.
모든 LLM 호출자에 대한 프롬프트 길이 가드
적어둘 가치가 있는 4월의 또 다른 커밋은 프롬프트 길이 가드 정리 작업이다. 런타임은 이제 여러 곳에서 Claude를 호출한다. NPC 행동 두뇌, 대화 모델, 월드 이벤트 내레이터, 절차적 콘텐츠 시더. 이 각각의 호출자는 컨텍스트, 지시사항, 현재 상태를 이어 붙여 프롬프트를 만든다. 이 각각의 호출자는 컨텍스트가 충분히 커지면 모델의 입력 윈도우를 초과하는 프롬프트를 만들어낼 수 있다.
순진한 실패 양상은 LLM 호출이 에러를 반환하는 것이다. 위험한 실패 양상은 LLM 호출이 잘린 응답을 반환하고, 호출자가 그 잘림을 감지하지 못하고, 엔진이 형식이 잘못된 답에 따라 행동하는 것이다. 문장 중간에 멈추는 NPC들. 아무것도 아닌 곳으로 갈라지는 대화 트리들. 잘못된 파라미터로 발동되는 월드 이벤트들.
PR #1465는 모든 LLM 호출자에 가드를 추가했다. 호출 전에 토큰 수를 추정하고, 그 수가 예산을 초과하면 보내기를 거부하고, 호출자가 대응할 수 있는 구조화된 에러를 내보낸다. PR #1466은 전체 코드베이스를 감사해 모든 호출 지점에 그 가드가 자리 잡고 있는지 검증했다. PR #1467은 다음 호출자가 그 규율을 알 수 있도록 문서를 추가했다.
이것이 중요한 이유는 런타임 내부의 AI 호출이 드문드문 일어나는 것이 아니기 때문이다. 그것은 피크 시 프레임당 여러 번 일어난다. 호출당 몇 토큰씩 프롬프트를 키우는 프롬프트 구성 버그는 백 프레임 동안은 보이지 않다가 천 프레임에서는 파국이 된다. 이 가드는 그 실패를 조용하고 무제한인 것에서 시끄럽고 상한이 있는 것으로 바꾼다.
API 네임스페이스 안정화
세 번째 커밋은 셋 중 가장 조용하고 아마도 가장 중대한 것이다. 런타임은 외부 도구, 대시보드, SDK가 대화할 수 있는 REST API를 노출한다. 그 API는 지난 6개월 동안 /api/v2/ 아래에 살아왔다. 이는 내부 전용이었던 이전 /api/v1/을 계승했기 때문이다.
/api/v2/는 잘못된 이름이다. 그것은 v1이 공개되었다가 대체되었다는 것을 암시하는데, 그렇지 않았다. 또한 v3가 오고 있다는 것을 암시하는데, 그것도 아니다. PR #1464는 코드베이스의 모든 참조와 문서의 모든 참조를 훑어서 그 네임스페이스를 /api/v2/에서 /api/raku/로 이름을 바꿨다. 이제 그 이름은 안정적이다. 나중에 협상할 버전 번호가 없다. 그 네임스페이스 안에서의 하위 호환성이 계약이다.
이것은 정확히 한 번만 일어나야 하고, 외부 개발자가 그 네임스페이스에 맞춰 코드를 쓰기 전에 일어나야 하는 종류의 이름 변경이다. 우리는 제때 잡아냈다. 다음에 팀 밖의 누군가가 그 API에 닿는 스크립트를 쓸 때, 그들은 /api/raku/를 볼 것이고 6개월 뒤에 자신의 작업을 다시 할 필요가 없을 것이다.
정리 작업으로서의 널 포인터 안전성
런타임의 가장 최근 커밋, 이틀 전인 4월 30일의 58c3a806은 “fix(build): resolve all build errors, warnings, and null-pointer safety”이다. “null-pointer safety”라는 문구가 그 제목에서 많은 일을 하고 있다. 그 아래의 변경은 코드베이스 전체를 훑어서 포인터가 널일 수 있는데 확인 없이 역참조되고 있는 모든 곳을 찾는 것이다.
이 정리 작업을 촉발한 패턴은 샘플 애플리케이션 중 하나에서 온 크래시 보고서였다. 텍스처 로더가 에셋 매니페스트에 대한 널 포인터를 넘겨받아 그것을 역참조하고 크래시했다. 수정은 진입점에서의 확인이었다. 감사는 이 질문이었다. 코드베이스의 다른 얼마나 많은 곳이 같은 패턴을 갖고 있는가.
답은 수십 곳이었다. 그 모두가 익스플로잇 가능한 것은 아니었다. 그 대부분은 호출하는 코드가 우연히 절대로 널을 넘기지 않아서 한 번도 걸린 적이 없는 코드 경로였다. 그것은 방어가 아니다. 방어는 경계에서의 확인이다.
이 정리 작업은 확인들을 추가했다. 정상 경로의 동작은 바꾸지 않았다. 그것은 반환값(RAKU_ERR_INVALID_PARAM)과 실패 경로를 위한 로그 줄을 추가했다. 런타임은 이제 예전에는 조용한 크래시였던 경우들에서 더 시끄럽다. 시끄러운 것이 조용한 것보다 낫다. AR 런타임에서의 조용한 크래시는 최악의 종류의 버그다.
이런 종류의 한 주가 가질 가치가 있는 이유
주말 단위로 에이전트 무리와 함께 엔진 프로젝트를 운영할 때의 유혹은 계속 새로운 기능을 배포하는 것이다. 매주 토요일 아침 큐가 다시 채워진다. 매주 토요일 저녁 diff는 지난주보다 커진다. 이 리듬은 전진을 보상한다.
이 리듬은 또한 기술 부채를 용인한다. 지난 여덟 달 동안 머지된 모든 PR은 빌드 환경, API 표면, LLM 입력 윈도우, 또는 널 안전성 계약에 대한 작은 가정을 했다. 그 가정들 중 어느 것도 그 자체로는 틀리지 않았다. 그것들이 합쳐지면 결국 금이 갈 무른 지점들의 목록이었다.
전진을 멈추고 무른 지점들을 고치는 한 주는 잃어버린 시간이 아니다. 그것은 그 이후 여덟 달의 전진이 빌드와, API와, LLM 입력과, 널 포인터와 대들지 않고 일어날 수 있게 되는 그 주다. 시간을 낭비하는 것은 그 다툼이다. 이 정리 작업이 끝낸 것은 바로 그 다툼이다.
다음은 무엇인가
에이전트들은 이번 주말 다시 큐로 돌아왔다. 다음 라운드의 이슈들은 내가 이번 토요일 아침 등록하고 있는 것들이다. 그것들은 더 이상 정리 이슈가 아니다. 기능 이슈들이다. 빌드는 그것들과 대들지 않을 것이다.
그것이 깨끗한 빌드가 당신에게 사주는 것이다. 스크린샷이 아니다. 배포할 수 있는 허락이다.
지루한 것들에서 신뢰를 얻는 런타임
모든 타깃에서 초록빛, 안정된 API들, 조용한 크래시 대신 시끄러운 실패. RakuAI는 프로덕션이 요구하는 규율로 만들어진 공간 런타임이다. 파트너들이 왜 이 위에 빌드하는지 확인하라.