@angar2/taskery 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/bin/close.js +2 -2
- package/bin/fork.js +1 -0
- package/bin/lib.js +64 -44
- package/bin/mcp.js +3 -2
- package/bin/status.js +6 -3
- package/package.json +1 -1
- package/template/.claude/hooks/closed-immutable.sh +5 -3
- package/template/.codex/hooks/closed-immutable.sh +2 -2
- package/template/.project/rules/GIT_RULE.md +17 -17
- package/template/.project/rules/TASK_DOC_RULE.md +12 -10
- package/template/AGENTS.md +13 -13
- package/template/CLAUDE.md +13 -13
- package/template/shared/skills/add-backlog/SKILL.md +1 -1
- package/template/shared/skills/plan-init/SKILL.md +2 -2
- package/template/shared/skills/project-init/SKILL.md +2 -2
- package/template/shared/skills/task-close/SKILL.md +24 -22
- package/template/shared/skills/task-init/SKILL.md +7 -7
- package/template/shared/skills/task-plan/SKILL.md +2 -2
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ draft → planned → developing → developed → testing → tested → closed
|
|
|
39
39
|
| `developed` | 구현 완료 (자가 검증 통과) |
|
|
40
40
|
| `testing` | 독립 검증 진행 중 |
|
|
41
41
|
| `tested` | 독립 검증 완료 |
|
|
42
|
-
| `closed` | git 커밋 +
|
|
42
|
+
| `closed` | git 커밋 + 부모 브랜치 병합 완료 |
|
|
43
43
|
|
|
44
44
|
> 상태별 전이 조건과 task 문서 양식은 [TASK-DOC.md](https://github.com/angar2/taskery/blob/main/plan/TASK-DOC.md)에서 확인 가능
|
|
45
45
|
|
|
@@ -153,8 +153,8 @@ my-app/
|
|
|
153
153
|
같은 프로젝트에서 여러 메인 세션을 동시에 운영해 독립 task를 병렬로 진행한다.
|
|
154
154
|
|
|
155
155
|
- 새 task를 시작하면(`/task-init`) 작업 폴더(`~/.taskery/worktrees/<projectId>/TASK-NNN_<출처>_<슬러그>/`)와 작업 브랜치를 함께 분기한다.
|
|
156
|
-
- 작업 폴더에서 새 메인 세션을 열어 진행하거나, 메인 세션 1개로 모든 작업을 지휘하거나, 메인이 서브 세션을 호출해 병렬로 진행한다 — 운영 방식은 자유. 메인 워크트리(원본 폴더)는 `dev`
|
|
157
|
-
- task 완료(`/task-close`) 시 머지 락으로 직렬화한 후
|
|
156
|
+
- 작업 폴더에서 새 메인 세션을 열어 진행하거나, 메인 세션 1개로 모든 작업을 지휘하거나, 메인이 서브 세션을 호출해 병렬로 진행한다 — 운영 방식은 자유. 메인 워크트리(원본 폴더)는 task를 시작한 브랜치(*부모 브랜치*, 기본은 `dev`)를 그대로 유지한다.
|
|
157
|
+
- task 완료(`/task-close`) 시 머지 락으로 직렬화한 후 **그 부모 브랜치**에 `--no-ff` 병합한다(개인은 `dev`, 회사/로드맵은 서 있던 브랜치). 다른 세션이 먼저 머지해 충돌이 발생하면 본 세션이 단순/의미적/판단 불가 3단계로 해결을 시도한다.
|
|
158
158
|
- 머지 직후 작업 폴더와 작업 브랜치는 자동 정리된다(보존 키워드 사용 시 양쪽 유지). 안전망으로 복구 명령이 함께 출력된다.
|
|
159
159
|
|
|
160
160
|
요건: git ≥ 2.31. 보조 명령 `npx @angar2/taskery status` / `prune`로 진행 현황과 stale 정리를 확인한다.
|
|
@@ -189,7 +189,7 @@ my-app/
|
|
|
189
189
|
| `/task-plan` | task | task의 요구사항·범위·개발 계획·테스트 계획 작성 |
|
|
190
190
|
| `/task-dev` | task | 계획에 따른 단계별 구현 + 자가 검증 |
|
|
191
191
|
| `/task-test` | task | 별도 격리 세션에서 독립 검증 (메인 가정 차단) |
|
|
192
|
-
| `/task-close` | task | 최종 검증 후 git 커밋 +
|
|
192
|
+
| `/task-close` | task | 최종 검증 후 git 커밋 + 부모 브랜치 `--no-ff` 병합 |
|
|
193
193
|
| `/add-backlog` | meta | 사용자 발화로 plan(기능 그룹)별 `tasks/<NNN_slug>/BACKLOG.md`에 task 후보 1건씩 추가 (0.1.2+) |
|
|
194
194
|
| `/log-friction` | meta | 사용자 불편을 `.project/FRICTION_LOG.md`에 한 행 기록 |
|
|
195
195
|
| `/run-team` | meta | 여러 작업을 묶을 수 있는 단위로 task로 나눠 자동 병렬 처리 — 리더 세션이 팀원(독립 세션)에게 task를 1건씩 분배하고 흐름을 관리 (Claude 전용 · 실험 기능 전제 · 0.3.x+) |
|
|
@@ -240,7 +240,7 @@ npx @angar2/taskery init
|
|
|
240
240
|
/task-plan TASK-001 # draft → planned : 요구사항·범위·개발 계획·테스트 계획 작성
|
|
241
241
|
/task-dev TASK-001 # planned → developed : 단계별 구현과 자체 검증
|
|
242
242
|
/task-test TASK-001 # developed → tested : 별도 격리 세션으로 독립 검증
|
|
243
|
-
/task-close TASK-001 # tested → closed : 최종 검증 후 git 커밋과
|
|
243
|
+
/task-close TASK-001 # tested → closed : 최종 검증 후 git 커밋과 부모 브랜치 병합
|
|
244
244
|
|
|
245
245
|
# 불편 발생 시 등록
|
|
246
246
|
/log-friction # 사용자 불편 한 행 기록
|
package/bin/close.js
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* `npx @angar2/taskery close <TASK-NNN>` — close 결정적 준비.
|
|
6
6
|
*
|
|
7
7
|
* 게이트(status=tested) → Phase 커밋(D2 자동 매핑) → status=closed → flows/문서 커밋 → 추적마커까지
|
|
8
|
-
* 코드가 수행하고 멈춘다. 이후 비가역(사전 rebase+충돌해결 / 머지 락 /
|
|
8
|
+
* 코드가 수행하고 멈춘다. 이후 비가역(사전 rebase+충돌해결 / 머지 락 / 부모 --no-ff 머지 /
|
|
9
9
|
* worktree remove / branch -d)은 스킬(task-close)이 git-guard hook + 복구출력 보호 하에 수행.
|
|
10
10
|
*
|
|
11
|
-
* stdout: { prepped, branch, wtPath, registered, commits,
|
|
11
|
+
* stdout: { prepped, branch, parent, wtPath, registered, commits, aheadOfParent }. parent = 머지 대상. exit 0.
|
|
12
12
|
* 콜백(스킬이 LLM 판단 후 처리):
|
|
13
13
|
* - exit 3 + { blocked:'status', current } — status가 tested 아님 (게이트 차단)
|
|
14
14
|
* - exit 2 + { blocked:'mapping', files, phases, reason } — Phase↔파일 매핑 모호 (수동 커밋 필요)
|
package/bin/fork.js
CHANGED
package/bin/lib.js
CHANGED
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
* - getInitLockPath(projectId): ~/.taskery/<projectId>.init.lock (task 분기 직렬화용)
|
|
23
23
|
* - withMergeLock(projectId, fn, opts): proper-lockfile로 머지 락 + fn 실행 + 자동 release
|
|
24
24
|
* - withMetaLock(filePath, fn, opts): 메타 파일 쓰기 락 + fn 실행
|
|
25
|
-
* - getActiveTasks(mainWtPath): SSoT 조회 (git branch --
|
|
26
|
-
* - getNextTaskNumber(mainWtPath): (진행중 ∪
|
|
27
|
-
* - forkTask(mainWtPath, {type,dev,src,slug}): init 락 안에서
|
|
28
|
-
* -
|
|
29
|
-
* -
|
|
25
|
+
* - getActiveTasks(mainWtPath): SSoT 조회 (git branch --list 'feature/*_TASK-*' ...)
|
|
26
|
+
* - getNextTaskNumber(mainWtPath): (진행중 ∪ 전 ref 머지 히스토리) 최대 + 1 (부모 무관 전역 채번)
|
|
27
|
+
* - forkTask(mainWtPath, {type,dev,src,slug}): init 락 안에서 부모(현재 브랜치) 캡처+채번+워크트리·브랜치 생성 원자 실행
|
|
28
|
+
* - currentBranch(mainWtPath): 메인 워크트리 현재 브랜치 = 부모 브랜치(detached면 'HEAD')
|
|
29
|
+
* - assertMainWorktreeOn(mainWtPath, branch): 메인 워크트리가 지정 브랜치 체크아웃 상태인지 검증
|
|
30
30
|
*
|
|
31
31
|
* 백로그 (0.1.2+):
|
|
32
32
|
* - getActiveVersion(mainWtPath): .project/AGENT-GUIDE.md에서 활성 plan 식별자(버전 또는 기능 그룹명) 추출
|
|
@@ -308,17 +308,15 @@ async function withMetaLock(filePath, fn, opts = {}) {
|
|
|
308
308
|
}
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
-
function
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
throw new Error('dev 브랜치 부재. 사용자 결정 필요 (생성?).');
|
|
315
|
-
}
|
|
311
|
+
function currentBranch(mainWtPath) {
|
|
312
|
+
// 메인 워크트리의 현재 브랜치 = 태스크의 부모 브랜치(0.6.0). detached HEAD면 'HEAD' 반환.
|
|
313
|
+
return gitCapture(mainWtPath, ['rev-parse', '--abbrev-ref', 'HEAD']);
|
|
316
314
|
}
|
|
317
315
|
|
|
318
|
-
function
|
|
319
|
-
const current =
|
|
320
|
-
if (current !==
|
|
321
|
-
throw new Error(`메인 워크트리가
|
|
316
|
+
function assertMainWorktreeOn(mainWtPath, branch) {
|
|
317
|
+
const current = currentBranch(mainWtPath);
|
|
318
|
+
if (current !== branch) {
|
|
319
|
+
throw new Error(`메인 워크트리가 ${branch} 아님 (현재: ${current}). 부모 브랜치로 체크아웃 필요.`);
|
|
322
320
|
}
|
|
323
321
|
}
|
|
324
322
|
|
|
@@ -341,8 +339,8 @@ function parseBranchName(branch) {
|
|
|
341
339
|
}
|
|
342
340
|
|
|
343
341
|
function getActiveTasks(mainWtPath) {
|
|
344
|
-
// SSoT — TASK-* 패턴 작업 브랜치 (분기 직후 빈 브랜치 포함). `--no-merged
|
|
345
|
-
// 워크트리 분기 직후 브랜치는
|
|
342
|
+
// SSoT — TASK-* 패턴 작업 브랜치 (분기 직후 빈 브랜치 포함). `--no-merged <부모>` 미사용:
|
|
343
|
+
// 워크트리 분기 직후 브랜치는 부모와 동일 commit이라 `--no-merged`가 *완전 머지 상태*로
|
|
346
344
|
// 처리 → 빈 결과 → getNextTaskNumber 충돌. 브랜치 존재 자체가 SSoT (정상 흐름은 task-close
|
|
347
345
|
// 자동 삭제로 자연 정리, 보존 키워드 잔존분도 *진행중*으로 잡힘이 정합).
|
|
348
346
|
const out = gitCapture(mainWtPath, ['branch', '--list', ...BRANCH_PATTERNS]);
|
|
@@ -358,12 +356,14 @@ function getActiveTasks(mainWtPath) {
|
|
|
358
356
|
function getNextTaskNumber(mainWtPath) {
|
|
359
357
|
// 1. 진행중 (SSoT)
|
|
360
358
|
const activeNums = getActiveTasks(mainWtPath).map((t) => t.taskNum);
|
|
361
|
-
// 2.
|
|
362
|
-
//
|
|
363
|
-
//
|
|
359
|
+
// 2. 머지 히스토리 (GIT_RULE 풍부 메시지) — 모든 ref(`--all`) 스캔.
|
|
360
|
+
// 부모 브랜치가 dev로 고정되지 않으므로(0.6.0) 닫힌 태스크는 각자의 부모에 병합돼 있다.
|
|
361
|
+
// 특정 부모(과거 'dev') 하나만 grep하면 다른 부모로 닫힌 번호를 놓쳐 채번 충돌 → `--all`로
|
|
362
|
+
// 전 ref의 TASK-NNN 최대를 취해 번호를 리포 전역으로 유지.
|
|
363
|
+
// --extended-regexp(ERE)에서 `+`는 수량자 — `\+`는 *리터럴 +* 라 "TASK-001"을 못 잡는다.
|
|
364
364
|
const log = gitCapture(mainWtPath, [
|
|
365
365
|
'log',
|
|
366
|
-
'
|
|
366
|
+
'--all',
|
|
367
367
|
'--grep',
|
|
368
368
|
'TASK-[0-9]+',
|
|
369
369
|
'--extended-regexp',
|
|
@@ -383,8 +383,11 @@ async function forkTask(mainWtPath, { type, dev, src, slug }) {
|
|
|
383
383
|
// getNextTaskNumber 직후 worktree add까지 끝내므로 다음 호출은 늘어난 번호를 본다.
|
|
384
384
|
const projectId = getProjectId(mainWtPath);
|
|
385
385
|
return withMetaLock(getInitLockPath(projectId), () => {
|
|
386
|
-
|
|
387
|
-
|
|
386
|
+
// 부모 = 메인 워크트리의 현재 브랜치(0.6.0). dev 하드코딩 제거 — 서 있는 브랜치가 곧 부모.
|
|
387
|
+
const parent = currentBranch(mainWtPath);
|
|
388
|
+
if (parent === 'HEAD') {
|
|
389
|
+
throw new Error('메인 워크트리가 detached HEAD — 부모로 삼을 브랜치에 체크아웃 필요.');
|
|
390
|
+
}
|
|
388
391
|
// SSoT 안전망 — 같은 출처(BL-NNN/RM-NNN)가 이미 진행중이면 거부. DR은 별도 ID 없어 검사 제외.
|
|
389
392
|
if (src !== 'DR') {
|
|
390
393
|
const dup = getActiveTasks(mainWtPath).find((t) => t.src === src);
|
|
@@ -397,11 +400,12 @@ async function forkTask(mainWtPath, { type, dev, src, slug }) {
|
|
|
397
400
|
const branch = `${type}/${dev}_TASK-${nnn}_${src}_${slug}`;
|
|
398
401
|
const wtPath = getWorktreePath(projectId, { taskNum, src, slug });
|
|
399
402
|
mkdirp(getWorktreesRoot(projectId));
|
|
400
|
-
// 동일 브랜치명은 git이 거부(같은 항목 동시 분기
|
|
401
|
-
|
|
403
|
+
// 부모 브랜치를 base로 워크트리·브랜치 생성. 동일 브랜치명은 git이 거부(같은 항목 동시 분기
|
|
404
|
+
// 차단). 실패 시 throw → fork.js가 사용자 보고.
|
|
405
|
+
execFileSync('git', ['-C', mainWtPath, 'worktree', 'add', wtPath, '-b', branch, parent], {
|
|
402
406
|
stdio: 'pipe',
|
|
403
407
|
});
|
|
404
|
-
return { taskNum, nnn, branch, wtPath, projectId };
|
|
408
|
+
return { taskNum, nnn, branch, wtPath, projectId, parent };
|
|
405
409
|
});
|
|
406
410
|
}
|
|
407
411
|
|
|
@@ -593,7 +597,7 @@ async function markBacklogChecked(mainWtPath, blId, taskNum) {
|
|
|
593
597
|
}
|
|
594
598
|
|
|
595
599
|
// ─── task 헤더 / 상태 전이 (코드화) ─────────────────────────────────
|
|
596
|
-
// 헤더 5컬럼
|
|
600
|
+
// 헤더 파싱(6컬럼 · 구 5컬럼 폴백) · task 문서 위치 판정 · 7상태 유효전이 검증을 코드로 단일화.
|
|
597
601
|
// 계약: TASK_DOC_RULE §1.1(헤더) · §1.2(7상태) · §1.5(문서 위치).
|
|
598
602
|
|
|
599
603
|
// 7×7 유효전이표 (TASK_DOC_RULE §1.2 + FAIL/UNCERTAIN 분기). from → 허용 to 목록.
|
|
@@ -639,7 +643,10 @@ function _locateHeaderRow(content) {
|
|
|
639
643
|
}
|
|
640
644
|
|
|
641
645
|
function parseTaskHeader(taskMdPath) {
|
|
642
|
-
//
|
|
646
|
+
// 헤더를 구조화 → { created, plan, type, size, parent, status }. 표 부재/컬럼 부족 시 throw.
|
|
647
|
+
// 6컬럼(0.6.0+): 생성일·플랜·유형·규모·부모 브랜치·상태. 부모 브랜치는 상태 *앞* — 상태를
|
|
648
|
+
// 항상 마지막 셀로 유지해 setStatus의 `length-2` 로직이 구·신 양쪽에서 성립하게 함.
|
|
649
|
+
// 5컬럼(구): 부모 칸 부재 → parent='dev' 폴백(0.6.0 이전 문서 하위호환).
|
|
643
650
|
const content = fs.readFileSync(taskMdPath, 'utf8');
|
|
644
651
|
const loc = _locateHeaderRow(content);
|
|
645
652
|
if (!loc) throw new Error(`헤더 표 파싱 실패 (TASK_DOC_RULE §1.1 형식 아님): ${taskMdPath}`);
|
|
@@ -647,13 +654,17 @@ function parseTaskHeader(taskMdPath) {
|
|
|
647
654
|
if (inner.length < 5) {
|
|
648
655
|
throw new Error(`헤더 표 컬럼 부족(${inner.length}/5): ${taskMdPath}`);
|
|
649
656
|
}
|
|
657
|
+
if (inner.length >= 6) {
|
|
658
|
+
const [created, plan, type, size, parent, status] = inner;
|
|
659
|
+
return { created, plan, type, size, parent, status };
|
|
660
|
+
}
|
|
650
661
|
const [created, plan, type, size, status] = inner;
|
|
651
|
-
return { created, plan, type, size, status };
|
|
662
|
+
return { created, plan, type, size, parent: 'dev', status };
|
|
652
663
|
}
|
|
653
664
|
|
|
654
665
|
function isProjectRegistered(mainWtPath) {
|
|
655
666
|
// .gitignore에 .project/ 등록 여부 — check-ignore exit 0 = 등록(퍼블릭 default, 문서는 메인WT 공유),
|
|
656
|
-
// exit 1 = 미등록(문서는 워크트리 안, 머지로
|
|
667
|
+
// exit 1 = 미등록(문서는 워크트리 안, 머지로 부모 반영). task-init Step7 분기와 동일 판정.
|
|
657
668
|
try {
|
|
658
669
|
execFileSync(
|
|
659
670
|
'git',
|
|
@@ -719,7 +730,8 @@ async function setStatus(taskMdPath, next) {
|
|
|
719
730
|
const content = fs.readFileSync(taskMdPath, 'utf8');
|
|
720
731
|
const loc = _locateHeaderRow(content);
|
|
721
732
|
if (!loc) throw new Error(`헤더 표 파싱 실패: ${taskMdPath}`);
|
|
722
|
-
const
|
|
733
|
+
const trimmed = loc.cells.slice(1, -1).map((c) => c.trim());
|
|
734
|
+
const cur = trimmed[trimmed.length - 1]; // 상태 = 항상 마지막 셀 (6컬럼: 부모 브랜치가 그 앞)
|
|
723
735
|
if (cur === next) return { changed: false, from: cur, to: next, path: taskMdPath };
|
|
724
736
|
const allowed = TRANSITIONS[cur] || [];
|
|
725
737
|
if (!allowed.includes(next)) {
|
|
@@ -767,14 +779,14 @@ function _buildTaskDocPath(base, plan, { taskNum, slug, promoted }) {
|
|
|
767
779
|
return { dir: planDir, file: path.join(planDir, `${nnn}_${slug}.md`) };
|
|
768
780
|
}
|
|
769
781
|
|
|
770
|
-
function _renderTaskSkeleton({ taskNum, title, created, plan, docType, size }) {
|
|
782
|
+
function _renderTaskSkeleton({ taskNum, title, created, plan, docType, size, parent }) {
|
|
771
783
|
const nnn = String(taskNum).padStart(3, '0');
|
|
772
784
|
return [
|
|
773
785
|
`# TASK-${nnn} — ${title}`,
|
|
774
786
|
'',
|
|
775
|
-
'| 생성일 | 플랜 | 유형 | 규모 | 상태 |',
|
|
776
|
-
'
|
|
777
|
-
`| ${created} | ${plan} | ${docType} | ${size} | draft |`,
|
|
787
|
+
'| 생성일 | 플랜 | 유형 | 규모 | 부모 브랜치 | 상태 |',
|
|
788
|
+
'|--------|------|------|------|-------------|------|',
|
|
789
|
+
`| ${created} | ${plan} | ${docType} | ${size} | ${parent} | draft |`,
|
|
778
790
|
'',
|
|
779
791
|
'## Requirements',
|
|
780
792
|
'',
|
|
@@ -800,14 +812,16 @@ function _renderTaskSkeleton({ taskNum, title, created, plan, docType, size }) {
|
|
|
800
812
|
}
|
|
801
813
|
|
|
802
814
|
function scaffoldTaskDoc(mainWtPath, wtPath, meta) {
|
|
803
|
-
// meta: { taskNum, slug, title, type(브랜치 타입), size, plan?, promoted?, created? }
|
|
815
|
+
// meta: { taskNum, slug, title, type(브랜치 타입), size, parent, plan?, promoted?, created? }
|
|
804
816
|
// 위치: 등록(.gitignore) 케이스 → 메인WT, 미등록 → 워크트리. plan 미지정 시 AGENT-GUIDE에서 검출.
|
|
817
|
+
// parent 미지정 시 현재 브랜치 캡처(단독 호출 안전망 — 통상 fork가 넘김).
|
|
805
818
|
// taskNum이 고유(fork init 락 채번)라 동일 문서 동시 쓰기 경합 없음 → 락 불요.
|
|
806
819
|
const registered = isProjectRegistered(mainWtPath);
|
|
807
820
|
const base = registered ? mainWtPath : wtPath;
|
|
808
821
|
const plan = meta.plan || getActiveVersion(mainWtPath);
|
|
809
822
|
const docType = DOC_TYPE_OF[meta.type] || meta.type;
|
|
810
823
|
const created = meta.created || _todayLocal();
|
|
824
|
+
const parent = meta.parent || currentBranch(mainWtPath);
|
|
811
825
|
const { dir, file } = _buildTaskDocPath(base, plan, {
|
|
812
826
|
taskNum: meta.taskNum,
|
|
813
827
|
slug: meta.slug,
|
|
@@ -826,6 +840,7 @@ function scaffoldTaskDoc(mainWtPath, wtPath, meta) {
|
|
|
826
840
|
plan,
|
|
827
841
|
docType,
|
|
828
842
|
size: meta.size,
|
|
843
|
+
parent,
|
|
829
844
|
}),
|
|
830
845
|
);
|
|
831
846
|
return { registered, promoted: !!meta.promoted, plan, file };
|
|
@@ -934,7 +949,7 @@ function initPlan(mainWtPath, slug, opts = {}) {
|
|
|
934
949
|
|
|
935
950
|
// ─── task close 결정적 준비 (코드화, D1·D2) ─────────────────────────
|
|
936
951
|
// closeTask는 *결정적 준비만* 수행: 게이트(status=tested) → Phase 커밋(D2) → status=closed →
|
|
937
|
-
// flows/문서 커밋 → 추적마커. 비가역 3종(
|
|
952
|
+
// flows/문서 커밋 → 추적마커. 비가역 3종(부모 브랜치 --no-ff 머지 · worktree remove · branch -d)과
|
|
938
953
|
// 충돌 해결·머지 락은 스킬(LLM)이 수행 — 위험 최소화 + 충돌은 LLM 판단 영역.
|
|
939
954
|
|
|
940
955
|
// 브랜치 타입 → 커밋 태그 (GIT_RULE 6-3).
|
|
@@ -1051,18 +1066,22 @@ function splitUncommittedByPhase(wtPath, taskMdPath, meta) {
|
|
|
1051
1066
|
|
|
1052
1067
|
async function closeTask(mainWtPath, { taskNum }, opts = {}) {
|
|
1053
1068
|
// 결정적 준비만 — 비가역(머지·정리)·충돌 해결은 스킬이 후속 수행.
|
|
1054
|
-
// 반환: { blocked } (게이트 미충족/매핑 모호) 또는
|
|
1069
|
+
// 반환: { blocked } (게이트 미충족/매핑 모호) 또는
|
|
1070
|
+
// { prepped, branch, parent, wtPath, registered, commits, aheadOfParent }. parent = 스킬 머지 대상.
|
|
1055
1071
|
const active = getActiveTasks(mainWtPath).find((t) => t.taskNum === taskNum);
|
|
1056
1072
|
if (!active) {
|
|
1057
1073
|
throw new Error(`TASK-${String(taskNum).padStart(3, '0')} 진행중 브랜치 없음 (SSoT 조회).`);
|
|
1058
1074
|
}
|
|
1059
|
-
assertMainWorktreeOnDev(mainWtPath);
|
|
1060
1075
|
const projectId = getProjectId(mainWtPath);
|
|
1061
1076
|
const wtPath = getWorktreePath(projectId, { taskNum, src: active.src, slug: active.slug });
|
|
1062
1077
|
const resolved = resolveTaskDocPath(mainWtPath, wtPath, { taskNum });
|
|
1063
1078
|
if (!resolved) throw new Error(`TASK-${taskNum} 문서 못 찾음.`);
|
|
1064
1079
|
|
|
1065
1080
|
const header = parseTaskHeader(resolved.file);
|
|
1081
|
+
// 부모 = 헤더 기록값(0.6.0, 구 문서는 'dev' 폴백). 메인 워크트리가 그 부모에 서 있어야
|
|
1082
|
+
// 스킬 후속 머지가 올바른 브랜치로 간다.
|
|
1083
|
+
const parent = header.parent;
|
|
1084
|
+
assertMainWorktreeOn(mainWtPath, parent);
|
|
1066
1085
|
if (header.status !== 'tested') {
|
|
1067
1086
|
return { blocked: 'status', current: header.status, docPath: resolved.file };
|
|
1068
1087
|
}
|
|
@@ -1114,9 +1133,9 @@ async function closeTask(mainWtPath, { taskNum }, opts = {}) {
|
|
|
1114
1133
|
}
|
|
1115
1134
|
}
|
|
1116
1135
|
|
|
1117
|
-
// 추적 마커 빈커밋 (
|
|
1136
|
+
// 추적 마커 빈커밋 (부모보다 앞선 커밋 0개 — docs/분석 task + .project gitignore 케이스)
|
|
1118
1137
|
const ahead = parseInt(
|
|
1119
|
-
gitCapture(wtPath, ['rev-list', '--count',
|
|
1138
|
+
gitCapture(wtPath, ['rev-list', '--count', `${parent}..HEAD`], { allowFail: true }) || '0',
|
|
1120
1139
|
10,
|
|
1121
1140
|
);
|
|
1122
1141
|
if (ahead === 0) {
|
|
@@ -1136,18 +1155,19 @@ async function closeTask(mainWtPath, { taskNum }, opts = {}) {
|
|
|
1136
1155
|
}
|
|
1137
1156
|
|
|
1138
1157
|
const aheadFinal = parseInt(
|
|
1139
|
-
gitCapture(wtPath, ['rev-list', '--count',
|
|
1158
|
+
gitCapture(wtPath, ['rev-list', '--count', `${parent}..HEAD`], { allowFail: true }) || '0',
|
|
1140
1159
|
10,
|
|
1141
1160
|
);
|
|
1142
1161
|
return {
|
|
1143
1162
|
prepped: true,
|
|
1144
1163
|
taskNum,
|
|
1145
1164
|
branch: active.branch,
|
|
1165
|
+
parent,
|
|
1146
1166
|
wtPath,
|
|
1147
1167
|
registered: resolved.registered,
|
|
1148
1168
|
docPath: resolved.file,
|
|
1149
1169
|
commits,
|
|
1150
|
-
|
|
1170
|
+
aheadOfParent: aheadFinal,
|
|
1151
1171
|
};
|
|
1152
1172
|
}
|
|
1153
1173
|
|
|
@@ -1187,8 +1207,8 @@ module.exports = {
|
|
|
1187
1207
|
getInitLockPath,
|
|
1188
1208
|
withMergeLock,
|
|
1189
1209
|
withMetaLock,
|
|
1190
|
-
|
|
1191
|
-
|
|
1210
|
+
currentBranch,
|
|
1211
|
+
assertMainWorktreeOn,
|
|
1192
1212
|
parseBranchName,
|
|
1193
1213
|
getActiveTasks,
|
|
1194
1214
|
getNextTaskNumber,
|
package/bin/mcp.js
CHANGED
|
@@ -213,6 +213,7 @@ server.registerTool(
|
|
|
213
213
|
title: a.title,
|
|
214
214
|
type: a.type,
|
|
215
215
|
size: a.size,
|
|
216
|
+
parent: result.parent,
|
|
216
217
|
promoted: !!a.promote,
|
|
217
218
|
});
|
|
218
219
|
out = { ...result, docPath: doc.file, registered: doc.registered, promoted: doc.promoted };
|
|
@@ -228,7 +229,7 @@ server.registerTool(
|
|
|
228
229
|
'task_close',
|
|
229
230
|
{
|
|
230
231
|
description:
|
|
231
|
-
'close 결정적 준비만 — Phase 커밋·status=closed·문서 커밋·추적마커. 게이트(status=tested)·매핑 모호 시 blocked 반환. 비가역(
|
|
232
|
+
'close 결정적 준비만 — Phase 커밋·status=closed·문서 커밋·추적마커. 게이트(status=tested)·매핑 모호 시 blocked 반환. 반환 parent = 스킬 머지 대상 부모 브랜치. 비가역(부모 --no-ff 머지·worktree/branch 정리·충돌 해결)은 스킬(task-close)이 후속 수행.',
|
|
232
233
|
inputSchema: { task: z.union([z.string(), z.number()]).describe('TASK-NNN 또는 번호') },
|
|
233
234
|
},
|
|
234
235
|
handler(async (a) => {
|
|
@@ -245,7 +246,7 @@ server.registerTool(
|
|
|
245
246
|
`Phase↔파일 매핑 모호 (${result.reason}) — Dev Plan '파일' 필드 확인 후 수동 Phase 커밋 → task_close 재호출.`,
|
|
246
247
|
);
|
|
247
248
|
}
|
|
248
|
-
return ok(result, `prep
|
|
249
|
+
return ok(result, `prep 완료 (부모=${result.parent}). 비가역(사전 rebase·충돌해결·머지 락·부모 --no-ff 머지·정리)은 task-close 스킬이 후속 수행.`);
|
|
249
250
|
}),
|
|
250
251
|
);
|
|
251
252
|
|
package/bin/status.js
CHANGED
|
@@ -56,11 +56,14 @@ function main() {
|
|
|
56
56
|
const lockTimeoutMs = manifest.lock_timeout_ms ?? DEFAULT_LOCK_TIMEOUT_MS;
|
|
57
57
|
|
|
58
58
|
const currentBranch = gitCapture(mainWt, ['rev-parse', '--abbrev-ref', 'HEAD']);
|
|
59
|
-
const
|
|
59
|
+
const detached = currentBranch === 'HEAD';
|
|
60
60
|
|
|
61
61
|
console.log(`\ntaskery status\n`);
|
|
62
62
|
console.log(`메인 워크트리: ${mainWt}`);
|
|
63
|
-
|
|
63
|
+
// 현재 브랜치 = 새 태스크의 부모 브랜치(0.6.0). detached HEAD면 fork 불가.
|
|
64
|
+
console.log(
|
|
65
|
+
`현재 브랜치(= 새 태스크 부모): ${currentBranch}${detached ? ' ⚠ (detached HEAD — 브랜치 체크아웃 필요)' : ''}`,
|
|
66
|
+
);
|
|
64
67
|
console.log(`projectId: ${projectId}`);
|
|
65
68
|
console.log(`stale_days: ${staleDays} / lock_timeout_ms: ${lockTimeoutMs}\n`);
|
|
66
69
|
|
|
@@ -126,7 +129,7 @@ function main() {
|
|
|
126
129
|
);
|
|
127
130
|
const orphans = folders.filter((f) => !activeFolders.has(f));
|
|
128
131
|
if (orphans.length > 0) {
|
|
129
|
-
console.log(`⚠ SSoT에 없는 워크트리 폴더 ${orphans.length}건 (이미
|
|
132
|
+
console.log(`⚠ SSoT에 없는 워크트리 폴더 ${orphans.length}건 (이미 부모 머지된 잔존?):`);
|
|
130
133
|
for (const o of orphans) {
|
|
131
134
|
console.log(` ${path.join(worktreesRoot, o)}`);
|
|
132
135
|
}
|
package/package.json
CHANGED
|
@@ -48,7 +48,9 @@ if [ ! -f "$TASK_MD" ]; then
|
|
|
48
48
|
exit 0
|
|
49
49
|
fi
|
|
50
50
|
|
|
51
|
-
# 헤더 status 추출
|
|
51
|
+
# 헤더 status 추출 — status는 헤더 표의 *마지막* 셀.
|
|
52
|
+
# 5컬럼(구): 생성일 / 플랜 / 유형 / 규모 / 상태
|
|
53
|
+
# 6컬럼(0.6.0+): 생성일 / 플랜 / 유형 / 규모 / 부모 브랜치 / 상태
|
|
52
54
|
# python3 정규식 사용 (mac BSD grep -P 미지원)
|
|
53
55
|
STATUS=$(TM="$TASK_MD" python3 -c "
|
|
54
56
|
import os, re
|
|
@@ -59,8 +61,8 @@ except Exception:
|
|
|
59
61
|
raise SystemExit(0)
|
|
60
62
|
# 7-state whitelist
|
|
61
63
|
WL = r'(draft|planned|developing|developed|testing|tested|closed)'
|
|
62
|
-
#
|
|
63
|
-
m = re.search(rf'^\|[^|]*\|
|
|
64
|
+
# status = 마지막 셀. 선행 셀 4개(구 5컬럼) 또는 5개(신 6컬럼) 뒤에 온다.
|
|
65
|
+
m = re.search(rf'^\|(?:[^|]*\|){{4,5}}\s*{WL}\s*\|\s*\$', content, re.MULTILINE)
|
|
64
66
|
print(m.group(1) if m else '')
|
|
65
67
|
" 2>/dev/null)
|
|
66
68
|
|
|
@@ -42,7 +42,7 @@ while IFS= read -r FILE_PATH; do
|
|
|
42
42
|
# 존재하는 파일만 (Add File 신규 생성은 통과)
|
|
43
43
|
[ -f "$FILE_PATH" ] || continue
|
|
44
44
|
|
|
45
|
-
# 헤더 status 추출
|
|
45
|
+
# 헤더 status 추출 — status는 헤더 표의 *마지막* 셀 (5컬럼 구 / 6컬럼 0.6.0+ 양쪽).
|
|
46
46
|
STATUS=$(TM="$FILE_PATH" python3 -c "
|
|
47
47
|
import os, re
|
|
48
48
|
try:
|
|
@@ -51,7 +51,7 @@ except Exception:
|
|
|
51
51
|
print('')
|
|
52
52
|
raise SystemExit(0)
|
|
53
53
|
WL = r'(draft|planned|developing|developed|testing|tested|closed)'
|
|
54
|
-
m = re.search(rf'^\|[^|]*\|
|
|
54
|
+
m = re.search(rf'^\|(?:[^|]*\|){{4,5}}\s*{WL}\s*\|\s*\$', content, re.MULTILINE)
|
|
55
55
|
print(m.group(1) if m else '')
|
|
56
56
|
" 2>/dev/null)
|
|
57
57
|
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
- **git 작업은 메인 세션이 `/task-close` 스킬 또는 직접 수행**.
|
|
12
12
|
- **main, dev 직접 커밋 절대 금지**: 모든 작업은 태스크 브랜치에서 진행.
|
|
13
13
|
- **하나의 브랜치는 하나의 태스크만 담당**.
|
|
14
|
-
- **작업 브랜치는 *task-init
|
|
15
|
-
-
|
|
14
|
+
- **작업 브랜치는 *task-init 시점의 현재 브랜치*(= 부모 브랜치)에서 분기, *task-close 시점에* 그 부모 브랜치로 병합** (멀티세션 0.1.2+ / 부모 파라미터화 0.6.0+). 부모는 `dev` 고정이 아니라 서 있는 브랜치 — 개인 기본 `dev`, 회사/로드맵 `dev_feat_x`/`master` 등. task 헤더에 기록돼 close가 그 값으로 되병합한다.
|
|
15
|
+
- **부모 → 상위(dev/voyager/master 등) 병합은 사용자가 직접 수행** (로컬 또는 PR). taskery는 부모까지만 병합.
|
|
16
16
|
- **수동 git 작업 시 정합성 보장 X** — taskery 명령(스킬 호출 / `npx @angar2/taskery <서브>`)으로만 운영. 수동 `git worktree add` / `branch` / `merge` 시 SSoT·락·자동 정리 흐름이 깨질 수 있음.
|
|
17
17
|
|
|
18
18
|
---
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
| 브랜치 | 용도 |
|
|
23
23
|
|--------|------|
|
|
24
24
|
| `main` | 프로덕션 |
|
|
25
|
-
| `dev` | 통합 개발 (메인
|
|
25
|
+
| `dev` | 통합 개발 — 개인 기본 부모 브랜치 (메인 워크트리는 *현재 부모 브랜치* 고정, dev는 그 기본값) |
|
|
26
26
|
| `{타입}/{개발자}_TASK-NNN_{출처}_{슬러그}` | 태스크 작업 브랜치 (멀티세션 시스템 단일 식별자) |
|
|
27
27
|
|
|
28
28
|
### 브랜치 타입
|
|
@@ -112,7 +112,7 @@ feat: [TASK-001] Phase 1 - 사용자 인증 로직 구현
|
|
|
112
112
|
1. Phase별 기능 커밋 (각 Phase마다 1개)
|
|
113
113
|
2. 태스크 문서 커밋
|
|
114
114
|
3. CHANGELOG 커밋
|
|
115
|
-
4.
|
|
115
|
+
4. 부모 브랜치 병합 (--no-ff, 머지 락 직렬화)
|
|
116
116
|
5. 워크트리 자동 제거 + 작업 브랜치 자동 삭제 (멀티세션 0.1.2+ — §"작업 브랜치 삭제 정책" 면제 조항)
|
|
117
117
|
```
|
|
118
118
|
|
|
@@ -132,10 +132,10 @@ docs: [TASK-{NNN}] CHANGELOG 업데이트
|
|
|
132
132
|
|
|
133
133
|
- 머지 커밋 메시지는 git 기본 메시지 사용
|
|
134
134
|
- `-m` 옵션으로 커밋 메시지 지정 금지
|
|
135
|
-
- **`--no-ff` 강제 (일반 타입)**: feature/bug/improvement/refactor 등 일반 작업 브랜치 →
|
|
136
|
-
- **추적 변경 0 → 마커 빈커밋 (`--allow-empty` 금지의 유일 예외)**: 코드 변경 0 + `.project` gitignore로 작업 브랜치가
|
|
135
|
+
- **`--no-ff` 강제 (일반 타입)**: feature/bug/improvement/refactor 등 일반 작업 브랜치 → 부모 병합 시 `git merge --no-ff {브랜치명}` 사용. fast-forward 병합 절대 금지. 머지 커밋이 없으면 작업 브랜치 삭제 후 분기 정보 영구 손실(90일 reflog GC 후 추적 불가).
|
|
136
|
+
- **추적 변경 0 → 마커 빈커밋 (`--allow-empty` 금지의 유일 예외)**: 코드 변경 0 + `.project` gitignore로 작업 브랜치가 부모보다 앞선 커밋이 0개이면 `--no-ff`도 *Already up to date*가 되어 머지 커밋이 생성되지 않는다. 이 경우 `/task-close`가 추적 마커 빈커밋 1개(`--allow-empty`)를 생성해 분기·채번 정보를 보존한다 (상세: task-close Step 6-8). 그 외 빈커밋은 금지.
|
|
137
137
|
- **plan/roadmap 임시 docs 브랜치 예외**: plan-init 단계의 단일 커밋(plan 신규 생성 — PLAN/ROADMAP + 제품 관통 문서 delta)에 한정 `--ff-only` 가능.
|
|
138
|
-
- **task 진행 중 ROADMAP/플랜 문서 갱신은 별도 `docs/*` 브랜치 분리 절대 금지**: 해당 task의 작업 브랜치 (`feature/*` / `improve/*` 등) 안에서 수정 + 다른 구현 커밋과 함께 `--no-ff` 머지로
|
|
138
|
+
- **task 진행 중 ROADMAP/플랜 문서 갱신은 별도 `docs/*` 브랜치 분리 절대 금지**: 해당 task의 작업 브랜치 (`feature/*` / `improve/*` 등) 안에서 수정 + 다른 구현 커밋과 함께 `--no-ff` 머지로 부모 병합 (분기 정보 손실 방지).
|
|
139
139
|
|
|
140
140
|
---
|
|
141
141
|
|
|
@@ -144,7 +144,7 @@ docs: [TASK-{NNN}] CHANGELOG 업데이트
|
|
|
144
144
|
### 기본 (멀티세션 외 / 케이스 2 / 사용자 수동 작업)
|
|
145
145
|
|
|
146
146
|
작업 브랜치(feature/* / bug/* / improve/* / refactor/* / docs/* / chore/*)는 다음 조건 모두 만족 시에만 삭제한다:
|
|
147
|
-
1.
|
|
147
|
+
1. 부모 브랜치에 `--no-ff` 병합 완료 (머지 커밋이 부모에 기록됨)
|
|
148
148
|
2. 사용자 명시 승인 ("브랜치 삭제해" 등 직접 요청)
|
|
149
149
|
|
|
150
150
|
**메인 세션 룰**: 사용자 승인 없는 작업 브랜치 삭제 지시 절대 금지. 머지 후 자동 삭제 X. 사용자가 직접 요청 시에만 `git branch -d {브랜치명}` 실행. (단, plan/roadmap 임시 docs 브랜치는 단일 커밋 ff-only 머지 후 자동 삭제 가능.)
|
|
@@ -156,7 +156,7 @@ docs: [TASK-{NNN}] CHANGELOG 업데이트
|
|
|
156
156
|
| 동작 | 명령 | 면제 사유 |
|
|
157
157
|
|------|------|----------|
|
|
158
158
|
| 워크트리 제거 | `git -C "$MAIN_WT" worktree remove "$WT_PATH"` | task 완료 후 격리 폴더 정리 필요. 사전 안전 검증 (§"멀티세션 워크트리 정책" 참조) 후 진행 |
|
|
159
|
-
| 작업 브랜치 삭제 | `git -C "$MAIN_WT" branch -d "$BRANCH"` |
|
|
159
|
+
| 작업 브랜치 삭제 | `git -C "$MAIN_WT" branch -d "$BRANCH"` | 부모 `--no-ff` 머지 완료 후 안전 삭제 (-D 강제 X). 머지 커밋이 부모에 박혀 분기 정보 보존 |
|
|
160
160
|
|
|
161
161
|
#### 보존 키워드 (양쪽 다 보존)
|
|
162
162
|
|
|
@@ -172,7 +172,7 @@ docs: [TASK-{NNN}] CHANGELOG 업데이트
|
|
|
172
172
|
사용자 선택:
|
|
173
173
|
- 보존 → 워크트리 + 브랜치 모두 보존 (Step 12, 13 건너뜀)
|
|
174
174
|
- 삭제 → 정상 자동 흐름 (Step 12, 13 진행)
|
|
175
|
-
- 취소 → close 중단 (단,
|
|
175
|
+
- 취소 → close 중단 (단, 부모 브랜치 머지는 이미 완료된 상태)
|
|
176
176
|
|
|
177
177
|
#### 안전망 출력 (브랜치 삭제 직후)
|
|
178
178
|
|
|
@@ -189,11 +189,11 @@ docs: [TASK-{NNN}] CHANGELOG 업데이트
|
|
|
189
189
|
|
|
190
190
|
## 멀티세션 워크트리 정책 (0.1.2+)
|
|
191
191
|
|
|
192
|
-
### 메인 워크트리 =
|
|
192
|
+
### 메인 워크트리 = 부모 브랜치 고정
|
|
193
193
|
|
|
194
|
-
- 메인 워크트리는
|
|
194
|
+
- 메인 워크트리는 `/task-init` 시점에 서 있던 브랜치(= 그 task의 *부모 브랜치*)를 유지. 개인 기본 `dev`, 회사/로드맵 `dev_feat_x`/`master` 등 — taskery는 현재 브랜치를 부모로 삼는다(이름 고정 X).
|
|
195
195
|
- 모든 태스크 작업은 *예외 없이* 별도 워크트리에서 수행
|
|
196
|
-
-
|
|
196
|
+
- **진행 중 태스크가 있는 동안 메인 워크트리 HEAD를 옮기는 어떤 명령도 영구 금지**: `git checkout <task-branch>` / `git switch <task-branch>` / `git reset` HEAD 이동 / `git rebase` HEAD 이동 등. close가 부모로 되병합해야 하므로 부모에 서 있어야 한다. *"잠깐만 메인에서"* / *"테스트 한 번만"* 같은 예외 발화도 거부 (별도 워크트리로 처리). (부모 브랜치 자체 전환은 진행 중 태스크가 없을 때만.)
|
|
197
197
|
- **모호 발화 자의 해석 금지**: *"워크트리 없이"* / *"메인에서"* / *"이 자리에서"* 류 발화 = 본 규칙 충돌 신호 → 즉시 정지 + 규칙 명시 + 1줄 confirm 요청 (자의 해석 후 진행 영구 금지). 사용자 의도가 *오타/모호*일 가능성은 *워크트리 사용*으로 읽는 게 자연스러움
|
|
198
198
|
- `/task-init` / `/task-close` 시작 시 검증 (위배 시 사용자 호출 + 중단)
|
|
199
199
|
|
|
@@ -235,12 +235,12 @@ git ≥ 2.31 필요 (`--path-format=absolute` 옵션).
|
|
|
235
235
|
### SSoT 조회 (진행중 태스크)
|
|
236
236
|
|
|
237
237
|
```sh
|
|
238
|
-
git -C "$MAIN_WT" branch --
|
|
238
|
+
git -C "$MAIN_WT" branch --list \
|
|
239
239
|
'feature/*_TASK-*' 'bug/*_TASK-*' 'improve/*_TASK-*' \
|
|
240
240
|
'refactor/*_TASK-*' 'docs/*_TASK-*' 'chore/*_TASK-*'
|
|
241
241
|
```
|
|
242
242
|
|
|
243
|
-
`_TASK-*` 패턴 강제로 케이스 2(TASK 없는 브랜치) 자동 제외. 별도 상태 파일/락 운영 X — git 분산 락(동일 브랜치명 자동 거부) + 결정적 슬러그(백로그/로드맵 항목 제목 → kebab-case)가 race 차단 2층 안전망.
|
|
243
|
+
`_TASK-*` 패턴 강제로 케이스 2(TASK 없는 브랜치) 자동 제외. (부모 기준 `--no-merged` 미사용 — 분기 직후 브랜치는 부모와 동일 commit이라 완전 머지로 오판돼 채번이 충돌하기 때문. 브랜치 존재 자체가 SSoT. `bin/lib.js` `getActiveTasks` 참조.) 별도 상태 파일/락 운영 X — git 분산 락(동일 브랜치명 자동 거부) + 결정적 슬러그(백로그/로드맵 항목 제목 → kebab-case)가 race 차단 2층 안전망.
|
|
244
244
|
|
|
245
245
|
### 머지 직렬화 (head-to-head race 차단)
|
|
246
246
|
|
|
@@ -253,7 +253,7 @@ git -C "$MAIN_WT" branch --no-merged dev --list \
|
|
|
253
253
|
|
|
254
254
|
여러 세션이 병렬로 검수용 dev 서버·터널을 띄울 때 포트·프로세스가 충돌하지 않도록 task별로 자원을 독립 소유한다. CLI·라이브러리 등 검수 서버가 없는 프로젝트는 무관.
|
|
255
255
|
|
|
256
|
-
- **포트 격리 (결정적)**:
|
|
256
|
+
- **포트 격리 (결정적)**: 메인 세션(부모 브랜치) = 기준 포트, task 워크트리 = 기준 포트 + TASK번호. 기준 포트는 프로젝트가 `CLAUDE.md`(코덱스는 `AGENTS.md`) `## 검수 실행 명령`에 선언한다 (멀티 프로젝트 동시 운영 시 프로젝트마다 다른 기준 포트). TASK번호는 1부터라 offset 0(기준 포트)이 메인 몫으로 항상 비어 충돌하지 않는다.
|
|
257
257
|
- **터널 (필수 아님)**: 모바일 검수 등 필요한 task만, 자기 포트를 가리키는 별도 프로세스로 판다 → task별 독립 URL.
|
|
258
258
|
- **자기 것만 정리**: `/task-close` 시 본 task가 띄운 서버·터널(자기 포트 것)만 종료한다. **`pkill -f` 같은 광역 종료 금지** — 다른 세션의 서버까지 죽이는 원인.
|
|
259
259
|
- **검수 기동 (자동)**: 사용자가 결과를 *눈으로 검수*하는 시점에 위 포트로 서버를 백그라운드 기동하고 접속 URL을 보고한다 (사용자가 매번 실행을 지시하지 않게). 정확한 발동 지점은 각 스킬 참조 — `/task-dev`는 세션이 거기서 끝날 때(Step 9), `/task-test`는 UNCERTAIN 검수 직전(Step 5)·PASS 후 종료 시(Step 7). FAIL→수정 흐름에서는 기동하지 않는다. 단계가 *기능상* 서버를 요구하면(E2E 등) 검수 시점과 무관하게 띄운다.
|
|
@@ -282,7 +282,7 @@ git -C "$MAIN_WT" branch --no-merged dev --list \
|
|
|
282
282
|
- 민감 정보 커밋 금지 (`.env`, API KEY, Secret Key 등)
|
|
283
283
|
- 여러 태스크를 하나의 브랜치에서 작업 금지
|
|
284
284
|
- 커밋 순서 임의 변경 금지
|
|
285
|
-
- 승인 없이
|
|
285
|
+
- 승인 없이 부모 브랜치 병합 금지
|
|
286
286
|
- **destructive 명령 사용자 승인 필수**: 다음 명령은 사용자 명시 승인 없이 절대 실행 금지:
|
|
287
287
|
- `git reset --hard ...`
|
|
288
288
|
- `git push --force ...`
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
|
|
8
8
|
## 1. 양식 Spec
|
|
9
9
|
|
|
10
|
-
### 1.1 헤더 표 —
|
|
10
|
+
### 1.1 헤더 표 — 6컬럼
|
|
11
11
|
|
|
12
|
-
| 생성일 | 플랜 | 유형 | 규모 | 상태 |
|
|
13
|
-
|
|
14
|
-
| 2026-05-08 | 001_mvp | feature | medium | developing |
|
|
12
|
+
| 생성일 | 플랜 | 유형 | 규모 | 부모 브랜치 | 상태 |
|
|
13
|
+
|--------|------|------|------|-------------|------|
|
|
14
|
+
| 2026-05-08 | 001_mvp | feature | medium | dev | developing |
|
|
15
15
|
|
|
16
16
|
- **생성일**: ISO 형식 (YYYY-MM-DD)
|
|
17
17
|
- **플랜**: 어느 plan(기능 그룹) 하위인지 (예: 001_mvp, 002_compare-products) — `tasks/<NNN_slug>/` 디렉토리 명과 일치
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
- `small` — 구현 파트 2-3개 (단순 기능)
|
|
22
22
|
- `medium` — 구현 파트 4-7개 (일반 기능 또는 새 모듈)
|
|
23
23
|
- `large` — 구현 파트 8개 이상 (또는 분리 검토 권장)
|
|
24
|
-
-
|
|
24
|
+
- **부모 브랜치**: 이 task를 분기한 기점 브랜치 = `/task-init` 시점의 메인 워크트리 현재 브랜치 (`dev` / `dev_feat_x` / `master` 등). `/task-close`가 이 브랜치로 되병합한다. `/task-init`이 자동 기록 — 수기 입력 아님. (0.6.0 이전 문서는 이 칸이 없으며, close 시 `dev`로 폴백.)
|
|
25
|
+
- **상태**: 7상태 머신 (1.2 참조) — 표의 *마지막* 컬럼(부모 브랜치가 그 앞)
|
|
25
26
|
|
|
26
27
|
### 1.2 상태 set — 7상태 (-ing/-ed 페어 일관성)
|
|
27
28
|
|
|
@@ -53,9 +54,9 @@ draft → planned → developing → developed → testing → tested → closed
|
|
|
53
54
|
```markdown
|
|
54
55
|
# TASK-NNN — <태스크 이름>
|
|
55
56
|
|
|
56
|
-
| 생성일 | 플랜 | 유형 | 규모 | 상태 |
|
|
57
|
-
|
|
58
|
-
| ... | ... | ... | ... | ... |
|
|
57
|
+
| 생성일 | 플랜 | 유형 | 규모 | 부모 브랜치 | 상태 |
|
|
58
|
+
|--------|------|------|------|-------------|------|
|
|
59
|
+
| ... | ... | ... | ... | ... | ... |
|
|
59
60
|
|
|
60
61
|
## Requirements
|
|
61
62
|
<요구사항 + 메인이 증폭/구체화한 내용>
|
|
@@ -121,11 +122,12 @@ draft → planned → developing → developed → testing → tested → closed
|
|
|
121
122
|
2. kebab-slug = 태스크 이름 한국어 → 영어 kebab-case (예: "로그인 기능 추가" → `login-feature`)
|
|
122
123
|
3. 파일명 = `NNN_kebab-slug.md` (예: `003_login-feature.md`). *사용자 명시 시* 폴더 승격 (`TASK-003_login-feature/task.md`) — 규모 large여도 자동 승격 X.
|
|
123
124
|
4. 파일 제목 작성: `# TASK-003 — 로그인 기능 추가`
|
|
124
|
-
5. 헤더 표 작성 (
|
|
125
|
+
5. 헤더 표 작성 (6컬럼 모두 채움):
|
|
125
126
|
- 생성일: 오늘 (YYYY-MM-DD)
|
|
126
127
|
- 플랜: 현재 active plan (예: 001_mvp — `NNN_slug` 폴더명)
|
|
127
128
|
- 유형: 사용자/메인 합의 (feature/bug/...)
|
|
128
129
|
- 규모: 사용자/메인 합의 (micro/small/medium/large)
|
|
130
|
+
- 부모 브랜치: `/task-init`(fork) 시점의 현재 브랜치 — 코드가 자동 기록(수기 X)
|
|
129
131
|
- 상태: `draft` (고정)
|
|
130
132
|
6. 6 섹션 placeholder 작성 (Requirements / Scope / Dev Plan / Test Plan / Result는 빈 헤딩만).
|
|
131
133
|
|
|
@@ -263,7 +265,7 @@ Test Plan 작성 완료 후 *반드시* 점검. *"누적/리셋 안 함"* 같은
|
|
|
263
265
|
### 3.1 일관성
|
|
264
266
|
|
|
265
267
|
- 양식 그대로 채워. 섹션 추가/삭제 금지.
|
|
266
|
-
- 헤더
|
|
268
|
+
- 헤더 6컬럼 모두 채움 — *"미정"* 같은 placeholder 금지 (의사결정 위임 X). 부모 브랜치는 코드 자동 기록.
|
|
267
269
|
- 상태 7개 외 단어 사용 금지.
|
|
268
270
|
|
|
269
271
|
### 3.2 자기완결성
|
package/template/AGENTS.md
CHANGED
|
@@ -29,26 +29,26 @@
|
|
|
29
29
|
1. **범위 준수** — 사용자가 명시한 범위/행동만 수행. 명시 외 자체 진입 영구 금지. 예: 사용자가 *"X부터 Y까지"* 지시 → Y 끝에서 정지 + 상태 보고 + 다음 단계 명시 호출 대기.
|
|
30
30
|
2. **Skill 정식 발동** — task 단계는 반드시 해당 스킬(`.agents/skills/<스킬>/SKILL.md`)을 정식 호출. 가이드 본문을 머릿속 절차로 대체 금지 (컴팩트 세션 / 시스템 리마인더 인지 상태에서도 동일).
|
|
31
31
|
3. **워크트리 자가 진단** — 세션 시작 시 현재 cwd가 *메인 워크트리*인지 *태스크 워크트리*인지 자가 판단 (`git rev-parse --show-toplevel` + `~/.taskery/worktrees/` 경로 비교). 태스크 워크트리면 *해당 진행중 태스크 컨텍스트*로 진입, 메인 워크트리면 *새 태스크 / 진행중 목록 인터뷰* 흐름.
|
|
32
|
-
4. **모호 발화 자의 해석 금지** — *"워크트리 없이"* / *"메인에서"* / *"이 자리에서 그냥"* 류로 읽히는 발화 = 코어 규칙(메인 워크트리 =
|
|
32
|
+
4. **모호 발화 자의 해석 금지** — *"워크트리 없이"* / *"메인에서"* / *"이 자리에서 그냥"* 류로 읽히는 발화 = 코어 규칙(메인 워크트리 = 부모 브랜치 고정, task는 워크트리에서) 충돌 신호. 즉시 정지 + 규칙 한 줄 명시 + 1줄 confirm 요청. 자의 해석 후 진행 영구 금지. 사용자 의도가 *오타/모호*일 가능성을 *워크트리 사용*으로 읽는 게 자연스러움.
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
|
36
36
|
## 멀티세션 워크트리 (0.1.2+)
|
|
37
37
|
|
|
38
|
-
> 멀티세션 = 같은 프로젝트에서 *여러 메인 세션이 독립 태스크를 병렬*로 진행. git worktree로 작업 폴더 격리,
|
|
38
|
+
> 멀티세션 = 같은 프로젝트에서 *여러 메인 세션이 독립 태스크를 병렬*로 진행. git worktree로 작업 폴더 격리, 부모 브랜치 머지 시 직렬화.
|
|
39
39
|
|
|
40
|
-
- **메인 워크트리 =
|
|
40
|
+
- **메인 워크트리 = 부모 브랜치 고정**: 메인 워크트리는 `/task-init` 시점에 체크아웃돼 있는 브랜치(= 그 task의 *부모 브랜치*)를 유지한다. 개인 기본은 `dev`, 회사/로드맵은 `dev_feat_x` / `master` 등 — taskery는 *현재 서 있는 브랜치*를 부모로 삼는다(특정 이름 고정 X). 모든 태스크 작업은 *예외 없이* 별도 워크트리에서 수행. 진행 중 태스크가 있는 동안 메인 워크트리 HEAD를 옮기는 어떤 작업(`git checkout <task-branch>` / `git switch` / `git reset` HEAD 이동 / `git rebase` HEAD 이동 등)도 영구 금지 — close가 부모로 되병합해야 하므로 부모에 서 있어야 한다. *"잠깐만 메인에서"* / *"테스트 한 번만 메인에서"* 같은 예외 발화도 거부 — 별도 워크트리에서 처리. (부모 브랜치 자체를 바꾸려면 진행 중 태스크가 없을 때 원하는 브랜치로 체크아웃 후 새 task 시작.)
|
|
41
41
|
- **워크트리 위치**: `~/.taskery/worktrees/<projectId>/TASK-NNN_<출처>_<슬러그>/`
|
|
42
42
|
- `<projectId>`: `.taskery-manifest.json` `projectId` (8자 hex)
|
|
43
43
|
- `<출처>`: `BL-NNN` / `RM-NNN` / `DR`
|
|
44
|
-
- **`/task-init`이 워크트리 생성** + **`/task-close`가
|
|
44
|
+
- **`/task-init`이 부모 브랜치에서 워크트리 생성** + **`/task-close`가 부모 브랜치 머지 + 워크트리 자동 제거** (자동 흐름. 보존 키워드 `keep` / `브랜치 남겨` 등 발화 시 둘 다 보존).
|
|
45
45
|
- **메인 메타 접근**: 워크트리에서 `.project/`, `AGENTS.md` 등 접근 시 *메인 워크트리 절대 경로* 사용:
|
|
46
46
|
```sh
|
|
47
47
|
MAIN_WT=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")
|
|
48
48
|
```
|
|
49
49
|
- **수동 git 작업 시 정합성 보장 X** — taskery 명령(스킬 / `npx @angar2/taskery <서브>`)으로만 운영.
|
|
50
50
|
- **CLI 보조 명령**:
|
|
51
|
-
- `npx @angar2/taskery status` — 진행중 태스크 + 워크트리 +
|
|
51
|
+
- `npx @angar2/taskery status` — 진행중 태스크 + 워크트리 + 부모 머지 상태 요약
|
|
52
52
|
- `npx @angar2/taskery prune` — stale 워크트리 대화형 정리
|
|
53
53
|
|
|
54
54
|
상세: `.project/rules/GIT_RULE.md` "멀티세션 워크트리 정책" 섹션.
|
|
@@ -72,11 +72,11 @@
|
|
|
72
72
|
|
|
73
73
|
- **`/add-backlog`**: 사용자 *"~ 백로그에 추가"* 발화 → 얕은 분석(코드 탐색 X, 추정 수준) → BL-NNN 채번 → BACKLOG.md append (`[ ]` 대기).
|
|
74
74
|
- **`/task-init` 연동**: 사용자 *"백로그의 BL-NNN 진행"* 발화 → 해당 항목 *확인 마킹*. `[ ]` → `[x]` + `- TASK: TASK-NNN` 추가 (다회 진행 시 콤마).
|
|
75
|
-
- **체크박스 의미**: `[ ]` = 미확인 (task로 옮기지 않은 메모) / `[x]` = 확인 완료 (task로 옮김). `[x]`는 *task로 옮겼다*는 메모일 뿐,
|
|
76
|
-
- **`[x]` 항목이 실제로 완료된 작업인지 굳이 확인하려 들지 않는다.** 진행·완료 상태는 백로그가 아니라 `taskery status`(살아있는 브랜치) +
|
|
75
|
+
- **체크박스 의미**: `[ ]` = 미확인 (task로 옮기지 않은 메모) / `[x]` = 확인 완료 (task로 옮김). `[x]`는 *task로 옮겼다*는 메모일 뿐, 부모 머지·완료 여부와는 무관하다.
|
|
76
|
+
- **`[x]` 항목이 실제로 완료된 작업인지 굳이 확인하려 들지 않는다.** 진행·완료 상태는 백로그가 아니라 `taskery status`(살아있는 브랜치) + 부모 머지커밋이 판단한다.
|
|
77
77
|
- **머지 여부를 직접 알아야 할 경우에만** 아래 순서로 작업 완료 여부를 확인한다:
|
|
78
|
-
1. `taskery status` 진행중 목록에 그 TASK가 **있으면** → 아직 진행 중(
|
|
79
|
-
2. 목록에 **없으면** → `git log
|
|
78
|
+
1. `taskery status` 진행중 목록에 그 TASK가 **있으면** → 아직 진행 중(부모 미머지)이므로 `git log --all --grep`이 비는 것이 정상이다. 재조회하지 않는다.
|
|
79
|
+
2. 목록에 **없으면** → `git log --all --grep 'BL-NNN'` + 브랜치명으로 작업 완료 여부를 확인한다.
|
|
80
80
|
- **글로벌 `.project/BACKLOG.md`** (다음 기능 그룹 후보 카탈로그) 는 본 흐름 무관 — `/plan-init` 영역.
|
|
81
81
|
|
|
82
82
|
상세: `.agents/skills/add-backlog/SKILL.md`.
|
|
@@ -116,11 +116,11 @@
|
|
|
116
116
|
## 검수 실행 명령 (해당 시 — 앱 실행·검수가 필요한 프로젝트)
|
|
117
117
|
|
|
118
118
|
> `/task-dev`·`/task-test`가 검수 시점(세션 중단점)에 이 명령으로 서버를 *백그라운드* 기동하고 접속 URL을 보고한다.
|
|
119
|
-
> 멀티세션 포트 격리:
|
|
119
|
+
> 멀티세션 포트 격리: 메인 세션(부모 브랜치) = 기준 포트, task 워크트리 = 기준 포트 + TASK번호. 상세는 GIT_RULE "멀티세션 검수 환경" 섹션.
|
|
120
120
|
> CLI/라이브러리 등 검수 서버가 없는 프로젝트는 본 섹션 전체 삭제.
|
|
121
121
|
|
|
122
122
|
- 기준 포트: `<예: 5100>`
|
|
123
|
-
- 실행 명령: `<예: npm run dev -- --port <PORT>>` (`<PORT>` = 기준 포트 + TASK번호,
|
|
123
|
+
- 실행 명령: `<예: npm run dev -- --port <PORT>>` (`<PORT>` = 기준 포트 + TASK번호, 메인은 기준 포트)
|
|
124
124
|
- 터널 (모바일 검수 등 필요 시 — 선택): `<예: cloudflared tunnel --url http://localhost:<PORT>>`
|
|
125
125
|
|
|
126
126
|
---
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
|
|
130
130
|
| 룰 | 위치 | 역할 |
|
|
131
131
|
|----|------|------|
|
|
132
|
-
| TASK_DOC_RULE | `.project/rules/TASK_DOC_RULE.md` | task 문서 양식 (헤더
|
|
132
|
+
| TASK_DOC_RULE | `.project/rules/TASK_DOC_RULE.md` | task 문서 양식 (헤더 6컬럼 / 6 섹션 / 7 상태) |
|
|
133
133
|
| GIT_RULE | `.project/rules/GIT_RULE.md` (있으면) → 글로벌 `~/.claude/rules/GIT_RULE.md` (fallback) | git 정책 (브랜치 / 커밋 / 머지) |
|
|
134
134
|
| CHANGELOG_RULE | `.project/rules/CHANGELOG_RULE.md` | CHANGELOG 작성 정책 (위치 / 형식 / 필수 필드) |
|
|
135
135
|
| MOCKUP_RULE | `.project/rules/MOCKUP_RULE.md` | UX/UI task의 HTML 목업 위치 / 형식 / 네이밍 |
|
|
@@ -147,7 +147,7 @@
|
|
|
147
147
|
| `/task-plan` | task | Requirements/Scope/Dev Plan/Test Plan 작성 (draft → planned) |
|
|
148
148
|
| `/task-dev` | task | Phase 순서 구현 + self-check (planned → developed) |
|
|
149
149
|
| `/task-test` | task | task-tester 서브에이전트 격리 검증 (developed → tested) |
|
|
150
|
-
| `/task-close` | task | 검증 명령 게이트 + 커밋 +
|
|
150
|
+
| `/task-close` | task | 검증 명령 게이트 + 커밋 + 부모 브랜치 병합 (tested → closed) |
|
|
151
151
|
| `/add-backlog` | meta | 사용자 발화로 활성 plan BACKLOG.md에 항목 1건 추가 (얕은 분석 + BL-NNN 채번 — 0.1.2+) |
|
|
152
152
|
| `/log-friction` | meta | FRICTION_LOG.md에 사용자 불편 한 행 기록 |
|
|
153
153
|
|
package/template/CLAUDE.md
CHANGED
|
@@ -24,26 +24,26 @@
|
|
|
24
24
|
1. **범위 준수** — 사용자가 명시한 범위/행동만 수행. 명시 외 자체 진입 영구 금지. 예: 사용자가 *"X부터 Y까지"* 지시 → Y 끝에서 정지 + 상태 보고 + 다음 단계 명시 호출 대기.
|
|
25
25
|
2. **Skill 정식 발동** — task 단계는 반드시 Skill 도구로 정식 호출. 가이드 본문을 머릿속 절차로 대체 금지 (컴팩트 세션 / 시스템 리마인더 인지 상태에서도 동일).
|
|
26
26
|
3. **워크트리 자가 진단** — 세션 시작 시 현재 cwd가 *메인 워크트리*인지 *태스크 워크트리*인지 자가 판단 (`git rev-parse --show-toplevel` + `~/.taskery/worktrees/` 경로 비교). 태스크 워크트리면 *해당 진행중 태스크 컨텍스트*로 진입, 메인 워크트리면 *새 태스크 / 진행중 목록 인터뷰* 흐름.
|
|
27
|
-
4. **모호 발화 자의 해석 금지** — *"워크트리 없이"* / *"메인에서"* / *"이 자리에서 그냥"* 류로 읽히는 발화 = 코어 규칙(메인 워크트리 =
|
|
27
|
+
4. **모호 발화 자의 해석 금지** — *"워크트리 없이"* / *"메인에서"* / *"이 자리에서 그냥"* 류로 읽히는 발화 = 코어 규칙(메인 워크트리 = 부모 브랜치 고정, task는 워크트리에서) 충돌 신호. 즉시 정지 + 규칙 한 줄 명시 + 1줄 confirm 요청. 자의 해석 후 진행 영구 금지. 사용자 의도가 *오타/모호*일 가능성을 *워크트리 사용*으로 읽는 게 자연스러움.
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
31
31
|
## 멀티세션 워크트리 (0.1.2+)
|
|
32
32
|
|
|
33
|
-
> 멀티세션 = 같은 프로젝트에서 *여러 메인 세션이 독립 태스크를 병렬*로 진행. git worktree로 작업 폴더 격리,
|
|
33
|
+
> 멀티세션 = 같은 프로젝트에서 *여러 메인 세션이 독립 태스크를 병렬*로 진행. git worktree로 작업 폴더 격리, 부모 브랜치 머지 시 직렬화.
|
|
34
34
|
|
|
35
|
-
- **메인 워크트리 =
|
|
35
|
+
- **메인 워크트리 = 부모 브랜치 고정**: 메인 워크트리는 `/task-init` 시점에 체크아웃돼 있는 브랜치(= 그 task의 *부모 브랜치*)를 유지한다. 개인 기본은 `dev`, 회사/로드맵은 `dev_feat_x` / `master` 등 — taskery는 *현재 서 있는 브랜치*를 부모로 삼는다(특정 이름 고정 X). 모든 태스크 작업은 *예외 없이* 별도 워크트리에서 수행. 진행 중 태스크가 있는 동안 메인 워크트리 HEAD를 옮기는 어떤 작업(`git checkout <task-branch>` / `git switch` / `git reset` HEAD 이동 / `git rebase` HEAD 이동 등)도 영구 금지 — close가 부모로 되병합해야 하므로 부모에 서 있어야 한다. *"잠깐만 메인에서"* / *"테스트 한 번만 메인에서"* 같은 예외 발화도 거부 — 별도 워크트리에서 처리. (부모 브랜치 자체를 바꾸려면 진행 중 태스크가 없을 때 원하는 브랜치로 체크아웃 후 새 task 시작.)
|
|
36
36
|
- **워크트리 위치**: `~/.taskery/worktrees/<projectId>/TASK-NNN_<출처>_<슬러그>/`
|
|
37
37
|
- `<projectId>`: `.taskery-manifest.json` `projectId` (8자 hex)
|
|
38
38
|
- `<출처>`: `BL-NNN` / `RM-NNN` / `DR`
|
|
39
|
-
- **`/task-init`이 워크트리 생성** + **`/task-close`가
|
|
39
|
+
- **`/task-init`이 부모 브랜치에서 워크트리 생성** + **`/task-close`가 부모 브랜치 머지 + 워크트리 자동 제거** (자동 흐름. 보존 키워드 `keep` / `브랜치 남겨` 등 발화 시 둘 다 보존).
|
|
40
40
|
- **메인 메타 접근**: 워크트리에서 `.project/`, `CLAUDE.md` 등 접근 시 *메인 워크트리 절대 경로* 사용:
|
|
41
41
|
```sh
|
|
42
42
|
MAIN_WT=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")
|
|
43
43
|
```
|
|
44
44
|
- **수동 git 작업 시 정합성 보장 X** — taskery 명령(스킬 / `npx @angar2/taskery <서브>`)으로만 운영.
|
|
45
45
|
- **CLI 보조 명령**:
|
|
46
|
-
- `npx @angar2/taskery status` — 진행중 태스크 + 워크트리 +
|
|
46
|
+
- `npx @angar2/taskery status` — 진행중 태스크 + 워크트리 + 부모 머지 상태 요약
|
|
47
47
|
- `npx @angar2/taskery prune` — stale 워크트리 대화형 정리
|
|
48
48
|
|
|
49
49
|
상세: `.project/rules/GIT_RULE.md` "멀티세션 워크트리 정책" 섹션.
|
|
@@ -71,11 +71,11 @@
|
|
|
71
71
|
|
|
72
72
|
- **`/add-backlog`**: 사용자 *"~ 백로그에 추가"* 발화 → 얕은 분석(코드 탐색 X, 추정 수준) → BL-NNN 채번 → BACKLOG.md append (`[ ]` 대기).
|
|
73
73
|
- **`/task-init` 연동**: 사용자 *"백로그의 BL-NNN 진행"* 발화 → 해당 항목 *확인 마킹*. `[ ]` → `[x]` + `- TASK: TASK-NNN` 추가 (다회 진행 시 콤마).
|
|
74
|
-
- **체크박스 의미**: `[ ]` = 미확인 (task로 옮기지 않은 메모) / `[x]` = 확인 완료 (task로 옮김). `[x]`는 *task로 옮겼다*는 메모일 뿐,
|
|
75
|
-
- **`[x]` 항목이 실제로 완료된 작업인지 굳이 확인하려 들지 않는다.** 진행·완료 상태는 백로그가 아니라 `taskery status`(살아있는 브랜치) +
|
|
74
|
+
- **체크박스 의미**: `[ ]` = 미확인 (task로 옮기지 않은 메모) / `[x]` = 확인 완료 (task로 옮김). `[x]`는 *task로 옮겼다*는 메모일 뿐, 부모 머지·완료 여부와는 무관하다.
|
|
75
|
+
- **`[x]` 항목이 실제로 완료된 작업인지 굳이 확인하려 들지 않는다.** 진행·완료 상태는 백로그가 아니라 `taskery status`(살아있는 브랜치) + 부모 머지커밋이 판단한다.
|
|
76
76
|
- **머지 여부를 직접 알아야 할 경우에만** 아래 순서로 작업 완료 여부를 확인한다:
|
|
77
|
-
1. `taskery status` 진행중 목록에 그 TASK가 **있으면** → 아직 진행 중(
|
|
78
|
-
2. 목록에 **없으면** → `git log
|
|
77
|
+
1. `taskery status` 진행중 목록에 그 TASK가 **있으면** → 아직 진행 중(부모 미머지)이므로 `git log --all --grep`이 비는 것이 정상이다. 재조회하지 않는다.
|
|
78
|
+
2. 목록에 **없으면** → `git log --all --grep 'BL-NNN'` + 브랜치명으로 작업 완료 여부를 확인한다.
|
|
79
79
|
- **글로벌 `.project/BACKLOG.md`** (다음 기능 그룹 후보 카탈로그) 는 본 흐름 무관 — `/plan-init` 영역.
|
|
80
80
|
|
|
81
81
|
상세: `.claude/skills/add-backlog/SKILL.md`.
|
|
@@ -115,11 +115,11 @@
|
|
|
115
115
|
## 검수 실행 명령 (해당 시 — 앱 실행·검수가 필요한 프로젝트)
|
|
116
116
|
|
|
117
117
|
> `/task-dev`·`/task-test`가 검수 시점(세션 중단점)에 이 명령으로 서버를 *백그라운드* 기동하고 접속 URL을 보고한다.
|
|
118
|
-
> 멀티세션 포트 격리:
|
|
118
|
+
> 멀티세션 포트 격리: 메인 세션(부모 브랜치) = 기준 포트, task 워크트리 = 기준 포트 + TASK번호. 상세는 GIT_RULE "멀티세션 검수 환경" 섹션.
|
|
119
119
|
> CLI/라이브러리 등 검수 서버가 없는 프로젝트는 본 섹션 전체 삭제.
|
|
120
120
|
|
|
121
121
|
- 기준 포트: `<예: 5100>`
|
|
122
|
-
- 실행 명령: `<예: npm run dev -- --port <PORT>>` (`<PORT>` = 기준 포트 + TASK번호,
|
|
122
|
+
- 실행 명령: `<예: npm run dev -- --port <PORT>>` (`<PORT>` = 기준 포트 + TASK번호, 메인은 기준 포트)
|
|
123
123
|
- 터널 (모바일 검수 등 필요 시 — 선택): `<예: cloudflared tunnel --url http://localhost:<PORT>>`
|
|
124
124
|
|
|
125
125
|
---
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
|
|
129
129
|
| 룰 | 위치 | 역할 |
|
|
130
130
|
|----|------|------|
|
|
131
|
-
| TASK_DOC_RULE | `.project/rules/TASK_DOC_RULE.md` | task 문서 양식 (헤더
|
|
131
|
+
| TASK_DOC_RULE | `.project/rules/TASK_DOC_RULE.md` | task 문서 양식 (헤더 6컬럼 / 6 섹션 / 7 상태) |
|
|
132
132
|
| GIT_RULE | `.project/rules/GIT_RULE.md` (있으면) → 글로벌 `~/.claude/rules/GIT_RULE.md` (fallback) | git 정책 (브랜치 / 커밋 / 머지) |
|
|
133
133
|
| CHANGELOG_RULE | `.project/rules/CHANGELOG_RULE.md` | CHANGELOG 작성 정책 (위치 / 형식 / 필수 필드) |
|
|
134
134
|
| MOCKUP_RULE | `.project/rules/MOCKUP_RULE.md` | UX/UI task의 HTML 목업 위치 / 형식 / 네이밍 |
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
| `/task-plan` | task | Requirements/Scope/Dev Plan/Test Plan 작성 (draft → planned) |
|
|
147
147
|
| `/task-dev` | task | Phase 순서 구현 + self-check (planned → developed) |
|
|
148
148
|
| `/task-test` | task | Task tool 격리 검증 (developed → tested) |
|
|
149
|
-
| `/task-close` | task | 검증 명령 게이트 + 커밋 +
|
|
149
|
+
| `/task-close` | task | 검증 명령 게이트 + 커밋 + 부모 브랜치 병합 (tested → closed) |
|
|
150
150
|
| `/add-backlog` | meta | 사용자 발화로 활성 plan BACKLOG.md에 항목 1건 추가 (얕은 분석 + BL-NNN 채번 — 0.1.2+) |
|
|
151
151
|
| `/log-friction` | meta | FRICTION_LOG.md에 사용자 불편 한 행 기록 |
|
|
152
152
|
| `/run-team` | meta · **Claude 전용** | agent teams로 다건 태스크를 팀원(독립 세션)에 분배해 자동 병렬 처리 — 트리거 발화 시에만 (실험 기능 전제). 위 "agent teams 자동 병렬" 섹션 참조 |
|
|
@@ -43,4 +43,4 @@ description: 사용자 발화로 *plan(기능 그룹)별* tasks/<NNN_slug>/BACKL
|
|
|
43
43
|
|
|
44
44
|
- **얕은 분석만** — 도메인 코드 Read · Grep 금지. 본격 분석은 `/task-plan`.
|
|
45
45
|
- **글로벌 `.project/BACKLOG.md`(plan 기획 후보)와 혼동 X** — 본 스킬은 *활성 plan별*만. 글로벌은 `/plan-init` 영역.
|
|
46
|
-
- **체크박스 의미** — `[ ]` = 미확인(메모) / `[x]` = 확인(task로 옮김).
|
|
46
|
+
- **체크박스 의미** — `[ ]` = 미확인(메모) / `[x]` = 확인(task로 옮김). 부모 머지·완료 여부와 무관 — 완료 추적은 `taskery status` + 부모 머지커밋.
|
|
@@ -92,9 +92,9 @@ PLAN.md는 *얇은 인덱스*다 — 이 기능 그룹이 건드린 루트 문
|
|
|
92
92
|
작성 산출물 목록 + 다음 단계:
|
|
93
93
|
- *"`<NNN_slug>/` plan 생성 완료 — PLAN/ROADMAP + FEATURES/UX-UI 의도 stub. AGENT-GUIDE 활성 plan `<NNN_slug>` 갱신. 다음은 `/task-init`으로 첫 task (스키마/엔드포인트 상세는 task 진행에서 구현 동반)."*
|
|
94
94
|
|
|
95
|
-
**commit 흐름** (GIT_RULE): dev 직접 commit *금지*(git-guard 차단). default 둘:
|
|
95
|
+
**commit 흐름** (GIT_RULE): 현재 브랜치(= 부모)가 dev/main이면 직접 commit *금지*(git-guard 차단). default 둘:
|
|
96
96
|
1. **첫 task에 묶기 (권장)**: `/project-init` 직후면 init 산출물과 같은 작업 브랜치(보통 TASK-001 부트스트랩 chore)에 함께. 새 기능 그룹이면 새 task 브랜치 또는 임시 docs 브랜치.
|
|
97
|
-
2. **임시 docs 브랜치**: `git checkout -b docs/{개발자}_plan-{NNN_slug}` → commit →
|
|
97
|
+
2. **임시 docs 브랜치**: `git checkout -b docs/{개발자}_plan-{NNN_slug}` → commit → **현재 브랜치(부모) `--no-ff` 머지**(부모가 dev면 dev, dev_feat_x면 dev_feat_x).
|
|
98
98
|
|
|
99
99
|
## 주의사항
|
|
100
100
|
|
|
@@ -287,9 +287,9 @@ git branch -a # main(또는 master) + dev
|
|
|
287
287
|
- *"PROJECT.md(초기 빌드 로드맵 포함) / AGENT-GUIDE.md / LINKED-REPOS.md / GLOSSARY.md / .env + 제품 관통 문서(그룹 A 작성: SERVICE-POLICY·TECH-STACK·ARCHITECTURE / 그룹 B 골격: DATA-MODEL·API-SPEC·FEATURES·UX-UI, 타입 해당분) 생성. (FRICTION_LOG.md / TEST-GUIDE.md(검증 방법 문서, 빈 골격) / 빈 골격 폴더는 npx init이 이미 카피한 것 그대로 — project-init이 생성 X). **빈 폴더 케이스라 git init + dev 분기까지 완료** (이미 git 리포면 건너뜀). 다음은 `/plan-init <기능그룹>`으로 첫 plan(MVP면 여러 기능 그룹을 묶은 큰 plan) 작성."*
|
|
288
288
|
|
|
289
289
|
**결과 commit 흐름** (GIT_RULE 정합):
|
|
290
|
-
- dev 직접 commit *금지* (git-guard.sh 차단). 두 가지 default 흐름:
|
|
290
|
+
- 현재 브랜치(= 부모, 기본 dev)가 dev/main이면 직접 commit *금지* (git-guard.sh 차단). 두 가지 default 흐름:
|
|
291
291
|
1. **첫 task에 묶기 (권장)**: `/plan-init` 끝나고 첫 `/task-init` (보통 TASK-001 부트스트랩 chore)으로 만든 작업 브랜치에서 init 산출물(PROJECT/AGENT-GUIDE/LINKED-REPOS/.env)도 함께 commit. task-close 단계의 "태스크 문서 커밋"에 자연스럽게 묶임.
|
|
292
|
-
2. **임시 docs 브랜치**: `git checkout -b docs/{개발자}_init-bootstrap` 후 commit →
|
|
292
|
+
2. **임시 docs 브랜치**: `git checkout -b docs/{개발자}_init-bootstrap` 후 commit → 현재 브랜치(부모)에 `--no-ff` 머지. 첫 task 생성 *전*에 init 결과를 깔끔히 기록하고 싶을 때.
|
|
293
293
|
|
|
294
294
|
## 도구 가이드
|
|
295
295
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: task-close
|
|
3
|
-
description: task git 마무리 — 검증 게이트 + 커밋 순서 + 머지 락 직렬화 +
|
|
3
|
+
description: task git 마무리 — 검증 게이트 + 커밋 순서 + 머지 락 직렬화 + 부모 브랜치 --no-ff 병합 + 워크트리/브랜치 자동 정리, tested → closed (멀티세션 0.1.2+)
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# /task-close
|
|
7
7
|
|
|
8
8
|
## 개요
|
|
9
9
|
|
|
10
|
-
`tested` 상태 task의 git 마무리. **호출 위치 자유** — 워크트리 cwd / 메인 워크트리 cwd / 다른 세션이 호출한 서브 세션 모두 동작 동일 (모든 git 명령이 `git -C <경로>` 형태라 cwd 무관). `close` CLI 결정적 준비(Phase 커밋·status=closed·문서 커밋·추적 마커) → 사전 rebase → 충돌 해결 (3단계 에스컬레이션) → 머지 락 → 락 안 재 rebase →
|
|
10
|
+
`tested` 상태 task의 git 마무리. **호출 위치 자유** — 워크트리 cwd / 메인 워크트리 cwd / 다른 세션이 호출한 서브 세션 모두 동작 동일 (모든 git 명령이 `git -C <경로>` 형태라 cwd 무관). `close` CLI 결정적 준비(Phase 커밋·status=closed·문서 커밋·추적 마커) → 사전 rebase → 충돌 해결 (3단계 에스컬레이션) → 머지 락 → 락 안 재 rebase → 부모 브랜치 `--no-ff` 머지 → 워크트리 제거 + 작업 브랜치 자동 삭제.
|
|
11
11
|
|
|
12
12
|
**최종 게이트**: 모든 검증 명령 재실행 — 린트/타입체크/빌드 PASS여야 진행.
|
|
13
13
|
|
|
@@ -56,17 +56,17 @@ LOCK_FILE="$HOME/.taskery/${PROJECT_ID}.merge.lock"
|
|
|
56
56
|
### Step 1 — 사전 검증
|
|
57
57
|
|
|
58
58
|
1. **메인 워크트리 검출** (위 핵심 변수).
|
|
59
|
-
2. **메인 워크트리 =
|
|
59
|
+
2. **메인 워크트리 = 부모 브랜치 확인**: 메인 워크트리는 이 task의 *부모 브랜치*(task-init 시점 현재 브랜치, 헤더 기록)에 서 있어야 close가 그 부모로 되병합한다. 정확한 부모 일치는 Step 3 `close`가 검증(불일치 시 명확한 에러로 중단)하므로, 여기서는 detached HEAD만 조기 차단한다.
|
|
60
60
|
```sh
|
|
61
61
|
CURRENT=$(git -C "$MAIN_WT" rev-parse --abbrev-ref HEAD)
|
|
62
|
-
[ "$CURRENT"
|
|
62
|
+
[ "$CURRENT" != "HEAD" ] || abort "메인 워크트리가 detached HEAD — 부모 브랜치로 체크아웃 필요"
|
|
63
63
|
```
|
|
64
64
|
3. **워크트리 미커밋 변경 확인**:
|
|
65
65
|
```sh
|
|
66
66
|
git -C "$WT_PATH" status --porcelain
|
|
67
67
|
```
|
|
68
68
|
- 결과 *있음* = 정상 흐름 (taskery 정책: `/task-dev` = *git 작업 X*). 본 변경분은 Step 3 `close`가 *Dev Plan Phase 매핑 → Phase별 자동 커밋*으로 처리.
|
|
69
|
-
- 결과 *없음* = 변경분 0건 → Step 2 검증만 수행. **워크트리 브랜치가
|
|
69
|
+
- 결과 *없음* = 변경분 0건 → Step 2 검증만 수행. **워크트리 브랜치가 부모보다 앞선 커밋이 0개면 Step 3 `close`가 추적 마커 빈커밋 1개 생성** (`.project` gitignore + docs/분석 전용 task → 채번 보존).
|
|
70
70
|
- **차단 X** — 본 단계는 *상태 인지*용. close 중단 사유 X.
|
|
71
71
|
4. **task 파일 + GIT_RULE 확인**:
|
|
72
72
|
- task 문서 위치 분기 (`.gitignore` 케이스):
|
|
@@ -107,9 +107,9 @@ FAIL 시:
|
|
|
107
107
|
- **Phase 기능 커밋** — uncommitted 코드 변경분(`.project/` 외)을 task.md `### Phase N`의 `- 파일:` 필드에 매핑해 Phase 순서대로 자동 커밋 (GIT_RULE 메시지 형식, 태그 자동: feature→`feat:` / bug→`fix:` / improve→`improve:` / refactor→`refactor:` / docs·chore→`docs:`).
|
|
108
108
|
- **status → `closed`** — task 문서 헤더 (`.gitignore` 케이스 자동 판정 — 등록=메인WT / 미등록=워크트리).
|
|
109
109
|
- **flows/문서 커밋** (미등록 케이스) — 워크트리 안 `.project/flows/` + task 문서.
|
|
110
|
-
- **추적 마커 빈커밋** —
|
|
110
|
+
- **추적 마커 빈커밋** — 부모 브랜치보다 앞선 커밋이 0개(코드 0·`.project` gitignore인 docs/분석 task)면 채번 보존용 1개.
|
|
111
111
|
|
|
112
|
-
반환 JSON `{ prepped, branch, wtPath, registered, commits,
|
|
112
|
+
반환 JSON `{ prepped, branch, parent, wtPath, registered, commits, aheadOfParent }`. **`parent`(= task 헤더에 기록된 부모 브랜치, 예 `dev` / `dev_feat_x`)가 이후 Step 4·5의 rebase·머지 대상이다** — 아래 명령의 `$PARENT`로 사용. (task-init이 fork 시점 현재 브랜치를 헤더에 기록해뒀다.)
|
|
113
113
|
|
|
114
114
|
**콜백 (반환 `blocked` 필드 / npx는 특수 exit — 메인이 LLM 판단 후 처리):**
|
|
115
115
|
- **`blocked:'mapping'`** (npx exit 2 — `{files, phases, reason}`): Dev Plan `- 파일:` 필드가 변경 파일을 못 덮음(미매핑) 또는 한 파일이 여러 Phase에 걸침. → 메인이 Dev Plan + `git -C "$WT_PATH" diff --name-only` 정독 후 **코드 변경분을 Phase별로 수동 커밋**(GIT_RULE 형식, 위 태그) → `task_close` 도구(또는 `npx ... close`) **재호출**. 코드가 이미 커밋돼 있으므로 close가 status=closed + 문서 커밋 + 마커를 자동 완료한다.
|
|
@@ -119,10 +119,10 @@ FAIL 시:
|
|
|
119
119
|
|
|
120
120
|
### Step 4 — 사전 rebase + 충돌 자체 해결 (락 외, UX용 조기 감지)
|
|
121
121
|
|
|
122
|
-
`close`가 모든 변경을 커밋했으므로 rebase가 깨끗하게 replay된다.
|
|
122
|
+
`close`가 모든 변경을 커밋했으므로 rebase가 깨끗하게 replay된다. `$PARENT` = Step 3 `close` 반환의 `parent`(머지 대상 부모 브랜치).
|
|
123
123
|
|
|
124
124
|
```sh
|
|
125
|
-
git -C "$WT_PATH" rebase
|
|
125
|
+
git -C "$WT_PATH" rebase "$PARENT"
|
|
126
126
|
```
|
|
127
127
|
|
|
128
128
|
- 충돌 없음 → Step 5 (머지).
|
|
@@ -140,7 +140,7 @@ git -C "$WT_PATH" rebase dev
|
|
|
140
140
|
| 순위 | 자료 | 위치 |
|
|
141
141
|
|------|------|------|
|
|
142
142
|
| 1 | 태스크 문서 + 제품/plan 문서 (양쪽 정독 후 의도 종합) | 태스크: `$MAIN_WT/.project/tasks/<NNN_slug>/...` (등록 케이스) / `~/.taskery/worktrees/<projectId>/TASK-...` 다른 워크트리 (미등록 케이스, SSoT 조회로 경로 산출). 제품 관통 문서(FEATURES / ARCHITECTURE / TECH-STACK / DATA-MODEL / API-SPEC 등) = `$MAIN_WT/.project/<doc>.md` (루트 평평). plan 로컬(ROADMAP / PLAN) = `$MAIN_WT/.project/plans/<NNN_slug>/` |
|
|
143
|
-
| 2 | 커밋 메시지 | `git log
|
|
143
|
+
| 2 | 커밋 메시지 | `git log --all --grep` (GIT_RULE 풍부 메시지) |
|
|
144
144
|
| 3 | diff | 변경 코드 자체 |
|
|
145
145
|
|
|
146
146
|
- 1순위 부재 시 2/3순위 자동 fallback + 사용자에게 *자료 한계* 보고
|
|
@@ -152,9 +152,9 @@ git -C "$WT_PATH" rebase dev
|
|
|
152
152
|
- 해결 방향 후보 제시 + 사용자 결정 후 재개.
|
|
153
153
|
- 사용자가 중단 결정 시: `git -C "$WT_PATH" rebase --abort` 자동 + close 중단.
|
|
154
154
|
|
|
155
|
-
### Step 5 — 머지 락 + 락 안 재 rebase +
|
|
155
|
+
### Step 5 — 머지 락 + 락 안 재 rebase + 부모 브랜치 머지
|
|
156
156
|
|
|
157
|
-
머지 락 안에서 [락 안 rebase → 메인WT 검증 →
|
|
157
|
+
머지 락 안에서 [락 안 rebase → 메인WT 검증 → 부모 브랜치 `--no-ff` 머지]를 직렬 수행한다 (`bin/lib.js` `withMergeLock(projectId, fn)` — 다른 세션 머지와 직렬화). Phase 커밋·status=closed·문서 커밋·추적마커는 Step 3 `close`가 이미 끝냈으므로 본 단계는 *락 + rebase + 머지*만 담당. 메인 워크트리는 부모 브랜치(`$PARENT`)에 서 있어야 하며(close가 이미 검증), 머지는 그 부모 위로 이뤄진다.
|
|
158
158
|
|
|
159
159
|
#### 5-1. 머지 락 획득
|
|
160
160
|
|
|
@@ -163,13 +163,13 @@ git -C "$WT_PATH" rebase dev
|
|
|
163
163
|
#### 5-2. 락 안에서 rebase 재실행
|
|
164
164
|
|
|
165
165
|
```sh
|
|
166
|
-
git -C "$WT_PATH" rebase
|
|
166
|
+
git -C "$WT_PATH" rebase "$PARENT"
|
|
167
167
|
```
|
|
168
168
|
|
|
169
|
-
락 외(Step 4) rebase 이후 *다른 세션이
|
|
169
|
+
락 외(Step 4) rebase 이후 *다른 세션이 부모 브랜치에 머지했을 수 있음* — 락 안 rebase로 흡수.
|
|
170
170
|
- 재충돌 발견 시 Step 4 에스컬레이션 재실행 (해결 내역은 커밋 메시지 — 문서 closed).
|
|
171
171
|
|
|
172
|
-
> Phase 커밋 · flows/문서 커밋 · status=closed · 추적 마커는 **Step 3 `close`가 이미 완료**했다. 본 단계는 머지 락 안에서 *재 rebase + 메인WT 검증 +
|
|
172
|
+
> Phase 커밋 · flows/문서 커밋 · status=closed · 추적 마커는 **Step 3 `close`가 이미 완료**했다. 본 단계는 머지 락 안에서 *재 rebase + 메인WT 검증 + 부모 머지*만 수행한다.
|
|
173
173
|
|
|
174
174
|
#### 5-3. 메인 워크트리 uncommitted 검증 (머지 직전)
|
|
175
175
|
|
|
@@ -180,7 +180,9 @@ git -C "$MAIN_WT" status --porcelain
|
|
|
180
180
|
- 변경 있으면 → 사용자 호출 + 결정 (stash / 중단).
|
|
181
181
|
- 등록 케이스에서 task 문서 직접 수정이 *.gitignore 차단으로 status에 안 잡힘* — 정상.
|
|
182
182
|
|
|
183
|
-
#### 5-4.
|
|
183
|
+
#### 5-4. 부모 브랜치 `--no-ff` 머지
|
|
184
|
+
|
|
185
|
+
메인 워크트리는 부모 브랜치(`$PARENT`)에 체크아웃돼 있다(close 검증). 그 위로 작업 브랜치를 머지한다.
|
|
184
186
|
|
|
185
187
|
```sh
|
|
186
188
|
git -C "$MAIN_WT" merge --no-ff "$BRANCH"
|
|
@@ -188,7 +190,7 @@ git -C "$MAIN_WT" merge --no-ff "$BRANCH"
|
|
|
188
190
|
|
|
189
191
|
- `-m` 옵션 금지 (git 기본 메시지).
|
|
190
192
|
- 머지 커밋 해시 캡처: `MERGE_COMMIT=$(git -C "$MAIN_WT" rev-parse HEAD)`
|
|
191
|
-
- 브랜치가
|
|
193
|
+
- 브랜치가 부모보다 1커밋 이상 앞서야 머지 커밋이 생성된다 — Step 3 `close`의 추적 마커가 docs/분석 task의 0-커밋 케이스를 이미 보장(채번 보존).
|
|
192
194
|
|
|
193
195
|
#### 5-5. 머지 락 해제
|
|
194
196
|
|
|
@@ -220,7 +222,7 @@ git -C "$WT_PATH" ls-files --others --exclude-standard # 추적 X 파일
|
|
|
220
222
|
- 발견 시 사용자 호출 + 결정:
|
|
221
223
|
- 보존 → 7-2, 7-3 모두 건너뜀 (브랜치도 보존 — 정합)
|
|
222
224
|
- 삭제 → 7-2 진행
|
|
223
|
-
- 취소 → close 중단 (
|
|
225
|
+
- 취소 → close 중단 (부모 브랜치 머지는 이미 완료)
|
|
224
226
|
|
|
225
227
|
#### 7-2. 워크트리 제거
|
|
226
228
|
|
|
@@ -245,7 +247,7 @@ git -C "$MAIN_WT" worktree remove "$WT_PATH"
|
|
|
245
247
|
- 작업 브랜치: <BRANCH> (삭제 / 보존)
|
|
246
248
|
- 워크트리: <WT_PATH> (제거 / 보존)
|
|
247
249
|
- 커밋: Phase <N>개 + 태스크 문서 + (CHANGELOG)
|
|
248
|
-
-
|
|
250
|
+
- 부모 브랜치 병합: <PARENT> ← --no-ff 완료 ($MERGE_COMMIT)
|
|
249
251
|
- 충돌 해결: <건수, 자료 한계 보고 있으면 포함>
|
|
250
252
|
- 상태: tested → closed
|
|
251
253
|
- 다음: 새 task 시작은 /task-init. 마찰 신호 감지된 경우 /log-friction 등록 제안
|
|
@@ -262,14 +264,14 @@ git -C "$MAIN_WT" worktree remove "$WT_PATH"
|
|
|
262
264
|
|
|
263
265
|
- **사용자 명시 호출 외 자체 진입 영구 금지** (stash FRICTION_LOG #6+10/#20) — task-close는 git 영역. 사용자가 *"X부터 Y까지"* 범위 명시했는데 Y가 close 이전 단계면 close 자체 진입 영구 X.
|
|
264
266
|
- **검증 명령 재실행 게이트** — `/task-dev` self-check와 *별개*. 환경 변화 / 부분 작업 잡는 안전망.
|
|
265
|
-
-
|
|
267
|
+
- **부모 브랜치 직접 커밋 절대 X** — 작업 브랜치에서만. 부모가 dev/main이면 그 브랜치에서 `git commit` 시도 시 git-guard.sh가 차단(git-guard는 main/dev 보호). 모든 git 명령은 cwd 무관 `git -C "$WT_PATH" ...` / `git -C "$MAIN_WT" ...` 형태 강제. 셸 prefix(`cd && git`) / `--git-dir=` / `--work-tree=` 변형 영구 금지 (§호출 위치 정책).
|
|
266
268
|
- **`--no-ff` 강제** — 머지 커밋 없으면 분기 정보 영구 손실.
|
|
267
269
|
- **머지 락 직렬화** — 두 세션이 동시 머지 시도해도 한쪽씩 순차 진행. 락 외 rebase로 race 흡수 + 락 안 재 rebase로 다른 세션 머지 흡수.
|
|
268
270
|
- **충돌 자체 해결 3단계** — 단순 자동 / 의미적 자료 / 판단 불가 사용자. 자료 한계 발견 시 *반드시 사용자에게 보고* (1순위 자료 부재 등).
|
|
269
271
|
- **자료 한계 보고** — 충돌 분석 시 *2/3순위 fallback 사용했음*을 사용자에게 명시. 묵묵히 진행 X.
|
|
270
272
|
- **destructive 명령 사용자 승인 필수** — `git reset --hard` / `git push --force` / `git branch -D` / `git clean -fd` 사용자 명시 승인 없이 절대 실행 X. (단 `/task-close` 자동 흐름 `git branch -d` + `worktree remove`는 §"작업 브랜치 삭제 정책 — 자동 삭제 면제 조항" 면제)
|
|
271
273
|
- **민감 정보 staging X** — `.env` / `credentials.json` / API key 등 staging 절대 X.
|
|
272
|
-
- **빈 commit 금지 (예외 1건)** — `close`가 변경 없는 영역은 빈 commit을 만들지 않는다. **단 Step 3 `close`의 추적 마커 빈커밋만 예외** — 워크트리 브랜치가
|
|
274
|
+
- **빈 commit 금지 (예외 1건)** — `close`가 변경 없는 영역은 빈 commit을 만들지 않는다. **단 Step 3 `close`의 추적 마커 빈커밋만 예외** — 워크트리 브랜치가 부모보다 앞선 커밋이 0개일 때 채번 보존용으로 1개 허용.
|
|
273
275
|
- **`-m` 옵션 머지 커밋에 사용 금지** — git 기본 메시지 사용.
|
|
274
276
|
- **검수 서버/터널 정리는 자기 것만** — 본 task가 검수용으로 띄운 서버·터널(자기 포트 것)만 종료. `pkill -f` 류 광역 종료 금지 (다른 세션 프로세스까지 죽임). 상세: GIT_RULE "멀티세션 검수 환경".
|
|
275
277
|
|
|
@@ -287,4 +289,4 @@ git -C "$MAIN_WT" worktree remove "$WT_PATH"
|
|
|
287
289
|
- **task 문서 단일 진실 소스** — `.gitignore` 케이스에 따라 메인 워크트리 또는 워크트리. 다른 세션의 충돌 해결 자료 정독 시 *SSoT 조회 → 워크트리 경로 산출* (미등록 케이스) 또는 *메인 워크트리 절대 경로* (등록 케이스).
|
|
288
290
|
- **워크트리 + 브랜치 자동 제거** — GIT_RULE.md "작업 브랜치 삭제 정책" 면제 조항 (taskery 한정). 보존 키워드 시 양쪽 보존.
|
|
289
291
|
- **safety net** — Step 8 복구 명령 출력. 사용자가 잘못 삭제 인지 시 즉시 복구 가능.
|
|
290
|
-
- **BACKLOG.md 무관** — `.project/tasks/<NNN_slug>/BACKLOG.md` 체크 마킹은 `/task-init` Step 7.5가 처리(`[ ]` → `[x]` + `- TASK: TASK-NNN`). `[x]` = *task로 옮김* 의미라 close 시점에 추가 마킹 X. 완료 추적은 `npx @angar2/taskery status`(진행 중이면
|
|
292
|
+
- **BACKLOG.md 무관** — `.project/tasks/<NNN_slug>/BACKLOG.md` 체크 마킹은 `/task-init` Step 7.5가 처리(`[ ]` → `[x]` + `- TASK: TASK-NNN`). `[x]` = *task로 옮김* 의미라 close 시점에 추가 마킹 X. 완료 추적은 `npx @angar2/taskery status`(진행 중이면 부모 미머지가 정상) → 목록에 없을 때만 `git log --all --grep 'BL-NNN'` 순.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: task-init
|
|
3
|
-
description: task 시작 — 워크트리 분기 + 6 섹션 빈 골격 + 헤더
|
|
3
|
+
description: task 시작 — 워크트리 분기 + 6 섹션 빈 골격 + 헤더 6컬럼 (부모 브랜치 자동 기록 · status=draft) task.md 생성 (멀티세션 0.1.2+)
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# /task-init
|
|
@@ -9,7 +9,7 @@ description: task 시작 — 워크트리 분기 + 6 섹션 빈 골격 + 헤더
|
|
|
9
9
|
|
|
10
10
|
새 task 시작 시 호출. **워크트리 분기 + task.md 빈 골격 작성**. Requirements / Scope / Dev Plan / Test Plan 본문은 다음 단계 `/task-plan`에서 채운다.
|
|
11
11
|
|
|
12
|
-
> 메인 워크트리(
|
|
12
|
+
> 메인 워크트리(부모 브랜치 체크아웃 — 기본 dev)에서 호출. 서 있는 브랜치가 곧 부모가 된다. 채번·워크트리·브랜치 생성·골격 작성은 `task_init` 도구(fork)가 init 락 안에서 원자 수행한다.
|
|
13
13
|
|
|
14
14
|
## 호출 시점 + 입력
|
|
15
15
|
|
|
@@ -41,22 +41,22 @@ description: task 시작 — 워크트리 분기 + 6 섹션 빈 골격 + 헤더
|
|
|
41
41
|
|
|
42
42
|
## Step 4 — 분기 + 골격 생성 (`task_init`) + BL 마킹
|
|
43
43
|
|
|
44
|
-
`task_init` 도구(또는 `npx @angar2/taskery fork <타입> <개발자> <출처> <슬러그> --size <규모> --title "<한국어 제목>" [--promote]`) 한 번으로 **채번 → 워크트리·브랜치 생성**(init 락 원자 — 병렬 task-init 번호 충돌·동일 출처 중복·동일 브랜치명을 코드가 차단)에 더해 **빈 골격 task.md 자동 생성**(생성일=오늘 · plan=활성 plan · 유형 자동매핑 `improve`→`improvement` · status=draft · `.gitignore` 케이스로 위치 자동 판정)까지 수행한다.
|
|
44
|
+
`task_init` 도구(또는 `npx @angar2/taskery fork <타입> <개발자> <출처> <슬러그> --size <규모> --title "<한국어 제목>" [--promote]`) 한 번으로 **채번 → 워크트리·브랜치 생성**(init 락 원자 — 병렬 task-init 번호 충돌·동일 출처 중복·동일 브랜치명을 코드가 차단)에 더해 **빈 골격 task.md 자동 생성**(생성일=오늘 · plan=활성 plan · 유형 자동매핑 `improve`→`improvement` · **부모 브랜치=fork 시점 현재 브랜치 자동 기록**(dev 고정 아님) · status=draft · `.gitignore` 케이스로 위치 자동 판정)까지 수행한다.
|
|
45
45
|
|
|
46
46
|
- 반환 `{ taskNum, nnn, branch, wtPath, projectId, docPath, registered, promoted }`. 폴더 승격은 `--promote`/`promote:true`.
|
|
47
|
-
- 에러 시(*"… 이미 진행중"* / *"…
|
|
47
|
+
- 에러 시(*"… 이미 진행중"* / *"… detached HEAD"* — 부모 브랜치(현재 브랜치) 확인·SSoT를 fork가 강제) stderr 메시지 그대로 보고 + 중단.
|
|
48
48
|
- **BL 출처면** 직후 `backlog_mark` 도구(또는 `npx @angar2/taskery backlog-mark BL-NNN TASK-NNN`)로 `[ ]→[x]` + `- TASK:` 마크(콤마 누적, withMetaLock). RM/DR은 skip.
|
|
49
49
|
|
|
50
50
|
## Step 5 — 골격 확인 + 보고
|
|
51
51
|
|
|
52
|
-
반환 `docPath`(절대 경로)를 Read해 헤더
|
|
52
|
+
반환 `docPath`(절대 경로)를 Read해 헤더 6컬럼 + 6 섹션 placeholder + status=draft 확인만 한다. **본문 작성 금지** — Requirements / Scope / Dev Plan / Test Plan은 *반드시* `/task-plan`에서.
|
|
53
53
|
|
|
54
54
|
> **폴백** (반환에 `docPath` 없이 `scaffoldError`만 있을 때만): TASK_DOC_RULE §1.3 형식으로 해당 위치에 직접 작성(생성일=오늘, status=draft, 단일=`<NNN>_<slug>.md` / 폴더=`TASK-<NNN>_<slug>/task.md`).
|
|
55
55
|
|
|
56
56
|
```
|
|
57
57
|
✅ TASK-<NNN> 생성 완료
|
|
58
58
|
- 워크트리: <wtPath> / 브랜치: <branch> / task 문서: <docPath>
|
|
59
|
-
- 헤더: <생성일> / <plan> / <유형> / <규모> / draft
|
|
59
|
+
- 헤더: <생성일> / <plan> / <유형> / <규모> / <부모 브랜치> / draft
|
|
60
60
|
- BL 마킹: BL-<NNN> [x] (BL 출처 시. RM/DR 생략)
|
|
61
61
|
- 다음: /task-plan TASK-<NNN> 으로 기획 채우기
|
|
62
62
|
```
|
|
@@ -66,7 +66,7 @@ description: task 시작 — 워크트리 분기 + 6 섹션 빈 골격 + 헤더
|
|
|
66
66
|
- **워크트리 생성 + 골격 작성만** — 본문 채우기 금지. Requirements / Scope / Dev Plan / Test Plan은 *반드시* `/task-plan`.
|
|
67
67
|
- **단계 경계** — 허용: ROADMAP §4 확인 / SSoT 조회 / `task_init` 호출 / 빈 골격 확인. 금지: ARCHITECTURE/API-SPEC/FEATURES 등 루트 제품 관통 문서 본문 Read · 도메인 코드 Read·Grep · 기존 task 본문 Read (그건 `/task-plan` 영역).
|
|
68
68
|
- **자동 추정 진행 X** — 맥락 명확해도 *제안 + OK* 후 생성.
|
|
69
|
-
- **상태는 `draft` 고정** · **slug 영어 kebab-case** · **헤더
|
|
69
|
+
- **상태는 `draft` 고정** · **slug 영어 kebab-case** · **헤더 6컬럼 모두 채움**(*"미정"* placeholder X — 코드가 채움. 부모 브랜치도 코드 자동 기록).
|
|
70
70
|
- **fork 실패(같은 브랜치명 / 같은 출처 진행중) → 사용자 호출** + 중단(*"다른 세션이 같은 항목 진행 중"*).
|
|
71
71
|
|
|
72
72
|
## 상태 전이
|
|
@@ -24,7 +24,7 @@ MAIN_WT=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")
|
|
|
24
24
|
| 케이스 | task 문서 위치 | 제품 관통 문서 위치 | 동시 쓰기 |
|
|
25
25
|
|--------|--------------|----------------|----------|
|
|
26
26
|
| 등록 (퍼블릭 리포 default) | `$MAIN_WT/.project/tasks/<NNN_slug>/...` | `$MAIN_WT/.project/<doc>.md` (루트 평평) | `withMetaLock` (`bin/lib.js`) |
|
|
27
|
-
| 미등록 | `$WT_PATH/.project/tasks/<NNN_slug>/...` (워크트리 안, 머지 시
|
|
27
|
+
| 미등록 | `$WT_PATH/.project/tasks/<NNN_slug>/...` (워크트리 안, 머지 시 부모 브랜치 반영) | `$MAIN_WT/.project/<doc>.md` (제품 문서는 여전히 단일 소스) | 단일 세션 가정 (워크트리 내부) |
|
|
28
28
|
|
|
29
29
|
본문 모든 `.project/...` 경로는 *위 분기에 따라* 적용. spec-diff / mockup / screenshots는 task 문서와 같은 위치.
|
|
30
30
|
|
|
@@ -364,7 +364,7 @@ Test Plan 작성 완료 후 *반드시* 점검 단계 수행. *"누적/리셋
|
|
|
364
364
|
|
|
365
365
|
## 주의사항
|
|
366
366
|
|
|
367
|
-
- **본문 영역만 작성** — 헤더
|
|
367
|
+
- **본문 영역만 작성** — 헤더 6컬럼 중 *상태*만 갱신(`draft` → `planned`). 생성일/플랜/유형/규모/부모 브랜치는 `/task-init`에서 작성된 값 유지. 단 Step 4에서 *규모가 명백히 잘못 추정*된 게 드러나면 사용자 confirm 후 갱신 OK (예: medium 추정했는데 Phase 8개 나옴 → large 갱신 + 분리 검토).
|
|
368
368
|
- **Phase 선제적 일괄 작성 금지** — small task에 medium 분량 Phase 작성하는 함정 회피. 명확한 것만 작성하고 진행 중 추가.
|
|
369
369
|
- **Test Plan = 실질 동작 시나리오 (유닛 테스트 X)** — Test Plan은 *요구사항이 정상 동작하는지* 검증. 유닛 테스트는 task-dev Step 6.5에서 단일 시점 실행 (코드 정상성 영역, Test Plan과 직교).
|
|
370
370
|
- **Test Plan 자기완결적** — 격리 세션이 *task.md만 보고* 수행 가능해야. 메인의 plan/dev 컨텍스트 *"위 Phase에서 만든 X"* 같은 표현 금지. 코드/동작/명령만 명시.
|