@decencia/ch-cli 1.8.2 → 1.11.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/agent/2t-decencia-channel-issue-coder.md +2 -1
- package/agent/2t-decencia-channel-pr-merger.md +1 -1
- package/agent/2t-decencia-channel-terraformer.md +6 -3
- package/dist/commands/db-tables.d.ts.map +1 -1
- package/dist/commands/db-tables.js +24 -3
- package/dist/commands/db-tables.js.map +1 -1
- package/dist/commands/specs.d.ts.map +1 -1
- package/dist/commands/specs.js +163 -3
- package/dist/commands/specs.js.map +1 -1
- package/dist/utils.d.ts +9 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +20 -0
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
- package/skill/2t-decencia-channel-change-manager/SKILL.md +21 -5
- package/skill/2t-decencia-channel-change-propagation-v2/SKILL.md +62 -12
- package/skill/2t-decencia-channel-cli-v2/SKILL.md +46 -25
- package/skill/2t-decencia-channel-db-schema-v2/SKILL.md +73 -11
- package/skill/2t-decencia-channel-github-issue/SKILL.md +1 -1
- package/skill/2t-decencia-channel-orchestrator/SKILL.md +1 -1
- package/skill/2t-decencia-channel-prd-v2/SKILL.md +13 -9
- package/skill/2t-decencia-channel-project-bootstrap/SKILL.md +3 -3
- package/skill/2t-decencia-channel-spec-v2/SKILL.md +202 -118
- package/skill/2t-decencia-channel-sprint-builder-v2/SKILL.md +9 -8
- package/skill/2t-decencia-channel-sprint-runner/SKILL.md +1 -1
- package/skill/2t-decencia-channel-sqa-v2/SKILL.md +25 -18
- package/skill/2t-decencia-channel-work-status-v2/SKILL.md +5 -6
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
name: 2t-decencia-channel-spec-v2
|
|
3
3
|
description: |
|
|
4
4
|
[2t][v2] 소통채널 기능명세(=Story) 작성/수정 표준.
|
|
5
|
-
Epic-Story
|
|
5
|
+
Epic-Story 구조에서 UI/Logic/DB 필드 각각의 작성 패턴, JSON 템플릿을 다룬다.
|
|
6
|
+
Story는 Task로 쪼개지 않는다 — 규모는 spec 단위 Story Point(`points`)로 매긴다.
|
|
6
7
|
Spec ID는 JIRA 스타일 사용자 지정(`LOGIN-001`) 또는 자동 생성 선택 가능 — §1.5.
|
|
7
8
|
작업 일지(workStatus)는 라이프사이클이 달라 [[2t-decencia-channel-work-status-v2]]로 분리.
|
|
8
9
|
Use when:
|
|
9
10
|
(1) spec(=Story)을 작성·수정할 때,
|
|
10
11
|
(2) UI 메타데이터·비즈니스 로직·DB 참조를 함께 다룰 때,
|
|
11
|
-
(3)
|
|
12
|
+
(3) Story 규모를 Story Point(`--points`)로 매길 때,
|
|
12
13
|
(4) 신규 spec ID를 `LOGIN-001` 같은 JIRA 스타일로 부여하고 싶을 때 (`ch specs create --id`).
|
|
13
|
-
version: 1.
|
|
14
|
+
version: 1.11.0
|
|
14
15
|
---
|
|
15
16
|
|
|
16
17
|
<!-- ch-version-gate -->
|
|
@@ -28,7 +29,7 @@ ch check
|
|
|
28
29
|
구버전 스킬·CLI 사용을 막기 위한 게이트다. 건너뛰지 말 것.
|
|
29
30
|
|
|
30
31
|
|
|
31
|
-
# 기능명세 —
|
|
32
|
+
# 기능명세 — Epic / Story 작성 표준
|
|
32
33
|
|
|
33
34
|
CLI 명령은 [[2t-decencia-channel-cli-v2]], DB 테이블 작성은 [[2t-decencia-channel-db-schema-v2]] 참조.
|
|
34
35
|
|
|
@@ -41,14 +42,16 @@ CLI 명령은 [[2t-decencia-channel-cli-v2]], DB 테이블 작성은 [[2t-decenc
|
|
|
41
42
|
| **Spec ID** | `spec.id` (string) | 식별자. **JIRA 스타일 사용자 지정 가능** (예: `LOGIN-001`). §1.5 |
|
|
42
43
|
| **Epic** | `spec.epic` (string) | 분류 라벨. 같은 에픽 spec끼리 자동 그룹핑 |
|
|
43
44
|
| **Story** | spec 자체 (1 spec = 1 story) | 명세 1건. CLI 명칭은 `specs` 그대로 |
|
|
44
|
-
| **
|
|
45
|
+
| **Story Point** | `spec.points` (number) | Story 규모 점수. Story를 Task로 쪼개지 않고 spec 단위로 매긴다. §3 |
|
|
45
46
|
| **UI** | `spec.ui` (object) | Figma·route·file·access·interactionMap |
|
|
46
|
-
| **Logic** | `spec.logic` (object) |
|
|
47
|
+
| **Logic** | `spec.logic` (object) | dataFlowScenarios·stateTransitions·businessRules |
|
|
47
48
|
| **DB** | `spec.dbTableRefs` (string[]) | db-tables 컬렉션의 테이블 ID 배열. 양방향 동기화 |
|
|
48
49
|
|
|
49
50
|
`spec.workStatus`(작업 일지)는 개발 진행 중에 누적되는 별개 라이프사이클 — [[2t-decencia-channel-work-status-v2]] 참조.
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
**Story는 Task로 쪼개지 않는다.** 규모는 spec 단위 Story Point(`points`)로 매긴다(§3). 진행률(%) 개념은 없다 — 완료 여부는 `spec.status`로 표현한다.
|
|
53
|
+
|
|
54
|
+
**완료 조건**: 별도 acceptanceCriteria 없음. **연관 SQA 통과 = 완료**. (`tasks`는 deprecated — 하위호환용으로만 남는다.)
|
|
52
55
|
|
|
53
56
|
---
|
|
54
57
|
|
|
@@ -56,12 +59,12 @@ CLI 명령은 [[2t-decencia-channel-cli-v2]], DB 테이블 작성은 [[2t-decenc
|
|
|
56
59
|
|
|
57
60
|
```bash
|
|
58
61
|
ch specs get <specId> --json > /tmp/spec.json # 1) 현재 상태 백업
|
|
59
|
-
# 편집 (
|
|
60
|
-
ch specs update <specId> --
|
|
61
|
-
ch specs get <specId> --json | jq '.
|
|
62
|
+
# 편집 (ui/logic JSON 통째 교체 위험)
|
|
63
|
+
ch specs update <specId> --logic /tmp/logic.json --no-version
|
|
64
|
+
ch specs get <specId> --json | jq '.logic' # 3) 검증
|
|
62
65
|
```
|
|
63
66
|
|
|
64
|
-
`--
|
|
67
|
+
`--ui` / `--logic` / `--work-status`는 **각 필드 자체를 통째로 교체**. 일부만 바꾸려면 반드시 전체 가져와서 편집 후 전송. (`--points`는 스칼라라 통째 교체 위험 없음 — 그 값만 바뀐다.)
|
|
65
68
|
|
|
66
69
|
---
|
|
67
70
|
|
|
@@ -71,11 +74,11 @@ ch specs get <specId> --json | jq '.tasks' # 3) 검증
|
|
|
71
74
|
|---|---|
|
|
72
75
|
| `content` | 개요 1~3줄 + 권한 요약. 짧게 유지 |
|
|
73
76
|
| `ui.*` | 화면 메타 (figma/route/file/access) + 인터랙션 맵 |
|
|
74
|
-
| `logic.
|
|
75
|
-
| `logic.stateTransitions` | 상태
|
|
77
|
+
| `logic.dataFlowScenarios` | 시나리오별 데이터 흐름. 시나리오 → 순서 있는 단계 → 단계별 테이블 참조(refs) |
|
|
78
|
+
| `logic.stateTransitions` | 화면 상태 전이 (저장되는 엔티티 상태는 db-table.stateMachines) |
|
|
76
79
|
| `logic.businessRules` | 도메인 규칙 |
|
|
77
80
|
| `dbTableRefs[]` | 참조 테이블 ID (스키마 본문은 db-tables 문서에) |
|
|
78
|
-
| `
|
|
81
|
+
| `points` | Story 규모 점수 (Story Point). Story 단위로 매긴다 (§3) |
|
|
79
82
|
|
|
80
83
|
---
|
|
81
84
|
|
|
@@ -159,73 +162,40 @@ ch db-tables list --json
|
|
|
159
162
|
|
|
160
163
|
---
|
|
161
164
|
|
|
162
|
-
## 3.
|
|
165
|
+
## 3. Story Point — spec 단위 규모
|
|
163
166
|
|
|
164
|
-
|
|
167
|
+
Story(=spec)를 Task로 쪼개지 않는다. Story 하나의 규모를 **Story Point 한 값**으로 매긴다. Sprint 용량 산정은 Σ spec.points로 한다([[2t-decencia-channel-sprint-builder-v2]]).
|
|
165
168
|
|
|
166
|
-
|
|
167
|
-
[
|
|
168
|
-
{
|
|
169
|
-
"id": "",
|
|
170
|
-
"title": "로그인 폼 UI 구현",
|
|
171
|
-
"done": false,
|
|
172
|
-
"assignee": "박준하",
|
|
173
|
-
"points": 5,
|
|
174
|
-
"description": "## 세부\n- React Hook Form + Yup\n- error UI 별도 컴포넌트\n- 참고: [Figma](https://...)"
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
"id": "",
|
|
178
|
-
"title": "Firebase Auth 연결",
|
|
179
|
-
"done": false,
|
|
180
|
-
"points": 3,
|
|
181
|
-
"description": "- signInWithEmailAndPassword 사용\n- 5회 실패 시 잠금 로직은 별도 spec"
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
"id": "a1b2c3d4",
|
|
185
|
-
"title": "유효성 검증",
|
|
186
|
-
"done": true,
|
|
187
|
-
"points": 1,
|
|
188
|
-
"completedAt": "2026-05-29T08:00:00.000Z"
|
|
189
|
-
}
|
|
190
|
-
]
|
|
191
|
-
```
|
|
169
|
+
### 3-1. 부여 방법
|
|
192
170
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
- 전체 progress(%) 자동 재계산 (`Math.round(doneCount / total * 100)`)
|
|
198
|
-
|
|
199
|
-
### 3-2. 입자 기준
|
|
171
|
+
```bash
|
|
172
|
+
ch specs create --name "로그인 화면" --device web --domain user --type 인증 --points 5
|
|
173
|
+
ch specs update <specId> --points 8 --no-version
|
|
174
|
+
```
|
|
200
175
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
| "로그인 기능 구현" (points 13+) | "로그인 폼 UI 구현" (3~5p) | "input 태그 추가" (0p) |
|
|
176
|
+
- `--points <n>` — 0 이상의 숫자. 보통 피보나치(1/2/3/5/8/13)를 쓴다.
|
|
177
|
+
- 진행률(%)·완료율은 없다. 완료 여부는 `spec.status`로 표현한다.
|
|
204
178
|
|
|
205
|
-
-
|
|
206
|
-
- 13 이상이면 쪼개라.
|
|
207
|
-
- 1 이하면 description bullet으로 합쳐라.
|
|
179
|
+
### 3-2. points 부여 가이드 (피보나치)
|
|
208
180
|
|
|
209
|
-
|
|
181
|
+
Story 하나를 끝내는 데 드는 규모를 매긴다.
|
|
210
182
|
|
|
211
|
-
| points | 의미 | 예 |
|
|
183
|
+
| points | 의미 | 예 (Story 1건) |
|
|
212
184
|
|---|---|---|
|
|
213
|
-
| 1 | 거의 자명.
|
|
214
|
-
| 2 | 1일
|
|
215
|
-
| 3 | 1~2일. 신규
|
|
216
|
-
| 5 | 2~3일.
|
|
217
|
-
| 8 | 1주~. 큰 모듈, 모르는 영역 포함 | 결제
|
|
218
|
-
| 13 | 너무 큼 —
|
|
185
|
+
| 1 | 거의 자명. 반나절 이내 | 단순 텍스트/색상 변경 화면 |
|
|
186
|
+
| 2 | 1일 이내 | 기존 화면에 필드 1개 추가 |
|
|
187
|
+
| 3 | 1~2일. 신규 화면/엔드포인트 1개 | 로그인 화면 |
|
|
188
|
+
| 5 | 2~3일. UI + API + 상태 통합 | 회원가입 플로우 |
|
|
189
|
+
| 8 | 1주~. 큰 모듈, 모르는 영역 포함 | 결제 연동 화면 |
|
|
190
|
+
| 13 | 너무 큼 — **Story를 쪼개라** (spec 분리) | — |
|
|
219
191
|
|
|
220
|
-
### 3-
|
|
192
|
+
### 3-3. 너무 크면 Story를 나눈다
|
|
221
193
|
|
|
222
|
-
|
|
223
|
-
- 들어가야 할 것: 구체 산출물, 의존성, 참고 자료(Figma/문서 링크), 엣지케이스
|
|
224
|
-
- 들어가지 말 것: 작업 일지(→ [[work-status-v2]] 참조), 완료 보고(→ done + completedAt)
|
|
194
|
+
13 이상이면 한 Sprint에 못 끝낸다. Task로 쪼개는 게 아니라 **spec 자체를 여러 Story로 나눈다**(§9).
|
|
225
195
|
|
|
226
|
-
### 3-
|
|
196
|
+
### 3-4. tasks 필드는 deprecated
|
|
227
197
|
|
|
228
|
-
|
|
198
|
+
과거의 `spec.tasks[]`(체크리스트)는 deprecated다. 신규 spec에는 쓰지 않는다. `--tasks`는 하위호환으로 계속 통과되지만 사용 시 경고가 뜬다. 기존 spec의 tasks는 그대로 두거나, 규모가 필요하면 `--points`로 대체한다.
|
|
229
199
|
|
|
230
200
|
---
|
|
231
201
|
|
|
@@ -283,54 +253,159 @@ ch db-tables list --json
|
|
|
283
253
|
|
|
284
254
|
```json
|
|
285
255
|
{
|
|
286
|
-
"
|
|
256
|
+
"dataFlowScenarios": [],
|
|
287
257
|
"stateTransitions": "...",
|
|
288
258
|
"businessRules": "..."
|
|
289
259
|
}
|
|
290
260
|
```
|
|
291
261
|
|
|
292
|
-
|
|
262
|
+
`dataFlowScenarios`는 배열(아래 §5-2). `stateTransitions`·`businessRules`는 마크다운 문자열. 필요한 것만 채운다.
|
|
293
263
|
|
|
294
|
-
### 5-2.
|
|
264
|
+
### 5-2. dataFlowScenarios — 시나리오 → 순서 있는 단계 → 참조
|
|
295
265
|
|
|
296
|
-
|
|
266
|
+
데이터 흐름을 **시나리오 단위**로 적는다. 한 시나리오는 하나의 동작 경로다(예: "로그인 성공", "비밀번호 5회 실패로 잠금").
|
|
297
267
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
268
|
+
시나리오는 두 방식 중 **하나**로 쓴다. 흐름이 선형이면 **순서 있는 단계(steps)**로 적는다. 분기(성공/실패, 조건 갈림)가 있으면 **분기 그래프(nodes/edges)**로 적는다(§5-2-1). 각 단계·노드는 그 지점이 건드리는 테이블·컬럼을 `refs`로 가리킨다. steps와 nodes가 **둘 다 비면 무효**다.
|
|
269
|
+
|
|
270
|
+
아래는 선형(steps) 예시다.
|
|
271
|
+
|
|
272
|
+
```json
|
|
273
|
+
{
|
|
274
|
+
"dataFlowScenarios": [
|
|
275
|
+
{
|
|
276
|
+
"name": "로그인 성공",
|
|
277
|
+
"description": "회원이 이메일과 비밀번호로 로그인에 성공하면 인증 토큰을 발급하고 마지막 로그인 시각을 갱신한다.",
|
|
278
|
+
"steps": [
|
|
279
|
+
{
|
|
280
|
+
"description": "클라이언트가 입력한 이메일과 비밀번호를 인증 서버로 전송한다."
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
"description": "인증 서버가 자격 증명을 확인하고 ID 토큰을 발급해 httpOnly 쿠키에 저장한다.",
|
|
284
|
+
"refs": [
|
|
285
|
+
{ "tableId": "tbl_sessions", "direction": "WRITE", "note": "세션 레코드 생성" }
|
|
286
|
+
]
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"description": "인증에 성공하면 사용자 문서의 마지막 로그인 시각을 현재 시각으로 갱신한다.",
|
|
290
|
+
"refs": [
|
|
291
|
+
{ "tableId": "tbl_users", "field": "lastLoginAt", "direction": "WRITE", "note": "로그인 성공 시 갱신" }
|
|
292
|
+
]
|
|
293
|
+
}
|
|
294
|
+
]
|
|
295
|
+
}
|
|
296
|
+
]
|
|
297
|
+
}
|
|
306
298
|
```
|
|
307
299
|
|
|
300
|
+
#### 구조
|
|
301
|
+
|
|
302
|
+
| 필드 | 타입 | 의미 |
|
|
303
|
+
|---|---|---|
|
|
304
|
+
| `dataFlowScenarios[]` | 배열 | 시나리오 목록 |
|
|
305
|
+
| `…[].name` | 문자열(필수) | 시나리오 이름. 동작 경로를 짧게 가리킨다. 예: `로그인 성공` |
|
|
306
|
+
| `…[].description` | 문자열(선택) | 시나리오 전체를 요약한 완결 문장 1~2개 |
|
|
307
|
+
| `…[].steps[]` | 배열(선택) | **순서 있는** 단계(선형 흐름). 배열 순서 = 실행 순서. nodes를 쓰면 생략 |
|
|
308
|
+
| `…steps[].description` | 문자열(필수) | 그 단계에서 일어나는 일을 적은 완결 문장 |
|
|
309
|
+
| `…steps[].refs[]` | 배열(선택) | 이 단계가 읽거나 쓰는 테이블·컬럼 참조(§6-2-1) |
|
|
310
|
+
| `…[].nodes[]` | 배열(선택) | 분기 그래프의 노드(§5-2-1). steps를 쓰면 생략 |
|
|
311
|
+
| `…[].edges[]` | 배열(선택) | 분기 그래프의 간선(§5-2-1) |
|
|
312
|
+
|
|
313
|
+
> steps와 nodes는 **둘 중 하나**만 채운다. 둘 다 비면 무효다.
|
|
314
|
+
|
|
315
|
+
#### 작성 원칙 — 완결 문장으로
|
|
316
|
+
|
|
317
|
+
- `description`은 **6하원칙(누가·언제·어디서·무엇을·어떻게·왜)을 담은 완결 문장**으로 쓴다. 필드명이나 단어만 나열하지 않는다.
|
|
318
|
+
- 단, "누가", "어디에" 같은 **소제목·라벨은 달지 않는다**. 6하원칙은 문장 안에 자연스럽게 녹인다.
|
|
319
|
+
- ❌ 나쁜 예 (단어 나열): `이메일/비밀번호 → Firebase Auth → ID Token, users.lastLoginAt WRITE`
|
|
320
|
+
- ❌ 나쁜 예 (소제목): `누가: 회원 / 무엇을: 로그인 / 어디에: users 테이블`
|
|
321
|
+
- ⭕ 좋은 예 (완결 문장): `회원이 이메일과 비밀번호로 로그인하면, 인증 서버가 자격 증명을 확인한 뒤 마지막 로그인 시각을 갱신한다.`
|
|
322
|
+
- 한 단계 = 흐름의 한 매듭. 너무 잘게 쪼개지 말고, 데이터가 한 번 이동·변형하는 단위로 묶는다.
|
|
323
|
+
- 외부 API 호출도 한 단계로 적는다(예: "결제 서버에 승인을 요청한다"). 그 단계가 우리 테이블을 건드리면 `refs`로 표시한다.
|
|
324
|
+
- 🔒 컬럼은 **이름으로만** 가리킨다 — `type`/`nullable` 등 정의는 적지 않는다(§6-2 SSOT 규칙). 기계 추적 가능한 참조는 `refs`로 구조화한다(§6-2-1).
|
|
325
|
+
|
|
326
|
+
### 5-2-1. 분기 그래프 (nodes/edges) — 분기가 있을 때
|
|
327
|
+
|
|
328
|
+
흐름이 조건에 따라 갈리면(성공/실패, 재고 있음/품절 등) steps 대신 **노드(nodes)와 간선(edges)**으로 한 시나리오 안에 분기 흐름을 한 장에 그린다.
|
|
329
|
+
|
|
330
|
+
- **node** = 흐름의 한 지점. `kind`로 종류를 나눈다: `step`(기본, 처리 단계)·`decision`(마름모, 조건 갈림)·`terminal`(종료 지점).
|
|
331
|
+
- **edge** = 노드 사이의 이동. `label`에 분기 조건을 적는다(예: `재고 있음`, `결제 실패`).
|
|
332
|
+
- decision 노드에서 edge 여러 개가 갈라져 나가고, 각 edge의 `label`이 그 갈림의 조건이다. terminal 노드에서 끝난다.
|
|
333
|
+
- 각 노드도 steps처럼 `refs`로 그 지점이 R/W하는 테이블·컬럼을 가리킨다(§6-2-1).
|
|
334
|
+
|
|
335
|
+
```json
|
|
336
|
+
{
|
|
337
|
+
"dataFlowScenarios": [
|
|
338
|
+
{
|
|
339
|
+
"name": "주문 결제 처리",
|
|
340
|
+
"description": "재고를 확인한 뒤 결제를 승인하거나 품절로 중단한다.",
|
|
341
|
+
"nodes": [
|
|
342
|
+
{ "id": "n1", "kind": "step", "description": "주문 상태와 재고를 확인한다.",
|
|
343
|
+
"refs": [{ "tableId": "tbl_orders", "field": "status", "direction": "READ" }] },
|
|
344
|
+
{ "id": "d1", "kind": "decision", "description": "재고가 있는가?" },
|
|
345
|
+
{ "id": "n2", "kind": "step", "description": "결제를 승인하고 주문을 결제완료로 바꾼다.",
|
|
346
|
+
"refs": [{ "tableId": "tbl_orders", "field": "status", "direction": "WRITE", "toState": "paid" }] },
|
|
347
|
+
{ "id": "t_ok", "kind": "terminal", "description": "결제 완료" },
|
|
348
|
+
{ "id": "t_no", "kind": "terminal", "description": "품절 안내 후 종료" }
|
|
349
|
+
],
|
|
350
|
+
"edges": [
|
|
351
|
+
{ "from": "n1", "to": "d1" },
|
|
352
|
+
{ "from": "d1", "to": "n2", "label": "재고 있음" },
|
|
353
|
+
{ "from": "d1", "to": "t_no", "label": "품절" },
|
|
354
|
+
{ "from": "n2", "to": "t_ok" }
|
|
355
|
+
]
|
|
356
|
+
}
|
|
357
|
+
]
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
필드:
|
|
362
|
+
|
|
363
|
+
| 필드 | 필수 | 의미 |
|
|
364
|
+
|---|---|---|
|
|
365
|
+
| `nodes[].id` | 필수 | 노드 식별자. 한 시나리오 안에서 유일 |
|
|
366
|
+
| `nodes[].kind` | 선택 | `step`(기본)·`decision`·`terminal` |
|
|
367
|
+
| `nodes[].description` | 필수 | 그 지점에서 일어나는 일(완결 문장) |
|
|
368
|
+
| `nodes[].refs[]` | 선택 | 그 지점이 R/W하는 테이블·컬럼(§6-2-1) |
|
|
369
|
+
| `edges[].from`/`to` | 필수 | 잇는 두 노드의 `id`. 같은 시나리오 nodes에 실재해야 함 |
|
|
370
|
+
| `edges[].label` | 선택 | 분기 조건. decision에서 갈리는 edge에 붙인다 |
|
|
371
|
+
|
|
308
372
|
규칙:
|
|
309
|
-
-
|
|
310
|
-
-
|
|
311
|
-
-
|
|
312
|
-
|
|
313
|
-
|
|
373
|
+
- **선형이면 steps, 분기가 있으면 nodes/edges. 둘 중 하나로 작성한다**(둘 다 비면 무효).
|
|
374
|
+
- decision 노드에서 나가는 edge마다 `label`로 조건을 밝힌다.
|
|
375
|
+
- 상태 컬럼을 WRITE하는 node ref는 `toState`로 목표 상태를 명시한다(§6-2-1).
|
|
376
|
+
|
|
377
|
+
### 5-3. stateTransitions — 화면 상태 전이 표
|
|
378
|
+
|
|
379
|
+
`logic.stateTransitions`는 **UI/화면의 휘발성 상태 전이 전용**이다. 화면이 떠 있는 동안만 존재하고 새로고침하면 사라지는 상태만 적는다. 예: `idle → loading → error → success`, 모달 열림/닫힘, 폼 단계.
|
|
380
|
+
|
|
381
|
+
**엔티티의 생애주기는 여기 적지 않는다.** DB에 저장되는 상태(주문 `pending→paid→delivered`, 배포 `대기→진행→완료`, 계정 `active→locked` 등)는 `db-table.stateMachines`가 SSOT다 — [[2t-decencia-channel-db-schema-v2]] §5-4. spec은 그 상태를 *이용하는* 흐름만 `dataFlowScenarios`·`businessRules`로 이름 참조한다.
|
|
382
|
+
|
|
383
|
+
두 개념은 이렇게 갈린다:
|
|
384
|
+
|
|
385
|
+
| 구분 | 어디에 | 무엇 |
|
|
386
|
+
|---|---|---|
|
|
387
|
+
| **화면 전이** (휘발성) | `spec.logic.stateTransitions` (여기, prose 표) | idle/loading/error, 모달 열림/닫힘, 폼 단계 |
|
|
388
|
+
| **엔티티 상태** (저장됨) | `db-tables.stateMachines` (구조화, SSOT) | 주문 status, 배포 phase, 계정 상태 |
|
|
314
389
|
|
|
315
|
-
|
|
390
|
+
엔티티 상태를 건드리는 흐름은 `dataFlowScenarios`의 ref로 잇는다 — 상태 컬럼을 WRITE하는 ref에 `toState`를 붙인다(§6-2-1). 같은 엔티티 전이를 stateTransitions 표에도 적으면 **중복·불일치**다. 저장되는 상태는 db-tables에만.
|
|
316
391
|
|
|
317
|
-
|
|
392
|
+
화면 상태는 머신 다이어그램 대신 표로:
|
|
318
393
|
|
|
319
394
|
```markdown
|
|
320
395
|
| 현재 상태 | 이벤트 | 다음 상태 | 사이드이펙트 |
|
|
321
396
|
|---|---|---|---|
|
|
322
397
|
| idle | submit | loading | spinner 표시 |
|
|
323
398
|
| loading | success | authenticated | 토큰 저장 + /dashboard 이동 |
|
|
324
|
-
| loading | error | error | 에러 메시지
|
|
399
|
+
| loading | error | error | 에러 메시지 표시 |
|
|
325
400
|
| error | retry | loading | spinner |
|
|
326
401
|
| authenticated | logout | idle | 토큰 제거 |
|
|
327
|
-
| error | (lockCount >= 5) | locked | 1시간 잠금 타이머 |
|
|
328
402
|
```
|
|
329
403
|
|
|
330
404
|
규칙:
|
|
331
405
|
- 컬럼: 현재 / 이벤트 / 다음 / 사이드이펙트 (이펙트 컬럼은 옵션)
|
|
332
406
|
- 가드 조건은 이벤트 컬럼에 `(조건)` 또는 별도 컬럼
|
|
333
407
|
- 진입 시점 = idle (또는 init)을 명시
|
|
408
|
+
- 저장되는 엔티티 상태(주문/계정/배포 등)는 여기 말고 `db-table.stateMachines`에
|
|
334
409
|
|
|
335
410
|
### 5-4. businessRules — 도메인 규칙 목록
|
|
336
411
|
|
|
@@ -348,7 +423,7 @@ ch db-tables list --json
|
|
|
348
423
|
- 1 규칙 = 1 불릿
|
|
349
424
|
- 임계값·기간·조건을 구체 수치로
|
|
350
425
|
- 큰 규칙은 별도 spec 분리 가능
|
|
351
|
-
- 🔒 enum/상태값의 **허용값 집합 정의는 적지 않는다**(db-tables가 SSOT) — 그 값을 *이용한 규칙*만 적는다(§6-2). 규칙이
|
|
426
|
+
- 🔒 enum/상태값의 **허용값 집합 정의는 적지 않는다**(db-tables가 SSOT) — 그 값을 *이용한 규칙*만 적는다(§6-2). 규칙이 어떤 테이블·컬럼을 검사·갱신하는지는 그 규칙이 작동하는 시나리오의 단계 `refs`로 가리킨다(§5-2, §6-2-1). businessRules에는 별도 구조적 참조 필드를 두지 않는다.
|
|
352
427
|
|
|
353
428
|
---
|
|
354
429
|
|
|
@@ -364,7 +439,7 @@ ch db-tables list --json
|
|
|
364
439
|
|
|
365
440
|
| 위치 | 내용 |
|
|
366
441
|
|---|---|
|
|
367
|
-
| **spec** | "이 화면/기능이 어느 테이블의 어떤 컬럼을 R/W하는가" — `logic.
|
|
442
|
+
| **spec** | "이 화면/기능이 어느 테이블의 어떤 컬럼을 R/W하는가" — `logic.dataFlowScenarios`의 단계 `refs`로 **이름으로 참조만** |
|
|
368
443
|
| **db-tables** | 컬럼 정의, 인덱스, 보안 규칙, 역할 매트릭스 (스키마의 SSOT) |
|
|
369
444
|
|
|
370
445
|
spec.dbTableRefs는 **포인터만**. 스키마 본문은 [[2t-decencia-channel-db-schema-v2]] 참조.
|
|
@@ -376,28 +451,36 @@ spec.dbTableRefs는 **포인터만**. 스키마 본문은 [[2t-decencia-channel-
|
|
|
376
451
|
- enum/상태값의 **정의**(허용값 집합)는 db-tables. businessRules는 그 값을 이용한 **규칙**만 적는다. (예: ❌ "status는 draft|published|archived" / ⭕ "published 상태에서만 외부 노출")
|
|
377
452
|
- 같은 정보를 db-tables와 spec 양쪽에 쓰면 스키마 변경 시 반드시 어긋난다. 의심되면 정의는 지우고 `dbTableRefs` 링크로 대체.
|
|
378
453
|
|
|
379
|
-
### 6-2-1.
|
|
454
|
+
### 6-2-1. 구조적 참조 (`steps[].refs[]` · `nodes[].refs[]`)
|
|
380
455
|
|
|
381
|
-
|
|
456
|
+
각 단계·노드가 읽거나 쓰는 테이블·컬럼을 **기계가 추적 가능하게** 가리키는 참조다. 시나리오의 `steps[]`(선형) 또는 `nodes[]`(분기 그래프) 안에 둔다(§5-2, §5-2-1).
|
|
382
457
|
|
|
383
458
|
```json
|
|
384
459
|
{
|
|
385
|
-
"
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
460
|
+
"dataFlowScenarios": [
|
|
461
|
+
{
|
|
462
|
+
"name": "로그인 성공",
|
|
463
|
+
"steps": [
|
|
464
|
+
{
|
|
465
|
+
"description": "인증에 성공하면 마지막 로그인 시각을 갱신한다.",
|
|
466
|
+
"refs": [
|
|
467
|
+
{ "tableId": "tbl_users", "field": "lastLoginAt", "direction": "WRITE", "note": "로그인 성공 시 갱신" },
|
|
468
|
+
{ "tableId": "tbl_sessions", "direction": "READ" }
|
|
469
|
+
]
|
|
470
|
+
}
|
|
471
|
+
]
|
|
472
|
+
}
|
|
393
473
|
]
|
|
394
474
|
}
|
|
395
475
|
```
|
|
396
476
|
|
|
397
|
-
- `tableId` **필수** — db-tables의 실제 id. `field` 선택(컬럼명). `direction` `READ|WRITE|READWRITE`
|
|
477
|
+
- `tableId` **필수** — db-tables의 실제 id. `field` 선택(컬럼명). `direction` 선택(`READ|WRITE|READWRITE`). `note` 선택.
|
|
478
|
+
- **상태 전이 링크 (`toState`)** — `field`가 그 테이블 db-table의 `stateMachines[].columnRef`와 같으면, 그 ref는 그 엔티티의 **상태머신 접근**이다. WRITE면 `toState`로 목표 상태를 밝힌다. `toState` 값은 그 머신 `states[].value` 중 하나여야 한다. 예: `{ "tableId": "tbl_orders", "field": "status", "direction": "WRITE", "toState": "paid" }`
|
|
479
|
+
- 상태 **정의**(허용 상태·전이)는 db-tables.stateMachines가 SSOT다. ref는 tableId+field+toState **링크만** 저장한다 — 상태 목록을 spec에 베끼지 않는다. `ch specs get`이 그 머신 요약을 읽기 시점에 인라인해 준다([[2t-decencia-channel-db-schema-v2]] §5-4).
|
|
398
480
|
- 서버가 **실존 검증**: 없는 tableId → 400, 컬럼 정의가 있는 테이블에 없는 field → 400(끊긴 참조 차단).
|
|
399
481
|
- CLI는 `--logic logic.json`으로 통째 전달(별도 플래그 없음). 웹은 spec 상세의 DB 섹션에서 칩+피커로 편집.
|
|
400
482
|
- **여전히 정의는 금지** — refs는 가리키기만 한다. type/nullable/enum 허용값 등은 db-tables.
|
|
483
|
+
- 레거시 spec의 `dataFlowRefs`/`businessRuleRefs`(logic 최상위 배열)는 하위호환으로 읽히지만, 신규·수정 시에는 해당 컬럼을 건드리는 **단계·노드의 refs**로 옮긴다.
|
|
401
484
|
|
|
402
485
|
### 6-3. 신규 spec에서 신규 테이블이 필요한 경우 순서
|
|
403
486
|
|
|
@@ -436,22 +519,22 @@ spec 작성 시점이 아니라 **개발 진행 중에 누적**되는 일지(`sp
|
|
|
436
519
|
### 8-A. 화면 명세 (가장 흔함)
|
|
437
520
|
|
|
438
521
|
- `ui` 모든 필드 채움 (figmaNodeId/route/file/access/interactionMap)
|
|
439
|
-
- `logic.
|
|
440
|
-
- `
|
|
522
|
+
- `logic.dataFlowScenarios` 필수 (화면이 다루는 주요 동작 경로별 시나리오)
|
|
523
|
+
- `points` 화면 하나의 규모 (보통 3~8)
|
|
441
524
|
- `dbTableRefs` 화면이 R/W하는 테이블 전부
|
|
442
525
|
|
|
443
526
|
### 8-B. 기능 명세 (Cron · 배치 · 외부 연동)
|
|
444
527
|
|
|
445
528
|
- `ui` 통째 생략
|
|
446
|
-
- `logic.
|
|
447
|
-
- `logic.stateTransitions` 작업 상태
|
|
448
|
-
- `
|
|
529
|
+
- `logic.dataFlowScenarios` + `logic.businessRules` 필수
|
|
530
|
+
- `logic.stateTransitions` 작업 진행 상태 전이 (저장되는 엔티티 상태는 db-table.stateMachines)
|
|
531
|
+
- `points` 트리거·처리·에러처리를 포함한 규모
|
|
449
532
|
|
|
450
533
|
### 8-C. 공통 명세 (인증 · 알림 · 업로드)
|
|
451
534
|
|
|
452
535
|
- `ui` 생략 (모듈이라 화면 없음)
|
|
453
536
|
- `logic.businessRules` 중심 (정책·제약)
|
|
454
|
-
- `
|
|
537
|
+
- `points` 인터페이스 구현·통합까지 포함한 규모
|
|
455
538
|
- 다른 spec이 이걸 import한다는 점을 `note`에 명시
|
|
456
539
|
|
|
457
540
|
---
|
|
@@ -462,9 +545,8 @@ spec 작성 시점이 아니라 **개발 진행 중에 누적**되는 일지(`sp
|
|
|
462
545
|
|
|
463
546
|
| 지표 | 한계 | 조치 |
|
|
464
547
|
|---|---|---|
|
|
465
|
-
| `
|
|
466
|
-
|
|
|
467
|
-
| `logic.dataFlow` 표 행 수 | 15+ | 화면/기능이 다중일 가능성 → 분리 |
|
|
548
|
+
| `points` (Story Point) | 13+ | sprint 1회로 못 끝남 → spec 분리 |
|
|
549
|
+
| `logic.dataFlowScenarios` 시나리오 수 | 6+ | 화면/기능이 다중일 가능성 → 분리 |
|
|
468
550
|
| `ui.interactionMap` 줄 수 | 80+ | 화면이 너무 큼 → 분리 |
|
|
469
551
|
| 같은 spec에 화면 2개 이상 | 항상 분리 | 1 화면 = 1 spec |
|
|
470
552
|
|
|
@@ -472,11 +554,13 @@ spec 작성 시점이 아니라 **개발 진행 중에 누적**되는 일지(`sp
|
|
|
472
554
|
|
|
473
555
|
## 10. 흔한 함정
|
|
474
556
|
|
|
475
|
-
- `--
|
|
557
|
+
- `--ui/--logic` JSON 통째 교체 → 일부 수정도 **반드시 get 후 편집**. (`--points`는 스칼라라 안전.)
|
|
476
558
|
- `dbTableRefs`를 손으로 JSON에 넣어도 서버가 무시 (CLI `--db-tables` 또는 웹 picker만 사용).
|
|
477
|
-
-
|
|
478
|
-
-
|
|
559
|
+
- **Story를 Task로 쪼개려 하지 말 것.** 규모는 spec 단위 `--points`로 매기고, 너무 크면 spec 자체를 나눈다(§9).
|
|
560
|
+
- 시간축 진행 메모는 workStatus(별도 스킬)에 적는다.
|
|
561
|
+
- `tasks`는 deprecated. `--tasks`는 하위호환으로만 통과된다(사용 시 경고).
|
|
479
562
|
- **스키마 정의를 logic에 베껴 넣기** — 컬럼 type/nullable/index/보안규칙/enum 허용값은 db-tables에만. spec은 이름 참조 + `dbTableRefs` 링크만(§6-2).
|
|
563
|
+
- **dataFlow를 단어 나열로 적기** — 시나리오의 단계 `description`은 화살표·필드명 나열이 아니라 완결 문장으로(§5-2). "누가/어디에" 소제목도 달지 않는다.
|
|
480
564
|
|
|
481
565
|
---
|
|
482
566
|
|
|
@@ -487,10 +571,10 @@ spec 작성 시점이 아니라 **개발 진행 중에 누적**되는 일지(`sp
|
|
|
487
571
|
1. `ch specs meta --json` → 스키마 옵션 확인
|
|
488
572
|
2. 화면이 사용하는 테이블 정리 → 없으면 `ch db-tables create` 먼저
|
|
489
573
|
3. **Spec ID 결정** (§1.5) — JIRA 스타일(`EPIC-NNN`) 사용 시 다음 번호 결정. 자동 ID로 가도 됨
|
|
490
|
-
4. `
|
|
574
|
+
4. `points`(Story Point) 산정 — Story 하나의 규모 (§3, 보통 3~8)
|
|
491
575
|
5. `ui.json` 작성 (figmaNodeId/route/file/access/interactionMap)
|
|
492
|
-
6. `logic.json` 작성 (
|
|
493
|
-
7. `ch specs create [--id LOGIN-001] --name ... --epic ... --
|
|
576
|
+
6. `logic.json` 작성 (dataFlowScenarios 필수 — 시나리오→단계→refs, 나머지 해당 시)
|
|
577
|
+
7. `ch specs create [--id LOGIN-001] --name ... --epic ... --points 5 --ui ... --logic ... --db-tables tbl_a,tbl_b`
|
|
494
578
|
8. `ch specs get <id> --json`으로 결과 검증
|
|
495
579
|
9. SQA 항목은 [[2t-decencia-channel-sqa-v2]]에 따라 별도 등록
|
|
496
580
|
10. 개발 착수 후의 진행 일지는 [[2t-decencia-channel-work-status-v2]]에서 관리
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
name: 2t-decencia-channel-sprint-builder-v2
|
|
3
3
|
description: |
|
|
4
4
|
[2t][v2] 소통채널 Sprint 구성 가이드.
|
|
5
|
-
spec의 epic·
|
|
5
|
+
spec의 epic·points(Story Point)를 활용한 스프린트 산정과 자동 배치 원칙.
|
|
6
6
|
Use when:
|
|
7
7
|
(1) sprint를 구성할 때,
|
|
8
|
-
(2)
|
|
8
|
+
(2) Σ spec.points 기반 sprint 용량 산정이 필요할 때,
|
|
9
9
|
(3) Epic·도메인·의존성을 함께 고려해 spec을 sprint에 배분할 때.
|
|
10
|
-
version: 1.
|
|
10
|
+
version: 1.11.0
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
<!-- ch-version-gate -->
|
|
@@ -30,7 +30,7 @@ ch check
|
|
|
30
30
|
## 0. Read-before-Write
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
ch specs list --json > /tmp/specs.json # 모든 spec과
|
|
33
|
+
ch specs list --json > /tmp/specs.json # 모든 spec과 points 확보
|
|
34
34
|
ch sprints list --json > /tmp/sprints.json
|
|
35
35
|
# 분석 후
|
|
36
36
|
ch sprints create --name "Sprint 1" --start 2026-06-01 --end 2026-06-14 --specs <id1>,<id2>
|
|
@@ -39,8 +39,8 @@ ch sprints create --name "Sprint 1" --start 2026-06-01 --end 2026-06-14 --specs
|
|
|
39
39
|
## 1. 산정 절차
|
|
40
40
|
|
|
41
41
|
1. 모든 spec을 `ch specs list --json`로 수집.
|
|
42
|
-
2. 각 spec의 `epic`, `
|
|
43
|
-
3.
|
|
42
|
+
2. 각 spec의 `epic`, `points`(Story Point) 추출.
|
|
43
|
+
3. Sprint 용량 = **Σ spec.points**. spec 하나가 규모 단위이며 별도 하위 합산은 없다. `points` 미지정 spec은 산정 전에 [[2t-decencia-channel-spec-v2]] §3으로 부여한다(미지정은 0이 아니라 누락으로 본다).
|
|
44
44
|
4. Sprint 용량(예: 30 SP / 2주) 설정 후, 다음 순서로 채우기:
|
|
45
45
|
- **의존성** (PRD/spec.dbTableRefs 분석): DB 테이블 신규 생성 spec → 그걸 참조하는 spec 순.
|
|
46
46
|
- **Epic 단위 묶음**: 같은 Epic은 한 sprint에 몰아주는 게 컨텍스트 비용 ↓.
|
|
@@ -72,6 +72,7 @@ Sprint 1 (06-01 ~ 06-14, 용량 30 SP)
|
|
|
72
72
|
|
|
73
73
|
## 4. 흔한 함정
|
|
74
74
|
|
|
75
|
-
- points
|
|
76
|
-
- Sprint에
|
|
75
|
+
- `points` 미지정 spec이 많으면 산정 부정확 → spec마다 `--points` 부여(피보나치 1/2/3/5/8/13).
|
|
76
|
+
- Sprint에 담은 spec의 `points`를 바꾸면 sprint SP 합이 변동 — sprint 재산정 필요.
|
|
77
|
+
- Story를 Task로 쪼개 합산하지 않는다 — 규모는 spec 단위 `points` 하나다([[2t-decencia-channel-spec-v2]] §3).
|
|
77
78
|
- Sprint와 spec.workStatus는 별개. Sprint 진행 상황은 sprint 자체 status로, spec별 메모는 workStatus로.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: 2t-decencia-channel-sprint-runner
|
|
3
3
|
description: 특정 Sprint를 끝까지 실행(run)하는 오케스트레이터 스킬. Sprint의 spec을 시드로 이슈 목록을 분해·발행(github-issue)하고, GitHub milestone에 묶인 이슈를 상태별로 분류해 issue-coder(병렬)·pr-merger(직렬)로 전진시킨 뒤, 사람확인이 필요한 곳에서 멈춘다. 사람이 확인·체크 후 재호출하면 멱등하게 이어간다(반복 루프). "스프린트 돌려줘"·"sprint N 실행"·"이 스프린트 개발 진행해줘" 류 요청 시.
|
|
4
4
|
allowed-tools: Skill Agent Read Bash(ch:*) Bash(gh:*) Bash(git:*)
|
|
5
|
-
version: 1.
|
|
5
|
+
version: 1.11.0
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
<!-- ch-version-gate -->
|