GitHub Agentic Workflows로 저장소 간 문서화 자동화하기

TMT

https://github.blog/ai-and-ml/github-copilot/automating-cross-repo-documentation-with-github-agentic-workflows/

“문서는 어디 있나요?” 제품 팀에서 누구도 달가워하지 않는 질문입니다. 솔직한 대답은 대개 “밀려 있어요”의 어떤 변형이죠. 문서 작성자는 이미 닫힌 풀 리퀘스트를 들여다보며 무엇이 바뀌었는지 역으로 파악하려 애쓰고, 풀 리퀘스트 작성자는 벌써 다음 일로 넘어간 뒤입니다. 문서가 실제로 발행될 즈음이면 기능은 이미 출시되어 있고, 때로는 그 사이 한 번 더 출시되기도 합니다.

Aspire 팀의 우리 모습이 딱 그랬습니다(우리는 분산 애플리케이션용 개발 도구를 만드는 10명짜리 작은 팀입니다). 몇 달 전, 이미 신뢰하고 있던 자동화에 AI를 어떻게 안전하게 들일 수 있을지 고민하던 중 GitHub Agentic Workflows를 발견했습니다. 저는 microsoft/aspire에 프로토타입을 붙여 보기 시작했습니다.

그 결과로 얻은 것을 GitHub에서 바로 뽑은 숫자로 보여드리면 이렇습니다. Aspire 13.3과 13.4에서 기능 문서 풀 리퀘스트 82건이 제품 풀 리퀘스트 이후 중앙값 44.8시간 만에 머지되었고, 전부 해당 기능을 출시한 엔지니어가 직접 리뷰했습니다. 인원을 늘리지도 않았고, 프로세스를 다시 교육하지도 않았습니다. “이건 누가 쓰지?”라는 질문의 방식을 바꿨을 뿐입니다.

제약: 어려운 건 저장소 간 자동화

우리 제품은 microsoft/aspire에, 문서 사이트는 microsoft/aspire.dev에 있습니다. 저장소도, 배포 대상도, 리뷰 체계도 서로 다릅니다. 같은 저장소 안의 자동화는 대부분의 팀이 금방 해내지만, 저장소를 넘나드는 자동화부터는 일이 까다로워집니다. 저장소 전체에 걸친 광범위한 권한 토큰은 박물관에나 어울리는 유물이고, 책임 있는 보안 정책이라면(우리 팀도 그렇습니다) 당연히 그런 토큰을 제한합니다. 좋은 일이죠. 하지만 문서를 쓰는 곳과 코드를 쓰는 곳이 다르다면 이는 실질적인 병목이기도 합니다.

수년간 기본 워크플로는 이랬습니다.

  1. 엔지니어가 microsoft/aspire에 기능을 출시한다.
  2. 문서 작성자가 몇 주 뒤에야 그 사실을 알아챈다.
  3. 문서 작성자가 풀 리퀘스트를 열어 diff를 읽고, 무엇이 바뀐 건지 엔지니어에게 물어본다.
  4. 엔지니어는 이미 다음 기능을 작업 중이라 어렴풋한 기억으로 절반짜리 답을 준다.
  5. 문서 초안이 나가는데, 때로는 이미 세상에 나온 릴리스를 뒤늦게 좇는 꼴이 된다.

이것이 바로 역설계 비용입니다. 우리에게는 에이전트에게 아무 데나 쓸 수 있는 토큰을 쥐여 주지 않고도 저장소를 넘나드는 자동화가 필요했습니다. 그 답이 GitHub Agentic Workflows였습니다.

왜 GitHub Agentic Workflows인가

GitHub Agentic Workflows는 GitHub Next 팀의 프로젝트로, 저는 사람들에게 “작업 항목을 처리하는 주체가 모델이고, 보안 심사를 통과할 만한 가드레일이 달린 GitHub Actions”라고 설명하곤 합니다. 단순화한 표현이지만 실제와 크게 다르지 않습니다.

대략의 구조는 이렇습니다.

  • 워크플로를 마크다운 파일 하나(.github/workflows/my-thing.md)로 작성합니다. 위쪽에는 YAML 형식의 프런트매터를, 그 아래에는 영어로 된 프롬프트를 씁니다.
  • GitHub Agentic Workflows의 컴파일을 실행하면 같은 위치에 .lock.yml(일반적인 GitHub Actions 워크플로)이 생성되고, 이를 함께 커밋합니다.
  • 런타임에는 워크플로가 제한된 도구 모음을 갖춘 에이전트를 프롬프트에 따라 실행합니다.
  • 결정적으로, 에이전트는 GitHub에 직접 쓰지 않습니다. 에이전트는 의도만 내보내고(만들려는 풀 리퀘스트·이슈·댓글을 기술한 JSON 덩어리), 좁은 범위로 권한이 제한된 별도의 잡(safe-outputs 핸들러)이 워크플로별 GitHub 앱을 통해 그 의도를 실제로 반영합니다.

마지막 항목이 잠금을 푸는 열쇠입니다. 에이전트에게는 읽기 권한과 프롬프트만 줍니다. 쓰기는 명시적인 허용 목록을 갖춘 작고 검증 가능한 파이프라인을 거칩니다. 보안 심사가 고개를 끄덕이고, 우리는 배포합니다.

짧은 여담: 닮은꼴 스택

내가 쓰는 도구가 내가 쓰는 것과 같은 도구로 만들어져 있으면 참 반갑습니다. GitHub Agentic Workflows 문서는 Astro와 Starlight로 만들어졌습니다. aspire.dev도 마찬가지입니다. Astro에 Starlight를 얹고, 폭넓은 Starlight 플러그인 생태계로 꾸몄습니다(astro-mermaid, starlight-llms-txt, starlight-sidebar-topics, starlight-image-zoom, 근사한 @catppuccin/starlight 테마 등. Chris Swithinbank와 Starlight 메인테이너들께 감사를 전합니다. 생태계 전체가 진심으로 아끼는 사람들이 설계한 느낌입니다).

둘 사이에는 진짜 동질감이 있습니다. 문서 자동화에 쓰는 도구와 그 자동화 결과가 흘러드는 문서 사이트가 같은 기반을 공유하니까요. 덕분에 다음 절의 Mermaid 시퀀스 다이어그램이 양쪽 세계에서 완전히 똑같이 렌더링되니 편리하기도 합니다.

엔드투엔드 파이프라인

우리가 정착한 흐름은 이렇습니다. 주인공은 microsoft/aspire에 있는 pr-docs-check.md라는 워크플로입니다.

자동화된 문서 워크플로를 보여 주는 시퀀스 다이어그램: microsoft/aspire에서 기능 풀 리퀘스트가 머지되면 GitHub Actions 체크가 실행되어 에이전트가 문서 초안을 작성하고, microsoft/aspire.dev에 드래프트 풀 리퀘스트를 열고, SME 리뷰를 요청해 문서가 기능과 함께 출시되도록 한다.

자동화된 문서 워크플로를 보여 주는 시퀀스 다이어그램: microsoft/aspire에서 기능 풀 리퀘스트가 머지되면 GitHub Actions 체크가 실행되어 에이전트가 문서 초안을 작성하고, microsoft/aspire.dev에 드래프트 풀 리퀘스트를 열고, SME 리뷰를 요청해 문서가 기능과 함께 출시되도록 한다.

실행은 main 또는 release/*를 대상으로 한 pull_request: closed 이벤트에서 시작되며, merged == true 조건을 통과해야 합니다. 그다음 워크플로는 에이전트가 깨어나기도 전에, 순수 bash로 작성된 결정적(deterministic) 대상 브랜치 결정 로직을 먼저 실행합니다.

  1. 풀 리퀘스트의 마일스톤 제목(예: 13.4 → aspire.dev의 release/13.4).
  2. 연결된 이슈의 마일스톤 제목(본문에서 Fixes/Closes/Resolves #N을 파싱해 각 이슈를 조회하고, 비어 있지 않은 첫 마일스톤을 사용).
  3. 풀 리퀘스트의 base ref(release/X.Y.Z 패턴과 일치하는 경우).
  4. 그 외에는 main으로 폴백.

이 부분이 핵심 축입니다. 제품 저장소의 마일스톤이 문서 저장소의 릴리스 브랜치에 깔끔하게 대응됩니다. 에이전트가 마침내 실행될 때는 대상 브랜치를 지어내거나 추측할 필요 없이, 문서가 어디에 들어가야 하는지 정확히 알고 있습니다.

에이전트는 diff를 읽고 연결된 이슈를 훑은 뒤 판단합니다. 이 변경에 문서가 필요한가? 필요하다면 체크아웃해 둔 microsoft/aspire.dev 작업 공간에서 우리의 기존 문서 작성 스킬(문체, MDX 규칙, Starlight 컴포넌트)을 따라 실제 내용을 초안으로 작성합니다. 그런 다음 create_pull_request safe-output을 내보내고 손을 뗍니다.

이어서 safe-outputs 핸들러가 넘겨받습니다.

  • 제목 접두사: [docs]
  • 레이블: docs-from-code
  • draft: true (자동 머지는 절대 하지 않습니다)
  • Base 브랜치: 에이전트가 지정하되 main 또는 release/*로 제한
  • 대상 저장소: microsoft/aspire.dev
  • 리뷰어: 원본 풀 리퀘스트의 리뷰 기록에서 식별한 SME. 즉, 제품 팀이 기능 승인을 맡겼던 바로 그 사람이 이번에는 그 기능의 문서 승인을 요청받습니다.

동반 잡 하나가 원본 풀 리퀘스트에 문서 풀 리퀘스트 링크가 담긴 표시용 댓글을 남기고, 재실행 시에는 이전 pr-docs-check 댓글을 접어 둡니다. 방금 Merge 버튼을 누른 엔지니어는 몇 분 안에 알림을 받습니다. “문서 초안이 준비됐어요. 한번 봐 주시겠어요?”

safe-outputs 계약

보안 이야기의 전부는 짧고 심심한 프런트매터 한 토막으로 요약됩니다.

tools: 
  github: 
    toolsets: [repos, issues, pull_requests] 
    min-integrity: approved          # only run pinned, integrity-checked actions 
    allowed-repos: 
      - microsoft/* 
    github-app: 
      app-id: ${{ secrets.ASPIRE_BOT_APP_ID }} 
      private-key: ${{ secrets.ASPIRE_BOT_PRIVATE_KEY }} 
      owner: "microsoft" 
      repositories: ["aspire.dev", "aspire"] 

safe-outputs: 
  create-pull-request: 
    title-prefix: "[docs] " 
    labels: [docs-from-code] 
    draft: true                      # human-in-the-loop, always 
    base-branch: main 
    allowed-base-branches: [main, release/*] 
    target-repo: "microsoft/aspire.dev" 
    protected-files: blocked         # AGENTS.md, manifests, security config: hands off 
    fallback-as-issue: true

계약 내용이 이렇게 평문으로 다 드러나 있습니다. 에이전트가 받는 GitHub App 토큰은 설치 범위가 정확히 두 저장소, 즉 제품 저장소와 문서 저장소로 한정되어 조직 내 다른 어떤 곳에도 접근할 수 없습니다. 풀 리퀘스트는 main이나 release/*에만 만들 수 있습니다. AGENTS.md와 의존성 매니페스트는 정책상 손댈 수 없습니다. 풀 리퀘스트 생성이 실패하면(네트워크 순단, 충돌, 그 무엇이든) 프레임워크가 이슈 등록으로 폴백하므로 아무것도 조용히 사라지지 않습니다.

보안 심사가 실제로 마음에 들어 한 부분이 바로 여기입니다. 에이전트의 추론은 모호할 수 있어도, 행동 범위는 그렇지 않으니까요.

숫자로 보기

Aspire 13.3 릴리스 후반부와 13.4 준비 기간에 걸친 최근 30일 구간(2026년 5월 3일~6월 2일)의 통계입니다.

지표
microsoft/aspire에 머지된 제품 풀 리퀘스트396건 (main 338 / release/13.3 50 / release/13.2 8)
pr-docs-check 워크플로 실행396회
microsoft/aspire.dev에 생성된 드래프트 문서 풀 리퀘스트82건
– 머지됨82건 (100%)
– 머지 없이 닫힘0건
– 아직 열려 있음0건
문서 풀 리퀘스트 대상 브랜치release/13.3 52건, release/13.4 27건, main 3건
문서 머지까지 걸린 시간(중앙값)44.8시간
24시간 / 7일 내 머지 비율38% / 96%

참고: 작성 시점에 집계한 숫자이며, 워크플로는 지금도 돌고 있으므로 총계는 늘어나기만 합니다.

이 가운데 몇몇 숫자는 한 번 더 들여다볼 만합니다.

  • 396회 실행에 풀 리퀘스트 82건이 나온 것은 결함이 아닙니다. 워크플로는 머지된 모든 풀 리퀘스트에 대해 실행되는데, 대부분은 내부 리팩터링, 테스트 수정, 의존성 버전 올리기처럼 사용자에게 드러나는 변화가 없는 것들입니다. 에이전트가 300번 넘게 “문서 불필요”라고 판단한 것 자체가 기능입니다.
  • 100% 머지율은 에이전트가 문서화 대상을 정확히 골라내고 있다는 뜻입니다. v1의 오탐 시기를 겪고 나서 프롬프트를 조인 것이 효과를 내고 있습니다.

잘된 것과 그렇지 못한 것

잘된 것

  • 마일스톤 → 릴리스 브랜치 매핑. 우리가 내린 선택 중 단연 지렛대 효과가 가장 컸습니다. 엔지니어들은 이미 풀 리퀘스트와 이슈에 마일스톤을 달고 있었기에, 정확한 대상 브랜치 라우팅을 공짜로 얻었습니다.
  • 드래프트 전용, SME를 리뷰어로 지정. 에이전트는 절대 머지하지 않습니다. 기능을 출시한 엔지니어가 문서가 맞는지 직접 확인합니다. 이제 문서 단계에서 기능을 역설계하는 일은 사라졌습니다. 엔지니어는 자기가 이미 있는 자리에서, 문서 초안에 뭐라고 써야 할지 알려 주기만 하면 됩니다.
  • 워크플로별로 범위를 제한한 GitHub 앱. 워크플로마다 저장소와 권한 범위가 명시된 전용 앱 토큰을 받습니다. 보안 심사가 승인했고, 처음 키를 교체할 일이 생겼을 때는 우리 스스로도 이 구조를 인정하게 됐습니다.
  • protected-files: blocked. 에이전트는 AGENTS.md, 패키지 매니페스트, 저장소 보안 설정을 건드릴 수 없습니다. 예외는 없습니다.

(처음에는) 잘 안 된 것

  • 첫 버전에서는 에이전트의 “이거 문서화할 가치가 있나?” 판정이 너무 후했습니다. CI 손질이나 로깅 리팩터링처럼 순전히 내부적인 변경에도 풀 리퀘스트 초안을 만들었죠. 그 결과 69건 중 9건(약 13%)이 닫혔고, 우리는 프롬프트의 사용자 노출 변경(user-facing change) 정의를 조이고 명시적인 반례(CI, 내부 헬퍼, 테스트 전용)를 추가했습니다. 지금은 그 비율이 내려가는 추세입니다.
  • 저장소 간 풀 리퀘스트 생성에는 문서만 봐서는 알기 어려운 미러 체크아웃 패턴이 필요했습니다. 에이전트는 한 저장소에서 작업하는데, safe-outputs는 브랜치를 푸시할 대상 저장소를 찾아야 합니다. microsoft/aspire.dev를 두 번, 즉 한 번은 현재 작업 공간으로, 한 번은 _repos/aspire.dev 아래에 체크아웃해서 safe-outputs 핸들러가 대상을 결정적으로 다시 찾을 수 있게 하는 방식으로 해결했습니다.
  • 큰 diff는 프롬프트 예산을 다 잡아먹습니다. 그래서 pre-agent-steps의 bash에서 풀 리퀘스트 메타데이터(연결된 이슈, 마일스톤, base ref)를 미리 추출해, 에이전트가 거대한 페이로드 대신 작고 구조화된 요약을 받게 했습니다. 이는 GitHub Agentic Workflows가 설계 단계부터 의도한 패턴이고, 실제로 잘 작동합니다.

마치며

이 변화는 우리의 사고방식을 바꿔 놓았습니다. 문서가 끝나야 기능도 끝난 것으로 보게 됐습니다. 문서가 더는 기능 꽁무니에 매달려 덜그럭거리며 끌려오지 않습니다. 엔지니어의 리뷰가 관문이고, 타이핑은 봇이 합니다.

중요한 점은, 이것이 문서 작성자를 대체하지 않는다는 사실입니다. 오히려 짐을 덜어 줍니다. 예전에는 작성자들이 시간 대부분을 기능 역설계에 썼습니다. 이제는 사람만이 잘할 수 있는 일에 시간을 씁니다. 서사가 있는 페이지, 예제 프로그램, 개념 안내처럼 diff에서 저절로 나오지 않는 문서들 말이죠. “새 옵션이 추가됐으니 레퍼런스 페이지를 이렇게 고친다”처럼 누구에게도 즐겁지 않았던 기계적인 작업은 봇이 처리합니다.

GitHub Agentic Workflows를 만들어 준(그리고 safe-outputs 프리미티브를 설계의 일급 요소로 만들어 준) GitHub Next 팀, 그리고 우리의 자동화가 흘러드는 문서 플랫폼을 만들어 준 Chris Swithinbank와 Starlight 메인테이너들께 깊이 감사드립니다. 가드레일을 세워 처음부터 올바르게 설계하도록 이끌어 준 보안 담당자들께도 진심으로 감사를 전합니다. 좋은 자동화의 심심한 비밀은, 강한 보안 제약이 시스템을 더 신뢰할 수 있고 더 올바르게 만든다는 것입니다.

제품은 한 저장소에서 만들고 문서는 다른 저장소로 내보내고 있다면, 특히 만만치 않은 보안 경계 안에서 그렇게 해야 한다면, GitHub Agentic Workflows를 진지하게 살펴볼 가치가 있습니다. pr-docs-check 같은 워크플로 하나로 시작해서, 문서가 나오기까지 걸리는 시간의 중앙값이 어떻게 변하는지 지켜보시기 바랍니다.

그 밖의 워크플로들

이 글에서 다룬 것은 pr-docs-check지만, 혼자 돌고 있는 것은 아닙니다. 나머지가 궁금하다면 소스가 공개되어 있습니다.

  • milestone-changelog.md: 2시간마다 실행되어 활성 마일스톤에 새로 머지된 풀 리퀘스트를 수집하고, 13.x 변경 로그 위키 페이지(신규 기능, 개선 사항, 주요 버그 수정)를 편집 피드백용 이슈와 함께 관리합니다. 346회 실행.
  • release-update-support-mdx.md: Aspire 안정 버전이 릴리스되면 aspire.dev에 지원 정책 페이지를 갱신하는 [support] 풀 리퀘스트 초안을 만듭니다(새 버전을 올리고, 이전 버전을 내리고, “Last updated” 배지를 새로 고칩니다).
  • update-integration-data.md: 문서 저장소에 있으며, 매일 pnpm update:all을 실행해 NuGet 메타데이터, GitHub 통계, 샘플 데이터를 갱신하고 chore: Update integration data 풀 리퀘스트를 엽니다. 오래된 실행분은 새 것으로 대체하고 닫는(supersede-and-close) 로직도 갖추고 있습니다. 27회 실행, 풀 리퀘스트 8건 머지.
  • repo-pulse.md: 최근 3일 치 저장소 대시보드를 고정된 이슈 하나에서 계속 갱신합니다. 최근 머지, 리뷰 대기 중인 풀 리퀘스트, 새 이슈, 토론 활동을 담습니다. 이슈는 하나, 내용은 늘 최신입니다.
Edit this page