@silbaram/artifact-driven-agent 0.1.5 → 0.1.6
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 +294 -661
- package/bin/cli.js +8 -1
- package/core/artifacts/decision.md +1 -1
- package/core/artifacts/plan.md +5 -5
- package/core/artifacts/project.md +4 -2
- package/core/artifacts/sprints/_template/docs/release-notes.md +37 -0
- package/core/artifacts/sprints/_template/meta.md +54 -0
- package/core/artifacts/sprints/_template/retrospective.md +50 -0
- package/core/artifacts/sprints/_template/review-reports/review-template.md +49 -0
- package/core/artifacts/sprints/_template/tasks/task-template.md +43 -0
- package/core/roles/analyzer.md +6 -33
- package/core/roles/developer.md +69 -99
- package/core/roles/documenter.md +293 -0
- package/core/roles/manager.md +391 -341
- package/core/roles/planner.md +61 -79
- package/core/roles/reviewer.md +71 -129
- package/core/rules/document-priority.md +40 -38
- package/core/rules/escalation.md +15 -14
- package/core/rules/iteration.md +24 -24
- package/core/rules/rfc.md +1 -1
- package/core/rules/rollback.md +4 -4
- package/package.json +1 -1
- package/src/commands/run.js +95 -46
- package/src/commands/sessions.js +21 -1
- package/src/commands/sprint.js +262 -0
- package/src/commands/validate.js +133 -64
- package/src/commands/validate.test.js +84 -0
- package/src/utils/files.js +134 -134
- package/src/utils/sessionState.js +30 -11
- package/core/artifacts/architecture-options.md +0 -85
- package/core/artifacts/backlog.md +0 -177
- package/core/artifacts/current-sprint.md +0 -125
- package/core/artifacts/qa-report.md +0 -104
- package/core/artifacts/review-report.md +0 -103
- package/core/roles/architect.md +0 -270
- package/core/roles/qa.md +0 -306
- package/core/rules/role-state-protocol.md +0 -281
- package/core/rules/session-state.md +0 -255
- package/templates/cli/roles/cli-developer.md +0 -243
- package/templates/game/roles/game-logic.md +0 -198
- package/templates/game/roles/rendering.md +0 -142
- package/templates/library/roles/library-developer.md +0 -184
- package/templates/web-dev/roles/backend.md +0 -139
- package/templates/web-dev/roles/frontend.md +0 -166
package/src/utils/files.js
CHANGED
|
@@ -1,134 +1,134 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
|
|
5
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
-
const __dirname = path.dirname(__filename);
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 패키지 루트 디렉토리 반환
|
|
10
|
-
*/
|
|
11
|
-
export function getPackageRoot() {
|
|
12
|
-
return path.resolve(__dirname, '../..');
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* 작업 디렉토리 (ai-dev-team) 경로 반환
|
|
17
|
-
*/
|
|
18
|
-
export function getWorkspaceDir() {
|
|
19
|
-
return path.join(process.cwd(), 'ai-dev-team');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* 세션 디렉토리 경로 반환
|
|
24
|
-
*/
|
|
25
|
-
export function getSessionsDir() {
|
|
26
|
-
return path.join(getWorkspaceDir(), '.sessions');
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* 로그 디렉토리 경로 반환
|
|
31
|
-
*/
|
|
32
|
-
export function getLogsDir() {
|
|
33
|
-
return path.join(getWorkspaceDir(), '.sessions', 'logs');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* 현재 템플릿 파일 경로 반환
|
|
38
|
-
*/
|
|
39
|
-
export function getCurrentTemplateFile() {
|
|
40
|
-
return path.join(getWorkspaceDir(), '.current-template');
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* 현재 세팅된 템플릿 반환
|
|
45
|
-
*/
|
|
46
|
-
export function getCurrentTemplate() {
|
|
47
|
-
const templateFile = getCurrentTemplateFile();
|
|
48
|
-
if (fs.existsSync(templateFile)) {
|
|
49
|
-
return fs.readFileSync(templateFile, 'utf-8').trim();
|
|
50
|
-
}
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* 세션 ID 생성
|
|
56
|
-
*/
|
|
57
|
-
export function generateSessionId() {
|
|
58
|
-
const now = new Date();
|
|
59
|
-
const date = now.toISOString().slice(0, 10).replace(/-/g, '');
|
|
60
|
-
const time = now.toTimeString().slice(0, 8).replace(/:/g, '');
|
|
61
|
-
const random = Math.random().toString(16).slice(2, 10);
|
|
62
|
-
return `${date}-${time}-${random}`;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* 타임스탬프 생성
|
|
67
|
-
*/
|
|
68
|
-
export function getTimestamp() {
|
|
69
|
-
return new Date().toISOString().replace('T', ' ').slice(0, 19);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* 사용 가능한 템플릿 목록
|
|
74
|
-
*/
|
|
75
|
-
export function getAvailableTemplates() {
|
|
76
|
-
const templatesDir = path.join(getPackageRoot(), 'templates');
|
|
77
|
-
if (!fs.existsSync(templatesDir)) {
|
|
78
|
-
return [];
|
|
79
|
-
}
|
|
80
|
-
return fs.readdirSync(templatesDir).filter(f => {
|
|
81
|
-
return fs.statSync(path.join(templatesDir, f)).isDirectory();
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* 사용 가능한 역할 목록
|
|
87
|
-
*/
|
|
88
|
-
export function getAvailableRoles() {
|
|
89
|
-
const workspaceRoles = path.join(getWorkspaceDir(), 'roles');
|
|
90
|
-
if (!fs.existsSync(workspaceRoles)) {
|
|
91
|
-
return [];
|
|
92
|
-
}
|
|
93
|
-
return fs.readdirSync(workspaceRoles)
|
|
94
|
-
.filter(f => f.endsWith('.md'))
|
|
95
|
-
.map(f => f.replace('.md', ''));
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* 사용 가능한 도구 목록
|
|
100
|
-
*/
|
|
101
|
-
export function getAvailableTools() {
|
|
102
|
-
return ['claude', 'codex', 'gemini', 'copilot'];
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* 워크스페이스가 세팅되어 있는지 확인
|
|
107
|
-
*/
|
|
108
|
-
export function isWorkspaceSetup() {
|
|
109
|
-
const workspace = getWorkspaceDir();
|
|
110
|
-
const rolesDir = path.join(workspace, 'roles');
|
|
111
|
-
return fs.existsSync(rolesDir) && fs.readdirSync(rolesDir).filter(f => f.endsWith('.md')).length > 0;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* 디렉토리 복사 (머지)
|
|
116
|
-
*/
|
|
117
|
-
export function copyDirMerge(src, dest) {
|
|
118
|
-
if (!fs.existsSync(src)) return;
|
|
119
|
-
|
|
120
|
-
fs.ensureDirSync(dest);
|
|
121
|
-
const items = fs.readdirSync(src);
|
|
122
|
-
|
|
123
|
-
for (const item of items) {
|
|
124
|
-
const srcPath = path.join(src, item);
|
|
125
|
-
const destPath = path.join(dest, item);
|
|
126
|
-
const stat = fs.statSync(srcPath);
|
|
127
|
-
|
|
128
|
-
if (stat.isDirectory()) {
|
|
129
|
-
copyDirMerge(srcPath, destPath);
|
|
130
|
-
} else {
|
|
131
|
-
fs.copyFileSync(srcPath, destPath);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 패키지 루트 디렉토리 반환
|
|
10
|
+
*/
|
|
11
|
+
export function getPackageRoot() {
|
|
12
|
+
return path.resolve(__dirname, '../..');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 작업 디렉토리 (ai-dev-team) 경로 반환
|
|
17
|
+
*/
|
|
18
|
+
export function getWorkspaceDir() {
|
|
19
|
+
return path.join(process.cwd(), 'ai-dev-team');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 세션 디렉토리 경로 반환
|
|
24
|
+
*/
|
|
25
|
+
export function getSessionsDir() {
|
|
26
|
+
return path.join(getWorkspaceDir(), '.sessions');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 로그 디렉토리 경로 반환
|
|
31
|
+
*/
|
|
32
|
+
export function getLogsDir() {
|
|
33
|
+
return path.join(getWorkspaceDir(), '.sessions', 'logs');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 현재 템플릿 파일 경로 반환
|
|
38
|
+
*/
|
|
39
|
+
export function getCurrentTemplateFile() {
|
|
40
|
+
return path.join(getWorkspaceDir(), '.current-template');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 현재 세팅된 템플릿 반환
|
|
45
|
+
*/
|
|
46
|
+
export function getCurrentTemplate() {
|
|
47
|
+
const templateFile = getCurrentTemplateFile();
|
|
48
|
+
if (fs.existsSync(templateFile)) {
|
|
49
|
+
return fs.readFileSync(templateFile, 'utf-8').trim();
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 세션 ID 생성
|
|
56
|
+
*/
|
|
57
|
+
export function generateSessionId() {
|
|
58
|
+
const now = new Date();
|
|
59
|
+
const date = now.toISOString().slice(0, 10).replace(/-/g, '');
|
|
60
|
+
const time = now.toTimeString().slice(0, 8).replace(/:/g, '');
|
|
61
|
+
const random = Math.random().toString(16).slice(2, 10);
|
|
62
|
+
return `${date}-${time}-${random}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 타임스탬프 생성
|
|
67
|
+
*/
|
|
68
|
+
export function getTimestamp() {
|
|
69
|
+
return new Date().toISOString().replace('T', ' ').slice(0, 19);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 사용 가능한 템플릿 목록
|
|
74
|
+
*/
|
|
75
|
+
export function getAvailableTemplates() {
|
|
76
|
+
const templatesDir = path.join(getPackageRoot(), 'templates');
|
|
77
|
+
if (!fs.existsSync(templatesDir)) {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
return fs.readdirSync(templatesDir).filter(f => {
|
|
81
|
+
return fs.statSync(path.join(templatesDir, f)).isDirectory();
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 사용 가능한 역할 목록
|
|
87
|
+
*/
|
|
88
|
+
export function getAvailableRoles() {
|
|
89
|
+
const workspaceRoles = path.join(getWorkspaceDir(), 'roles');
|
|
90
|
+
if (!fs.existsSync(workspaceRoles)) {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
return fs.readdirSync(workspaceRoles)
|
|
94
|
+
.filter(f => f.endsWith('.md'))
|
|
95
|
+
.map(f => f.replace('.md', ''));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 사용 가능한 도구 목록
|
|
100
|
+
*/
|
|
101
|
+
export function getAvailableTools() {
|
|
102
|
+
return ['claude', 'codex', 'gemini', 'copilot'];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 워크스페이스가 세팅되어 있는지 확인
|
|
107
|
+
*/
|
|
108
|
+
export function isWorkspaceSetup() {
|
|
109
|
+
const workspace = getWorkspaceDir();
|
|
110
|
+
const rolesDir = path.join(workspace, 'roles');
|
|
111
|
+
return fs.existsSync(rolesDir) && fs.readdirSync(rolesDir).filter(f => f.endsWith('.md')).length > 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 디렉토리 복사 (머지)
|
|
116
|
+
*/
|
|
117
|
+
export function copyDirMerge(src, dest) {
|
|
118
|
+
if (!fs.existsSync(src)) return;
|
|
119
|
+
|
|
120
|
+
fs.ensureDirSync(dest);
|
|
121
|
+
const items = fs.readdirSync(src);
|
|
122
|
+
|
|
123
|
+
for (const item of items) {
|
|
124
|
+
const srcPath = path.join(src, item);
|
|
125
|
+
const destPath = path.join(dest, item);
|
|
126
|
+
const stat = fs.statSync(srcPath);
|
|
127
|
+
|
|
128
|
+
if (stat.isDirectory()) {
|
|
129
|
+
copyDirMerge(srcPath, destPath);
|
|
130
|
+
} else {
|
|
131
|
+
fs.copyFileSync(srcPath, destPath);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -40,18 +40,37 @@ export function readStatus(retries = 3) {
|
|
|
40
40
|
return initialStatus;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
43
|
+
let content = fs.readFileSync(statusFile, 'utf-8');
|
|
44
|
+
try {
|
|
45
|
+
const status = JSON.parse(content);
|
|
46
|
+
// 스키마 검증 및 누락된 필드 추가
|
|
47
|
+
const emptyStatus = getEmptyStatus();
|
|
48
|
+
Object.keys(emptyStatus).forEach(key => {
|
|
49
|
+
if (status[key] === undefined) {
|
|
50
|
+
status[key] = emptyStatus[key];
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return status;
|
|
54
|
+
} catch (parseError) {
|
|
55
|
+
if (parseError instanceof SyntaxError && parseError.message.includes('JSON')) {
|
|
56
|
+
// 파일 내용에서 백슬래시 이스케이프 처리
|
|
57
|
+
content = content.replace(/\\/g, '\\\\');
|
|
58
|
+
const status = JSON.parse(content);
|
|
59
|
+
|
|
60
|
+
// 스키마 검증 및 누락된 필드 추가
|
|
61
|
+
const emptyStatus = getEmptyStatus();
|
|
62
|
+
Object.keys(emptyStatus).forEach(key => {
|
|
63
|
+
if (status[key] === undefined) {
|
|
64
|
+
status[key] = emptyStatus[key];
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// 복구된 파일 저장
|
|
69
|
+
fs.writeFileSync(statusFile, JSON.stringify(status, null, 2));
|
|
70
|
+
return status;
|
|
51
71
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return status;
|
|
72
|
+
throw parseError;
|
|
73
|
+
}
|
|
55
74
|
} catch (err) {
|
|
56
75
|
if (i === retries - 1) {
|
|
57
76
|
console.error('⚠️ 상태 파일 읽기 실패:', err.message);
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
# Architecture Options
|
|
2
|
-
|
|
3
|
-
> 아키텍처 협상 과정 기록
|
|
4
|
-
> Architect와 사용자 간 논의 내용
|
|
5
|
-
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## 0. 문서 메타
|
|
9
|
-
|
|
10
|
-
| 항목 | 값 |
|
|
11
|
-
|------|-----|
|
|
12
|
-
| 상태 | 협의 중 / 확정 |
|
|
13
|
-
| 최종 수정 | YYYY-MM-DD |
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## 1. 결정 상태
|
|
18
|
-
|
|
19
|
-
| 항목 | 상태 | 선택 |
|
|
20
|
-
|------|:----:|------|
|
|
21
|
-
| 규모 | ✅ | M (Medium) |
|
|
22
|
-
| 언어 | ✅ | [선택] |
|
|
23
|
-
| 프레임워크 | ⏳ | - |
|
|
24
|
-
| 데이터 저장 | ❌ | - |
|
|
25
|
-
|
|
26
|
-
상태: ✅ 확정 / ⏳ 협의 중 / ❌ 미정
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## 2. 규모 분석
|
|
31
|
-
|
|
32
|
-
### 분석 결과
|
|
33
|
-
|
|
34
|
-
| 항목 | 값 |
|
|
35
|
-
|------|-----|
|
|
36
|
-
| 핵심 기능 수 | N개 |
|
|
37
|
-
| 예상 복잡도 | 낮음 / 중간 / 높음 |
|
|
38
|
-
| 권장 규모 | S / M / L |
|
|
39
|
-
|
|
40
|
-
### 규모 근거
|
|
41
|
-
|
|
42
|
-
[규모 판단 근거 설명]
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
## 3. 옵션 제안
|
|
47
|
-
|
|
48
|
-
### [항목명] 옵션
|
|
49
|
-
|
|
50
|
-
| 옵션 | 장점 | 단점 |
|
|
51
|
-
|------|------|------|
|
|
52
|
-
| A: [선택지] | [장점] | [단점] |
|
|
53
|
-
| B: [선택지] | [장점] | [단점] |
|
|
54
|
-
| C: [선택지] | [장점] | [단점] |
|
|
55
|
-
|
|
56
|
-
### Architect 권장
|
|
57
|
-
|
|
58
|
-
옵션 [X] 권장
|
|
59
|
-
|
|
60
|
-
이유: [권장 이유]
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## 4. 사용자 선택 기록
|
|
65
|
-
|
|
66
|
-
| 날짜 | 항목 | 선택 | 이유 |
|
|
67
|
-
|------|------|------|------|
|
|
68
|
-
| YYYY-MM-DD | 규모 | M | 기능 수 기준 |
|
|
69
|
-
| YYYY-MM-DD | 언어 | [선택] | [이유] |
|
|
70
|
-
|
|
71
|
-
---
|
|
72
|
-
|
|
73
|
-
## 5. 미결정 항목
|
|
74
|
-
|
|
75
|
-
| 항목 | 옵션 | 결정 예정 |
|
|
76
|
-
|------|------|----------|
|
|
77
|
-
| [항목] | A / B / C | [시점] |
|
|
78
|
-
|
|
79
|
-
---
|
|
80
|
-
|
|
81
|
-
## 6. 확정 시 다음 단계
|
|
82
|
-
|
|
83
|
-
모든 항목 확정 후:
|
|
84
|
-
1. project.md 생성 (Frozen)
|
|
85
|
-
2. 스프린트 계획 시작
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
# Backlog (Task 목록)
|
|
2
|
-
|
|
3
|
-
> 모든 Task의 단일 진실 (Single Source of Truth)
|
|
4
|
-
> Planner가 생성, Manager가 상태 관리
|
|
5
|
-
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## ✅ Task 완성도 체크리스트
|
|
9
|
-
|
|
10
|
-
> ⚠️ Task가 READY 상태가 되려면 모든 항목 충족 필요
|
|
11
|
-
|
|
12
|
-
### 필수 항목 (Required)
|
|
13
|
-
- [ ] Task ID 부여됨 (TASK-NNN)
|
|
14
|
-
- [ ] 출처(기능 ID)가 plan.md에 존재함
|
|
15
|
-
- [ ] 수용 조건 최소 1개 이상
|
|
16
|
-
- [ ] 크기(S/M/L) 결정됨
|
|
17
|
-
- [ ] 우선순위(P0/P1/P2) 결정됨
|
|
18
|
-
|
|
19
|
-
### 품질 체크 (Quality)
|
|
20
|
-
- [ ] 수용 조건이 테스트 가능함
|
|
21
|
-
- [ ] 의존성이 명시됨 (없으면 "-")
|
|
22
|
-
- [ ] Task 설명이 구체적임 (모호한 표현 없음)
|
|
23
|
-
- [ ] L 크기 Task가 분해 검토됨
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## 0. 문서 메타
|
|
28
|
-
|
|
29
|
-
| 항목 | 값 |
|
|
30
|
-
|------|-----|
|
|
31
|
-
| 총 Task 수 | N개 |
|
|
32
|
-
| READY | N개 |
|
|
33
|
-
| IN_SPRINT | N개 |
|
|
34
|
-
| DONE | N개 |
|
|
35
|
-
| 최종 갱신 | YYYY-MM-DD |
|
|
36
|
-
|
|
37
|
-
---
|
|
38
|
-
|
|
39
|
-
## 1. Task 상태 정의
|
|
40
|
-
|
|
41
|
-
| 상태 | 설명 | 다음 상태 |
|
|
42
|
-
|------|------|----------|
|
|
43
|
-
| BACKLOG | 정의됨, 상세화 필요 | READY |
|
|
44
|
-
| READY | 스프린트 투입 가능 | IN_SPRINT |
|
|
45
|
-
| IN_SPRINT | 현재 스프린트에 포함 | IN_DEV |
|
|
46
|
-
| IN_DEV | 개발 중 | IN_REVIEW |
|
|
47
|
-
| IN_REVIEW | 리뷰 중 | IN_QA, IN_DEV |
|
|
48
|
-
| IN_QA | QA 검증 중 | DONE, IN_DEV |
|
|
49
|
-
| DONE | 완료 | - |
|
|
50
|
-
| BLOCKED | 차단됨 | (해결 후 원래 상태) |
|
|
51
|
-
| DEFERRED | 보류/이월 | BACKLOG |
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
## 2. Task 목록
|
|
56
|
-
|
|
57
|
-
### TASK-001: [Task 이름]
|
|
58
|
-
|
|
59
|
-
| 항목 | 값 |
|
|
60
|
-
|------|-----|
|
|
61
|
-
| 상태 | BACKLOG |
|
|
62
|
-
| 우선순위 | P0 / P1 / P2 |
|
|
63
|
-
| 크기 | S / M / L |
|
|
64
|
-
| 출처 | F001 (plan.md 기능 ID) |
|
|
65
|
-
| 담당 | - |
|
|
66
|
-
| 의존성 | - |
|
|
67
|
-
|
|
68
|
-
**수용 조건 (Acceptance Criteria):**
|
|
69
|
-
- [ ] 조건 1
|
|
70
|
-
- [ ] 조건 2
|
|
71
|
-
- [ ] 조건 3
|
|
72
|
-
|
|
73
|
-
---
|
|
74
|
-
|
|
75
|
-
### TASK-002: [Task 이름]
|
|
76
|
-
|
|
77
|
-
| 항목 | 값 |
|
|
78
|
-
|------|-----|
|
|
79
|
-
| 상태 | BACKLOG |
|
|
80
|
-
| 우선순위 | P1 |
|
|
81
|
-
| 크기 | M |
|
|
82
|
-
| 출처 | F001 |
|
|
83
|
-
| 담당 | - |
|
|
84
|
-
| 의존성 | TASK-001 |
|
|
85
|
-
|
|
86
|
-
**수용 조건:**
|
|
87
|
-
- [ ] 조건 1
|
|
88
|
-
- [ ] 조건 2
|
|
89
|
-
|
|
90
|
-
---
|
|
91
|
-
|
|
92
|
-
### TASK-003: [Task 이름]
|
|
93
|
-
|
|
94
|
-
| 항목 | 값 |
|
|
95
|
-
|------|-----|
|
|
96
|
-
| 상태 | BACKLOG |
|
|
97
|
-
| 우선순위 | P1 |
|
|
98
|
-
| 크기 | S |
|
|
99
|
-
| 출처 | F002 |
|
|
100
|
-
| 담당 | - |
|
|
101
|
-
| 의존성 | - |
|
|
102
|
-
|
|
103
|
-
**수용 조건:**
|
|
104
|
-
- [ ] 조건 1
|
|
105
|
-
- [ ] 조건 2
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
## 3. 완료된 Task (Archive)
|
|
110
|
-
|
|
111
|
-
> DONE 상태 Task는 여기로 이동
|
|
112
|
-
|
|
113
|
-
### ~~TASK-000: [Task 이름]~~ ✅
|
|
114
|
-
|
|
115
|
-
| 항목 | 값 |
|
|
116
|
-
|------|-----|
|
|
117
|
-
| 상태 | DONE |
|
|
118
|
-
| 완료일 | YYYY-MM-DD |
|
|
119
|
-
| 스프린트 | Sprint 1 |
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## 4. Task 작성 규칙
|
|
124
|
-
|
|
125
|
-
### 4.1 Task 분해 원칙
|
|
126
|
-
|
|
127
|
-
- 기능 1개 = Task 1개 이상
|
|
128
|
-
- Task는 독립적으로 완료 가능해야 함
|
|
129
|
-
- 의존성이 있으면 명시
|
|
130
|
-
|
|
131
|
-
### 4.2 크기 기준
|
|
132
|
-
|
|
133
|
-
| 크기 | 설명 | 예상 시간 |
|
|
134
|
-
|:----:|------|----------|
|
|
135
|
-
| S | 단순 구현 | 1-2시간 |
|
|
136
|
-
| M | 일반 기능 | 반나절 |
|
|
137
|
-
| L | 복합 기능 | 1일+ |
|
|
138
|
-
|
|
139
|
-
### 4.3 수용 조건 규칙
|
|
140
|
-
|
|
141
|
-
- 검증 가능해야 함 (테스트 가능)
|
|
142
|
-
- 구체적이어야 함 (모호함 금지)
|
|
143
|
-
- 완료 기준이 명확해야 함
|
|
144
|
-
|
|
145
|
-
---
|
|
146
|
-
|
|
147
|
-
## 5. 상태 전환 규칙
|
|
148
|
-
|
|
149
|
-
### 5.1 전환 권한
|
|
150
|
-
|
|
151
|
-
| 전환 | 권한 |
|
|
152
|
-
|------|------|
|
|
153
|
-
| BACKLOG → READY | Planner |
|
|
154
|
-
| READY → IN_SPRINT | Manager |
|
|
155
|
-
| IN_SPRINT → IN_DEV | Developer |
|
|
156
|
-
| IN_DEV → IN_REVIEW | Developer |
|
|
157
|
-
| IN_REVIEW → IN_QA | Reviewer (PASS 시) |
|
|
158
|
-
| IN_REVIEW → IN_DEV | Reviewer (REJECT 시) |
|
|
159
|
-
| IN_QA → DONE | Manager (PASS 후 승인) |
|
|
160
|
-
| IN_QA → IN_DEV | QA (FAIL 시) |
|
|
161
|
-
| * → BLOCKED | 누구나 (사유 명시) |
|
|
162
|
-
| * → DEFERRED | Manager |
|
|
163
|
-
|
|
164
|
-
### 5.2 역전환 규칙
|
|
165
|
-
|
|
166
|
-
- REJECT/FAIL 시에만 역전환 가능
|
|
167
|
-
- 역전환 시 사유 필수 기록
|
|
168
|
-
|
|
169
|
-
---
|
|
170
|
-
|
|
171
|
-
## 6. 변경 이력
|
|
172
|
-
|
|
173
|
-
| 날짜 | Task ID | 변경 내용 | 작성자 |
|
|
174
|
-
|------|---------|----------|--------|
|
|
175
|
-
| YYYY-MM-DD | TASK-001 | 생성 | Planner |
|
|
176
|
-
| YYYY-MM-DD | TASK-001 | BACKLOG → READY | Planner |
|
|
177
|
-
| YYYY-MM-DD | TASK-001 | READY → IN_SPRINT | Manager |
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
# Current Sprint
|
|
2
|
-
|
|
3
|
-
> 현재 진행 중인 스프린트의 작업 범위
|
|
4
|
-
> Manager가 관리
|
|
5
|
-
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## 0. 스프린트 메타
|
|
9
|
-
|
|
10
|
-
| 항목 | 값 |
|
|
11
|
-
|------|-----|
|
|
12
|
-
| 스프린트 | Sprint N |
|
|
13
|
-
| 시작일 | YYYY-MM-DD |
|
|
14
|
-
| 종료 예정 | YYYY-MM-DD |
|
|
15
|
-
| 상태 | 계획 / 진행 / 완료 |
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## 1. 스프린트 목표
|
|
20
|
-
|
|
21
|
-
> 이번 스프린트에서 달성하고자 하는 것
|
|
22
|
-
|
|
23
|
-
- [목표 1]
|
|
24
|
-
- [목표 2]
|
|
25
|
-
|
|
26
|
-
---
|
|
27
|
-
|
|
28
|
-
## 2. 스프린트 Task 목록
|
|
29
|
-
|
|
30
|
-
| Task | 상태 | 담당 | 우선순위 | 크기 |
|
|
31
|
-
|------|:----:|------|:--------:|:----:|
|
|
32
|
-
| TASK-001 | IN_DEV | Developer | P0 | M |
|
|
33
|
-
| TASK-002 | IN_SPRINT | - | P1 | S |
|
|
34
|
-
| TASK-003 | IN_REVIEW | Developer | P1 | M |
|
|
35
|
-
|
|
36
|
-
### 상태 요약
|
|
37
|
-
|
|
38
|
-
| 상태 | 개수 |
|
|
39
|
-
|------|:----:|
|
|
40
|
-
| IN_SPRINT | N |
|
|
41
|
-
| IN_DEV | N |
|
|
42
|
-
| IN_REVIEW | N |
|
|
43
|
-
| IN_QA | N |
|
|
44
|
-
| DONE | N |
|
|
45
|
-
| **총합** | **N** |
|
|
46
|
-
|
|
47
|
-
---
|
|
48
|
-
|
|
49
|
-
## 3. 역할별 현재 작업
|
|
50
|
-
|
|
51
|
-
### Developer
|
|
52
|
-
|
|
53
|
-
| Task | 상태 | 비고 |
|
|
54
|
-
|------|------|------|
|
|
55
|
-
| TASK-001 | IN_DEV | 진행 중 |
|
|
56
|
-
|
|
57
|
-
### Reviewer
|
|
58
|
-
|
|
59
|
-
| Task | 상태 | 비고 |
|
|
60
|
-
|------|------|------|
|
|
61
|
-
| TASK-003 | IN_REVIEW | 리뷰 대기 |
|
|
62
|
-
|
|
63
|
-
### QA
|
|
64
|
-
|
|
65
|
-
| Task | 상태 | 비고 |
|
|
66
|
-
|------|------|------|
|
|
67
|
-
| - | - | 대기 중 |
|
|
68
|
-
|
|
69
|
-
---
|
|
70
|
-
|
|
71
|
-
## 4. 차단 사항 (Blockers)
|
|
72
|
-
|
|
73
|
-
> 현재 진행을 막는 이슈
|
|
74
|
-
|
|
75
|
-
| Task | 차단 사유 | 담당자 | 상태 |
|
|
76
|
-
|------|----------|--------|------|
|
|
77
|
-
| - | - | - | - |
|
|
78
|
-
|
|
79
|
-
---
|
|
80
|
-
|
|
81
|
-
## 5. 스프린트 중 변경 사항
|
|
82
|
-
|
|
83
|
-
> 스프린트 시작 후 발생한 변경
|
|
84
|
-
|
|
85
|
-
| 날짜 | 변경 내용 | 사유 | 승인 |
|
|
86
|
-
|------|----------|------|------|
|
|
87
|
-
| - | - | - | - |
|
|
88
|
-
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
## 6. 스프린트 회고 (완료 시)
|
|
92
|
-
|
|
93
|
-
> 스프린트 종료 후 Manager가 작성
|
|
94
|
-
|
|
95
|
-
### 완료된 Task
|
|
96
|
-
|
|
97
|
-
| Task | 설명 |
|
|
98
|
-
|------|------|
|
|
99
|
-
| TASK-XXX | [설명] |
|
|
100
|
-
|
|
101
|
-
### 미완료 Task
|
|
102
|
-
|
|
103
|
-
| Task | 사유 | 처리 |
|
|
104
|
-
|------|------|------|
|
|
105
|
-
| TASK-XXX | [사유] | 이월 / 취소 |
|
|
106
|
-
|
|
107
|
-
### 잘된 점
|
|
108
|
-
|
|
109
|
-
- [내용]
|
|
110
|
-
|
|
111
|
-
### 개선할 점
|
|
112
|
-
|
|
113
|
-
- [내용]
|
|
114
|
-
|
|
115
|
-
### 다음 스프린트 제안
|
|
116
|
-
|
|
117
|
-
- [제안]
|
|
118
|
-
|
|
119
|
-
---
|
|
120
|
-
|
|
121
|
-
## 7. 스프린트 히스토리
|
|
122
|
-
|
|
123
|
-
| 스프린트 | 기간 | 목표 | 완료 Task | 결과 |
|
|
124
|
-
|----------|------|------|:---------:|:----:|
|
|
125
|
-
| Sprint 1 | MM/DD ~ MM/DD | [목표] | N개 | ✅ / ⚠️ |
|