AX 노트 · 방법

여러 에이전트 세션이 고치는 정적 사이트: 커밋 전 --check 게이트 하나

생성 파일은 손대지 않고 커밋 전 --check로 재빌드 변경 0개, 게이트 오류 0건을 보면 작업을 합친 뒤 생기는 예산 초과와 날짜 흔들림을 미리 잡습니다.

작성 SugarMount 웹사이트와 검색 읽는 시간 12분 최종 업데이트

python tools/build.py --check가 멈춘 것은 2026년 10월 19일 밤, 다른 세션의 커밋 위로 rebase한 직후였습니다. 오류는 한 줄이었습니다. [BUDGET-FONT] index.html: font files this page needs 421 KB > 400 KB. rebase 전 빌드에서 홈에는 오류가 없었고, 두 작업을 합친 뒤에야 드러났습니다. 이 사이트는 프레임워크 없이 Python 표준 라이브러리만 쓰는 빌드 모듈 6개(3,616줄)로 만들고, 사람이든 에이전트든 커밋 전에 이 명령을 통과시키는 것이 규칙입니다. 10월 5일부터 22일까지 이 명령이 멈춘 일 가운데 네 번을 봅니다.

근거는 이 사이트 저장소의 git 기록과 tools/ 코드, 개편 명세 리뷰 문서, 작업 세션 기록, AX 노트 발행 기록입니다. 줄 수·테스트 수·파일 수는 2026년 10월 24일 커밋 1d5d432에서 다시 확인했습니다.

목차 (10)
  1. --check가 커밋 전에 멈춘 네 번
  2. 손으로 쓰는 곳은 본문과 데이터 파일뿐입니다
  3. 멱등성은 내일 날짜로 한 번 더 빌드해 확인합니다
  4. 첫 통합에서 틀린 쪽은 게이트였습니다
  5. 글꼴 예산은 다른 글의 제목으로도 깨집니다
  6. 공유 이미지는 실제 화면만 쓰고 글자를 넣지 않습니다
  7. 브라우저 편집, 많은 페이지, CI 없는 저장소에는 맞지 않습니다
  8. 다른 사이트에 --check 게이트를 들이는 순서
  9. 다음에 확인할 것
  10. 자주 묻는 질문

--check가 커밋 전에 멈춘 네 번

--check는 메모리에서 사이트 전체를 다시 만든 뒤 두 가지를 봅니다. 디스크에 있는 파일과 한 바이트라도 다른 출력이 있는지, 그리고 검사 모듈 tools/checks.py가 오류를 냈는지입니다. 하나라도 걸리면 exit 1입니다. 핵심만 줄이면 아래와 같습니다.

outputs = build_all()              # pages, feeds, sitemap, CSS: in memory
changed = [p for p, text in outputs.items()
           if read_file(p) != text]
report = checks.run(ROOT, outputs)  # 106 issue codes
if "--check" in argv:
    if changed or report.errors:
        exit(1)                     # nothing is written
else:
    write_files(changed)

10월 24일 기준 출력 파일은 162개, 검사 코드(SD-CRUMB-HOME 같은 고정 이름)는 106개이고, 이 PC에서 한 번 도는 데 11.6초가 걸렸습니다.

멈춘 네 번은 10월 5일 첫 빌드 직후 out of date 2개, 10월 18일 통합 빌드의 게이트 오류 101건, 10월 19일 rebase 뒤 홈 글꼴 421KB(상한 400KB), 10월 22일 새 글 제목 때문에 다른 글 2편이 482KB와 473KB(상한 470KB)였습니다. 10월 23일에는 그림 도구가 글꼴이 빈칸으로 그리는 글자에서 멈추도록 검사를 더했습니다.

단계 그림: 10월 5일 빌드 직후 out of date 2개, 10월 18일 통합 빌드 게이트 오류 101건, 10월 19일 rebase 뒤 홈 글꼴 421KB, 10월 22일 새 제목 때문에 다른 글 2편이 482KB와 473KB, 10월 23일 그림 도구에 BLANK GLYPH 검사 추가.
네 번 모두 커밋 전에 걸렸고, 홈 초과는 다른 세션의 커밋 위로 rebase한 뒤에야 보였습니다.

손으로 쓰는 곳은 본문과 데이터 파일뿐입니다

파일마다 쓰는 쪽은 하나뿐입니다. 저장소의 AGENTS.md에 있는 표를 줄이면 이렇습니다.

누가 쓰나무엇을
글 쓰는 사람·에이전트손 페이지의 <title>, description, <main>(빌드 블록 제외), data/apps.json, data/ax.json
빌드 담당 세션 하나tools/*.py, 통합 빌드 실행
빌드만head·헤더·푸터·이동 경로·JSON-LD, 홈·목록·주제 페이지, sitemap·RSS·robots·llms.txt, 날짜 파일, 생성 CSS

손 페이지 안에서도 빌드가 채우는 자리는 <!-- build:meta --> 같은 표시로 구분합니다. 빌드는 매번 이 블록을 비우고 다시 넣으며, 그 밖의 <main>은 바이트 그대로 둡니다(앱 설치 링크의 추적 값 하나만 예외). 글을 쓰는 쪽은 빌드를 돌리지 않고 읽기 전용 검사 python tools/checks.py --file <파일>만 씁니다.

이 규칙은 처음부터 있지 않았습니다. 10월 18일 개편 명세는 에이전트가 썼고, 작업 묶음마다 "끝에 build와 --check를 돌려라"와 "생성 HTML은 건드리지 마라"를 함께 요구했습니다. 명세를 공격하는 역할을 맡은 리뷰 에이전트가 이 둘이 모순이라고 지적했습니다. 빌드는 모든 손 페이지의 머리와 꼬리를 다시 쓰므로, 한 세션이 검증하려고 빌드를 돌리면 다른 세션이 고치는 파일 60여 개가 달라집니다. 그 뒤로 통합 빌드는 한 번, 한 곳에서만 돌립니다.

rebase 충돌도 같은 원칙으로 해결합니다. 10월 19일 충돌 난 파일은 404.html, 소개·앱 페이지, rss.xml처럼 빌드가 다시 쓰는 파일이었고, 이 파일들은 다른 세션 쪽을 받고 AX 글만 내 쪽을 고른 뒤 다시 빌드했습니다. 생성 파일은 손으로 고치지도 병합하지도 마십시오. 한쪽을 고르고 다시 빌드한 뒤 --check가 0을 낼 때만 커밋하십시오.

빌드 모듈 6개는 hashlib, html.parser, xml.etree 같은 표준 라이브러리만 씁니다. 설치할 패키지가 없습니다. 그림 도구 4개(775줄)는 Pillow가 필요하고 --check 밖에 있습니다.

멱등성은 내일 날짜로 한 번 더 빌드해 확인합니다

10월 5일 에이전트가 첫 빌드 스크립트(커밋 시점 725줄)를 쓰고 바로 돌렸을 때 결과는 이랬습니다.

45 files, 45 changed
out of date:
  apps/sugarcard/index.html
  sitemap.xml

한 번 더 빌드하자 2개가 바뀌었고, 그다음에는 up to date였습니다. 달라진 줄은 sitemap의 lastmod 날짜였습니다.

원인은 두 단계였습니다. 가까운 원인은 계산 순서입니다. 빌드가 lastmod를 git status와 git log에서 읽었는데, 파일을 쓰기 전에 읽었습니다. 빌드가 파일을 쓰면 git 상태가 바뀌고, 다음 빌드는 다른 입력을 봅니다. 그 밑의 원인은 날짜를 저장소 상태와 벽시계에서 가져왔다는 점입니다. 본문이 그대로여도 날짜는 움직였습니다.

리뷰 에이전트가 짚은 것과 그날 고친 것은 가까운 원인뿐이었습니다. "이번 빌드가 쓰는 파일은 오늘 날짜"라는 집합을 하나 더하자 같은 날 두 번 빌드는 통과했지만, 날짜를 git에서 읽는 구조는 남았습니다. 두 번 돌려 수렴하면 멱등처럼 보이지만, 같은 입력으로 다음 날 빌드하면 날짜가 또 움직입니다.

13일 뒤 개편에서 이 구조를 걷어냈는데, 개편 명세(역시 에이전트 작성)에도 같은 종류의 순환이 둘 있었습니다. 앱 페이지의 "최종 업데이트" 줄이 해시 대상 안에 있어 날짜가 해시를, 해시가 다시 날짜를 바꿨고, 빌드가 site.css에 생성 블록을 쓰는데 CSS 버전 값은 그 파일의 해시였습니다. 리뷰 에이전트는 두 번째 것이 "--check가 영원히 한 번씩 실패한다"고 적었습니다. 둘 다 구현 전에 고쳤습니다.

CSS 쪽은 생성 CSS를 별도 파일로 메모리에서 먼저 만들고, 버전 값을 계산해 넣은 뒤에 디스크에 쓰게 했습니다. 날짜 쪽 규칙(본문 해시, SOURCE_DATE 환경 변수)은 사이트맵 lastmod가 전부 같은 날짜였다: 본문 해시로 수정일 정하기에 적었습니다.

10월 18일 통합 때는 파일 445개의 sha256을 빌드 1회차, 2회차, SOURCE_DATE 실행 뒤에 떠서 비교했고 차이는 0이었습니다. 10월 24일 스크래치 사본에서 다시 돌린 결과입니다.

$ SOURCE_DATE=2026-10-25 python tools/build.py
162 files, 0 changed
$ SOURCE_DATE=2026-12-31 python tools/build.py
162 files, 0 changed

첫 통합에서 틀린 쪽은 게이트였습니다

10월 18일 개편은 에이전트 36명이 나눠 맡은 워크플로였고, 2시간 24분 걸렸습니다. 작업 묶음을 합친 첫 --check는 다시 쓸 파일 0개, 게이트 오류 101건으로 실패했습니다.

막대그림: 2026년 10월 18일 통합 빌드의 첫 --check 오류 101건 중 BUDGET-FONT 43건, ANCHOR-DUP 24건, DATA-APP 19건, BUDGET-OG 14건, LINK-BROKEN 1건.
가장 많은 오류는 게이트의 집계 방식에서 나왔습니다.

가장 많은 BUDGET-FONT 43건은 대부분 검사 코드가 틀린 경우였습니다. 영어 페이지의 ©는 CSS에서 시스템 글꼴로 그리는데, 검사 코드는 이 글자까지 세어 글꼴 조각이 더 필요하다고 계산했습니다. 진짜 낭비도 하나 있었습니다. 이동 경로의 구분 기호 › 한 글자 때문에 이동 경로가 있는 페이지마다 25KB 조각 하나를 더 받고 있었습니다. 검사 코드의 글자 집계를 CSS 규칙과 맞추고, 구분 기호는 시스템 글꼴로 돌렸습니다. 나머지는 같은 문구로 두 주소를 가리킨 링크, 디자인 예시의 앱 색 표시, 200KB를 넘은 공유 이미지, 깨진 링크였습니다.

게이트를 고칠 때는 테스트를 같이 고칩니다. 이때 테스트 6개를 더했고, 새로 넣은 가드 8개를 하나씩 지운 사본에서 테스트가 모두 실패하는 것을 확인했습니다. 10월 24일 기준 검사 코드 106개는 하나도 빠짐없이 test_checks.py에 이름이 나오고, 테스트는 전부 188개입니다.

통합 에이전트는 게이트 두 곳을 스스로 정했습니다. 명세에 상한이 없는 개별 방침과 디자인 예시 페이지는 글꼴 초과를 경고로만 냈고, 생성 글꼴 CSS 상한은 20KB에서 24KB로 올렸습니다. 둘 다 "확인이 필요하다"고 보고서에 적었습니다. 운영자는 같은 보고서의 다른 결정 항목(방침의 책임자 표기를 사업자 이름으로)을 그날 저녁 정했습니다. 24KB 쪽 코드 주석은 10월 24일에도 "확인하거나 낮춰야 한다"는 문장 그대로입니다.

글꼴 예산은 다른 글의 제목으로도 깨집니다

이 사이트의 한글 글꼴은 92개 조각으로 나뉘어 있고, 페이지는 자기 글자가 들어 있는 조각만 받습니다. 상한은 글 페이지 470KB, 홈과 목록 페이지 400KB입니다. 드문 음절 하나가 17~40KB 조각 하나를 더 부르기 때문에, 첫날 AX 노트 세 편 중 두 편이 619KB와 533KB였습니다. 그날 드문 음절이 든 단어를 흔한 말로 고쳤습니다.

홈에는 최신 AX 노트 제목 3개가 들어갔고, 그 제목은 매일 달라집니다. rebase로 다른 세션의 변경과 합쳐지자 421KB가 됐습니다. 제목을 고치는 대신 홈 목록을 주제 페이지 링크로 바꿨습니다(663f04e). 주제 이름은 고정 문구라 비용이 움직이지 않습니다. 10월 22일 허브의 주제 카드에서 글 제목을 없앤 것도 같은 이유였고, 허브는 468KB에서 389KB로 줄었습니다(6b96643).

10월 22일에는 새 글 제목에 든 쟀 한 글자가 관련 글 목록을 타고 다른 글 두 편에 들어가, 그 두 편이 482KB와 473KB가 됐습니다. 새 글 파일 하나만 검사하면 통과합니다. 제목을 "측정했나"로 바꾸자 450KB와 441KB로 내려갔습니다.

공유 이미지는 실제 화면만 쓰고 글자를 넣지 않습니다

tools/og.py는 앱·도움말 글의 공유 이미지를 실제 Play 스크린샷과 중립 배경만으로 만들고, 글자와 로고를 넣지 않습니다. 글자 카드는 홈·허브·소개·방침·AX 노트에만 씁니다.

이미지는 --check가 다시 만들어 비교하는 대상이 아닙니다. 10월 24일 스크래치 사본에서 og.py를 돌리자 155개 중 4개가 커밋본과 달랐습니다. 10월 19일 새 앱 페이지와 함께 og.py를 거치지 않고 커밋된 앱 카드 2장(제목과 문구가 들어 있음)과, 그 앱 아이콘이 들어가기 전의 홈 카드 2장이었습니다. og.py는 두 번 돌려도 바이트가 같았으니, 생성기 출력과 다른 쪽은 커밋본입니다. 발행 절차는 관련 없는 이미지 변경을 커밋하지 않으므로, 10월 20일부터 24일까지 다섯 번 og.py를 돌린 뒤 이 4개를 커밋본으로 되돌렸습니다.

10월 23일 그림 도구에 넣은 빈 글자 검사의 경위는 안드로이드에서 한글 일부 글자만 빈칸: 나눔스퀘어 cmap 11,172자 중 8,693자가 빈 글자였다에 있습니다.

브라우저 편집, 많은 페이지, CI 없는 저장소에는 맞지 않습니다

글을 브라우저에서 쓰고 미리 보는 팀에는 맞지 않습니다. 여기서는 작성자가 HTML의 본문을 직접 고치고 명령을 실행합니다. 정적 사이트 생성기와 견줘 고른 기록은 없습니다. 다만 이 글의 네 번 중 세 번(10월 18일, 19일, 22일)은 전체 출력에 대한 검사에서 나왔으니, 생성기를 써도 그 검사와 날짜 규칙은 따로 만들어야 합니다.

페이지가 아주 많으면 비용이 커집니다. --check는 매번 전부 다시 만들고 전부 비교합니다. 162개 파일에 11.6초는 괜찮지만 증분 빌드는 없습니다.

게이트는 강제되지 않습니다. 저장소에 CI 작업이 없어서, 커밋 전에 누군가 이 명령을 돌려야 작동합니다. 게이트가 틀릴 수 있다는 점도 남습니다. 10월 18일 오류 43건이 그랬고, 상한을 올리는 커밋은 게이트가 막지 못합니다.

다른 사이트에 --check 게이트를 들이는 순서

  1. 누가 어떤 파일을 쓰는지 AGENTS.md 같은 지침 파일 첫머리에 표로 적고, 생성 파일은 손대지 않는다고 못박습니다.
  2. 손 페이지 안에서 빌드가 채우는 자리는 <!-- build:NAME --> 블록으로 표시하고, 빌드는 매번 비우고 다시 넣습니다.
  3. --check는 모든 출력을 메모리에 만든 뒤 디스크와 비교하고, 같은 출력에 검사를 돌려 둘 중 하나라도 걸리면 exit 1을 냅니다.
  4. 날짜와 버전 값은 내용 해시로 정하고, 해시 입력에서 날짜 줄과 자동 목록을 제외합니다.
  5. 멱등성은 세 번 확인합니다: 연속 두 번 빌드, 내일 날짜 빌드, 과거 날짜 빌드(날짜가 뒤로 가지 않는지).
  6. 검사마다 고정 코드를 붙이고, 그 코드가 실제로 나는지 확인하는 테스트를 둡니다. 가드를 지운 사본에서 테스트가 실패하는지도 봅니다.
  7. 글 쓰는 세션은 파일 단위 읽기 전용 검사만 하고, 통합 빌드는 한 세션이 한 번 돌립니다.
  8. rebase에서 생성 파일이 충돌하면 한쪽을 고른 뒤 다시 빌드하고 --check를 돌립니다.
  9. 다른 페이지의 글이 섞여 들어가는 예산(글꼴, 크기)은 전체 출력에서 검사하고, 매일 바뀌는 목록은 공통 페이지에 넣지 않습니다.
  10. 상한 숫자를 바꾸는 커밋은 사람이 따로 봅니다.

다음에 확인할 것

이미지는 아직 게이트 밖입니다. 다음 확인은 og.py 출력과 커밋본을 비교하는 단계를 --check에 넣는 것이고, 그 전에 글자가 든 앱 카드를 og.py의 예외로 둘지 생성기 출력으로 바꿀지 정해야 합니다. 글꼴 CSS 상한 24KB와 경고로만 내는 두 페이지는 아직 사람이 확인하지 않았습니다.

자주 묻는 질문

빌드 결과 HTML도 git에 커밋하나요?

커밋합니다. GitHub Pages가 저장소 파일을 그대로 내보내므로(.nojekyll로 Jekyll 처리도 끔) 빌드 결과가 곧 배포본입니다. --check는 커밋된 결과가 지금 입력으로 다시 만든 결과와 같은지 확인합니다.

정적 사이트 빌드가 멱등인지 어떻게 확인하나요?

연속 두 번, 내일 날짜, 과거 날짜로 빌드해 바뀌는 파일이 0개인지 봅니다. 같은 날 두 번은 통과해도 다음 날 날짜가 움직이는 결함이 있어서, 날짜를 환경 변수로 바꿀 수 있게 만들어 두는 것이 먼저입니다.

여러 AI 에이전트 세션이 같은 사이트 저장소를 고칠 때 충돌은 어떻게 푸나요?

본문과 데이터 같은 원본만 병합하고, 생성 파일은 한쪽을 고른 뒤 다시 빌드해 --check를 통과해야 커밋합니다. 이 사이트의 홈 글꼴 초과도 이 단계에서 처음 드러났습니다.

이 글은 SugarMount가 직접 측정한 값으로 쓰고, 초안 작성에 AI 코딩 에이전트를 썼습니다.

웹사이트와 검색 글 전체 보기 · AX 노트 전체 보기