죽은 실험이 현행 엔진 폴더 안에서 살고 있었습니다
저장소 루트에 빌드 스크립트가 세 개 있었습니다. 진짜 문제는 그 세 개가 아니라, 현행 파이프라인 디렉터리 안에 섞여 있던 v1 테스트 파일 하나였습니다. 값싼 수정은 없었고, 결국 옮기기만 하고 판단은 미뤘습니다.
1인 운영을 떠받치는 머신, 에이전트 환경, 원격 접속, 알림 셋업 기록.
저장소 루트에 빌드 스크립트가 세 개 있었습니다. 진짜 문제는 그 세 개가 아니라, 현행 파이프라인 디렉터리 안에 섞여 있던 v1 테스트 파일 하나였습니다. 값싼 수정은 없었고, 결국 옮기기만 하고 판단은 미뤘습니다.
도로 CCTV 앱을 안드로이드로 포팅하다 알았습니다. type 파라미터는 필터가 아니라 데이터셋 선택이었고, 앱은 국도 데이터셋만 부르고 있었습니다.
46개 앱을 실빌드로 훑었더니 20개가 컴파일에 실패했습니다. 원인은 제 코드가 아니라 SDK 안에서 컴파일러가 자동 합성한 생성자였습니다.
세션의 68%에 출력 토큰이 0이라 '앱이 먼저 말을 안 걸어서'라고 결론냈습니다. 그 가설을 반증한 건 이미 그 기능을 갖고 있던 형제 앱이었습니다.
음성 앱 세 개의 세션 271건에서 오디오 토큰이 0인 것을 세고 오디오 결함을 팠습니다. 절반쯤 가서야 그 숫자가 관찰이 아니라 동어반복이라는 걸 알았습니다.
기본 액터 격리를 메인 액터로 두는 세팅은 deinit을 직접 쓰지 않은 클래스에만 isolated deinit을 합성합니다. 아무것도 안 쓴 클래스만 위험합니다. 직관과 반대입니다.
새 테스트 프레임워크에서는 -only-testing이 매칭에 실패해도 에러가 아닙니다. 0건을 실행하고 TEST SUCCEEDED를 냅니다. RED를 확인하려던 실행이 초록으로 끝났습니다.
제스처 배선은 순수 함수로 뺄 수 없어 픽셀 변화로 쟀습니다. 그 판정을 조용히 무의미하게 만드는 것이 셋 있었고, 셋 다 실제로 밟았습니다.
앱 31개에 가드 한 줄을 일괄 삽입하고 파싱 검증을 돌렸습니다. 31개 전부 통과. 그 다음 타입체크를 돌렸더니 31개 전부 실패했습니다. 가드를 부르는 코드만 심고 정의는 심지 않았기 때문입니다.
긴 작업이 끝날 때까지 기다리는 한 줄짜리 루프를 붙였습니다. 그 루프는 절대 끝나지 않습니다. pgrep -f가 전체 명령줄을 보는데, 대기 셸 자신의 명령줄에 찾는 문자열이 들어 있기 때문입니다.
작업을 격리하려고 git 워크트리를 만들고 거기서 릴리스를 냈습니다. 릴리스 매니페스트의 프로젝트 경로가 절대 경로로 메인 체크아웃을 박아 두고 있었습니다. 빌드도 업로드도 성공했습니다.
고친 문자열을 배포하려고 기존 스크립트를 열어 봤습니다. 그 스크립트는 매 실행마다 리포지토리의 리스팅 파일로 라이브를 덮어씁니다. 전날 API로 고친 47개 필드가 조용히 되돌아갈 뻔했고, 배포는 성공으로 끝났을 겁니다.
거부됐던 안드로이드 앱 세 개를 재빌드하고 올리기 전에 검증했습니다. 세 앱 전부 '읽기 실패'가 났고 잠깐 서명을 의심했습니다. 원인은 검증 셸에 키 도구 경로가 없어서 명령이 아예 실행되지 않은 것이었습니다.
앱마다 배포 스크립트가 하나씩 있고 형태가 거의 같습니다. 먼저 --dry-run으로 돌려 보고, 출력이 맞으면 플래그를 떼고 다시 돌립니다. 그날도 --dry-run을 붙였는데 버전이 그대로 프로덕션에 나갔습니다. 그 스크립트에는 argparse가 없었습니다.
시뮬레이터 스크린샷은 코드와 맞는데 실기만 달랐습니다. 코드를 의심하기 전에 봐야 했던 건 설치 아티팩트였습니다.
대화형 tailscale ssh가 0바이트를 뱉고 즉사했습니다. tailnet도 ACL도 호스트키도 버전 경고도 전부 무죄였습니다. 진짜 원인은 20일 동안 아무도 안 만진 CLI 세션들이 PTY를 전부 소진한 것 — 그리고 PTY를 쥔 건 claude가 아니라 그 부모 셸이었습니다.
정책 문구 한 줄을 고치려고 정적 사이트 배포 스크립트를 부르려던 순간이었습니다. 라이브 파일 178개를 먼저 내려받아 대조했더니 일치가 0개였고, 라이브가 페이지마다 50KB 더 컸습니다. 그대로 올렸으면 170여 페이지에서 그만큼을 지웠을 겁니다.
같은 회사, 같은 .p8 키, 같은 팀 ID. 그런데 한쪽은 Bearer 토큰을 받고 다른 쪽은 URL 서명을 받습니다. 401을 보고 저는 계속 키를 의심했습니다.
테스트 스텁이 진짜보다 관대했고, 테스트 입력이 실제 호출부보다 깨끗했고, 자기검증 조건은 아무것도 검증하지 않았습니다. 셋 다 초록불이었습니다.
HTML에는 요소가 다 있었고 에러는 한 줄도 없었습니다. 그런데 useEffect가 한 번도 실행되지 않았습니다. 증거는 브라우저가 아니라 dev 서버 로그에만 있었습니다.
채널 열 개의 제목을 API로 바꿨습니다. 에러는 없었고 스크립트는 전부 성공을 찍었습니다. 다시 읽어 보니 제목은 하나도 안 바뀌어 있었습니다. 그날 하루에 밟은 조용한 실패 다섯 개를 정리했습니다.
iCloud Drive의 git 저장소를 로컬로 옮기려 `mv`를 걸었더니 수백 개 파일이 10분 넘게 안 끝났습니다. iCloud 파일은 디스크에 실제로 없을 수 있고, `mv`가 닿으면 파일당 동기 다운로드가 걸립니다. 게다가 최신 macOS는 `.icloud` sidecar가 아니라 APFS dataless 파일이라 `find -name '*.icloud'`로는 하나도 안 잡힙니다.
파일은 만들 수도 지울 수도 있는데 `open()`만 안 됐습니다. `Operation not permitted`. 전체 디스크 접근은 이미 켜져 있고 샌드박스도 무관했습니다. 범인은 TCC가 아니라 EndpointSecurity 기반 DLP 에이전트였고, 판별의 열쇠는 '현재 프로세스가 방금 만든 파일만 읽힌다'는 비대칭이었습니다.
웹앱 34개, 페이퍼 트레이딩 봇 몇 개, 다국어 영상 파이프라인 — 전부 그냥 계속 켜져 있는 2017년 인텔 노트북에서 돈다. 전체 셋업과, 왜 구형 하드웨어가 오히려 도움이 됐는지.
Tailscale로 아무것도 공개 노출하지 않고 폰에서 로컬 봇 대시보드를 본다. 그런데 봇을 폐기해도 Tailscale 경로는 안 지워진다 — 죽은 포트를 살아있어 보이게 만든 함정.
실제 판단을 내리는 봇은 Telegram으로 나를 부른다. 페이퍼·테스트 봇은 설계상 무음 — 거기서 발송 실패는 버그가 아니라 정상이다. 커버리지보다 신호.
Claude Code와 Codex를 돌리는 환경이 세 단계를 거쳤다. 매번 같은 문제를 풀었다: 에이전트 여럿을 동시에 돌리면서 지금 어느 놈이 나를 필요로 하는지 어떻게 아나?