티켓 직접 발행하기

요구사항 접수하기는 하고 싶은 말을 그대로 넣고 pm이 쪼개 주기를 기다리는 길입니다. 이 장은 반대쪽입니다. 무엇을 시킬지 이미 알고 있을 때 쓰세요. 버그 하나, 오타 하나, 이미 잘게 갈라져 있는 일에는 해석할 것이 없습니다. 없는 해석을 하자고 pm 세션을 한 번 태울 이유도 없고요. 그럴 때는 티켓을 직접 씁니다.

밑에서 파일 하나가 만들어지는 것은 똑같습니다. 그 파일을 쓰는 손이 pm 세션이 아니라 폼일 뿐입니다.

발행 다이얼로그

보드 우상단에 버튼이 둘 있는데, 왼쪽이 티켓 발행입니다(오른쪽 primary는 요구 접수). 보드에서 ⌘I를 눌러도 열립니다. 이 키는 기본값이고 설정의 키맵에서 바꿉니다. 페이지가 아니라 다이얼로그라서 취소가 곧 닫기이고, 뒤에는 보드가 필터·검색·스크롤 그대로 남아 있습니다.

칸은 일곱입니다.

무엇
title필수. 한 줄 제목
kindselect — work · request · feedback
personaselect — personas/ 아래 프로필이 실제로 있는 이름만 뜹니다
우선순위select 1~5. 기본값 3
마감날짜와 시간. 비워두면 마감 없음입니다
deps기존 티켓 멀티셀렉트. 자유 입력 없음
본문## Goal · ## Done when 골격이 미리 채워져 나옵니다
  • 해시는 서버가 만듭니다. 0~9와 a~f로 된 여덟 글자(hex)를 뽑고 이미 있는 값이면 다시 뽑습니다. 파일 생성이 O_EXCL이라 같은 이름이 두 번 나올 일도 없고요. 사람이 해시를 타이핑할 자리는 이 화면 어디에도 없습니다.
  • deps에 자유 입력이 없는 것이 그 칸의 요점입니다. 손으로 해시를 적던 시절에는 오타 한 글자가 존재하지 않는 선행이 됐습니다. 없는 티켓은 영원히 완료되지 않으니, 그것을 기다리는 티켓도 영원히 대기합니다. 고르기만 할 수 있으면 그런 사고가 구조적으로 생기지 않습니다.
  • 발행에 성공하면 그 티켓 상세로 넘어갑니다. 그 이동이 다이얼로그를 닫습니다. 자기 티켓이 큐에 들어간 것을 그 자리에서 보게 됩니다.
  • 본문 아래에 첨부 손잡이가 있습니다. 이미지를 붙여넣거나 파일을 골라 올리면 바이트가 아니라 경로가 본문 끝에 붙습니다(도는 세션에 말 걸기의 파일 첨부와 같은 방식입니다).
  • 쓰다 만 것이 있으면 닫기를 한 번 막습니다. 쓰던 내용이 있습니다를 묻고, 버리고 닫기를 고를 때만 지웁니다. 발행이 실패해도 다이얼로그는 열린 채 사유만 그 안에 뜹니다. 닫는 바람에 쓴 글을 날리는 일은 없습니다.

## Done when을 쓰는 법

이 장에서 제일 중요한 자리입니다. ## Goal이 왜 필요한지와 무엇을 만드는지를 2~4줄로 적는 칸이라면, ## Done when은 끝났는지를 판정하는 문장입니다.

판정할 수 있는 문장은 "명령 X를 돌리면 Y가 나온다" 꼴입니다.

나쁜 예:

## Done when
- [ ] CSV 내보내기가 잘 동작한다
- [ ] 필터도 개선한다
markdown

좋은 예:

## Done when
- [ ] `/orders``CSV 내보내기` 버튼이 보인다
- [ ] 누르면 지금 화면의 필터가 적용된 행만 담긴 파일이 떨어진다
- [ ] 헤더 행의 컬럼명이 한글이다
markdown

"잘 동작한다"는 세션이 자기 손으로 판정할 수 없는 문장입니다. 그러면 세션은 그 항목을 못 켜거나, 근거 없이 켭니다. 어느 쪽이든 화면이 거짓말을 하게 됩니다. 세션은 항목을 하나 만족시킬 때마다 그 자리에서 - [ ]- [x]로 바꾸고, 도는 티켓을 열어 어디까지 갔는지 보는 자리가 정확히 그 체크박스입니다(화면 소개의 진행 표시). 검증할 수 있는 문장이어야 그 표시가 사실이 됩니다.

"필터도 개선한다"는 다른 문제입니다. 한 티켓에 두 가지가 들어 있습니다. 항목이 여섯 개를 넘으면 쪼갤 신호로 보세요. 티켓 하나는 한 세션(5~25분)이 리뷰할 수 있는 변경 하나여야 합니다. 화면 3개와 그 테스트가 필요하다면 그건 티켓 하나가 아니라 넷입니다.

kind — 티켓의 분류 넷

값은 넷인데 셋만 select에 있습니다. 넷째는 앱이 씁니다.

work — 지시입니다. 받은 페르소나가 수행하고 끝나면 완료로 넘어갑니다. 직접 발행하는 티켓은 대부분 이것입니다.

request — 부탁이나 질문입니다. 받은 사람은 답을 원본에 덧붙이지 않고 새 티켓으로 만듭니다. 원본은 질문의 기록으로 그대로 남습니다. 요구 접수가 세우는 티켓이 이 값에 persona: pm이 붙은 것입니다.

feedback — 결과보고나 지적입니다. 받은 사람은 조치가 필요하면 새 티켓을 만들고 아니면 그냥 끝냅니다.

answer — 답변 파일입니다. 선택지에는 없습니다. 답변 대기 티켓에 사람이 답을 쓰면 앱이 이 값으로 파일을 하나 만드는데, 태어날 때부터 완료입니다. 아무도 그것을 수행하지 않기 때문입니다(다음 장 티켓이 지나는 상태 참고).

엔진은 kind 값을 강제하지 않습니다. 발행 폼은 강제합니다. select 밖의 값이 넘어오면 사유를 붙여 거부합니다. 보드의 kind 필터도 이 값으로 걸립니다. 다음에 이 티켓을 읽는 사람이나 세션이 지시인지 질문인지 보고인지를 알아볼 단서는 이 한 줄뿐입니다. 비워두지 마세요.

persona — 이 일을 맡는 역할

고른 이름의 personas/<이름>/PROFILE.md 본문이 세션 프롬프트 머리에 그대로 인라인됩니다. 같은 일이라도 developer가 받으면 코드를 짜고 qa가 받으면 그 코드를 깨러 갑니다. 이 칸 하나가 그 차이를 만듭니다(페르소나 참고).

  • 페르소나는 워커가 아니라 티켓에 붙습니다. 아무 워커나 집어 가고 집은 워커가 그 티켓의 persona:대로 세션을 엽니다.
  • select에는 프로필이 실제로 있는 이름만 뜹니다. 이름만 있고 PROFILE.md가 없으면 엔진이 경고만 남기고 그냥 돌지만, 그런 이름은 새 티켓의 선택지가 되지 않습니다.
  • 비워도 됩니다. 워커가 열린 티켓 하나를 집어 세션을 여는 일을 디스패치라고 하는데, 페르소나가 비어 있어도 디스패치는 그대로 되고 그게 정상 경로입니다. 역할이 판단을 가르는 티켓에만 붙이세요.

deps — 없으면 착수가 불가능한 것에만

멀티셀렉트에서 고른 티켓이 전부 완료여야 이 티켓이 큐에 뜹니다. 하나라도 안 끝났으면 보드 대기 레인에 주황색 deps 태그를 달고 앉아 있고 그동안 어떤 워커도 집어가지 않습니다.

병렬로 돌 수 있는 일에는 걸지 마세요. 남발하면 워커가 놀고 큐가 직렬화됩니다. 워커가 여섯이어도 사슬로 엮인 티켓 여섯은 한 번에 하나씩 돕니다. 기준은 하나입니다. 그것이 안 끝나면 착수 자체가 불가능한가. "먼저 끝나 있으면 참고하기 좋다"는 deps가 아닙니다.

우선순위마감 — 큐에서 몇 번째로 뜨는가

deps가 착수할 수 있는지를 가르고 이 둘은 그다음 순서를 정합니다. 기본값은 3이고 숫자가 클수록 먼저 뜹니다. 같은 값끼리는 만든 순서입니다.

양 끝 둘만 동작이 다릅니다.

  • 1은 진행중 티켓이 0건일 때만 후보입니다. 다른 티켓이 하나라도 돌고 있으면 순서가 와도 건너뜁니다.
  • 5는 도는 세션 하나를 끊습니다. 남은 워커가 없으면 유효 우선순위가 가장 낮은 세션에 TERM을 보내고 그 자리를 가져갑니다. 끊긴 티켓은 열림으로 돌아가고 무슨 일이 있었는지가 자기 본문 ## 선점 절에 적힙니다. 세션은 이 값을 쓰지 않습니다. 사람만 씁니다.

선행은 자기를 기다리는 티켓의 값을 물려받습니다. 5짜리 티켓이 3짜리 선행에 막혀 있으면 그 선행이 5로 떠서 먼저 풀립니다. 그러지 않으면 5가 3 뒤에 섭니다. 물려받은 값은 매번 다시 세는 값이라 파일의 priority:를 고치지 않습니다.

마감은 그 순서를 시계로 덮습니다. 남은 시간이 5시간 이하면 5로, 7일 이상이면 1로 취급합니다. 마감이 이미 지난 것도 5입니다. 그 사이 구간에서는 적어둔 우선순위가 그대로 섭니다. 그러니 2주 뒤 마감을 적으면 처음 며칠은 오히려 뒤로 밀립니다. 급한 것을 앞으로 당기려면 우선순위를 올리세요. 마감은 언제까지인지를 적는 칸입니다.

선행의 마감이 이 티켓보다 늦으면 발행 버튼이 잠기고 어긋난 해시가 입력 아래에 뜹니다. 선행이 후행보다 늦게 끝날 수는 없으니까요.

복제 — 이미 있는 티켓에서 시작하기

티켓 상세 머리의 복제는 이 발행 다이얼로그를 원본 값으로 채워서 여는 버튼입니다. 새 화면이 아니라 같은 폼이라, 열린 자리에서 제목과 본문을 고쳐 발행하면 됩니다. 상태는 가리지 않습니다. 원본을 읽기만 하므로 진행중 티켓에서도 안전합니다.

넘어오는 값은 넷입니다. title · kind · persona · 본문 전문. 본문에는 워커가 붙인 ## 결과·## 블록도 그대로 딸려 옵니다. 절 이름으로 잘라내는 규칙은 멀쩡한 절까지 먹기 때문이고, 지우는 자리가 눈앞의 textarea입니다.

deps는 비운 채로 열립니다. 미충족 선행을 그대로 복제하면 사본이 조용히 대기하게 됩니다. 선후는 그때의 큐 상태에 달린 값이라 멀티셀렉트에서 다시 고르세요. 우선순위와 마감도 같은 이유로 안 물려받습니다. 기본값 3과 빈칸으로 열립니다. 엔진이 쓰는 키(session_id·owner·pid·awaiting…)도 넘어오지 않습니다. 새 티켓에 그 값이 들어가면 이미 할당된 것으로 보여 영원히 디스패치되지 않습니다.

고칠 수 있는 티켓, 못 고치는 티켓

발행한 티켓의 title·kind·persona·우선순위·마감·본문은 상세 화면에서 계속 고칠 수 있습니다. deps는 그 폼에 없습니다. 손으로 치는 칸을 두면 오타 해시로 영구 대기가 생기고 고를 목록을 세우는 자리는 아직 발행 다이얼로그 하나뿐입니다. 선후를 바꿔야 하면 복제해서 다시 고르거나 티켓 파일의 deps: 줄을 직접 고치세요. 상태에 따라 둘은 더 잠깁니다.

  • 완료 티켓은 읽기 전용입니다. 편집·삭제·할당 해제가 전부 막힙니다. .done 파일이 있다는 것 자체가 이 큐의 이력이라서 그렇습니다. 같은 일을 조건만 바꿔 다시 시키고 싶다면 그 자리가 위의 복제입니다.
  • 진행중 티켓은 할당을 푼 뒤에야 고칩니다. 세션이 지금 그 파일을 쥐고 쓰는 중이라, 밑에서 고치면 도는 세션의 작업을 밟습니다. 잠금 카드의 할당 해제를 누르면 티켓이 큐로 되돌아오고 그다음에 편집 폼이 열립니다.

나머지 frontmatter 키

다이얼로그가 묻는 일곱 칸이 사람이 쓰는 값의 전부입니다. 나머지 키는 디스패처와 엔진이 씁니다. 티켓 파일 맨 위 --- 두 줄 사이에 키: 값으로 적히는 그 머리말을 frontmatter라고 부릅니다. 키 하나하나의 뜻과 누가 언제 쓰는지는 frontmatter 필드에 표로 정리해 두었습니다. 여기서 다시 세우지 않습니다.

다음은 티켓이 지나는 상태입니다.