@angar2/taskery 0.3.0 → 0.4.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 CHANGED
@@ -110,7 +110,7 @@ my-app/
110
110
  ├─ .gitignore
111
111
  ├─ .claude/
112
112
  │ ├─ settings.json # hook 등록 (PreToolUse 매칭)
113
- │ ├─ skills/<skill-name>/SKILL.md # 9 skill
113
+ │ ├─ skills/<skill-name>/SKILL.md # 9 skill (+ Claude 전용 run-team)
114
114
  │ └─ hooks/<hook-name>.sh # 2 hook
115
115
  └─ .project/
116
116
  ├─ PROJECT.md # 프로젝트 개요
@@ -133,7 +133,8 @@ my-app/
133
133
  ├─ shared/ # 관계 리포지토리 소통 메세지함
134
134
  │ ├─ sent/completed/
135
135
  │ └─ received/completed/
136
- └─ FRICTION_LOG.md # taskery 불편사항 누적 로그
136
+ ├─ FRICTION_LOG.md # taskery 불편사항 누적 로그
137
+ └─ TEST-GUIDE.md # 검증 방법 (방식별 이 프로젝트 실행 경로 — /task-plan이 채움)
137
138
  ```
138
139
 
139
140
  `.project/`는 플랫폼 무관 공통 자산이다. 진입 문서와 에이전트 자산 위치는 선택한 플랫폼에 따라 갈린다.
@@ -191,9 +192,12 @@ my-app/
191
192
  | `/task-close` | task | 최종 검증 후 git 커밋 + dev 브랜치 `--no-ff` 병합 |
192
193
  | `/add-backlog` | meta | 사용자 발화로 plan(기능 그룹)별 `tasks/<NNN_slug>/BACKLOG.md`에 task 후보 1건씩 추가 (0.1.2+) |
193
194
  | `/log-friction` | meta | 사용자 불편을 `.project/FRICTION_LOG.md`에 한 행 기록 |
195
+ | `/run-team` | meta | 여러 작업을 묶을 수 있는 단위로 task로 나눠 자동 병렬 처리 — 리더 세션이 팀원(독립 세션)에게 task를 1건씩 분배하고 흐름을 관리 (Claude 전용 · 실험 기능 전제 · 0.3.x+) |
194
196
 
195
197
  각 스킬은 슬래시로 직접 호출하거나, 사용자 발화의 의미가 스킬의 frontmatter description과 매칭되면 메인 세션이 자동으로 발동시킨다.
196
198
 
199
+ > `/run-team`은 Claude의 실험적 멀티 세션 기능(agent teams)을 사용하는 Claude 전용 스킬이다. 기본 흐름이 아니라 *"이 작업들 한 번에 팀으로 진행해"* 같은 발화가 있을 때만 발동하며, 활성화에는 환경 변수(`CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`) 설정이 필요하다. Codex 설치에는 포함되지 않는다.
200
+
197
201
  `/project-init`, `/plan-init`, `/task-init` 세 스킬은 호출 시 다음 문서를 생성한다.
198
202
 
199
203
  | 스킬 | 생성 위치 | 생성 문서 |
package/bin/fork.js ADDED
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * bin/fork.js
4
+ * `npx @angar2/taskery fork <type> <dev> <src> <slug>` — task 분기.
5
+ *
6
+ * [채번 → 워크트리·브랜치 생성]을 init 락으로 원자 실행 (병렬 task-init 번호 충돌 차단).
7
+ * 성공 시 결과 JSON 한 줄을 stdout으로 출력 ({ taskNum, nnn, branch, wtPath, projectId }).
8
+ * 실패(정책 위배 / 동일 항목 진행중 / 동일 브랜치명 등) 시 stderr + exit 1.
9
+ *
10
+ * 호출 주체: `/task-init` Step 6 (사용자 직접 호출도 가능하나 통상 스킬 경유).
11
+ */
12
+
13
+ const path = require('path');
14
+
15
+ const {
16
+ MANIFEST_NAME,
17
+ readManifest,
18
+ getMainWorktreePath,
19
+ forkTask,
20
+ } = require('./lib');
21
+
22
+ const VALID_TYPES = ['feature', 'bug', 'improve', 'refactor', 'docs', 'chore'];
23
+
24
+ async function main() {
25
+ const [type, dev, src, slug] = process.argv.slice(2);
26
+
27
+ if (!type || !dev || !src || !slug) {
28
+ console.error('사용법: npx @angar2/taskery fork <type> <dev> <src> <slug>');
29
+ console.error('예: npx @angar2/taskery fork feature claude BL-003 login-feature');
30
+ process.exit(1);
31
+ }
32
+ if (!VALID_TYPES.includes(type)) {
33
+ console.error(`type은 ${VALID_TYPES.join(' | ')} 중 하나 (받음: ${type})`);
34
+ process.exit(1);
35
+ }
36
+ if (!/^(BL-\d+|RM-\d+|DR)$/.test(src)) {
37
+ console.error(`src는 BL-NNN | RM-NNN | DR 형식 (받음: ${src})`);
38
+ process.exit(1);
39
+ }
40
+ if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(slug)) {
41
+ console.error(`slug은 영어 kebab-case (받음: ${slug})`);
42
+ process.exit(1);
43
+ }
44
+
45
+ let mainWt;
46
+ try {
47
+ mainWt = getMainWorktreePath();
48
+ } catch (e) {
49
+ console.error(`taskery fork 실패: ${e.message}`);
50
+ process.exit(1);
51
+ }
52
+ if (!readManifest(path.join(mainWt, MANIFEST_NAME))) {
53
+ console.error(`${MANIFEST_NAME} 없음 — 'npx @angar2/taskery init' 먼저 실행 필요.`);
54
+ process.exit(1);
55
+ }
56
+
57
+ try {
58
+ const result = await forkTask(mainWt, { type, dev, src, slug });
59
+ process.stdout.write(JSON.stringify(result) + '\n');
60
+ } catch (e) {
61
+ console.error(`taskery fork 실패: ${e.message}`);
62
+ process.exit(1);
63
+ }
64
+ }
65
+
66
+ main();
package/bin/lib.js CHANGED
@@ -19,10 +19,12 @@
19
19
  * - getWorktreesRoot(projectId): ~/.taskery/worktrees/<projectId>
20
20
  * - getWorktreePath(projectId, { taskNum, src, slug }): 특정 task 워크트리 경로
21
21
  * - getMergeLockPath(projectId): ~/.taskery/<projectId>.merge.lock
22
+ * - getInitLockPath(projectId): ~/.taskery/<projectId>.init.lock (task 분기 직렬화용)
22
23
  * - withMergeLock(projectId, fn, opts): proper-lockfile로 머지 락 + fn 실행 + 자동 release
23
24
  * - withMetaLock(filePath, fn, opts): 메타 파일 쓰기 락 + fn 실행
24
25
  * - getActiveTasks(mainWtPath): SSoT 조회 (git branch --no-merged dev --list 'feature/*_TASK-*' ...)
25
26
  * - getNextTaskNumber(mainWtPath): (진행중 ∪ dev 머지 히스토리) 최대 + 1
27
+ * - forkTask(mainWtPath, {type,dev,src,slug}): init 락 안에서 채번+워크트리·브랜치 생성 원자 실행
26
28
  * - assertMainWorktreeOnDev(mainWtPath): 메인 워크트리가 dev 체크아웃 상태인지 검증
27
29
  * - assertDevExists(mainWtPath): dev 브랜치 존재 검증
28
30
  *
@@ -254,6 +256,10 @@ function getMergeLockPath(projectId) {
254
256
  return path.join(TASKERY_HOME, `${projectId}.merge.lock`);
255
257
  }
256
258
 
259
+ function getInitLockPath(projectId) {
260
+ return path.join(TASKERY_HOME, `${projectId}.init.lock`);
261
+ }
262
+
257
263
  async function withMergeLock(projectId, fn, opts = {}) {
258
264
  const lockfile = require('proper-lockfile');
259
265
  const lockPath = getMergeLockPath(projectId);
@@ -369,6 +375,34 @@ function getNextTaskNumber(mainWtPath) {
369
375
  return Math.max(...all) + 1;
370
376
  }
371
377
 
378
+ async function forkTask(mainWtPath, { type, dev, src, slug }) {
379
+ // task 분기 — [채번 → 워크트리·브랜치 생성]을 init 락(withMetaLock)으로 원자화.
380
+ // 병렬 task-init이 같은 번호를 읽고 각자 브랜치를 만들던 레이스(TOCTOU)를 차단: 락 안에서
381
+ // getNextTaskNumber 직후 worktree add까지 끝내므로 다음 호출은 늘어난 번호를 본다.
382
+ const projectId = getProjectId(mainWtPath);
383
+ return withMetaLock(getInitLockPath(projectId), () => {
384
+ assertDevExists(mainWtPath);
385
+ assertMainWorktreeOnDev(mainWtPath);
386
+ // SSoT 안전망 — 같은 출처(BL-NNN/RM-NNN)가 이미 진행중이면 거부. DR은 별도 ID 없어 검사 제외.
387
+ if (src !== 'DR') {
388
+ const dup = getActiveTasks(mainWtPath).find((t) => t.src === src);
389
+ if (dup) {
390
+ throw new Error(`${src} 이미 진행중 (${dup.branch}) — 다른 세션이 같은 항목 진행`);
391
+ }
392
+ }
393
+ const taskNum = getNextTaskNumber(mainWtPath); // 락 안 → TOCTOU 차단
394
+ const nnn = String(taskNum).padStart(3, '0');
395
+ const branch = `${type}/${dev}_TASK-${nnn}_${src}_${slug}`;
396
+ const wtPath = getWorktreePath(projectId, { taskNum, src, slug });
397
+ mkdirp(getWorktreesRoot(projectId));
398
+ // 동일 브랜치명은 git이 거부(같은 항목 동시 분기 차단). 실패 시 throw → fork.js가 사용자 보고.
399
+ execFileSync('git', ['-C', mainWtPath, 'worktree', 'add', wtPath, '-b', branch, 'dev'], {
400
+ stdio: 'pipe',
401
+ });
402
+ return { taskNum, nnn, branch, wtPath, projectId };
403
+ });
404
+ }
405
+
372
406
  function computeNextPlanNumber(mainWtPath) {
373
407
  // .project/plans/ 하위 'NNN_<slug>' 디렉토리 스캔 → NNN max+1, 3자리 zero-pad 문자열 반환.
374
408
  // plan = 기능 그룹 단위, 폴더명 앞 3자리 채번(TASK-NNN과 결 맞춤). slug은 호출측(plan-init)이 결합.
@@ -589,6 +623,7 @@ module.exports = {
589
623
  getWorktreesRoot,
590
624
  getWorktreePath,
591
625
  getMergeLockPath,
626
+ getInitLockPath,
592
627
  withMergeLock,
593
628
  withMetaLock,
594
629
  assertDevExists,
@@ -596,6 +631,7 @@ module.exports = {
596
631
  parseBranchName,
597
632
  getActiveTasks,
598
633
  getNextTaskNumber,
634
+ forkTask,
599
635
  computeNextPlanNumber,
600
636
  // 백로그 (0.1.2+)
601
637
  BACKLOG_PLACEHOLDER,
package/bin/taskery.js CHANGED
@@ -9,6 +9,7 @@
9
9
  * update — 최신 버전 fetch + manifest 비교 + 머지 갱신
10
10
  * status — 멀티세션 현황 요약 (진행중 태스크 / 워크트리 / 머지 락, 0.1.2+)
11
11
  * prune — stale 워크트리 / 브랜치 대화형 정리 (0.1.2+)
12
+ * fork — task 분기 (채번+워크트리+브랜치 생성을 init 락으로 원자 실행, 0.3.2+)
12
13
  * help — 사용법 출력
13
14
  */
14
15
 
@@ -34,6 +35,7 @@ function help() {
34
35
  npx @angar2/taskery update 최신 버전 fetch + 머지 갱신
35
36
  npx @angar2/taskery status 멀티세션 현황 (진행중 태스크 / 워크트리 / 머지 락)
36
37
  npx @angar2/taskery prune stale 워크트리 / 브랜치 대화형 정리
38
+ npx @angar2/taskery fork <type> <dev> <src> <slug> task 분기 (통상 /task-init 경유)
37
39
  npx @angar2/taskery help 도움말
38
40
 
39
41
  새 프로젝트 시작:
@@ -74,6 +76,9 @@ switch (sub) {
74
76
  }
75
77
  runScript('prune.js');
76
78
  break;
79
+ case 'fork':
80
+ runScript('fork.js');
81
+ break;
77
82
  case 'help':
78
83
  case '--help':
79
84
  case '-h':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angar2/taskery",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Lightweight task system for Claude Code & Codex — 1 메인 세션(또는 멀티세션) + 스킬 9종 + catastrophic hook 안전망 2종",
5
5
  "bin": {
6
6
  "taskery": "bin/taskery.js",
@@ -0,0 +1,145 @@
1
+ ---
2
+ name: run-team
3
+ description: agent teams로 다건 태스크를 독립 워크트리에서 자동 병렬 처리 — 리더 세션이 팀원(독립 세션)에게 태스크 1개씩 분배하고 흐름을 관리한다. 트리거 발화(예 "백로그 한 번에 진행해" / "팀에게 전부 독립으로 맡겨") 시에만 발동. Claude 전용 · 실험 기능(agent teams) 전제.
4
+ ---
5
+
6
+ # /run-team
7
+
8
+ ## 개요
9
+
10
+ 다건 태스크(백로그 여러 건 / 명시된 작업 묶음)를 **agent teams로 자동 병렬 처리**한다. 리더 메인 세션 1개가 작업을 태스크 단위로 묶어 결정하고, 각 태스크를 **팀원(독립 Claude Code 세션)** 에게 1개씩 맡긴다. 각 팀원은 taskery 태스크 5단계(`/task-init` ~ `/task-close`)를 *독립 워크트리*에서 수행한다.
11
+
12
+ 이 스킬이 추가하는 것은 *세션 오케스트레이션 레이어*뿐이다. **워크트리 격리는 `/task-init`이, 머지 직렬화·충돌 3단계 해결은 `/task-close`가 이미 담당**한다 — 본 스킬은 그 위에서 누가 어떤 태스크를 맡고 어디서 멈추는지만 조율한다.
13
+
14
+ > **taskery 기본 플로우가 아니다.** 디폴트는 *1세션 1태스크* (사용자가 직접 지시). 본 스킬은 *트리거 발화가 있을 때만* 발동하는 고기능이다. 평소 단일 태스크 흐름을 침범하지 않는다.
15
+
16
+ ## 팀원 = agent teams 팀원 (Task tool 서브에이전트 아님)
17
+
18
+ 본 스킬의 "팀원"은 **Claude Code의 agent teams 기능으로 생성되는 독립 세션**이다. **Task tool 서브에이전트로 대체하는 것을 영구 금지**한다 — 둘은 다르다.
19
+
20
+ | | Task tool 서브에이전트 (금지) | agent teams 팀원 (본 스킬) |
21
+ |---|---|---|
22
+ | 세션 | 리더 컨텍스트 *안*의 백그라운드 워커 | **독립 세션 · 자체 컨텍스트 윈도우** |
23
+ | 사용자 접근 | 직접 들어가 지시 불가 | **사용자가 직접 들어가 확인·지시 가능** |
24
+ | 통신 | 결과만 리더에 반환 | 팀원↔리더 자동 통지 + 팀원끼리 직접 통신 (리더가 다 봄) |
25
+ | 스킬 호출 | 제한적 | 프로젝트 `.claude/skills/` 그대로 로드 → taskery 5스킬 직접 호출 |
26
+
27
+ 팀원은 각자 새 세션으로 시작하며 프로젝트 `CLAUDE.md`를 자동 로드하므로 taskery 룰을 그대로 따른다.
28
+
29
+ ## 호출 시점
30
+
31
+ - **트리거 발화** — 다건을 한 번에 / 팀으로 / 독립 병렬로 처리하라는 의도:
32
+ - *"백로그 BL-003, 005, 007 한 번에 진행해"*
33
+ - *"백로그에 쌓인 거 묶을 수 있는 단위로 팀한테 전부 맡겨"*
34
+ - *"이 작업들 독립으로 병렬 처리해"*
35
+ - **단일 태스크 의도면 발동 금지** — *"로그인 기능 추가해줘"* 류 단건은 기존 `/task-init` 흐름. 본 스킬 진입 X.
36
+
37
+ ## 입력 처리
38
+
39
+ 인자 = (선택) 대상 작업 목록 / 중단점 모드. 예: `/run-team BL-003 BL-005 BL-007` / `/run-team BL-003~009 --until test` / 인자 없이 발화로.
40
+
41
+ - 대상이 모호하면 한 번에 한 질문씩 확인. **자동 추정으로 팀 가동 금지** — 태스크 묶음·팀원 수·중단점 모드 확정 후 진입.
42
+
43
+ ## 단계
44
+
45
+ ### Step 0 — 가드 (플랫폼 + 활성화)
46
+
47
+ 본 스킬 진입 즉시 두 가드를 통과해야 한다. 하나라도 실패 시 *팀을 만들지 말고* 안내 후 중단.
48
+
49
+ 1. **플랫폼 가드** — agent teams는 Claude 전용. 현재 세션이 Codex면 *"agent teams는 Claude 전용이라 Codex에서는 자동 병렬을 지원하지 않는다. 단일 태스크 흐름(`/task-init`)으로 진행하라"* 안내 + 중단. (본 스킬은 Claude 자산으로만 설치되므로 정상 경로에선 도달하지 않으나, 발화가 Codex 세션에 들어온 경우 대비.)
50
+ 2. **활성화 가드** — agent teams는 기본 비활성. 활성 여부 확인:
51
+ ```sh
52
+ [ "$CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS" = "1" ] || echo "agent teams 비활성"
53
+ ```
54
+ - 비활성이면 *팀 가동을 시작하지 말고* 다음을 안내 + 중단:
55
+ - *"agent teams는 실험 기능이라 기본 비활성이다. `~/.claude/settings.json`의 `env`에 `\"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS\": \"1\"`를 넣고 세션을 재시작한 뒤 다시 호출하라. (팀원을 split pane으로 보려면 `env`가 아닌 최상위에 `\"teammateMode\": \"tmux\"`도 — 기본값 `auto`는 환경 자동 감지 후 in-process 폴백.)"*
56
+ - 꺼진 채로 헛도는 것을 막기 위한 안전망이다.
57
+
58
+ ### Step 1 — 작업 수집
59
+
60
+ - 대상이 백로그면 `$MAIN_WT/.project/tasks/<활성 plan>/BACKLOG.md`에서 해당 BL 항목 정독 (개요 / 대상 영역).
61
+ ```sh
62
+ MAIN_WT=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")
63
+ ```
64
+ - 대상이 명시 작업 목록이면 그 발화를 그대로.
65
+ - 활성 plan은 `$MAIN_WT/.project/AGENT-GUIDE.md`에서 검출.
66
+
67
+ ### Step 2 — 태스크 묶기 판단
68
+
69
+ 수집한 작업을 **태스크 단위로 묶는다**. 백로그 1건 = 태스크 1개가 *필수 아님* — 작업 범위·응집도에 따라 여러 건을 한 태스크로 묶거나 한 건을 그대로 둔다 (리더 판단).
70
+
71
+ - **응집 기준**: 같은 모듈·파일군을 건드리거나 의존 관계가 있으면 한 태스크로 묶어 *충돌·중복을 사전 회피*. 서로 독립이면 분리해 병렬 이득 확보.
72
+ - **팀원 수 권장 3~5** (agent teams 권장치). 결정된 태스크가 5개를 넘으면 *배치로 분할* — 1차 배치 완료(또는 중단점 도달) 후 2차 배치. 한 번에 다 띄우지 않는다 (토큰·조율 부담).
73
+ - 묶음 결과를 사용자에게 제시 + confirm: *"태스크 N개로 묶었다 — ① <묶음>(BL-003+005) ② <묶음>(BL-007) … 팀원 N명. 이대로 갈까?"*
74
+
75
+ ### Step 3 — 중단점 모드 결정
76
+
77
+ 각 팀원이 taskery 5단계 중 *어디까지 자동 진행하고 어디서 멈출지* 결정한다.
78
+
79
+ - **기본 = 단계별 정지** — 별도 지시가 없으면 각 팀원은 *한 단계 수행 후 멈추고 리더에 보고*. 사용자(또는 리더)가 다음 단계를 지시할 때까지 대기.
80
+ - **구간 자동** — 사용자 지시(*"init부터 test까지 자동으로"*, *"--until test"*)가 있으면 해당 구간을 멈춤 없이 진행.
81
+ - **`/task-close`(머지) 게이트 권장** — 자동 구간이라도 close는 *되돌릴 수 없는 dev 병합*이므로, 별도의 명시 지시가 없으면 close 직전에 멈춰 사용자 확인을 받도록 팀원에 지시한다.
82
+ - 모드는 팀원마다 다르게 줄 수 있다 (태스크별 난이도에 따라).
83
+
84
+ ### Step 4 — agent teams 팀원 생성 + 자기완결 지시
85
+
86
+ > **팀원은 agent teams 팀원으로만 생성**한다 (위 "팀원 = agent teams 팀원" 절). Task tool 서브에이전트 대체 영구 금지.
87
+
88
+ 리더가 결정한 태스크 수만큼 팀원을 만들고, 각 팀원에게 **태스크 1개 + 자기완결 지시**를 준다. 팀원 지시에 다음을 모두 포함한다 (팀원은 리더 컨텍스트를 공유하지 않으므로 *그 자체로 완결*되어야 한다):
89
+
90
+ 1. **맡을 작업** — 태스크 주제 / 출처(BL-NNN 등) / 묶음이면 포함 항목 전부 + 각 개요·대상 영역.
91
+ 2. **시작 위치** — *메인 워크트리(dev 체크아웃) cwd에서 `/task-init`을 먼저 호출*. `/task-init`이 독립 워크트리·브랜치를 분기한다.
92
+ 3. **이동** — `/task-init` 결과 보고에 출력된 *워크트리 경로로 cd*한 뒤 후속 단계(`/task-plan` 이후)를 그 워크트리에서 수행.
93
+ 4. **중단점 모드** — Step 3에서 정한 구간/정지 규칙. 예: *"`/task-init`과 `/task-plan`까지만 수행하고 멈춰 리더에 보고하라"*.
94
+ 5. **보고 의무** — 각 중단점 도달 시 *상태(어느 단계까지 / 결과 요약 / 막힌 점)* 를 리더에 보고.
95
+
96
+ - 여러 팀원이 동시에 메인 워크트리 cwd에서 `/task-init`을 호출해도, `/task-init`이 부르는 `fork` 명령이 **init 락으로 [채번~워크트리 분기]를 직렬화**(번호 충돌 차단)하고 브랜치명 유일성을 git이 보장하므로 충돌을 막는다. 각 팀원의 작업은 *물리적으로 분리된 워크트리 디렉토리*(`~/.taskery/worktrees/...`)에서 진행되므로 **agent teams의 "같은 파일 동시 편집 → 덮어쓰기" 제약이 구조적으로 회피된다.**
97
+
98
+ ### Step 5 — 모니터링
99
+
100
+ - 팀원이 중단점에 도달하거나 idle 상태가 되면 agent teams가 리더에 자동 통지한다 (폴링 불필요).
101
+ - 리더는 각 팀원의 보고를 모아 *사용자에게 한 곳에서 종합 보고*: *"팀원 ①은 TASK-007 plan 완료·대기 / 팀원 ②는 TASK-008 dev 진행 중 …"*.
102
+ - 사용자는 리더를 통해 다음 단계를 지시하거나, 특정 팀원 세션에 *직접 들어가* 지시할 수 있다 (in-process 화살표/Enter, 또는 `teammateMode` split pane).
103
+ - **task 상태 lag 주의** — 팀원이 단계 완료를 제때 마킹하지 못해 후속이 막히면, 리더가 해당 팀원에 *상태 갱신/진행을 한 번 nudge*한다.
104
+
105
+ ### Step 6 — 머지 조율
106
+
107
+ 각 팀원의 `/task-close`는 **머지 락으로 자동 직렬화**된다 (동시 close 시도해도 한쪽씩 순차). 리더가 추가로 할 일:
108
+
109
+ - 완료 순서를 *대기열로 관리* — 여러 팀원이 동시에 close 단계면 순차 진행을 안내.
110
+ - `/task-close`의 충돌 3단계(단순 자동 / 의미적 자료 / 판단 불가)는 각 팀원이 자체 수행한다. **판단 불가(4-c)에 이르면 팀원이 사용자 호출**하도록 되어 있으므로, 리더는 그 호출을 사용자에게 전달한다.
111
+ - 모든 태스크 close 후 *전체 결과를 종합 보고*: 머지된 TASK 목록 / 충돌 해결 내역 / 미완(중단점에서 멈춘 것) 목록.
112
+
113
+ ## 도구 가이드
114
+
115
+ - **Bash**: 메인 워크트리 검출 / 활성화 가드 확인 / BACKLOG·AGENT-GUIDE 정독용 경로 산출
116
+ - **Read**: `$MAIN_WT/.project/tasks/<활성 plan>/BACKLOG.md` / `$MAIN_WT/.project/AGENT-GUIDE.md`
117
+ - **agent teams (자연어)**: 팀원 생성 + 태스크별 지시 + 모니터링. (전용 슬래시 명령은 없음 — 리더가 자연어로 팀을 가동)
118
+ - **AskUserQuestion**: 태스크 묶음 / 팀원 수 / 중단점 모드 확인
119
+
120
+ ## 주의사항
121
+
122
+ - **agent teams 팀원으로만** — Task tool 서브에이전트 대체 영구 금지 (자체 컨텍스트·사용자 직접 접근·리더 관리가 본 스킬의 전제).
123
+ - **트리거 한정 발동** — 단일 태스크 의도에는 진입 금지. 기본 플로우(1세션 1태스크) 비침범.
124
+ - **두 가드 선통과** — 플랫폼(Claude) + 활성화(env) 미충족 시 팀을 만들지 말고 안내 후 중단.
125
+ - **워크트리·머지는 기존 스킬이 담당** — 본 스킬은 worktree를 직접 만들거나 머지하지 않는다. 팀원이 `/task-init`·`/task-close`를 호출하게 할 뿐. 본 스킬에서 git 명령 직접 실행 금지.
126
+ - **팀원 지시는 자기완결** — 리더 컨텍스트 미공유. 작업·시작 위치·이동·중단점·보고 의무를 지시에 모두 명시.
127
+ - **권장 3~5 팀원 / 초과 시 배치** — 토큰 비용은 팀원 수에 선형 증가. 한 번에 다 띄우지 않는다.
128
+ - **`/task-close` 게이트 보존 권장** — 자동 구간이라도 되돌릴 수 없는 머지는 사용자 확인 후.
129
+ - **in-process resume 취약** — agent teams는 `/resume`·`/rewind`로 in-process 팀원이 복원되지 않는다. 세션 중단 후 재개 시 *살아있지 않은 팀원에 메시지 시도*가 발생할 수 있으므로, 재개하면 팀원을 새로 만들고 진행 상태는 `npx @angar2/taskery status`로 재확인한다.
130
+ - **split pane 환경 의존** — 팀원을 분할 화면으로 보려면 tmux/iTerm2 필요. VS Code 통합 터미널 등은 in-process(화살표 전환)만 가능.
131
+
132
+ ## 상태 전이
133
+
134
+ | 진입 시 | 종료 시 |
135
+ |--------|--------|
136
+ | (해당 없음 — 오케스트레이터) | (해당 없음) |
137
+
138
+ 본 스킬은 task 상태를 직접 전이시키지 않는다. **상태 전이는 각 팀원이 호출하는 task 5스킬이 수행**한다 (`/task-init`=draft … `/task-close`=closed). 본 스킬은 누가 무엇을 어디까지 진행하는지만 조율한다.
139
+
140
+ ## 멀티세션 정합
141
+
142
+ - **기존 멀티세션 위에 얹힘** — taskery는 이미 *여러 메인 세션이 독립 워크트리로 병렬* 진행을 지원한다. 본 스킬은 그 "여러 세션"을 사용자가 일일이 띄우는 대신 *리더가 agent teams로 띄우고 관리*하게 하는 것뿐이다. 워크트리·머지 락·충돌 해결 정합은 기존과 동일.
143
+ - **머지 락** — `~/.taskery/<projectId>.merge.lock` 단일. 팀원들의 close가 직렬화된다.
144
+ - **race 차단** — 동시 `/task-init`은 브랜치명 유일성 + SSoT 2층으로 차단 (기존 `/task-init` 정합 그대로).
145
+ - **완료 추적** — `npx @angar2/taskery status` (진행 중 팀원/워크트리) + dev 머지 히스토리.
@@ -1,20 +1,32 @@
1
1
  # taskery 격리 검증 서브에이전트 — /task-test가 spawn하는 독립 tester
2
2
  #
3
3
  # 클로드의 "Task tool 격리 세션"에 대응하는 코덱스 서브에이전트 정의.
4
- # 메인 세션의 plan/dev 컨텍스트(확증편향) 없이 task.md만 보고 독립 검증한다.
4
+ # 메인 세션의 plan/dev 컨텍스트(확증편향) 없이 task.md + TEST-GUIDE.md를 보고 독립 검증한다.
5
+ #
6
+ # ⚠ 미러 — 아래 developer_instructions는 `.agents/skills/task-test/SKILL.md`의 격리 펜스(수행 룰)와
7
+ # *같은 효과*를 내야 한다. /task-test는 그 펜스 전체를 spawn prompt(task)로 전달하므로 펜스가 단일 룰
8
+ # 소스이며, 이 지침은 코덱스에서 동일 효과를 보장하는 기준선이다. 펜스의 룰이 바뀌면 이 지침도 함께 갱신한다.
9
+ # (클로드 = general-purpose 서브에이전트 + 펜스 / 코덱스 = 본 에이전트 + 펜스 — 양쪽 동일 효과)
5
10
  #
6
11
  # ⚠ model 명시 필수 — 생략하면 자식 모델 resolve가 service tier validation에서 실패한다.
7
12
  # 환경의 사용 가능 모델에 맞게 조정한다(기본 gpt-5.5).
8
13
 
9
14
  name = "task-tester"
10
- description = "격리 검증 에이전트. 메인의 확증편향 없이 task.md만 보고 Test Plan을 독립 수행한다. /task-test가 spawn한다."
15
+ description = "격리 검증 에이전트. 메인의 확증편향 없이 task.md + TEST-GUIDE를 보고 Test Plan을 증거 기반으로 독립 수행한다. /task-test가 spawn한다."
11
16
  model = "gpt-5.5"
12
17
  sandbox_mode = "read-only"
13
18
  developer_instructions = """
14
- 너는 격리된 tester다. 메인 세션의 가정을 일체 신뢰하지 말고, 넘겨받은 task.md 하나만 보고 자기완결적으로 검증하라.
19
+ 너는 격리된 tester다. 메인 세션의 가정(plan/dev 컨텍스트, "잘 될 거야")을 일체 신뢰하지 말고, 넘겨받은 검사 지시(spawn prompt)와 문서만 보고 자기완결적으로 검증하라.
20
+
21
+ 검사 룰의 단일 소스 = spawn 시 넘겨받은 검사 지시(prompt)다. 그것을 그대로 따르며, 아래는 그 핵심을 코덱스에서 동일하게 보장하는 기준선이다(넘겨받은 지시와 충돌 시 넘겨받은 지시 우선):
22
+
23
+ - 넘겨받은 task.md와 TEST-GUIDE.md를 직접 읽는다(task.md가 목업을 참조하면 목업도). "task.md 하나만"이 아니다 — TEST-GUIDE는 각 검증 방식을 *이 프로젝트에서 실제로 어떻게 실행하나*(DB 조회 경로 / API 호출법 / E2E 셋업 / 시각 실행)의 단일 소스다.
15
24
  - task.md의 ## Test Plan 시나리오를 순서대로 그대로 수행한다(임의 추가/스킵/변형 금지).
16
- - [AUTO] 시나리오는 실제 명령을 실행하고 로그를 확보한다. 실행 없이 pass 처리 금지.
17
- - [USER] 시나리오는 UNCERTAIN으로 분류하고 사용자 직접 검수 항목으로 리턴한다.
18
- - 코드와 동작만 신뢰한다. "잘 될 것"이라는 가정 금지. 코드 파일 수정 금지.
19
- - 검증을 마치면 task-test SKILL.md의 결과 형식(종합 판정 / 시나리오별 결과 / 검증·테스트 명령 결과)을 그대로 리턴한다.
25
+ - 문 앞 검사(먼저) — 각 [AUTO] 시나리오가 [실행 명령/입력 + 구체적·관측 가능한 기대값(출력값/상태코드/어서션/조회 결과/화면 상태)] 한 쌍을 갖췄는지 확인한다. "정상 동작/잘 됨/확인한다" 같은 소원은 기대값이 아니다. 미달인 [AUTO]는 검사하지 말고 "시험문제 결함(검증 불가)"으로 분류 + 무엇이 빠졌는지 지목한다(PASS 반올림 금지).
26
+ - 증거 일치 시만 PASS — 자격 갖춘 [AUTO]는 실제로 실행해 *관측 결과가 기대값과 일치할 때만* PASS. 코드 정독 "그럴듯함"·추정으로 PASS 금지. 증거(명령 출력/응답 로그/캡처/조회 결과)를 첨부한다.
27
+ - [AUTO]/[USER] 분류 — [USER]는 정답지가 *주관*인 것만(시각 미세 취향 / 색조·2px / UX 느낌) → UNCERTAIN(사람 검수) + 증거 캡처로 리턴. 시각 *객관* 깨짐(요소 빠짐/빈 화면/배치 딴판/색 완전히 틀림)은 [USER]가 아니라 [AUTO] — 실제 화면 캡처 → 승인 목업과 대조해 네가 직접 판정한다(어긋나면 *구체적 어긋남 목록* + FAIL). 시각이라고 무조건 사람에게 넘기지 마라.
28
+ - UNCERTAIN 두 종류 — (사람 검수)=정답지가 주관인 영역 / (검증 불가)=[AUTO]인데 대조할 기대값을 구성 못 함=시험문제 결함. 검증 불가는 PASS로 반올림하거나 사람 검수로 떠넘기지 말고, *무엇을 시도했고 왜 기대값을 구성할 수 없는지* 근거를 반드시 기록한다(task-plan으로 Test Plan을 보수해야 하는 신호).
29
+ - 신규 테스트 추가 시 카운트 +N / 모든 PASS만으로 단정 X — 신규 *테스트 식별자*(suite/class/function 등)가 runner 로그에 실제 등장했는지 grep으로 확인한다(1 hit 이상, 없으면 Suite 미실행 → FAIL). grep/Read 존재 확인은 보조용 — 실동작 PASS를 대체할 수 없다.
30
+ - 코드와 동작만 신뢰한다. 코드 파일 수정 금지. 임시 파일(테스트 스크립트 등)은 종료 후 삭제한다.
31
+ - 검증을 마치면 넘겨받은 검사 지시의 결과 형식(종합 판정 PASS/FAIL/UNCERTAIN / 시나리오별 결과 / 검증·테스트 명령 결과 / UNCERTAIN 2종 / 시험문제 결함)을 그대로 리턴한다.
20
32
  """
@@ -0,0 +1,51 @@
1
+ # TEST-GUIDE
2
+
3
+ > 이 프로젝트에서 각 테스트 방식을 *실제로 어떻게 실행하나*의 단일 소스.
4
+ > **제품 관통 문서 아님 — 검증 방법 문서.** (제품 스펙은 `.project/` 루트 그룹 A/B 문서 참조.)
5
+ > `/task-test` 격리 세션이 task.md와 함께 이 파일을 읽어, 명세된 방식을 *이 프로젝트의 실제 경로*로 실행한다.
6
+
7
+ **사용 규칙**:
8
+ - 처음엔 빈 골격 — 검증 방법을 아직 모름. `/task-plan`이 방식별 실행 경로를 *사용자에게 확인받은 즉시* 채운다.
9
+ - 각 항목 = "이 프로젝트에서의 실제 실행 방법" (명령 · 경로 · 연결). 추상 설명 아니라 *그대로 돌릴 수 있는* 형태.
10
+ - 모르면 비워 두지 말고 `/task-plan`이 사용자에게 물어 채운다 (모르면 묻고, 알면 재사용).
11
+ - 부정확이 드러나면 정정. 손수 편집보다 task 진행 중 갱신 권장.
12
+
13
+ ---
14
+
15
+ ## 데이터 검증 (DB / 파일 / 상태 조회)
16
+
17
+ > 시나리오 실행 후 데이터 상태를 *직접 조회*하는 실제 경로. (예: ORM repository / DB 클라이언트 / 쿼리 명령 / 파일 확인 명령.)
18
+
19
+ <아직 미정 — 데이터 쓰기·변경·삭제 검증이 필요한 task에서 `/task-plan`이 사용자에게 확인 후 기록>
20
+
21
+ ---
22
+
23
+ ## API 호출 (엔드포인트 / 외부 인터페이스)
24
+
25
+ > 엔드포인트를 실제로 호출하고 상태코드·응답 필드를 확인하는 경로. (예: curl / httpie / 테스트 클라이언트 + 베이스 URL · 인증 방법.)
26
+
27
+ <아직 미정>
28
+
29
+ ---
30
+
31
+ ## UI 동작 · E2E (클릭 / 입력 / 흐름 자동화)
32
+
33
+ > UI 동작·흐름을 자동화하는 도구와 셋업. (예: Playwright / Cypress 설치 여부 · 실행 명령 · 셋업 단계.) 도구가 없으면 "없음 — 사람 검수"로 명시.
34
+
35
+ <아직 미정>
36
+
37
+ ---
38
+
39
+ ## 시각 실행 (앱 기동 + 화면 캡처)
40
+
41
+ > 앱을 띄워 실제 화면을 캡처해 승인 목업과 대조하는 경로. (예: 개발 서버 실행 명령 · 포트 · 캡처 방법.) 검수 서버는 `CLAUDE.md`(코덱스 `AGENTS.md`)의 `## 검수 실행 명령` 참조.
42
+
43
+ <아직 미정>
44
+
45
+ ---
46
+
47
+ ## 기타
48
+
49
+ > 위 분류에 안 맞는 프로젝트 고유 검증 방법.
50
+
51
+ <아직 미정>
@@ -210,7 +210,7 @@ docs: [TASK-{NNN}] CHANGELOG 업데이트
210
210
 
211
211
  | 작업 | 명령 | 주체 |
212
212
  |------|------|------|
213
- | 생성 | `git -C "$MAIN_WT" worktree add "$WT_PATH" -b "$BRANCH" dev` | `/task-init` |
213
+ | 생성 | `npx @angar2/taskery fork <type> <dev> <src> <slug>` (내부: init 락 + `worktree add -b`) | `/task-init` |
214
214
  | 제거 | `git -C "$MAIN_WT" worktree remove "$WT_PATH"` | `/task-close` (자동 흐름 + 안전 검증) |
215
215
  | stale 정리 | `git -C "$MAIN_WT" worktree prune` | `npx @angar2/taskery prune` (사용자 확인 후) |
216
216
 
@@ -304,5 +304,6 @@ git -C "$MAIN_WT" branch --no-merged dev --list \
304
304
  | 2026-05-31 | 0.1.2 백로그 스킬 추가 정합 — 출처 표 `BL-NNN` 채번 주체 `/backlog-add` → `/add-backlog`로 갱신 + 버전별 경로(`.project/tasks/<vX.X>/BACKLOG.md`) 명시 |
305
305
  | 2026-06-02 | 0.1.3 F2·F3 정합 — §멀티세션 워크트리 정책 *메인 HEAD 떼기 금지* + *모호 발화 자의 해석 금지* / §git-guard.sh 5종 변형 인식 (-C / --git-dir / --work-tree) + 셸 prefix 금지 명시. stash FRICTION_LOG 2026-06-01 반영 |
306
306
  | 2026-06-25 | 추적 변경 0(코드 0 · `.project` gitignore) close 시 마커 빈커밋으로 분기·채번 정보 보존 — `--no-ff` 머지 커밋 미생성 케이스의 `--allow-empty` 유일 예외 명시 (상세: task-close Step 6-8). FRICTION 검토 F1 반영 |
307
+ | 2026-06-28 | 워크트리 생성 메커니즘 갱신 — `git worktree add` 직접 실행 → `npx @angar2/taskery fork`(init 락 안에서 채번+분기 원자 실행). 병렬 task-init 번호 충돌(레이스) 차단 |
307
308
  | 2026-06-25 | §멀티세션 워크트리 정책에 "워크트리 실행 환경" 추가 — 워크트리에 의존성 심링크 금지(개발 서버 fs 접근 제한으로 로딩 실패), 실행·검수 task는 워크트리 안에 의존성 실제 마련. FRICTION 검토 F6 반영 |
308
309
  | 2026-06-25 | §멀티세션 워크트리 정책에 "멀티세션 검수 환경" 추가 — task별 dev 서버 포트 격리(메인=기준 포트, task=기준+TASK번호) + 터널 독립 + close 시 자기 것만 종료(광역 종료 금지) + 사용자 검수 시점 서버 자동 기동(task-test UNCERTAIN/PASS·task-dev 종료, FAIL 제외). FRICTION 검토 F7 반영 |
@@ -39,12 +39,15 @@ draft → planned → developing → developed → testing → tested → closed
39
39
  | `tested` | 격리 세션 PASS 결과 받은 후 | `/task-test` 끝에 메인 |
40
40
  | `closed` | git 마무리 완료 | `/task-close` 끝에 메인 |
41
41
 
42
- **FAIL/UNCERTAIN 분기**:
42
+ **FAIL/UNCERTAIN 분기** (세 갈래 — 코드 결함 / 시험문제 결함 / 주관 검수):
43
43
  - `/task-test` PASS → `tested` 갱신 → `/task-close` 진행
44
- - `/task-test` FAIL → 메인이 격리 결과(로그/근거) 보고 → 사용자에게 *"고쳐? OK 마무리?"* 묻기 → "고쳐" 시 `developing`으로 되돌림 → `/task-dev` 재진입
45
- - `/task-test` UNCERTAIN → 메인이 결과 보고 → 사용자 판단
44
+ - `/task-test` FAIL (**코드 결함**) → 메인이 격리 결과(로그/근거) 보고 → 사용자에게 *"고쳐? OK 마무리?"* 묻기 → "고쳐" 시 `developing`으로 되돌림 → `/task-dev` 재진입
45
+ - `/task-test` UNCERTAIN(**검증 불가 = 시험문제 결함**) → `[AUTO]`인데 대조할 기대값을 구성 못 한 경우. 코드는 멀쩡 → `developing` 아님. **status=`testing` 유지** + `/task-plan`을 *Test Plan 보수 모드*로 재호출(Test Plan 섹션만 보수, Requirements/Scope/Dev Plan/코드 보존) → 보수 후 `/task-test` 재실행. (되돌림용 별도 status 신설 X — 7상태 유지.)
46
+ - `/task-test` UNCERTAIN(**사람 검수**) → 정답지가 주관인 `[USER]` 항목(시각 미세 취향 / UX 느낌). `testing` 유지 + 사용자 직접 검수 → 모두 ✓면 `tested`, ✗면 `developing`(고쳐).
46
47
  - `/task-dev` 중 self-check FAIL → `developing` 그대로, 메인 자체 수정 → PASS 시 `developed` 갱신
47
48
 
49
+ > **시험문제 결함 되돌림이 새 status를 안 만드는 이유**: 새 상태(예: `replanning`)는 -ing/-ed 페어 원칙(본 §1.2)·7상태 고정과 충돌하고 `closed-immutable.sh` 화이트리스트와도 어긋난다. 코드 미변경이라 `developing`을 거칠 이유도 없으므로 `testing`을 유지한 채 Test Plan만 보수한다.
50
+
48
51
  ### 1.3 섹션 구성 — 6 섹션
49
52
 
50
53
  ```markdown
@@ -167,39 +170,49 @@ draft → planned → developing → developed → testing → tested → closed
167
170
  - UX/UI 시각: 레이아웃 / 색상 / 간격 / 호버 효과 (목업 기준)
168
171
  - 백엔드 동작: 시스템 호출 → 외부 응답 / 사이드 이펙트 / 에러 처리
169
172
 
170
- **시나리오 형식**:
173
+ **시나리오 형식** — 모든 `[AUTO]` = [실행 명령/입력] + [구체적·관측 가능한 기대값] 한 쌍:
171
174
 
172
175
  ```markdown
173
176
  ## Test Plan
174
177
 
175
- | # | 시나리오 | 분류 | 방식 | PASS 기준 |
178
+ | # | 시나리오 | 분류 | 방식 | PASS 기준 (구체적 기대값) |
176
179
  |---|---------|------|------|----------|
177
- | 1 | <한 줄 설명> | [AUTO] / [USER] | <카탈로그 방식> | <명확한 기대값> |
180
+ | 1 | <쪼갠 장면 한 줄: 무엇을 실행> | [AUTO] / [USER] | <카탈로그 방식> | <관측 가능한 구체값 — 출력/상태코드/조회결과/화면상태> |
178
181
  | 2 | ... | ... | ... | ... |
179
182
  ```
180
183
 
184
+ - **소원 금지** — "정상 동작 / 잘 됨 / 확인한다"는 PASS 기준이 아님. 눈에 보이는 결과여야 함.
185
+ - **장면 쪼개기** — 한 덩어리 금지, 진짜 떨어질 장면 여러 개로.
186
+ - **잣대** — "고장나면 어떻게 걸리나"를 못 적는 장면 = 버린다. (이게 task-test "문 앞 검사" 자격 — 미달 시 *시험문제 결함*으로 반려됨.)
187
+
181
188
  **분류 강제**:
182
- - `[AUTO]` — 자동화 가능. 격리 세션이 *실제 명령 실행 + 결과 로그* 확보.
183
- - `[USER]` — 자동화 불가 (UI 인터랙션 / 시각 디자인 / 사용자 입력). 격리 세션은 UNCERTAIN 분류 + *사용자 직접 검수 항목*으로 리턴.
189
+ - `[AUTO]` — 정답지(구체적 기대값)를 구성할 수 있는 영역. 격리 세션이 *실제 명령 실행 + 결과 로그* 확보 → 기대값과 일치 시만 PASS. **시각 *객관* 깨짐(레이아웃/요소 유무/색/배치)도 `[AUTO]`** (캡처-목업 대조, 목업이 정답지).
190
+ - `[USER]` — *정답지가 주관*인 영역만 (시각 미세 취향 / 색조·2px / UX 느낌). 격리 세션은 UNCERTAIN(사람 검수) 분류 + *사용자 직접 검수 항목*으로 리턴(증거 캡처 첨부). UI 인터랙션은 E2E 도구 있으면 `[AUTO]`, 없으면 `[USER]`.
184
191
 
185
- **방식 카탈로그** (시나리오마다 1개 이상):
192
+ **방식 선택 규칙** (요구사항 성격 → 필수 방식, 재량 0): 화면 변경→시각 / 상호작용→UI 동작 / 엔드포인트→API / 데이터 쓰기→상태 조회 / 순수 계산→입출력 / 기존 영역 건드림→회귀. 여러 개 걸리면 다 붙인다.
186
193
 
187
- | 방식 | 적용 영역 |
188
- |------|----------|
189
- | 수동 검수 | UI / UX / 인터랙션 |
190
- | 시나리오 스크립트 실행 | 백엔드 / 통합 흐름 (end-to-end script — bash / node 등) |
191
- | API/엔드포인트 호출 | 백엔드 / 외부 인터페이스 (실제 호출 + 응답·상태 코드 확인) |
192
- | 입출력 비교 | 함수 / 변환 / 처리 (snapshot / golden file) |
193
- | 사이드 이펙트 확인 | DB / 파일 / 로그 |
194
- | 회귀 시나리오 | 기존 기능 영향 (직전 동작하던 시나리오 재실행) |
195
- | E2E 자동화 도구 | UI 자동화 (Playwright / Cypress / XCTest UI Test 등) |
194
+ **방식 카탈로그** (시나리오마다 1개 이상 — 각 방식은 *비교할 정답지*가 다름):
195
+
196
+ | 방식 | 적용 영역 | 정답지 | 누가 검사 |
197
+ |------|----------|------|------|
198
+ | 수동 검수 | UI 미세 / UX 느낌 | 주관(목업·사용감) | 사람 (증거 첨부) |
199
+ | 시나리오 스크립트 실행 | 백엔드 / 통합 흐름 (end-to-end script) | 구체적 출력·종료코드 | test 자동 |
200
+ | API/엔드포인트 호출 | 백엔드 / 외부 인터페이스 | 상태코드 + 응답 필드값 | test 자동 |
201
+ | 입출력 비교 | 함수 / 변환 / 처리 | 구체적 기대 출력 (snapshot / golden) | test 자동 |
202
+ | 사이드 이펙트 확인 | DB / 파일 / 로그 | 조회 결과(행/파일/카운트) | test 자동 |
203
+ | 시각 (캡처-목업 대조) | UI 레이아웃/색/배치 | 승인된 목업 | test 자동 (+ 미세는 사람) |
204
+ | 회귀 시나리오 | 기존 기능 영향 | 이전과 동일 결과 | test 자동 |
205
+ | E2E 자동화 도구 | UI 동작 자동화 | 액션 후 관측 변화 | 도구면 test, 아니면 사람 |
206
+
207
+ **관통 규칙**: 어느 방식이든 *실제 관측 결과* == *구체적 정답지*여야 PASS. 정답지가 주관이면 사람한테(단 증거 깔고). 어느 방식도 코드 정독·grep으로 PASS 대체 금지.
196
208
 
197
209
  **UX/UI 영역 분리** (UX/UI 구현 task 한정):
198
210
 
199
- | 영역 | 자동화 가능 | 자동화 불가 |
200
- |------|------------|------------|
201
- | 동작 (클릭 / 호버 / 드래그 / 입력 → 결과) | `[AUTO]` E2E 자동화 스크립트 | `[USER]` 체크리스트 |
202
- | 시각 (레이아웃 / 색상 / 간격 / 호버 효과) | (자동 비교 의미 X — 픽셀 단위 + 주관 판단 영역) | `[USER]` 체크리스트 + 목업 기준 (`mockup/<task-doc-name>-mockup.html`) |
211
+ | 영역 | 분류 | 기준 |
212
+ |------|------|------|
213
+ | 동작 (클릭 / 호버 / 드래그 / 입력 → 결과) | 도구 있으면 `[AUTO]` E2E, 없으면 `[USER]` | 액션 후 관측 변화 |
214
+ | 시각 *객관* (레이아웃 / 요소 유무 / 색 / 배치 깨짐) | `[AUTO]` 캡처-목업 대조 | 승인 목업 (`mockup/<task-doc-name>-mockup.html`) — 화면 캡처 → 대조 → 어긋남 목록 |
215
+ | 시각 *미세 취향* (2px / 색조 / 호버 강도 / 느낌) | `[USER]` 체크리스트 + 목업 참조 | 사용자 최종 사인 (증거 캡처) |
203
216
 
204
217
  **시각 영역 fix 사이클 사전 예고** — 시각 시나리오 있으면 Test Plan 끝에 *"시각 영역 항목 N개 — fix 사이클 1~2회 예상"* 명시 (사용자 기대치 사전 정렬).
205
218
 
@@ -209,7 +222,7 @@ draft → planned → developing → developed → testing → tested → closed
209
222
  - **자기완결적**: `/task-test` 격리 세션이 *task.md만 보고도* 수행 가능. 메인의 plan/dev 컨텍스트 없이.
210
223
  - **명령 / 기대값 포함**: 무엇을 실행하고 무엇이 기대되는지 명확.
211
224
  - **메인 가정 X**: 격리 세션이 *코드와 동작만 신뢰*. *"잘 될 거야"* 같은 가정 금지.
212
- - **grep/Read-only 존재 확인 시나리오 = 보조용** (stash FRICTION_LOG 2026-06-01 반영) — 구현 코드의 존재 여부를 grep/Read로 재확인하는 시나리오는 *동어반복*. PASS 카운트에서 제외하거나 실동작 시나리오의 *보조*로만 사용. 단독 검증 가치 X.
225
+ - **grep/Read-only 존재 확인 시나리오 = 보조용, PASS 대체 영구 금지** (stash FRICTION_LOG 2026-06-01 반영) — 구현 코드의 존재 여부를 grep/Read로 재확인하는 시나리오는 *동어반복*. PASS 카운트에서 제외, 실동작 시나리오의 *보조*로만. grep으로 실동작 검증 *대체 불가* (task-test 룰 10과 동일 강도).
213
226
  - **요구사항당 end-to-end 1개 이상 강제** — Requirements 각 항목이 *사용자 관점에서 실제 작동하는지* 검증하는 end-to-end 실동작 시나리오 최소 1개 보장. 산출물 실행 → 결과 확인 형태.
214
227
  - **무거운 검증 회피 영구 금지** — 부담(빌드 수 분+ / 외부 의존 / 환경 설정) 이유로 실동작 시나리오를 grep 동어반복으로 대체 X. 최소 1회는 실제 산출물로 확인. 부담은 *fix 사이클 1~2회 예상* 같은 사전 예고로 처리 (생략 사유 X).
215
228
 
@@ -57,6 +57,15 @@
57
57
 
58
58
  ---
59
59
 
60
+ ## agent teams 자동 병렬 — Codex 미지원
61
+
62
+ > 다건 태스크를 리더가 팀원(독립 세션)에 분배해 자동 병렬 처리하는 고기능(`/run-team`)은 **Claude 전용**이다 — 기반인 agent teams 기능이 Codex에 없다.
63
+
64
+ - *"백로그 한 번에 진행해"* / *"팀에게 전부 독립으로 맡겨"* 류 자동 병렬 발화가 들어와도 Codex에서는 지원하지 않는다. **단일 태스크 흐름**(`/task-init` ~ `/task-close`)으로 안내하고 진행한다.
65
+ - 멀티세션 병렬 자체(사용자가 워크트리별 세션을 직접 띄워 진행)는 Codex에서도 가능 — 자동화(리더가 팀을 띄우고 관리)만 미지원이다.
66
+
67
+ ---
68
+
60
69
  ## 백로그 (0.1.2+)
61
70
 
62
71
  > `.project/tasks/<NNN_slug>/BACKLOG.md` = *plan(기능 그룹)별 task 후보 누적*. 사용자 발화로 1건씩 얕은 분석(개요 / 대상 영역) 곁들여 추가.
@@ -100,6 +109,8 @@
100
109
 
101
110
  > 위 항목 중 *프로젝트에 해당 없는 것*은 행 자체 삭제. 빈 백틱(``)은 사용 금지.
102
111
 
112
+ > **검증 방법 → `.project/TEST-GUIDE.md`** — 각 테스트 방식(데이터 조회 / API 호출 / E2E / 시각 실행)을 *이 프로젝트에서 실제로 어떻게 돌리나*는 거기 단일 소스. `/task-plan`이 채우고 `/task-test` 격리 세션이 읽는다.
113
+
103
114
  ---
104
115
 
105
116
  ## 검수 실행 명령 (해당 시 — 앱 실행·검수가 필요한 프로젝트)
@@ -52,6 +52,19 @@
52
52
 
53
53
  ---
54
54
 
55
+ ## agent teams 자동 병렬 (Claude 전용 · 실험 · 트리거 한정)
56
+
57
+ > 다건 태스크를 *리더 메인 세션 1개가 agent teams로 팀원(독립 세션)에 분배*해 자동 병렬 처리하는 고기능. 위 멀티세션을 사용자가 일일이 띄우는 대신 리더가 띄우고 관리한다. 워크트리 격리·머지 직렬화는 기존 `/task-init`·`/task-close`가 그대로 담당.
58
+
59
+ - **기본 플로우 아님** — 디폴트는 *1세션 1태스크*. *트리거 발화*(예: *"백로그 한 번에 진행해"* / *"팀에게 전부 독립으로 맡겨"*)가 있을 때만 `/run-team` 발동. 단일 태스크 의도(*"~ 기능 추가해줘"*)는 기존 `/task-init` 흐름.
60
+ - **활성화 전제** — agent teams는 실험 기능이라 기본 비활성. `~/.claude/settings.json`의 `env`에 `"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"` 설정 후 세션 재시작. (팀원을 split pane으로 보려면 최상위에 `"teammateMode": "tmux"`도 — `teammateMode`는 `env`가 아닌 최상위 키이며 기본값 `auto`는 환경 자동 감지 후 in-process 폴백.) 미설정이면 `/run-team`이 안내하고 중단(헛도는 것 방지).
61
+ - **팀원 = agent teams 팀원** — Task tool 서브에이전트 아님(독립 세션 · 자체 컨텍스트 · 사용자 직접 접근 가능).
62
+ - **Claude 전용** — agent teams가 Codex에 없어 Codex에서는 미지원.
63
+
64
+ 상세: `.claude/skills/run-team/SKILL.md`.
65
+
66
+ ---
67
+
55
68
  ## 백로그 (0.1.2+)
56
69
 
57
70
  > `.project/tasks/<NNN_slug>/BACKLOG.md` = *plan(기능 그룹)별 task 후보 누적*. 사용자 발화로 1건씩 얕은 분석(개요 / 대상 영역) 곁들여 추가.
@@ -95,6 +108,8 @@
95
108
 
96
109
  > 위 항목 중 *프로젝트에 해당 없는 것*은 행 자체 삭제. 빈 백틱(\`\`)은 사용 금지.
97
110
 
111
+ > **검증 방법 → `.project/TEST-GUIDE.md`** — 각 테스트 방식(데이터 조회 / API 호출 / E2E / 시각 실행)을 *이 프로젝트에서 실제로 어떻게 돌리나*는 거기 단일 소스. `/task-plan`이 채우고 `/task-test` 격리 세션이 읽는다.
112
+
98
113
  ---
99
114
 
100
115
  ## 검수 실행 명령 (해당 시 — 앱 실행·검수가 필요한 프로젝트)
@@ -121,7 +136,7 @@
121
136
 
122
137
  ---
123
138
 
124
- ## 스킬 9종 — project > plan > task 위계 + 회고
139
+ ## 스킬 — 공통 9종 + Claude 전용 1종(run-team)
125
140
 
126
141
  | 스킬 | 레벨 | 역할 |
127
142
  |------|------|------|
@@ -134,9 +149,12 @@
134
149
  | `/task-close` | task | 검증 명령 게이트 + 커밋 + dev 병합 (tested → closed) |
135
150
  | `/add-backlog` | meta | 사용자 발화로 활성 plan BACKLOG.md에 항목 1건 추가 (얕은 분석 + BL-NNN 채번 — 0.1.2+) |
136
151
  | `/log-friction` | meta | FRICTION_LOG.md에 사용자 불편 한 행 기록 |
152
+ | `/run-team` | meta · **Claude 전용** | agent teams로 다건 태스크를 팀원(독립 세션)에 분배해 자동 병렬 처리 — 트리거 발화 시에만 (실험 기능 전제). 위 "agent teams 자동 병렬" 섹션 참조 |
137
153
 
138
154
  본문은 `.claude/skills/<스킬>/SKILL.md` 참조.
139
155
 
156
+ > `/run-team`은 Claude 전용 — agent teams(실험 기능)가 Codex에 없어 Codex 설치에는 미포함.
157
+
140
158
  ---
141
159
 
142
160
  ## Hook 안전망 — Catastrophic 2종
@@ -156,6 +156,7 @@ taskery 도입 직후 **1회성**으로 호출. 사용자 또는 동료가 새
156
156
  5. `.project/GLOSSARY.md` — 도메인 용어집 (영문/한글 표기 일관성 단일 진실 소스)
157
157
  6. `.project/tasks/<활성 plan>/BACKLOG.md` — 현재 plan 진행 중 누적된 후속 task 후보 (사용자 발화 *"이건 다음 task로"* / task-close 직후 메인 감지로 추가)
158
158
  7. `.project/AGENT-GUIDE.md` — 본 파일 (그대로)
159
+ 8. `.project/TEST-GUIDE.md` — 검증 방법 문서 (각 테스트 방식의 이 프로젝트 실제 실행 경로). 매 세션 통독 아니라 *`/task-plan`·`/task-test` 시 참조* — 발견성 위해 등재
159
160
 
160
161
  ## 작업 흐름
161
162
  - task 시작: `/task-init` 또는 `/task-plan`
@@ -171,6 +172,7 @@ taskery 도입 직후 **1회성**으로 호출. 사용자 또는 동료가 새
171
172
  - `.project/shared/` — 멀티리포 메시지
172
173
  - `.project/changelog/` — 월별 변경 이력
173
174
  - `.project/FRICTION_LOG.md` — 불편 데이터
175
+ - `.project/TEST-GUIDE.md` — 검증 방법 문서 (각 테스트 방식의 이 프로젝트 실제 실행 경로. 제품 관통 문서 아님)
174
176
 
175
177
  ## 멀티리포
176
178
  <단일 리포면 "단일 리포. LINKED-REPOS.md 빈 템플릿."
@@ -282,7 +284,7 @@ git branch -a # main(또는 master) + dev
282
284
  ### Step 8 — 결과 보고
283
285
 
284
286
  작성된 파일 목록 + 다음 단계 안내:
285
- - *"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 / 빈 골격 폴더는 npx init이 이미 카피한 것 그대로). **빈 폴더 케이스라 git init + dev 분기까지 완료** (이미 git 리포면 건너뜀). 다음은 `/plan-init <기능그룹>`으로 첫 plan(MVP면 여러 기능 그룹을 묶은 큰 plan) 작성."*
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) 작성."*
286
288
 
287
289
  **결과 commit 흐름** (GIT_RULE 정합):
288
290
  - dev 직접 commit *금지* (git-guard.sh 차단). 두 가지 default 흐름:
@@ -33,6 +33,7 @@ self-check 명령은 워크트리(`$WT_PATH`)에서 실행 — 코드 변경이
33
33
  - `/task-plan` 끝나고 기획 합의된 직후.
34
34
  - 도중 끊겼던 task 이어서 구현 (status=`developing`).
35
35
  - `/task-test` FAIL 후 사용자 *"고쳐"* 응답 → status가 `developing`으로 되돌아온 경우 재진입.
36
+ - **시각 어긋남 FAIL의 경우** — 격리 세션이 준 *어긋남 목록*(예: "헤더 색 틀림 / 카드 세로로 쌓임 / 저장버튼 없음")을 **한 번에 배치로** 수정한다 (흠을 사용자가 찾는 게 아니라 test가 찾아준 목록을 보고 고침). 수정 후 `/task-test` 재검사 — 라운드 상한 3회(task-test 흐름 참조).
36
37
 
37
38
  ## 입력 처리
38
39
 
@@ -93,26 +93,9 @@ description: task 시작 — 워크트리 분기 + 6 섹션 빈 골격 + 헤더
93
93
 
94
94
  ### Step 4 — TASK 번호 결정 + 결정적 슬러그 + SSoT 안전망
95
95
 
96
- #### 4.1 다음 TASK-NNN 계산
96
+ #### 4.1 TASK 번호는 Step 6 `fork`가 채번
97
97
 
98
- `bin/lib.js` `getNextTaskNumber(MAIN_WT)` 호출. 내부 동작:
99
- ```sh
100
- # 진행중 (SSoT) — TASK-* 패턴 작업 브랜치 전체. `--no-merged dev` 미사용:
101
- # 워크트리 분기 직후 브랜치는 dev와 동일 commit이라 `--no-merged dev`가 *완전 머지 상태*로
102
- # 처리 → 빈 결과 → getNextTaskNumber 충돌. 브랜치 존재 자체가 SSoT.
103
- A=$(git -C "$MAIN_WT" branch --list \
104
- 'feature/*_TASK-*' 'bug/*_TASK-*' 'improve/*_TASK-*' \
105
- 'refactor/*_TASK-*' 'docs/*_TASK-*' 'chore/*_TASK-*' \
106
- | grep -oE 'TASK-[0-9]+')
107
- # dev 머지 히스토리
108
- B=$(git -C "$MAIN_WT" log dev --grep='TASK-[0-9]\+' --extended-regexp --oneline \
109
- | grep -oE 'TASK-[0-9]+')
110
- # 합집합 최대 + 1
111
- MAX=$( (echo "$A"; echo "$B") | sort -uV | tail -1 | sed 's/TASK-//')
112
- NEXT=$((MAX + 1))
113
- ```
114
- - 3자리 zero-padded (`001`, `015`, `120`).
115
- - 합집합 비어 있으면 `001`.
98
+ TASK-NNN은 **여기서 미리 계산하지 않는다.** 번호 읽기와 워크트리·브랜치 생성을 분리하면 병렬 task-init이 같은 번호를 읽는 레이스(TOCTOU)가 난다. 채번은 Step 6 `npx @angar2/taskery fork`가 **init 락 안에서 생성과 한 덩어리로** 수행한다 (읽기 직후 분기까지 원자 실행 → 동시 호출은 늘어난 번호를 봄). 확정 번호(`nnn`)는 fork 반환 JSON에서 받는다.
116
99
 
117
100
  #### 4.2 메타 가져오기 (BL/RM)
118
101
 
@@ -134,16 +117,9 @@ NEXT=$((MAX + 1))
134
117
  - 한국어 → 영어 kebab-case. 짧고 명확하게 (3 단어 이내 권장).
135
118
  - 같은 항목이면 같은 슬러그 → 같은 브랜치명 → git이 동시 분기 자동 거부 (race 차단 1층).
136
119
 
137
- #### 4.4 SSoT 안전망 (race 차단 2층)
120
+ #### 4.4 SSoT 안전망 (fork가 락 안에서 수행)
138
121
 
139
- ```sh
140
- git -C "$MAIN_WT" branch --no-merged dev --list \
141
- 'feature/*_TASK-*' 'bug/*_TASK-*' 'improve/*_TASK-*' \
142
- 'refactor/*_TASK-*' 'docs/*_TASK-*' 'chore/*_TASK-*' \
143
- | grep -E "_${SRC}_"
144
- ```
145
- - BL-NNN/RM-NNN이 *진행중*에 있으면 → 중단 + 사용자 알림 (*"BL-003은 이미 X 세션에서 진행 중"*).
146
- - DR은 본 안전망 검사 X (별도 ID 없음).
122
+ 같은 출처(BL-NNN/RM-NNN)가 이미 *진행중*이면 분기를 거부하는 안전망은 **Step 6 `fork`가 init 락 안에서 권위적으로 수행**한다 (채번과 동일 임계구역 → 별도 racy 중복검사 불필요). 진행중이면 fork가 *"BL-003 이미 진행중 (…브랜치)"* 로 실패하고, 스킬은 그 메시지로 중단 + 사용자 알림. DR은 별도 ID가 없어 본 검사 제외.
147
123
 
148
124
  ### Step 5 — 파일 vs 폴더 결정 + 브랜치명 산출
149
125
 
@@ -159,20 +135,21 @@ git -C "$MAIN_WT" branch --no-merged dev --list \
159
135
  ```
160
136
  - **타입**: feature → `feature`, bug → `bug`, improvement → `improve`, refactor → `refactor`, docs → `docs`, chore → `chore`
161
137
  - **개발자**: 메인 세션 = `claude`, 사용자 세션 = `angar2` 등
162
- 3. 사용자에게 브랜치명 + 파일/폴더 confirm.
138
+ - **NNN은 Step 6 `fork`가 확정** — confirm 시점엔 미정(`TASK-???`). 분기 전 번호 못 박기는 병렬 레이스 차단의 귀결.
139
+ 3. 사용자에게 브랜치 구성요소(`{타입}/{개발자}_TASK-???_{출처}_{슬러그}`) + 파일/폴더 confirm. 확정 NNN은 fork 후 §8에서 보고.
140
+
141
+ ### Step 6 — 워크트리 분기 (`fork` — 채번+생성 원자 실행)
163
142
 
164
- ### Step 6 — 워크트리 생성
143
+ `npx @angar2/taskery fork <타입> <개발자> <출처> <슬러그>` 한 번으로 **채번 → 워크트리·브랜치 생성**을 init 락 안에서 원자 실행한다 (Step 4.1 채번 + 본 단계 생성이 한 임계구역 → 병렬 task-init 번호 충돌 차단).
165
144
 
166
145
  ```sh
167
- PROJECT_ID=$(jq -r '.projectId' "$MAIN_WT/.taskery-manifest.json")
168
- WORKTREES_ROOT="$HOME/.taskery/worktrees/$PROJECT_ID"
169
- WT_PATH="$WORKTREES_ROOT/TASK-${NNN}_${SRC}_${SLUG}"
170
- mkdir -p "$WORKTREES_ROOT"
171
- git -C "$MAIN_WT" worktree add "$WT_PATH" -b "$BRANCH" dev
146
+ npx @angar2/taskery fork "$TYPE" "$DEV" "$SRC" "$SLUG"
147
+ # 예: npx @angar2/taskery fork feature claude BL-003 login-feature
172
148
  ```
173
149
 
174
- - git이 동일 브랜치명 자동 거부 (race 차단 1층).
175
- - 동시 충돌 시 *"브랜치 이미 존재 — 다른 세션이 같은 항목 진행 중"* 보고 + 중단.
150
+ - 성공 시 결과 JSON 한 줄 출력 — `{ "taskNum", "nnn", "branch", "wtPath", "projectId" }`. 이후 단계(§7 문서 / §7.5 BL 마킹 / §8 보고)는 이 반환값의 `nnn` · `branch` · `wtPath`를 사용한다.
151
+ - fork가 락 안에서 함께 수행: 채번(TOCTOU 차단) + SSoT 안전망(§4.4) + 동일 브랜치명 거부(git — 같은 항목 동시 분기 차단).
152
+ - fork 비정상 종료(exit 1) 시 stderr 메시지(*"… 이미 진행중"* / *"… 정책 위배"* 등)를 그대로 사용자에게 보고 + 중단.
176
153
 
177
154
  ### Step 7 — task 문서 위치 결정 + 빈 골격 작성
178
155
 
@@ -237,6 +214,8 @@ git -C "$MAIN_WT" check-ignore -q "$MAIN_WT/.project/dummy"
237
214
 
238
215
  ### Step 8 — 결과 보고
239
216
 
217
+ `<NNN>` · `<WT_PATH>` · `<BRANCH>`는 Step 6 fork 반환 JSON의 `nnn` · `wtPath` · `branch` 값으로 채운다.
218
+
240
219
  ```
241
220
  ✅ TASK-<NNN> 생성 완료
242
221
  - 워크트리: <WT_PATH>
@@ -249,7 +228,7 @@ git -C "$MAIN_WT" check-ignore -q "$MAIN_WT/.project/dummy"
249
228
 
250
229
  ## 도구 가이드
251
230
 
252
- - **Bash**: 메인 워크트리 검출 / 사전 검증 / `git worktree add` / SSoT 조회 / TASK-NNN 계산
231
+ - **Bash**: 메인 워크트리 검출 / 사전 검증 / `npx @angar2/taskery fork` 호출(채번+워크트리·브랜치 생성) / 결과 JSON 파싱
253
232
  - **Read**: `$MAIN_WT/.project/AGENT-GUIDE.md` / `$MAIN_WT/.project/tasks/<활성 plan>/BACKLOG.md` / `$MAIN_WT/.project/plans/<활성 plan>/ROADMAP.md`
254
233
  - **Write**: task.md 빈 골격 작성 (위치는 .gitignore 케이스에 따라)
255
234
  - **AskUserQuestion**: 분기 2 인터뷰 (한 번에 한 질문)
@@ -258,16 +237,16 @@ git -C "$MAIN_WT" check-ignore -q "$MAIN_WT/.project/dummy"
258
237
 
259
238
  - **워크트리 생성 + task 문서 작성만 담당** — 본문 채우기 금지. Requirements / Scope / Dev Plan / Test Plan 본문은 *반드시* `/task-plan`에서.
260
239
  - **단계 경계 — 허용/금지 명시 (stash FRICTION_LOG #11 반영)**:
261
- - **허용 (화이트리스트)**: `$MAIN_WT/.project/plans/<활성 plan>/ROADMAP.md` §4(다음 작업 영역) 확인 / SSoT 조회 / TASK-NNN 계산 / 빈 골격 Write
240
+ - **허용 (화이트리스트)**: `$MAIN_WT/.project/plans/<활성 plan>/ROADMAP.md` §4(다음 작업 영역) 확인 / SSoT 조회 / `fork` 호출(채번+분기) / 빈 골격 Write
262
241
  - **금지 (블랙리스트)**: ARCHITECTURE.md / API-SPEC.md / FEATURES.md 등 `.project/` 루트 제품 관통 문서 본문 Read / 도메인 코드 Read · Grep / 기존 task 본문 Read
263
242
  - 본문 정보 수집은 *다음 단계 `/task-plan`*에서 수행.
264
243
  - **자동 추정 진행 X** — 직전 맥락 명확해도 *제안 + 사용자 OK* 거친 후 워크트리/파일 생성.
265
244
  - **상태는 `draft` 고정** — `/task-init` 끝의 상태는 `draft` 외 작성 금지.
266
- - **NNN 충돌 회피** — SSoT 조회로 진행중 + dev 머지 히스토리 합집합 확인.
245
+ - **NNN 충돌 회피** — `fork`가 init 락 안에서 채번(진행중 ∪ dev 머지 히스토리 최대+1)과 워크트리 생성을 원자 실행 (병렬 task-init도 안전).
267
246
  - **slug 한국어 잔존 X** — 영어 kebab-case로 변환.
268
247
  - **헤더 5컬럼 모두 채움** — *"미정"* placeholder 작성 금지.
269
248
  - **메인 워크트리 검출 실패 시 즉시 중단** — git 버전 / 정책 위배 / dev 부재 등 안전망. 묵묵히 진행 X.
270
- - **race 발생 시 사용자 호출** — `git worktree add` 실패(같은 브랜치명) → SSoT 재조회 + 사용자에게 *"X 세션이 같은 항목 진행 중"* 알림.
249
+ - **race 발생 시 사용자 호출** — `fork` 실패(같은 브랜치명 / 같은 출처 진행중) → stderr 메시지 그대로 사용자에게 *"다른 세션이 같은 항목 진행 중"* 알림 + 중단.
271
250
 
272
251
  ## 상태 전이
273
252
 
@@ -32,6 +32,7 @@ MAIN_WT=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")
32
32
 
33
33
  - `/task-init`로 만든 빈 골격 task에 기획 채울 때.
34
34
  - 기존 task 기획 다시 짜고 싶을 때 (단 status=`draft`만. `planned` 이상이면 *직접 Edit*하거나 `/task-init` 새로 만들 것 권장).
35
+ - **Test Plan 보수 재진입** (status=`testing`만): `/task-test`가 *시험문제 결함(UNCERTAIN 검증 불가)*으로 반려한 직후 — Test Plan 시나리오를 [실행 명령 + 구체적 기대값]으로 다시 작성하러 온 경우. **Test Plan 섹션만** 보수하고 Requirements / Scope / Dev Plan / 코드는 보존한다 (아래 Step 1 가드 예외 참조).
35
36
 
36
37
  ## 입력 처리
37
38
 
@@ -49,7 +50,10 @@ MAIN_WT=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")
49
50
  2. task 파일 Read:
50
51
  - 인자 있음 → 해당 파일.
51
52
  - 인자 없음 → `ls .project/tasks/<NNN_slug>/` 결과 중 status=draft인 가장 최근 파일. 발견 시 *"TASK-<NNN> 진행할까요?"* confirm. 없으면 *"draft 상태 task가 없네요. `/task-init` 먼저 호출하세요."* + 종료.
52
- 3. 상태 = `draft` 검증. 다른 상태면 종료 + 안내.
53
+ 3. 상태 검증 — 두 진입만 허용, 그 외 종료 + 안내:
54
+ - **`draft`** → 일반 기획 (Step 2~ 전체 진행).
55
+ - **`testing` + Test Plan 보수 모드** → `/task-test`가 *시험문제 결함*으로 반려해 되돌아온 경우에 한해 허용. 이때는 **Test Plan 섹션만 보수**(Requirements / Scope / Dev Plan 보존), Step 2~4는 건너뛰고 Step 5(Test Plan)만 수행, status는 `testing` 그대로 둔다(전이 X — 보수 후 `/task-test` 재실행).
56
+ - **그 외(`planned` / `developing` / `developed` / `tested` / `closed`)** → 종료 + 안내. (특히 `tested` / `closed`는 보수 모드로도 진입 거부 — 우회 방지.)
53
57
 
54
58
  ### Step 2 — Requirements 인터뷰 + 증폭
55
59
 
@@ -140,7 +144,7 @@ MAIN_WT=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")
140
144
 
141
145
  ### Step 4.5 — HTML 목업 프로세스 (UX/UI 구현 task 한정 — stash FRICTION_LOG #14+19 반영)
142
146
 
143
- 본 task가 UX/UI 구현 포함하면 *시각 영역 사전 정합* + *task-test 시 USER 검수 기준 확보*용 HTML 목업 생성.
147
+ 본 task가 UX/UI 구현 포함하면 *시각 영역 사전 정합* + *task-test 시각 자동 대조 정답지 확보*용 HTML 목업 생성.
144
148
 
145
149
  #### 1. UX/UI 구현 task 판단
146
150
 
@@ -179,7 +183,10 @@ Step 2 Requirements 인터뷰 결과로 본 task가 *UX/UI 구현 (페이지/컴
179
183
 
180
184
  #### 5. Test Plan 연결
181
185
 
182
- 승인된 목업 = task-test의 *시각 영역 USER 검수 기준*. Step 5 Test Plan 작성 시 시각 시나리오에 `[USER]` 분류 + 목업 경로 참조 명시.
186
+ 승인된 목업 = task-test의 **시각 *자동 대조* 정답지** (+ 미세 취향은 사용자 최종 사인). 사용자가 맘에 들 때까지 승인 안 하므로 목업 = 이미 사용자가 검증한 정답지 — task-test 격리 세션이 *실제 화면 캡처 → 이 목업과 자동 대조*해 객관적 깨짐을 잡는다. Step 5 Test Plan 작성 시:
187
+ - **시각 *객관* 영역(레이아웃/요소 유무/색/배치) → `[AUTO]`** (캡처-목업 대조) + 목업 경로 참조 명시.
188
+ - **시각 *미세 취향*(2px·색조·느낌) → `[USER]`** (사용자 최종 사인, 증거 캡처).
189
+ (이전의 "승인 목업 = USER 검수 기준 / 시각=전부 [USER]"는 폐기 — 사용자에게 흠 사냥을 떠넘기던 원인.)
183
190
 
184
191
  ---
185
192
 
@@ -195,38 +202,65 @@ Test Plan = *본 task에서 구현한 요구사항이 정상 동작하는지*
195
202
  - UX/UI 시각: 레이아웃 / 색상 / 간격 / 호버 효과 (목업 기준)
196
203
  - 백엔드 동작: 시스템 호출 → 외부 응답 / 사이드 이펙트 / 에러 처리
197
204
 
205
+ #### 방식 선택 규칙 (요구사항 성격 → 필수 방식, 재량 0)
206
+
207
+ 방식을 *느낌으로* 고르지 말 것. 각 요구사항을 아래 표에 *기계적으로* 대조해 필수 방식을 정한다. 여러 행에 걸리면 *다 붙인다*:
208
+
209
+ | 요구사항이 이런 거면 | 필수 테스트 방식 |
210
+ |---|---|
211
+ | 화면을 새로 그리거나 바꾼다 | 시각 (캡처-목업 대조) |
212
+ | 클릭·입력 등 상호작용 | UI 동작 (E2E 또는 사람) |
213
+ | 엔드포인트 추가·변경 | API 호출 |
214
+ | 데이터 쓰기·변경·삭제 | 데이터 상태 조회 (사이드 이펙트) |
215
+ | 순수 계산·변환 | 입출력 비교 |
216
+ | 기존 동작 영역 건드림 | 회귀 |
217
+
218
+ 이렇게 고른 방식을 아래 카탈로그의 *구체적 방식*으로 옮긴다. (이 규칙은 뒤의 "요구사항 ↔ 시나리오 커버리지 점검"의 *입력* — 매핑이 방식을 정하고, 커버리지 점검이 빠진 요구사항을 잡는다. 중복 아니라 보강 관계.)
219
+
198
220
  #### 실질 테스트 방식 카탈로그
199
221
 
200
- 각 시나리오마다 아래 방식 중 적합한 1개 이상 선택:
222
+ 각 시나리오마다 위 규칙으로 고른 방식을 아래에서 구체화. **각 방식은 *비교할 정답지*가 다르다 — 정답지 없는 검사는 무의미**:
201
223
 
202
- | 방식 | 적용 영역 | 설명 |
203
- |------|----------|------|
204
- | 수동 검수 | UI / UX / 인터랙션 | 사용자가 실제 앱·환경에서 직접 조작·확인 |
205
- | 시나리오 스크립트 실행 | 백엔드 / 통합 흐름 | end-to-end script (bash / node 등) 일련의 명령 자동 실행 |
206
- | API/엔드포인트 호출 | 백엔드 / 외부 인터페이스 | 실제 호출 + 응답·상태 코드·결과 데이터 확인 |
207
- | 입출력 비교 | 함수 / 변환 / 처리 | 입력 → 출력 비교 (snapshot / golden file) |
208
- | 사이드 이펙트 확인 | DB / 파일 / 로그 | 시나리오 실행 후 상태 직접 조회 |
209
- | 회귀 시나리오 | 기존 기능 영향 | 직전 동작하던 시나리오 재실행 |
210
- | E2E 자동화 도구 | UI 자동화 (도구 있을 시) | Playwright / Cypress / XCTest UI Test 등 — 가능하면 자동화, 안 되면 수동 검수 |
224
+ | 방식 | 적용 영역 | 정답지 (무엇과 비교) | 누가 검사 | 설명 |
225
+ |------|----------|------|------|------|
226
+ | 수동 검수 | UI 미세 / UX 느낌 | 주관(목업·사용감) | 사람 (증거 첨부) | 정답지가 주관인 것만 — 객관 깨짐은 아래 시각 자동으로 |
227
+ | 시나리오 스크립트 실행 | 백엔드 / 통합 흐름 | 구체적 출력·종료코드 | test 자동 | end-to-end script (bash / node 등) 일련의 명령 자동 실행 |
228
+ | API/엔드포인트 호출 | 백엔드 / 외부 인터페이스 | 상태코드 + 응답 필드값 | test 자동 | 실제 호출 + 응답·상태 코드·결과 데이터 확인 |
229
+ | 입출력 비교 | 함수 / 변환 / 처리 | 구체적 기대 출력 | test 자동 | 입력 → 출력 비교 (snapshot / golden file) |
230
+ | 사이드 이펙트 확인 | DB / 파일 / 로그 | 조회 결과(행/파일/카운트) | test 자동 | 시나리오 실행 후 상태 직접 조회 |
231
+ | 시각 (캡처-목업 대조) | UI 레이아웃/색/배치 | 승인된 목업 | test 자동 (+ 미세는 사람) | 실제 화면 캡처 → 목업과 대조, 어긋남 목록 |
232
+ | 회귀 시나리오 | 기존 기능 영향 | 이전과 동일 결과 | test 자동 | 직전 동작하던 시나리오 재실행 |
233
+ | E2E 자동화 도구 | UI 동작 자동화 (도구 있을 시) | 액션 후 관측 변화 | 도구면 test, 아니면 사람 | Playwright / Cypress / XCTest UI Test 등 — 가능하면 자동화, 안 되면 수동 검수 |
211
234
 
212
235
  #### UX/UI 영역 분리 + 검증 방식 매트릭스 (UX/UI 구현 task 한정)
213
236
 
214
- | 영역 | 자동화 가능 시 | 자동화 불가 시 |
215
- |------|---------------|---------------|
216
- | 동작 (클릭/호버/드래그/입력 → 결과) | `[AUTO]` E2E 자동화 시나리오 스크립트 | `[USER]` 체크리스트 |
217
- | 시각 (레이아웃/색상/간격/호버 효과 강도) | (자동 비교 의미 없음 — 픽셀 단위 변화 + 주관 판단 영역) | `[USER]` 체크리스트 + 목업 기준 참조 |
237
+ | 영역 | 분류 | 기준 |
238
+ |------|------|------|
239
+ | 동작 (클릭/호버/드래그/입력 → 결과) | 도구 있으면 `[AUTO]` E2E, 없으면 `[USER]` 체크리스트 | 액션 후 관측되는 변화 |
240
+ | 시각 *객관* (레이아웃/요소 유무/색/배치 깨짐) | `[AUTO]` 캡처-목업 대조 | 승인된 목업 (격리 세션이 화면 캡처 → 대조 → 어긋남 목록) |
241
+ | 시각 *미세 취향* (2px·색조·호버 강도·느낌) | `[USER]` 체크리스트 + 목업 참조 | 사용자 최종 사인 (증거 캡처 첨부) |
242
+
243
+ #### 시나리오 형식 — [명령/입력] + [구체적 기대값] 한 쌍 강제
244
+
245
+ **모든 `[AUTO]` 시나리오 = [실제 실행할 명령/입력] + [실제로 나올 구체적·관측 가능한 값] 한 쌍.** 이게 task-test 격리 세션이 "문 앞 검사"에서 자격을 확인하는 형태다 — 안 갖추면 검사가 시작되지 않고 *시험문제 결함*으로 반려된다(빈 권고 아님).
218
246
 
219
- #### 시나리오 형식
247
+ - **소원 금지** — "정상 동작 / 잘 됨 / 확인한다 / 문제 없음"은 PASS 기준이 아니다. 눈에 보이는 결과(출력값 / 상태코드 / 어서션 / 조회 행수 / 화면 상태)여야 한다.
248
+ - **장면 쪼개기** — "로그인 되나 봐" 한 덩어리 금지. *진짜 떨어질 장면 여러 개*로 분해.
249
+ - **잣대** — "이게 고장나면 *어떻게 걸리나*"를 못 적는 장면 = 테스트 아님 → 버린다.
250
+
251
+ 예 (로그인 5회 실패 시 15분 잠금):
252
+ - ❌ `5번 실패하면 잠긴다 | 잠금이 정상 동작함`
253
+ - ✅ `틀린 비번 5회 POST → 6번째 응답코드 == 423 + body에 retry_after. 4번째는 401(미잠금).`
220
254
 
221
255
  ```markdown
222
256
  ## Test Plan
223
257
 
224
- | # | 시나리오 | 분류 | 방식 | PASS 기준 |
258
+ | # | 시나리오 | 분류 | 방식 | PASS 기준 (구체적 기대값) |
225
259
  |---|---------|------|------|----------|
226
- | 1 | <한 줄 설명> | [AUTO] / [USER] | <카탈로그 방식> | <명확한 기대값> |
260
+ | 1 | <쪼갠 장면 한 줄: 무엇을 실행> | [AUTO] / [USER] | <카탈로그 방식> | <관측 가능한 구체값 — 출력/상태코드/조회결과/화면상태> |
227
261
  | 2 | ... | ... | ... | ... |
228
262
 
229
- (UX/UI task — 목업 있으면) 시각 USER 시나리오 기준: `.project/tasks/<NNN_slug>/mockup/<task-doc-name>-mockup.html`
263
+ (UX/UI task — 목업 있으면) 시각 시나리오 정답지: `.project/tasks/<NNN_slug>/mockup/<task-doc-name>-mockup.html`
230
264
  ```
231
265
 
232
266
  #### 시각 영역 fix 사이클 사전 예고 (stash FRICTION_LOG #14 반영)
@@ -240,7 +274,7 @@ Test Plan = *본 task에서 구현한 요구사항이 정상 동작하는지*
240
274
  - **자기완결적**: `/task-test` 격리 세션이 *task.md만 보고* 수행 가능. 메인의 plan/dev 컨텍스트 없이.
241
275
  - **명령/기대값 포함**: 무엇을 실행하고 무엇이 기대되는지 명확.
242
276
  - **메인 가정 X**: *"잘 될 거야"* / *"문제 없을 듯"* 같은 가정 금지.
243
- - **grep/Read-only 존재 확인 시나리오 = 보조용** (stash FRICTION_LOG 2026-06-01 반영) — 구현 코드의 존재 여부를 grep/Read로 재확인하는 시나리오는 *동어반복*. PASS 카운트에서 제외하거나 실동작 시나리오의 *보조*로만 사용. 단독 검증 가치 X.
277
+ - **grep/Read-only 존재 확인 시나리오 = 보조용, PASS 대체 영구 금지** (stash FRICTION_LOG 2026-06-01 반영) — 구현 코드의 존재 여부를 grep/Read로 재확인하는 시나리오는 *동어반복*. PASS 카운트에서 제외, 실동작 시나리오의 *보조*로만 사용. 단독 검증 가치 X — grep으로 실동작 검증을 *대체*할 수 없다(task-test 룰 10과 동일 강도).
244
278
  - **요구사항당 end-to-end 1개 이상 강제** — Requirements 각 항목이 *사용자 관점에서 실제 작동하는지* 검증하는 end-to-end 실동작 시나리오 최소 1개 보장. 산출물 실행 → 결과 확인 형태.
245
279
  - **무거운 검증 회피 영구 금지** — 부담(빌드 수 분+ / 외부 의존 / 환경 설정) 이유로 실동작 시나리오를 grep 동어반복으로 대체 X. 최소 1회는 실제 산출물로 확인. 부담은 *fix 사이클 1~2회 예상* 같은 사전 예고로 처리 (생략 사유 X).
246
280
 
@@ -264,11 +298,23 @@ Test Plan 작성 완료 후 *반드시* 점검 단계 수행. *"누적/리셋
264
298
  | <Req 3 한 줄> | #5 |
265
299
  ```
266
300
 
301
+ #### 실행 경로 확인 — "이 프로젝트에서 이 방식을 실제로 어떻게 돌리나" (필수)
302
+
303
+ 방식을 골랐어도, **격리 세션이 *이 프로젝트의* DB/API/E2E에 실제로 어떻게 접근하는지 모르면 그 시나리오는 실행 불가능한 허울뿐인 명세다.** 스택마다 다르다(NestJS+TypeORM이면 그 DataSource/repository, 다른 프로젝트는 또 다름). 그래서 *모르면 묻고, 알면 재사용*:
304
+
305
+ 1. 필요 방식마다 `$MAIN_WT/.project/TEST-GUIDE.md`를 Read → **이 프로젝트에 실행 경로가 적혀 있나** 확인.
306
+ 2. **있으면** → 그 경로를 시나리오의 *방식* 칸에 반영(재사용). 앱은 기존 실행 명령으로 띄움.
307
+ 3. **없으면** → *멈추고 사용자에게 요구*: 예) *"이 요구사항은 DB 검증이 필요한데 이 프로젝트에 실행 경로가 없다. 어떻게 검증할지 알려주거나 도구를 깔아줘."* → 사용자가 경로를 주거나 결정.
308
+ 4. **확인받은 즉시 `TEST-GUIDE.md`에 기록** → 다음 task/세션부터 안 묻고 재사용. (재사용 = 기억이 아니라 *파일* — 세션/컴팩트로 사라지지 않게.)
309
+
310
+ > TEST-GUIDE는 init이 빈 골격으로 깔아둔다(`.project/TEST-GUIDE.md`). 방식별 섹션(데이터 검증 / API 호출 / UI·E2E / 시각 실행 / 기타)에 *이 프로젝트의 실제 실행 방법*을 채운다.
311
+
267
312
  #### 검증/테스트 명령 참조
268
313
 
269
- `/task-test` 격리 세션은 프로젝트 루트 `CLAUDE.md`의 두 섹션을 자동 참조 — Test Plan에 재인용 불필요:
314
+ `/task-test` 격리 세션은 프로젝트 루트 `CLAUDE.md`의 두 섹션 + `TEST-GUIDE.md`를 자동 참조 — Test Plan에 재인용 불필요:
270
315
  - `## 검증 명령` (코드 상태 — 빌드/린트/타입체크)
271
316
  - `## 테스트 명령` (테스트 실행 — 단위/통합/E2E)
317
+ - `.project/TEST-GUIDE.md` (각 방식의 *이 프로젝트 실제 실행 방법*)
272
318
 
273
319
  ### Step 6 — spec-diff 처리 (Phase 0 변경 있을 시)
274
320
 
@@ -333,5 +379,7 @@ Test Plan 작성 완료 후 *반드시* 점검 단계 수행. *"누적/리셋
333
379
  | 진입 시 | 종료 시 |
334
380
  |--------|--------|
335
381
  | `draft` | `planned` |
382
+ | `testing` (Test Plan 보수 모드) | `testing` (유지 — Test Plan만 보수 후 `/task-test` 재실행) |
336
383
 
337
384
  (예외 — 합의 못 함: status 그대로 `draft` 유지. 다음 호출 시 다시 시도.)
385
+ (Test Plan 보수 모드: `/task-test`가 *시험문제 결함*으로 반려한 경우만 — Step 1 가드 참조. Requirements/Scope/Dev Plan/코드 보존, status 전이 X.)
@@ -28,6 +28,8 @@ MAIN_WT=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")
28
28
 
29
29
  격리 세션은 *워크트리 안*에서 검증/테스트 명령 실행 (`$MAIN_WT/CLAUDE.md`의 `## 검증 명령` / `## 테스트 명령` 단일 진실 소스).
30
30
 
31
+ **검증 방법 출처 = `$MAIN_WT/.project/TEST-GUIDE.md`** — 각 테스트 방식(데이터 조회 / API 호출 / E2E / 시각 실행)을 *이 프로젝트에서 실제로 어떻게 돌리나*가 적힌 단일 소스. 격리 세션 prompt에 task 문서 경로와 *동일 방식*으로 이 절대 경로를 넘긴다 (메모리 아닌 파일 — 세션/컴팩트 무관 재사용).
32
+
31
33
  ## 호출 시점
32
34
 
33
35
  - `/task-dev` 끝나고 self-check PASS 받은 직후.
@@ -67,29 +69,39 @@ Task tool로 sub-agent spawn. prompt는 *자기완결적* — task.md 경로 +
67
69
 
68
70
  ## 대상 task
69
71
 
70
- 파일 경로: <ABSOLUTE_PATH_TO_TASK_MD>
72
+ task 파일 경로: <ABSOLUTE_PATH_TO_TASK_MD>
73
+ 검증 방법 문서: <ABSOLUTE_PATH_TO_TEST_GUIDE_MD>
71
74
 
72
- 이 파일을 직접 Read 해서 ## Test Plan 섹션 + ## Dev Plan의 Phase별 완료 기준을 정독하세요. 다른 문서 참조 X — task.md 하나만 봅니다.
73
- (단 task.md가 `mockup/<task-doc-name>-mockup.html` 참조하면 해당 목업 파일도 직접 Read — 시각 USER 시나리오의 *기준*으로 사용. 자동 비교 X.)
75
+ task 파일을 직접 Read 해서 ## Test Plan 섹션 + ## Dev Plan의 Phase별 완료 기준을 정독하세요. 그리고 *검증 방법 문서(TEST-GUIDE.md)* 도 직접 Read — 각 테스트 방식을 *이 프로젝트에서 실제로 어떻게 실행하나*(DB 조회 경로 / API 호출법 / E2E 셋업 등)의 단일 소스입니다. 이 둘 + 목업 외 다른 문서 참조 X.
76
+ (단 task.md가 `mockup/<task-doc-name>-mockup.html` 참조하면 해당 목업 파일도 직접 Read — *승인된 목업 = 시각 시나리오의 정답지*입니다. 시각 시나리오 = 실제 화면 캡처 → 이 목업과 자동 대조.)
74
77
 
75
78
  ## 수행 룰
76
79
 
77
80
  1. **본질 — Test Plan 시나리오 기반 실질 동작 검증**. 본 task에서 구현한 *요구사항이 정상 동작하는지* 검증이 목적. 단위 테스트 수치(카운트 +N / 모든 PASS) 자체로 task PASS 단정 절대 X — Test Plan에 명시된 *각 시나리오*가 PASS인지가 본질.
78
81
  2. ## Test Plan 시나리오를 *순서대로* 그대로 수행. 임의 추가 / 스킵 / 변형 금지. 각 시나리오의 `[AUTO]` / `[USER]` 분류 (task-plan에서 정의됨) 그대로 따름.
79
82
  3. `[AUTO]` 시나리오 — *실제 명령 실행*. 실행 없이 pass 처리 절대 금지. 검증 도구 (단위/통합/E2E 테스트 / API 호출 / 스크립트 실행 등) 직접 수행 + 결과 로그 확보.
80
- 4. `[USER]` 시나리오 — 자동 검증 불가 영역 (UI 인터랙션 / 시각 디자인 / 사용자 입력). UNCERTAIN으로 분류 + *사용자 직접 검수 항목*으로 리턴. 격리 세션에서 검증 시도 X.
83
+ 4. `[USER]` 시나리오 — *정답지가 주관*인 영역만 (시각 미세 취향 / 색조·여백 2px 수준 / UX 사용감·매끄러움). UNCERTAIN(사람 검수)으로 분류 + *사용자 직접 검수 항목*으로 리턴 (단 증거=캡처 첨부). **시각 *객관* 영역(레이아웃 깨짐 / 요소 빠짐 / 빈 화면 / 색 완전히 틀림 / 배치 딴판)은 `[USER]`가 아니라 `[AUTO]`** — 실제 화면 캡처 → 승인 목업과 직접 대조해 격리 세션이 판정. (시각이라고 무조건 사람에게 넘기지 말 것 — 객관 깨짐은 네가 잡는다.)
81
84
  5. CLAUDE.md `## 검증 명령` (빌드/린트/타입체크) + `## 테스트 명령` (단위/통합/E2E 테스트) 둘 다 직접 실행 + 결과 리턴.
82
85
  6. **신규 테스트 식별자 등장 확인 (stash FRICTION_LOG #19 반영)** — 본 task에서 신규 테스트 추가되었으면, 단순 *카운트 +N / 모든 PASS* 자체로 PASS 단정 X. 신규 *테스트 식별자* (suite / class / function / describe 등) 가 테스트 runner 결과 로그에 *실제 등장*했는지 grep 직접 확인 (1 hit 이상). 등장 안 하면 *Suite 미실행* → FAIL.
83
86
  7. 코드와 동작만 신뢰. *"잘 될 거야"* / *"문제 없을 듯"* 같은 가정 일체 금지.
84
87
  8. 코드 파일 직접 수정 절대 금지. 임시 파일(테스트 스크립트 등) 생성 시 종료 후 삭제.
85
88
  9. **task.md 본문 메타 발언과 raw 결과 분리** — task.md (Requirements / Scope / Dev Plan / Test Plan 본문)의 *어떠한 메타 발언* (*"본 Test Plan은 Dev Plan과 의도적 mismatch"* / *"본 task는 FAIL이 정상"* / *"aborted 시뮬"* 등) 도 raw 시나리오 결과 판정을 *덮을 수 없다*. raw 결과 판정 = *실행 명령 exit code / 로그 출력 / 어서션 통과 여부*만. 본문 의도 해석 영역 외 — 결과 기반 판정만.
86
89
  10. **grep/Read-only 존재 확인 시나리오 = 보조 검증** (stash FRICTION_LOG 2026-06-01 반영) — 시나리오가 *코드/파일에 X가 존재하나*를 grep / Read로 재확인하는 형태(동어반복)면 *보조 검증*으로 분류. 종합 판정의 PASS 카운트에서 제외하고, 같은 영역의 *실동작 시나리오*가 PASS인 경우에만 보조 자료로 첨부. 단독 PASS 가치 X. 실동작 시나리오 없이 grep-only 시나리오만으로 종합 PASS 단정 영구 금지 — 그 경우 *실동작 시나리오 부재*로 UNCERTAIN/FAIL 판정 + 메인 보고.
90
+ 11. **문 앞 검사 — 실행 전 시험문제 자격 확인 (먼저)**. 시나리오를 돌리기 *전에*, 각 `[AUTO]` 시나리오가 **[실행할 명령/입력] + [구체적 기대값(관측 가능한 결과 — 출력값 / 상태코드 / 어서션 / 조회 결과 / 화면 상태)]** 한 쌍을 갖췄는지 확인. *"정상 동작" / "잘 됨" / "확인한다"* 같은 *소원*은 기대값이 아님. 자격 미달 `[AUTO]` 시나리오가 있으면 → **그 시나리오는 검사하지 말고 "시험문제 결함(검증 불가)"으로 분류** + 무엇이 빠졌는지 지목 (아래 룰 13). 자격 미달을 PASS로 반올림 영구 금지.
91
+ 12. **증거 일치 시만 PASS**. 자격을 갖춘 `[AUTO]` 시나리오는 *실제로 실행*하여 — *관측한 결과가 기대값과 일치할 때만* PASS. 코드 정독 "그럴듯함" / 추정 / "될 듯"으로 PASS 영구 금지. 증거(명령 출력 · 응답 로그 · 캡처 · 조회 결과)를 결과 표 근거 칸에 첨부.
92
+ 13. **UNCERTAIN 두 종류 — 섞지 말 것**.
93
+ - **UNCERTAIN(사람 검수)** — 정답지가 *주관*인 영역(룰 4: 시각 미세 취향 / UX 느낌). 정상. 사용자 직접 검수 항목으로 리턴 (증거 캡처 첨부).
94
+ - **UNCERTAIN(검증 불가)** — `[AUTO]`로 분류됐는데 *대조할 기대값을 구성하지 못한* 경우 = **시험문제 결함**. PASS로 반올림 금지, 사람 검수로도 넘기지 말 것(주관 아님). **근거 의무**: *무엇을 시도했고 왜 기대값을 구성할 수 없는지* 반드시 기록. (이건 네가 쉴 자리가 아니라 task-plan으로 되돌아가 Test Plan을 보수해야 하는 신호 — 도망쳐도 이득 없음.)
95
+ 14. **시각 `[AUTO]` 시나리오 = 캡처-목업 대조**. 앱을 띄워 실제 화면을 캡처 → 승인된 목업(정답지)과 대조. 어긋나면 → *구체적 어긋남 목록*(예: "헤더 색 틀림 / 카드가 가로 아닌 세로로 쌓임 / 저장 버튼 없음")을 뽑아 **FAIL**로 보고. 합격선 = *픽셀 일치가 아니라* "사람이 보면 '이거네' 할 정도로 본질적으로 같다" (객관적 깨짐만 잡고 미세 취향은 룰 4로). 증거 = 스크린샷 + 어긋남 목록.
87
96
 
88
97
  ## 결과 형식 (이 포맷 그대로 리턴)
89
98
 
90
99
  ### 종합 판정
91
100
  - **PASS** / **FAIL** / **UNCERTAIN** 중 하나
92
- - UNCERTAIN = `[USER]` 시나리오 또는 자동 검증 불가 항목이 1개 이상 있으면. PASS 자동 전이 X — 사용자 직접 검수 필요.
101
+ - **UNCERTAIN** = 아래 둘 중 하나라도 1개 이상이면. *두 경우 모두 PASS 자동 전이 X* (공통). 그 다음 종류에 따라 라우팅이 갈림:
102
+ - **UNCERTAIN(사람 검수)** — 정답지가 주관인 `[USER]` 항목. → 사용자 직접 검수.
103
+ - **UNCERTAIN(검증 불가)** — `[AUTO]`인데 기대값 구성 불가 = 시험문제 결함. → task-plan으로 Test Plan 보수 (사람 검수 아님). 어느 시나리오가 왜 검증 불가인지 명시.
104
+ - 종합이 UNCERTAIN일 때 *두 종류 중 무엇인지* 반드시 표기 (둘 다면 둘 다).
93
105
  - 한 줄 요약
94
106
 
95
107
  ### 시나리오별 결과
@@ -112,10 +124,14 @@ Task tool로 sub-agent spawn. prompt는 *자기완결적* — task.md 경로 +
112
124
  ### 실패 / 불확정 상세 (해당 시)
113
125
  - <어느 시나리오 / 무엇이 깨졌는지 / 어느 로그가 그 증거인지>
114
126
 
115
- ### USER 검수 필요 항목 (UNCERTAIN — 해당 시)
116
- - <시나리오 N: 검수 항목 한 줄>
127
+ ### USER 검수 필요 항목 (UNCERTAIN 사람 검수 — 해당 시)
128
+ - <시나리오 N: 검수 항목 한 줄 (주관 정답지 — 시각 미세 / UX 느낌)>
117
129
  - <목업 파일 경로 — 있을 시 시각 영역 기준으로 참조>
118
130
 
131
+ ### 시험문제 결함 (UNCERTAIN 검증 불가 — 해당 시)
132
+ - <시나리오 N: 무엇이 [AUTO]인데 기대값을 못 만들었나 + 무엇을 시도했나 + 왜 불가한가>
133
+ → task-plan으로 Test Plan 보수 필요 (이 시나리오를 [실행 명령 + 구체적 기대값]으로 다시 작성).
134
+
119
135
  ## 종료 조건
120
136
 
121
137
  위 형식 그대로 리턴 후 종료. 코드 수정 시도 금지 — 결과 리턴만.
@@ -123,8 +139,10 @@ Task tool로 sub-agent spawn. prompt는 *자기완결적* — task.md 경로 +
123
139
 
124
140
  **구현 디테일**:
125
141
  - `<ABSOLUTE_PATH_TO_TASK_MD>` 자리에 `.project/tasks/<NNN_slug>/<NNN>_<slug>.md` (또는 폴더 승격 시 `<...>/task.md`)의 절대 경로 삽입.
142
+ - `<ABSOLUTE_PATH_TO_TEST_GUIDE_MD>` 자리에 `$MAIN_WT/.project/TEST-GUIDE.md`의 절대 경로 삽입 (위 "멀티세션 메타 위치"의 `MAIN_WT` 사용 — 워크트리에서 호출돼도 메인 워크트리의 단일 소스를 읽도록).
126
143
  - Task tool의 `subagent_type`: `general-purpose` (도구 풀 다 필요).
127
144
  - Task tool의 `description`: `Isolated test for TASK-<NNN>`.
145
+ - **코덱스 spawn (플랫폼 패리티)**: 코덱스에선 `.codex/agents/task-tester.toml`의 `task-tester` 서브에이전트로 spawn하되, *위 펜스 전체를 task prompt로 그대로 전달*한다 — 펜스가 양 플랫폼 공통 단일 룰 소스다(클로드 = `general-purpose` + 펜스 / 코덱스 = `task-tester` + 펜스, 동일 효과). `task-tester`의 `developer_instructions`는 같은 룰의 코덱스 기준선 미러 — 본 펜스의 수행 룰을 바꾸면 그 `developer_instructions`도 함께 갱신한다.
128
146
 
129
147
  ### Step 4 — 결과 리턴 + Result 섹션 기록
130
148
 
@@ -178,10 +196,26 @@ Task tool로 sub-agent spawn. prompt는 *자기완결적* — task.md 경로 +
178
196
  2. "OK 마무리" — 실패 알면서 /task-close 진행 (위험: 알려진 결함 채로 closed)
179
197
  ```
180
198
  3. 사용자 *"고쳐"* → 헤더 status `testing` → `developing` Edit + `/task-dev` 안내. 사용자 *"OK 마무리"* → status `testing` → `tested` Edit (단 Result 섹션에 *"알려진 결함 있음"* 명시) + `/task-close` 안내.
199
+ 4. **시각 어긋남 FAIL의 경우** — 격리 세션이 준 *어긋남 목록*을 그대로 보고에 실어, 사용자 *"고쳐"* 시 `/task-dev`가 그 목록을 **한 번에 배치로** 수정하도록 안내(흠을 사용자가 직접 찾지 않음 — test가 찾아줌). **재검사 라운드 상한 = 3회** (기본값, 프로젝트별 조정 가능): dev↔test를 3회 돌고도 어긋남이 남으면 멈추고 *현 상태 + 남은 어긋남*을 사용자에게 보고해 판단을 받는다. (전이는 매 라운드 사용자 *"고쳐"* 발화로 진행 — 무인 자동 루프 아님.)
200
+
201
+ #### UNCERTAIN(검증 불가) 분기 — 시험문제 결함 → Test Plan 보수
202
+
203
+ 격리 세션이 `[AUTO]` 시나리오인데 *대조할 기대값을 구성하지 못함*(시험문제 결함)을 보고한 경우. 코드 결함이 아니라 *시험문제* 결함이므로 `/task-dev`(코드 수정)가 아니라 `/task-plan`(Test Plan 보수)으로 간다.
204
+
205
+ 1. status는 `testing` 그대로 유지 (코드는 멀쩡 — 되돌리지 않음).
206
+ 2. 사용자에게 보고:
207
+ ```
208
+ ⚠️ TASK-<NNN> 격리 검증 — 시험문제 결함 (검증 불가)
209
+ - 결함 시나리오: <목록 + 무엇을 시도했고 왜 기대값 구성 불가인지>
210
+ - 코드 결함 아님 — 구현은 보존됨.
181
211
 
182
- #### UNCERTAIN 분기 (USER 검수 흐름)
212
+ → /task-plan 으로 Test Plan만 보수 (이 시나리오를 [실행 명령 + 구체적 기대값]으로 다시 작성) → 보수 후 /task-test 재실행
213
+ ```
214
+ 3. `/task-plan`은 *Test Plan 섹션만* 보수 (Requirements / Scope / Dev Plan / 코드 보존). 보수 후 `/task-test` 재실행 (코드 미변경이므로 `/task-dev` 재실행 없음). status는 `testing` 유지.
183
215
 
184
- 격리 세션이 `[USER]` 시나리오 또는 자동화 불가능한 시나리오(UI 인터랙션 / 시각 디자인 / 사용자 입력 등) 발견 시. **자동 PASS 전이 X — 사용자 직접 검수 PASS 받은 후에만 `tested` 전이**.
216
+ #### UNCERTAIN(사람 검수) 분기 (USER 검수 흐름)
217
+
218
+ 격리 세션이 `[USER]` 시나리오(정답지가 *주관*인 영역 — 시각 미세 취향 / UX 느낌) 발견 시. **자동 PASS 전이 X — 사용자 직접 검수 PASS 받은 후에만 `tested` 전이**. (시각 *객관* 깨짐은 여기 아니라 FAIL — 격리 세션이 캡처-목업 대조로 이미 잡음.)
185
219
 
186
220
  > **검수 서버 먼저 기동** (`## 검수 실행 명령` 선언 + 시각/UI 검수 항목 포함 시): 체크리스트를 제시하기 *전에* 서버를 백그라운드 기동(포트 = 기준 포트 + TASK번호)하고 접속 URL을 함께 제공한다 — 사용자가 실제 화면을 보며 ✓/✗ 검수하도록. 격리 세션이 기능상 이미 띄웠으면 재사용. 사용자 *"안 띄워도 돼"* 시 생략. 상세: GIT_RULE "멀티세션 검수 환경".
187
221
 
@@ -227,7 +261,7 @@ USER 검수 — N 항목 (목업 기준: <mockup path — 있을 시>)
227
261
  - **자기완결성 검증 먼저** — Step 1에서 Test Plan이 *"위에서 만든 X"* 같은 메인 컨텍스트 의존 표현 있으면 격리 세션이 헤맴. 보강 후 재시도.
228
262
  - **PASS 자체 판정 X** — 메인이 *"잘 됐을 듯"* 판정하지 X. 격리 세션 결과만 신뢰.
229
263
  - **임시 파일 정리** — 격리 세션이 만든 Playwright 스크립트 / 임시 데이터 종료 시 삭제. 코드베이스 오염 X.
230
- - **사용자검수 시나리오는 자동화 못 잡음** — UI 미세 조정 / 시각 디자인 / 외부 결제 등은 UNCERTAIN으로 가야 정상. PASS 강제 X.
264
+ - **사용자검수 시나리오는 자동화 못 잡음** — UI 미세 취향 / UX 느낌 / 외부 결제 등 *정답지가 주관*인 것만 UNCERTAIN(사람 검수)으로 가야 정상. PASS 강제 X. **단 시각 *객관* 깨짐(요소 빠짐 / 빈 화면 / 배치 딴판 / 색 완전히 틀림)은 사람 검수가 아니라 격리 세션이 캡처-목업 대조로 직접 잡아 FAIL** — 사용자에게 흠 사냥을 떠넘기지 않는다.
231
265
  - **FAIL → developing 자동 되돌림 X** — 사용자 *"고쳐"* 답 받기 전 status 갱신 금지. *대화로 OK = 자동 전이* 룰의 핵심.
232
266
  - **테스트 후 plumbing fix 발견 시** — 격리 PASS 후에도 plumbing(빌드 산출물 정리, 설정 수정 등) fix 필요할 수 있음. 분기:
233
267
  - 검증 명령 *동작 자체에 영향 X* (예: 산출물 위치 정리, 주석 수정) → 메인 자체 재검증 OK (lint/typecheck/build/test 재실행)
@@ -235,9 +269,13 @@ USER 검수 — N 항목 (목업 기준: <mockup path — 있을 시>)
235
269
 
236
270
  ## 상태 전이
237
271
 
238
- | 진입 시 | 종료 시 |
239
- |--------|--------|
240
- | `developed` | `tested` (PASS) |
241
- | `developed` | `developing` (FAIL + 사용자 *"고쳐"*) |
242
- | `developed` | `tested` (FAIL + 사용자 *"OK 마무리"* — 알려진 결함 명시) |
243
- | `testing` (이어가기) | 동일 분기 |
272
+ | 진입 시 | 종료 시 | 트리거 |
273
+ |--------|--------|--------|
274
+ | `developed` | `tested` | PASS |
275
+ | `developed` | `developing` | FAIL (코드 결함) + 사용자 *"고쳐"* |
276
+ | `developed` | `tested` | FAIL + 사용자 *"OK 마무리"* (알려진 결함 명시) |
277
+ | `developed` / `testing` | `testing` (유지) | UNCERTAIN(사람 검수) — 사용자 직접 검수 대기 |
278
+ | `developed` / `testing` | `testing` (유지) | UNCERTAIN(검증 불가, 시험문제 결함) → `/task-plan`으로 Test Plan 보수 → `/task-test` 재실행 (코드·status 보존) |
279
+ | `testing` (이어가기) | 동일 분기 | — |
280
+
281
+ > **세 갈래 요약**: PASS → `tested` / 코드 결함 → `developing`(사용자 *"고쳐"*) / 시험문제 결함 → Test Plan 보수(`task-plan`, 코드·status 보존) → 재검사. UNCERTAIN(사람 검수)은 검수 ✓ 받으면 `tested`, ✗면 `developing`.