대상: Unity SpriteAtlas에서 뽑은 UI용 PNG를 파이프라인으로 넘기는 크리에이티브테크 리드.
목표: 원격 Mac 워커가 “어젯밤 누군가 수출했다”를 게이트로 바꿉니다. 브리프가 요구하면 가로·세로 POT, HUD는 RGBA·아이콘 일부는 RGB처럼 채널 계약을 맞추고, 디스크 max_bytes를 넘지 않게 합니다. OpenClaw는 채팅 셸이 아니라 검토된 스크립트만 게이트웨이로 호출합니다.
목차
- 디렉터리 감시 전략 · 배치 명령 템플릿 · 실패 재시도와 로그 아카이브 · FAQ · 튜토리얼 예시
Retina 명명·슬라이스 정책은 9칸·소셜 슬라이스 매트릭스에 맞추고, 직선 알파·납품 어휘는 WebP→PNG 글과 통일합니다. 감시·JSONL 규율은 감시·재시도·아카이브 HowTo·Skills UI 감시독과 같은 테이블을 쓰면 온콜이 읽기 쉽습니다.
디렉터리 감시 전략
루트: 수출은 로컬 NVMe APFS에 두고 iCloud 동기화 Desktop은 피합니다. 크기가 안정된 뒤 stat을 돌려야 합니다. 원자 완료: 항상 임시 이름으로 쓰고 워처는 *.tmp를 무시한 뒤 *.png만 스캔합니다. 디바운스: 마지막 close 이벤트 후 quiet(Unity면 보통 8~20초) 뒤 batch_id당 한 잡만 큐에 넣습니다. single-flight: flock 또는 락 파일로 병렬 빌드가 같은 트리를 두 번 스캔하지 않게 합니다. OpenClaw: 설치 가이드대로 게이트웨이는 루프백에 두고 ATLAS_EXPORT_ROOT·quarantine/·logs/만 허용 목록에 넣으며, launchd의 EnvironmentVariables가 SSH 스모크와 동일한 TOKEN_FILE을 읽는지 재현합니다.
트리거는 brew install fswatch 후 소형 슈퍼바이저로 파이프하거나, 경로가 고정이면 launchd WatchPaths를 문서에 명시합니다. .DS_Store·0바이트·허용 목록 밖 경로 이벤트는 버려 게이트웨이 리뷰를 단순하게 유지합니다.
WATCH="/Volumes/WorkNVMe/unity_jobs/ui_atlas/Exports/SpriteAtlas"
fswatch -o "$WATCH" | while read -r _; do
sleep 12
flock -n /var/run/atlas-inspect.lock -c '$HOME/bin/atlas_scan.sh'
done
배치 명령 템플릿
ImageMagick 7의 magick을 plist에 기록한 PATH에 둡니다. OpenClaw나 launchd에서 호출하기 전에 ATLAS_EXPORT_ROOT·LOG_DIR·MAX_BYTES·REQUIRE_ALPHA를 export합니다.
is_pot() { local n="$1"; [[ "$n" =~ ^[0-9]+$ ]] && (( (n > 0) && (n & (n-1)) == 0 )); }
magick identify -format '%w %h %[channels]' "$f"
bytes=$(stat -f%z "$f"); test "$bytes" -le "$MAX_BYTES"
루프 골격(~/bin/atlas_scan.sh, chmod 750, 자동화에서만 호출):
#!/usr/bin/env bash
set -euo pipefail
ROOT="${ATLAS_EXPORT_ROOT:?}"
MAX_BYTES="${MAX_BYTES:-1200000}"
REQUIRE_ALPHA="${REQUIRE_ALPHA:-1}"
LOG_DIR="${LOG_DIR:-./logs}"
mkdir -p "$LOG_DIR"
is_pot() { local n="$1"; [[ "$n" =~ ^[0-9]+$ ]] && (( (n > 0) && (n & (n-1)) == 0 )); }
while IFS= read -r -d '' f; do
[[ "$f" == *.png ]] || continue
read -r w h ch < <(magick identify -format '%w %h %[channels]' "$f")
bytes=$(stat -f%z "$f")
ok=1; reasons=()
is_pot "$w" || { ok=0; reasons+=("pot_w"); }
is_pot "$h" || { ok=0; reasons+=("pot_h"); }
if [[ "$REQUIRE_ALPHA" == "1" && "$ch" != *rgba* ]]; then ok=0; reasons+=("alpha"); fi
if (( bytes > MAX_BYTES )); then ok=0; reasons+=("size"); fi
jq -nc --arg f "$f" --argjson w "$w" --argjson h "$h" --arg ch "$ch" \
--argjson bytes "$bytes" --argjson ok "$ok" --arg r "$(IFS=,; echo "${reasons[*]}")" \
'{file:$f,w:$w,h:$h,channels:$ch,bytes:$bytes,ok:$ok,reasons:$r}' >> "$LOG_DIR/atlas-scan.jsonl"
if [[ "$ok" -ne 1 ]]; then mkdir -p quarantine; mv "$f" "quarantine/$(basename "$f")"; fi
done < <(find "$ROOT" -type f -name '*.png' -print0)
jq가 없으면 Shortcuts 배치 글처럼 printf 한 줄 JSON로 대체해도 됩니다. 격리 샘플에만 magick identify -define identify:limit=0 -verbose "$f" | head로 color-type 힌트를 남기면 아트 역주입이 빨라집니다.
실패 재시도와 로그 아카이브
분류 후 재시도. 일시: 파일 락·GPU 플러시·바쁜 볼륨의 짧은 읽기 → 지수 백오프(예: 2·4·8초, 상한 60초)로 최대 3회/trace_id. 데이터: POT 불일치·채널 오류·실제 초과 용량 → quarantine·reason_code 후 사람이 Unity 프리셋이나 매니페스트를 고칠 때까지 재큐 금지. 운영: magick 누락·토큰 경로 오류·디스크 워터마크 → 워커 전역 일시정지·온콜; 로그만 도배하는 재시도는 금지.
JSONL 위생: 워커가 여럿이면 잡 헤더 줄에 trace_id·batch_id·호스트·도구 버전을 한 번 기록합니다. 매일 logs/YYYY-MM-DD.jsonl로 쪼개고 며칠 뒤 archive/YYYY-MM/에 gzip, 라이브 테일은 짧게 유지해 rg '"ok":false'가 즉시 나오게 합니다. 로테이션 주기는 앞선 감시 HowTo와 맞춥니다.
FAQ
POT·RGBA인데 용량만 큽니다. 워처가 자동 pngquant를 돌려야 하나요?
기본은 아니오. 자동 근손실은 의도를 바꿉니다. 게이트에서 실패시키고 검토된 OpenClaw 스킬로 통지한 뒤, 매니페스트에 “근손실 허용”으로 표시된 SKU만 별도 문서화된 잡으로 보냅니다.
식별 결과가 srgba인데 rgba 부분 문자열 검사에 걸립니다.
bash에서 rgba 또는 정책상 동등한 srgba를 YAML 화이트리스트로 명시해 QA와 엔지니어가 같은 문자열을 봅니다.
야간 빌드만 원격 Mac에서 깨집니다.
슬립·외장 미마운트·대화형 SSH와 다른 PATH가 흔합니다. 전원 정책·caffeinate를 명시하고, plist에서 디스크를 먼저 마운트하며, 기동 시 which magick을 JSONL에 남깁니다.
의도적으로 비POT 아틀라스도 있나요?
브리프가 예외를 적으면 매니페스트 행마다 require_pot: false를 두고 is_pot 분기만 꺼습니다. “다들 기억”에 의존하지 마세요.
튜토리얼 예시
가정: 아틀라스가 /Volumes/WorkNVMe/unity_jobs/ui_atlas/Exports/SpriteAtlas/에 떨어지고 파일명은 hud_main_1024.png 형태입니다. 아트 계약은 1024×1024 POT, 오버레이는 RGBA, CDN 티어당 파일 1.2MB 상한입니다. Unity는 *.png.tmp에 쓴 뒤 mv로 *.png를 완료합니다. 원격 Mac은 fswatch로 이벤트를 모으고, 마지막 이벤트 후 12초 quiet가 지나면 /var/run/atlas-inspect.lock을 잡아 스캔 스크립트를 한 번 실행합니다. 파일마다 JSONL 한 줄을 남기고, 실패는 quarantine/2026-04-23/로 옮기며 reason_code를 붙입니다. 이 단락을 README에 붙여 넣으면 타이베이 엔지니어와 베를린 PM이 같은 숫자를 기준으로 논의합니다.
| 검사 | 통과 신호 | 흔한 실패 |
|---|---|---|
| POT | 브리프가 POT일 때 w & (w-1) == 0 및 h도 동일 |
타이트 팩으로 홀수 패딩·비POT 크기 |
| 알파 | magick identify -format '%[channels]'에 반투명 HUD는 rgba |
프리셋이 알파를 떨어뜨려 rgb |
| 용량 | stat -f%z ≤ YAML max_bytes |
쓰이지 않는 투명 여백으로 PNG 팽창 |
요약: .tmp → .png 원자 수출, 디바운스·single-flight, magick identify+stat 게이트, reason_code 격리, JSONL gzip, 게이트웨이 경로 고정. 대여·구매·요금·SSH·VNC(로그인 불필요). 기술 인사이트에서 다른 플레이북을 이어 읽을 수 있습니다.
전용 원격 Mac에서 SpriteAtlas PNG 검수
아티스트 노트북에서 Unity 수출 QA를 분리하고, Apple Silicon에서 OpenClaw·ImageMagick 버전을 고정한 뒤 임계 표와 JSONL 런북을 스튜디오 간에 공유하세요.