Core Goal
GitHub Issues / Linear / Jira(system of record)와 hplan의 닫힌 실행 레이어(sprint의 .track/)를 잇는다.
ticket-bridge는 번역기다 — 추정치나 진척을 생성하지 않고, sprint/conductor의 산출물을 티켓 시스템 포맷으로 전달하거나 그 반대로 변환만 한다.
| 모드 | 책임 | 입력 → 출력 | LLM |
|---|---|---|---|
--mode pull |
이슈 → WBS 태스크 후보 | list_issues → harness/ticket-import.md |
✅ body 분해만 |
--mode estimate |
predicted.json p50/p90 → 이슈 코멘트 |
.track/predicted.json + ticket-map.json → add_comment |
✅ 산문만 |
--mode status |
.track/ + git/PR/CI/review 상태 → 이슈 코멘트 |
actual_log.jsonl + list_commits/pull_request_read → add_comment |
✅ 산문만 |
--mode push |
WBS 태스크 → 이슈 생성 | harness/ticket-import.md + ticket-map.json → create_issue |
✅ 제목/설명 생성만 |
기본값:
--mode미명시 → fail loud + 모드 목록 안내. auto-run 금지.
Rule 5 준수 경계
| 작업 | LLM 사용 | 근거 |
|---|---|---|
| Issue body → 태스크 후보 분해 (pull) | ✅ 분류 | 자연어 → sub-task 분류. sprint WBS와 동일 성격 |
| 코멘트 산문 생성 (estimate/status) | ✅ 자연어 생성 | Rule 5 허용 영역 |
| size 라벨 → complexity bucket 매핑 | ❌ 결정론 lookup | §매핑 규칙 순수 테이블 |
| complexity → baseline percentile 전달 | ❌ 결정론 | predicted.json 값 직접 인용 |
| 태스크 ↔ 이슈 매칭 | ❌ 결정론 | harness/ticket-map.json 명시 매핑 lookup |
| commit/PR ↔ 태스크 매칭 | ❌ 결정론 | ticket-map의 issue 번호 → commit/branch 정규식 |
| 중복 코멘트 감지 | ❌ 결정론 | 마커 문자열 + 태스크 ID grep |
자체 점검: 라벨 매핑·estimate 전달·태스크 매칭에서 LLM 호출이 감지되면 즉시 fail — Rule 5 위반.
Trigger Gate
Use This Skill When
- "이 이슈들 스프린트 계획에 넣어줘" →
--mode pull - "추정치를 이슈에 달아줘" →
--mode estimate - "진행 상황을 이슈에 업데이트해줘" →
--mode status
Route to Other Skills When
- WBS 수치 예측 계산 →
deliver/sprint --step plan(ticket-bridge는 계산 안 함) - 태스크 실제 실행 →
deliver/conductor - 추적 환경 초기화 →
deliver/sprint --step init
Boundary Checks
--mode미명시 → fail loud, 모드 목록 출력- 지정한
--system의 MCP 미연결 → fail loud (대체 API client 만들지 않음) - write-back 전 항상 사용자 확인 게이트 통과 (자동 코멘트 금지)
Inputs
| 입력 | 출처 | 처리 |
|---|---|---|
--mode |
$ARGUMENTS |
pull/estimate/status 분기 |
--system |
$ARGUMENTS |
github(기본)/linear/jira 선택 |
| issue 번호/필터 | $ARGUMENTS 나머지 |
대상 이슈 선택 |
.track/predicted.json |
sprint plan 산출 | estimate 전달 소스 |
.track/actual_log.jsonl |
sprint init+probe | status 전달 소스 |
harness/ticket-map.json |
pull 산출 또는 수동 | 태스크 ID ↔ 이슈 번호 매핑 |
$ARGUMENTS를 파싱해 mode, system, 대상을 분리한다.
결정론적 매핑 규칙
size 라벨 → complexity bucket (순수 lookup, LLM 0)
| GitHub 라벨 | complexity bucket |
|---|---|
size/XS |
1 |
size/S |
2 |
size/M |
3 |
size/L |
4 |
size/XL |
5 |
size/*라벨이 0개 → "매핑 불가 — 해당 이슈 skip, 나머지 진행" (부분 성공 명시 — Rule 8). complexity는 보수적 fallback 3으로 처리하되 estimate 코멘트에 "라벨 미설정 — p50/p90 신뢰도 낮음" 명시. LLM 추론으로 complexity를 추측하는 것은 금지.size/*라벨이 2개 이상 → ambiguous, fail loud (임의 선택 금지).
태스크 ↔ 이슈 매칭 — harness/ticket-map.json
{ "T-001": 42, "T-002": 43 }
- estimate/status는 이 매핑으로 predicted.json 태스크 ↔ 이슈 번호를 연결한다.
- 매핑 항목이 없으면 해당 태스크는 "매칭 불가"로 표시하고 나머지는 진행 (부분 성공 명시 — Rule 8).
- commit/PR 매칭(status)은 ticket-map의 이슈 번호를 commit message·branch명에서 정규식으로 찾는다.
Instructions
You are running ticket-bridge with arguments: $ARGUMENTS
공통 Step 0 — mode·system 파싱
args = parse("$ARGUMENTS")
mode = args.get("--mode") # 없으면 fail loud
system = args.get("--system", "github") # 기본값 github
mode 미명시 시:
"--mode 미명시 — 사용 가능:
--mode pull|estimate|status. auto-run하지 않습니다."
--batch 플래그
--batch 있으면: 개별 확인 게이트를 건너뛰고 전체 목록을 한 번에 표시 후 일괄 승인 (1회)
| 모드 | --batch 없음 | --batch 있음 |
|---|---|---|
| estimate | 이슈별 코멘트 초안 개별 확인 | 전체 코멘트 목록 일괄 승인 1회 |
| status | 이슈별 코멘트 초안 개별 확인 | 전체 코멘트 목록 일괄 승인 1회 |
| push | 이슈별 생성 초안 개별 확인 | 전체 생성 목록 일괄 승인 1회 |
- 개별 항목 문구를 수정하고 싶다면 --batch 없이 실행
--batch 없으면 (기본): 이슈별 개별 확인 게이트 유지
--system 플래그 파싱 (결정론)
| --system 값 | 사용 도구 | 비고 |
|---|---|---|
github (기본) |
mcp__github__* |
기존 동작 |
linear |
mcp__linear__* |
Linear MCP 연결 필요 |
jira |
mcp__jira__* |
Jira MCP 연결 필요 |
지정한 --system의 MCP 도구 가용성을 먼저 확인한다. 미연결이면:
"지정한 --system의 MCP가 연결되지 않았습니다. (
--system linear→ Linear MCP,--system jira→ Jira MCP)"
--system 미명시 시 github 기본값 사용. 도구 이름만 바뀌며 이후 로직은 동일하다.
--system jira 사전 설정 가이드
--system jira를 사용하려면 Jira MCP 서버를 Claude Code에 등록해야 한다. Jira Cloud와 Jira Server(Data Center)는 인증 방식이 다르다.
Jira Cloud (Atlassian Cloud)
# 1. API Token 발급: https://id.atlassian.com/manage-profile/security/api-tokens
# 2. claude settings.json에 MCP 서버 등록
claude mcp add jira-cloud \
--transport sse \
--url https://mcp.atlassian.com/v1/sse
# Atlassian Remote MCP는 OAuth 2.0 브라우저 인증 — 토큰 파일 불필요
또는 로컬 MCP 래퍼를 사용하는 경우:
// ~/.claude/settings.json → mcpServers 항목에 추가
{
"mcpServers": {
"jira": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-jira"],
"env": {
"JIRA_BASE_URL": "https://yourcompany.atlassian.net",
"JIRA_EMAIL": "you@yourcompany.com",
"JIRA_API_TOKEN": "<API_TOKEN>"
}
}
}
}
Jira Server / Data Center (온프레미스)
// ~/.claude/settings.json → mcpServers 항목에 추가
{
"mcpServers": {
"jira": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-jira"],
"env": {
"JIRA_BASE_URL": "https://jira.internal.yourcompany.com",
"JIRA_PAT": "<PERSONAL_ACCESS_TOKEN>"
}
}
}
}
Jira Server/DC는 API Token 대신 PAT(Personal Access Token)을 사용한다. Jira 7.2+ 에서 지원.
Jira Cloud/Server 공통 필드 차이
ticket-bridge가 --system jira로 동작할 때 아래 Jira 전용 필드를 코멘트에 포함한다:
| 필드 | ticket-bridge 동작 |
|---|---|
story_points |
.track/predicted.json의 p50 값을 스토리 포인트 후보로 명시 (PM이 Jira 필드에 직접 입력, 자동 설정 0) |
sprint |
.track/implementation-plan.md의 sprint 컬럼 값을 인용 (Jira sprint 이름과 일치 여부는 PM 확인) |
issue_type |
pull 모드에서 Jira issuetype 필드 읽기 지원 (Bug / Story / Task / Sub-task) |
priority |
pull 모드에서 Jira priority 필드 읽기 지원 |
사내 Jira 커스텀 필드: 조직마다
customfield_10016등 커스텀 필드명이 다르다. ticket-bridge는 기본 필드만 지원하며, 커스텀 필드 매핑이 필요하면harness/ticket-map.json에"jira_custom_fields"키로 매핑 테이블을 수동 작성한다.
mode: pull
list_issues도구로 대상 이슈를 읽는다 (필드:number,title,body,labels[].name,state).- 각 이슈 body를 WBS 태스크 후보로 분해한다 (LLM 분류 — sprint WBS와 동일).
- 각 이슈의
size/*라벨 → complexity bucket (결정론 매핑).size/*0개 → 해당 이슈 skip, 나머지 계속 진행 (fallback complexity 3, 신뢰도 낮음 명시).
harness/ticket-import.md에 태스크 후보 + complexity + 원본 이슈 번호를 기록한다.harness/ticket-map.json에 태스크 ID ↔ 이슈 번호 매핑을 append (덮어쓰기 금지).- 다음 단계 안내: "
sprint --step plan harness/ticket-import.md로 추정치를 계산하세요. ticket-bridge는 추정을 계산하지 않습니다."
pull은 티켓 시스템에 아무것도 쓰지 않는다 (read-only).
Jira 커스텀 필드 반자동 제안 (--system jira)
pull 모드에서 이슈를 읽을 때:
mcp__jira__list_issues응답의fields객체에서customfield_*키를 결정론으로 추출 (json keys grep)harness/ticket-map.json에custom_fields섹션이 없으면:발견된 커스텀 필드 목록: - customfield_10016 (sprint) - customfield_10014 (epic_link) - customfield_10028 (story_points) harness/ticket-map.json의 custom_fields에 추가할까요? [y/N]- 승인 시 ticket-map.json에 append:
"custom_fields": { "sprint": "customfield_10016", "epic_link": "customfield_10014", "story_points": "customfield_10028" } - 이후 estimate/status에서 story_points 필드를 코멘트에 포함.
이미 custom_fields가 ticket-map에 있으면 이 단계를 건너뛴다.
mode: estimate
.track/predicted.json로드. 없으면 fail loud ("sprint --step plan먼저").harness/ticket-map.json으로 각 태스크 ↔ 이슈 번호 매칭.- 각 매칭 태스크에 대해 predicted.json의 p50/p90 값을 그대로 인용해 코멘트 본문을 만든다 (계산 금지, 전달만).
- 코멘트 형식:
<!-- hplan:ticket-bridge -->
## hplan estimate (T-003 ↔ #43)
- complexity: 3 (label `size/M`)
- LOC p50/p90: 76 / 106
- tokens p50/p90: <predicted 값 또는 N/A>
- minutes p50/p90: <predicted 값 또는 N/A>
- baseline_ref: <ISO> (trust_grade: B)
> 출처: .track/predicted.json — sprint --step plan 산출. ticket-bridge는 전달만 함.
- 확인 게이트: 작성할 코멘트 전문을 사용자에게 보여주고 명시적 승인을 받는다. 승인 전
add_comment호출 금지. - 승인 후
add_comment로 작성.<!-- hplan:ticket-bridge -->마커 + 같은 태스크 ID 코멘트가 이미 있으면 skip + 안내.
mode: status
.track/actual_log.jsonl로드. 없으면 fail loud ("sprint --step init먼저").harness/ticket-map.json으로 태스크 ↔ 이슈 매칭.- 각 태스크의 진척을 결정론 집계 (complete 이벤트 유무, 경과, 블로커 수). 수치 생성 금지 — actual_log 값 인용.
- ticket-map의 이슈 번호로
list_commits/pull_request_read를 조회해 관련 commit/PR 상태를 매칭 (정규식).
4-b. (선택) CI/CD 상태 수집:
commit/PR 매칭 후 해당 PR의 check run 결과를 조회한다:
mcp__github__get_pull_request_status → CI: ✅ PASS / ❌ FAIL / ⏳ PENDING
결과를 status 코멘트에 한 줄로 포함한다. (--system linear/jira에서는 이 단계를 skip한다.)
4-c. (선택) PR review 상태 수집:
mcp__github__get_pull_request_reviews → APPROVED / CHANGES_REQUESTED / PENDING
리뷰어 수와 상태를 status 코멘트에 포함한다. (--system linear/jira에서는 이 단계를 skip한다.)
- 진척 코멘트 본문을 만든다 (마커 포함).
- 확인 게이트: 코멘트 전문 + (선택)
--close플래그 시 상태 전환 후보를 사용자에게 보여주고 승인받는다. - 승인 후
add_comment.--close플래그가 있고 모든 매칭 태스크가 complete면 해당 이슈를 closed 상태로 전환 (본문/제목 편집 금지).
mode: push
sprint --step plan 또는 harness/implementation-plan.md의 WBS 태스크를 실제 이슈로 생성한다. pull의 반대 방향 — hplan에서 외부 트래커로 내보내기.
harness/implementation-plan.md또는harness/ticket-import.md로드. 없으면 fail loud.- 각 태스크에 대해 이슈 제목/설명 생성 (LLM — 자연어 생성).
- complexity bucket →
size/*라벨 결정론 역매핑 (T1의 lookup 역순):1 → size/XS, 2 → size/S, 3 → size/M, 4 → size/L, 5 → size/XL - 확인 게이트: 생성할 이슈 목록 전체를 보여주고 승인받는다. --batch 없으면 이슈별 개별 확인, --batch 있으면 전체 목록 1회 일괄 승인 (--batch 섹션 참조).
- 승인 후
create_issue(system별 도구)로 이슈 생성.ticket-map.json에 태스크 ID ↔ 이슈 번호 append. - 이미 ticket-map에 해당 태스크가 있으면 skip + 안내 (중복 생성 금지).
push는 create만 한다 — 기존 이슈 편집/삭제 금지.
Failure Handling
| 실패 상황 | 감지 | 대응 |
|---|---|---|
--mode 미명시 |
미입력 | fail loud + 모드 목록 |
| 지정 system MCP 미연결 | mcp__{system}__* 미등록/실패 |
fail loud — 대체 client 안 만듦, silent degrade 금지 |
size/* 라벨 0개 (pull/estimate) |
라벨 매핑 결과 없음 | 해당 이슈 skip, 나머지 진행 + fallback complexity 3 + "라벨 미설정 — p50/p90 신뢰도 낮음" 명시 (Rule 8). LLM 추론 금지 |
size/* 라벨 2개+ |
라벨 매핑 충돌 | fail loud + 충돌 라벨 나열. 임의 선택 금지 |
predicted.json 없음 (estimate) |
file not found | fail loud — "sprint --step plan 먼저, 전달할 추정치 없음" |
actual_log.jsonl 없음 (status) |
file not found | fail loud — "sprint --step init 먼저" |
| ticket-map 매핑 누락 | 태스크 ID 키 없음 | 해당 태스크 "매칭 불가" 표시, 나머지 진행 (부분 성공 명시) |
| write-back 권한 없음 | add_comment 403 |
fail loud — "issue write 권한 없음. 계산 결과는 stdout으로 출력하니 수동 첨부 가능" — 거짓 done 금지 |
| 중복 코멘트 | 마커+태스크 ID 존재 | skip + 안내 (덮어쓰기 금지) |
| 확인 게이트 거부 | 사용자 미승인 | write-back 취소, 본문은 stdout 보존 |
| create_issue 권한 없음 | 403 | fail loud — "issue create 권한 없음. 수동 생성 필요" — 생성 목록 stdout 출력 |
| 태스크 ticket-map 이미 존재 | 키 충돌 | skip + "이미 이슈 #N와 매핑됨" 안내 |
원칙: 계산 단계와 write-back 단계 분리. write-back이 실패/거부돼도 계산 결과는 stdout으로 surface — "complete"로 거짓 보고하지 않는다 (Rule 8).
Quality Gate
pull
- 티켓 시스템에 write 0회 (read-only)
- 모든 이슈 size/* → complexity 매핑됨 또는 skip+fallback 명시 (LLM 매핑 0)
-
harness/ticket-map.jsonappend (덮어쓰기 0)
estimate
- 모든 수치 = predicted.json 인용값 (계산 0)
- write-back 전 확인 게이트 통과
- 마커 + 중복 코멘트 감지 동작
status
- 진척 수치 = actual_log 인용 (생성 0)
- commit/PR 매칭 = 정규식 (LLM 0)
- CI/CD 상태 =
get_pull_request_status인용 (github 모드) - PR review 상태 =
get_pull_request_reviews인용 (github 모드) - write-back 전 확인 게이트 통과
push
- ticket-map에 이미 있는 태스크 skip (중복 생성 0)
- 이슈 생성 전 확인 게이트 통과
- size/* 라벨 역매핑 = 결정론 (LLM 0)
Examples
Good Example
입력: --mode pull #42 #43
기대 동작:
- 이슈 2개 read → body 분해 (LLM) → size/* → complexity (결정론)
harness/ticket-import.md+harness/ticket-map.json기록- 티켓 시스템 write 0회, "sprint --step plan으로 추정 계산" 안내
Good Example
입력: --mode pull #99 (size/* 라벨 없는 이슈)
기대 동작: "#99: size/* 라벨 없음 — skip하고 나머지 진행. fallback complexity 3 사용, estimate 신뢰도 낮음." 부분 성공 명시
Good Example
입력: --mode estimate
기대 동작:
- predicted.json + ticket-map 로드 → p50/p90 인용 코멘트 생성
- 코멘트 전문 표시 → 사용자 승인 → add_comment
Good Example
입력: --mode status --system linear
기대 동작:
- Linear MCP 가용성 확인 → mcp__linear__* 도구로 이슈 조회
- actual_log + ticket-map 기반 진척 집계 (CI/CD·PR review 단계는 skip — linear 모드)
- 확인 게이트 → mcp__linear__create_comment로 코멘트 작성
Bad Example
입력: --mode estimate (predicted.json 없음)
기대 동작: "predicted.json 없음 — sprint --step plan 먼저. ticket-bridge는 추정을 계산하지 않습니다." fail loud
Bad Example
입력: --mode pull #99 (size/* 라벨 2개: size/S, size/M)
기대 동작: "#99: size/* 라벨 충돌 (size/S, size/M) — 하나만 남기고 재시도하세요. 임의 선택하지 않습니다." fail loud