Multi-Agent Issue Automation
GitHub Issue를 안전한 다중 에이전트 작업 큐로 바꾸는 로컬 실행 프로세스
한눈에 보기
Multi-Agent Issue Automation은 GitHub Issue를 에이전트 작업 큐로 쓰되, 자동 실행 권한을 좁게 제한하는 운영 방식입니다. 목표는 완전 자동 코딩이 아니라, 이슈, 로컬 resolver, 작업 branch, 검증 로그, PR 증거가 서로 맞는지 확인하며 안전하게 위임하는 것입니다.
- 핵심 질문: 다른 저장소의 이슈를 로컬 에이전트가 안전하게 처리하려면 무엇을 고정해야 하는가?
- 읽는 대상: GitHub Issue 기반 자동 개발을 운영하려는 maintainer, reviewer, agent operator
- 연결 문서: Agent Team Playbook, AX Solution Orchestration, Commands & Guardrails
이 문서에서 확인할 것
- control repo, issue repo, work repo를 분리하는 이유
- 로컬 path가 머신마다 달라도 같은 작업을 재현하는 resolver 계약
- 여러 에이전트를 투입할 때의 역할 분담과 종료 조건
- unattended 실행 전에 필요한 Project Status, label, repo policy gate
운영 원칙
GitHub Issue는 작업 요청의 source of truth입니다. 터미널 세션이나 tmux pane은 작업을 실행하는 표면일 수 있지만, 상태의 원장이 되면 안 됩니다.
flowchart LR
subgraph GH["GitHub Work Queue"]
I["Issue<br/>scope + acceptance"]
S["Project Status<br/>Ready for Agent"]
L["Labels<br/>auto-dev + approved"]
end
subgraph RUN["Local Runner"]
C["Portable<br/>Job Contract"]
R["Resolver<br/>repo id -> path"]
P{"Policy<br/>Pass?"}
end
subgraph WORK["Work Repository"]
B["Isolated<br/>Branch / Worktree"]
A["Agent Team<br/>Coordinator + Worker + QA"]
Q["Verification<br/>build + lint + E2E"]
PR["Pull Request<br/>evidence attached"]
end
I --> C
S --> C
L --> C
C --> R --> P
P -->|pass| B --> A --> Q --> PR
P -->|block| X["Blocked Artifact<br/>reason + next action"]
Q -->|fail| X
classDef queue fill:#1c2423,stroke:#86c9bd,color:#f1f1f1;
classDef runner fill:#252119,stroke:#d1a066,color:#f1f1f1;
classDef work fill:#1b2030,stroke:#5b8cff,color:#f1f1f1;
classDef block fill:#2b1e1f,stroke:#e98585,color:#f1f1f1;
class I,S,L queue;
class C,R,P runner;
class B,A,Q,PR work;
class X block;
작업 완료는 코드가 바뀐 순간이 아니라 다음 증거가 맞을 때 선언합니다.
| 증거 | 확인할 것 |
|---|---|
| Issue | 상태, 승인 label, assignee, blocking label |
| Job contract | target repo, base branch, issue number |
| Resolver | 실제 로컬 path, remote URL, branch |
| Git | isolated branch 또는 worktree |
| Verification | build, lint, typecheck, E2E, browser QA |
| PR | 변경 요약, 위험, 테스트 결과, issue link |
실행 타임라인
sequenceDiagram
autonumber
participant O as Operator
participant G as GitHub Issue
participant R as Local Runner
participant Z as Resolver
participant A as Agent Team
participant T as Test Gate
participant P as Pull Request
O->>G: Mark Ready for Agent
G->>R: issue snapshot
R->>R: check approval + execution labels
R->>Z: resolve work_repo_id
Z-->>R: path + remote + base branch
alt gates pass
R->>A: dispatch bounded tasks
A->>T: changed files + verification plan
T-->>R: pass/fail evidence
R->>P: open PR with report
R->>G: comment run id + PR link
else gate fails
R->>G: comment blocked reason
R-->>O: needs human action
end
저장소 역할
한 저장소 안에서 모든 일이 끝나는 경우도 있지만, 실제 운영에서는 이슈와 작업 저장소가 다를 수 있습니다.
| 역할 | 의미 | 예시 |
|---|---|---|
control_repo | 정책, runner, 공통 규칙을 가진 저장소 | codecleanup-dev/keystone-hub |
issue_repo | GitHub Issue와 Project 상태를 가진 저장소 | MAESTRO-BS/BisFramework |
work_repo | 코드 변경, branch, commit, PR이 생기는 저장소 | MAESTRO-BS/BisFramework |
pr_repo | PR을 여는 대상 저장소 | 보통 work_repo와 동일 |
cross-repo 작업은 허용할 수 있습니다.
다만 issue 본문이나 job contract에 work_repo, base_branch, clone_url, work_repo_id가 명확해야 합니다.
Portable Job Contract
portable contract에는 개인 머신의 절대 경로를 넣지 않습니다.
{
"control_repo": "codecleanup-dev/keystone-hub",
"issue_repo": "MAESTRO-BS/BisFramework",
"work_repo": "MAESTRO-BS/BisFramework",
"work_repo_id": "bis-framework",
"clone_url": "[email protected]:MAESTRO-BS/BisFramework.git",
"base_branch": "dev",
"work_branch": "feature/123-short-title",
"issue_number": 123,
"pr_repo": "MAESTRO-BS/BisFramework"
}
contract는 어디서 실행해도 같은 의도를 표현해야 합니다. 로컬 path, worktree path, SSH alias, machine id는 실행 시점의 resolver artifact에만 남깁니다.
Local Resolver
resolver는 work_repo_id를 현재 머신의 실제 local path로 바꿉니다.
{
"repos": {
"bis-framework": {
"allowed_roots": ["$HOME/dev-bs", "$HOME/lucy"],
"path_hints": [
"$HOME/dev-bs/BisFramework/BisFramework-master",
"$HOME/dev-bs/BisFramework/BisFramework"
],
"os_path_map": {
"macos": "$HOME/dev-bs/BisFramework/BisFramework-master",
"windows": "D:\\\\dev-bs\\\\BisFramework\\\\BisFramework-master",
"wsl": "/mnt/d/dev-bs/BisFramework/BisFramework-master"
},
"clone_root": "$HOME/dev-bs"
}
}
}
resolver는 다음 순서로 fail-closed 해야 합니다.
work_repo_id가 등록되어 있는지 확인합니다.- resolved path가
allowed_roots아래인지 확인합니다. git remote get-url origin이work_repo또는clone_url과 맞는지 확인합니다.base_branch가 존재하거나 fetch 가능한지 확인합니다.- clone이 필요하면 명시 승인 없이는 멈춥니다.
flowchart TD
J["job.json"] --> K{"work_repo_id<br/>registered?"}
K -->|no| B1["policy-blocked<br/>unknown repo id"]
K -->|yes| P1["expand path hints"]
P1 --> R1{"inside<br/>allowed_roots?"}
R1 -->|no| B2["policy-blocked<br/>unsafe local path"]
R1 -->|yes| R2["read git remote"]
R2 --> R3{"remote matches<br/>clone_url?"}
R3 -->|no| B3["policy-blocked<br/>target mismatch"]
R3 -->|yes| R4{"base branch<br/>exists?"}
R4 -->|no| B4["agent-blocked<br/>fetch or clone approval"]
R4 -->|yes| OK["resolved-state.json<br/>safe to create worktree"]
classDef input fill:#1c2423,stroke:#86c9bd,color:#f1f1f1;
classDef decision fill:#252119,stroke:#d1a066,color:#f1f1f1;
classDef blocked fill:#2b1e1f,stroke:#e98585,color:#f1f1f1;
classDef ok fill:#1b2030,stroke:#5b8cff,color:#f1f1f1;
class J,P1,R2 input;
class K,R1,R3,R4 decision;
class B1,B2,B3,B4 blocked;
class OK ok;
Issue Lifecycle
unattended 실행은 단순 label 하나로 시작하지 않습니다. Project Status, execution label, approval label, repo allowlist가 모두 맞아야 합니다.
stateDiagram-v2
[*] --> Open
Open --> ReadyForAgent: scope fixed
ReadyForAgent --> Claimed: approval + execution label
Claimed --> InDev: resolver passed
InDev --> PrReady: verification passed
PrReady --> Done: PR merged or accepted
InDev --> AgentBlocked: policy or QA failed
AgentBlocked --> ReadyForAgent: human resolved
Recommended start conditions:
- Issue is open.
- Project Status is the configured execution-ready value, for example
Ready for Agent. - Execution label is present, for example
auto-dev. - Approval label is present, for example
approved-for-agent. - Assignee is an allowed actor or team.
- Target repository and base branch are allowlisted.
- Repo completion policy inspection has no blockers.
- No active branch or PR exists for the same issue.
- No blocking labels such as
blocked,manual,needs-spec,unsafeare present.
If Project V2 is inaccessible, the runner should stop. Label-only fallback is allowed only when the target repo explicitly opts into that mode.
Multi-Agent Team
에이전트는 같은 파일을 서로 덮어쓰는 작업자가 아니라, 역할과 종료 조건을 가진 팀입니다.
| 역할 | 책임 | 종료 조건 |
|---|---|---|
| Coordinator | issue 분석, resolver 실행, 작업 분해, 최종 통합 | job contract와 run plan이 남음 |
| Explorer | 코드 구조, 관련 파일, 기존 패턴 확인 | affected files와 위험 지점이 정리됨 |
| Worker | 정해진 파일 또는 모듈 slice 구현 | 변경 범위가 계획 안에 있음 |
| Test Engineer | build, lint, typecheck, E2E, browser QA 정의 | 성공/실패 로그가 남음 |
| Reviewer | 변경 범위, policy, PR evidence 검토 | 승인 또는 blocker가 명확함 |
병렬 에이전트가 필요하면 파일 소유권을 먼저 나눕니다. 작업자가 직접 push, merge, issue close를 수행하지 않게 하고 coordinator가 policy gate를 통과한 뒤 처리합니다.
Safety Gates
작업 전 gate:
- target repo와 base branch 확인
- local resolver path와 remote URL 확인
- unrelated dirty worktree 변경 확인
- Project Status와 approval label 확인
- package install, scheduler 등록, secret 접근 필요 여부 확인
PR 전 gate:
- target repo verification command 실행
- diff와 changed files 확인
- completion git policy inspection 저장
- PR 본문에 issue link, 테스트, 위험, 미해결 항목 기록
- issue close는 merge 이후 또는 target repo 정책에 따름
blocked by default:
- package install without approval
- cron, LaunchAgent, Windows scheduled task 등록
- secret, token, credential file 접근
- destructive reset, protected branch mutation
- repo policy 없이 commit, push, merge
BISFramework Reference Case
BISFramework는 이 모델의 첫 적용 후보입니다.
| 항목 | 현재 관찰값 |
|---|---|
control_repo | codecleanup-dev/keystone-hub |
issue_repo | MAESTRO-BS/BisFramework |
work_repo | MAESTRO-BS/BisFramework |
work_repo_id | bis-framework |
base_branch | dev |
| local path example | /Users/choa712-mac/dev-bs/BisFramework/BisFramework-master |
현재 BISFramework는 GitHub Project 2026 Development Roadmap과 Ready -> In Dev 운영 신호를 갖고 있습니다.
다만 unattended recurring 실행에는 아직 명시적인 approval label gate와 repo-local git policy가 더 필요합니다.
Typical verification:
npm run typecheck
npm run lint
npm run build
npm run validate-menus
npm run test:e2e
UI 변경이면 Vite dev server를 띄운 뒤 http://localhost:5173 기준으로 browser QA를 수행합니다.
Operator Runbook
-
Issue를 고릅니다.
- Project Status, approval label, assignee, blocking label을 확인합니다.
-
Target repo를 resolve합니다.
work_repo_id를 local path로 바꾸고 remote URL과 base branch를 확인합니다.
-
Issue를 claim합니다.
agent-running또는 동등한 상태를 남기고 run id, branch name을 comment로 남깁니다.
-
격리된 작업 공간을 만듭니다.
- feature branch 또는 worktree를 사용합니다.
-
에이전트를 dispatch합니다.
- Explorer, Worker, Test Engineer, Reviewer의 책임을 분리합니다.
-
검증합니다.
- target repo command를 실행하고 artifact를 저장합니다.
-
PR을 준비합니다.
- repo policy가 허용할 때만 push하고 PR 본문에 evidence를 남깁니다.
-
종료합니다.
- issue는 review state로 옮기고, merge 전 자동 close는 하지 않습니다.
Minimum Artifact Set
각 run은 적어도 아래 artifact를 남깁니다.
job.json: portable job contractresolved-state.json: local resolver resultstatus.json: current state, attempts, timeout, heartbeatprompt.md: exact task promptplan.md: implementation planchanged-files.txtordiff.patch: change evidenceverification.log: verification summarycompletion-policy.json: completion git policy inspectionfinal-report.md: PR-ready summary and residual risks
Adoption Checklist
-
control_repo,issue_repo,work_repo,pr_repo를 정했다. -
work_repo_id,clone_url,base_branch를 정했다. - 머신별 local resolver config를 만들었다.
- execution-ready Project Status를 정했다.
- execution label과 approval label을 분리했다.
- blocking label과 stale approval 기준을 정했다.
- target repo verification command를 정했다.
- artifact directory와 run id 규칙을 정했다.
- worktree, container, branch 중 격리 방식을 정했다.
- repo-local commit, push, merge policy를 정했다.
- dry-run issue로 mutation 없이 한 번 검증했다.
- 낮은 위험도의 PR-only issue로 첫 실행을 끝냈다.